From df4214fa314c771ea20cddcda64568b5f7b08722 Mon Sep 17 00:00:00 2001 From: Ocasta Date: Thu, 9 Feb 2023 17:41:03 -0600 Subject: [PATCH 01/13] first pass at refactoring executor --- pkg/service/transaction.go | 248 +++++++++++++++++++++---------------- 1 file changed, 141 insertions(+), 107 deletions(-) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index f305a5b3..a2213578 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -89,201 +89,226 @@ func (t transaction) Quote(d model.TransactionRequest) (model.ExecutionRequest, return res, nil } -func (t transaction) Execute(e model.ExecutionRequest, userId string, deviceId string) (model.TransactionReceipt, error) { - res := model.TransactionReceipt{} +func (t transaction) Execute(e model.ExecutionRequest, userId string, deviceId string) (res model.TransactionReceipt, err error) { t.getStringInstrumentsAndUserId() + executor := NewExecutor() + + s := transactionSetupData{executionRequest: &e, userId: &userId, deviceId: &deviceId} - user, err := t.repos.User.GetById(userId) + s, err = t.transactionSetup(s, executor) if err != nil { return res, common.StringError(err) } - if user.ID != userId { - return res, common.StringError(errors.New("not logged in")) + + s, err = t.safetyCheck(s) + if err != nil { + return res, common.StringError(err) } - // Pull chain info needed for execution from repository - chain, err := ChainInfo(uint64(e.ChainID), t.repos.Network, t.repos.Asset) + // Send request to the blockchain and update model status, hash, transaction amount + txID, value, err := t.initiateTransaction(*s.executor, e, *s.processingFeeAsset, s.transactionModel.ID, userId) if err != nil { return res, common.StringError(err) } - // Create new Tx in repository, populate it with known info - db, err := t.repos.Transaction.Create(model.Transaction{Status: "Created", NetworkID: chain.UUID, DeviceID: deviceId, PlatformID: t.ids.StringPlatformId}) + status := "Transaction Initiated" + txAmount := value.String() + updateDB := &model.TransactionUpdates{Status: &status, TransactionHash: &txID, TransactionAmount: &txAmount} + err = t.repos.Transaction.Update(s.transactionModel.ID, updateDB) if err != nil { return res, common.StringError(err) } + // this Executor will not exist in scope of postProcess + executor.Close() + + // Send required information to new thread and return TxID to the endpoint + post := postProcessRequest{ + TxID: txID, + Chain: *s.chain, + Authorization: *s.cardAuthorization, + UserAddress: e.UserAddress, + CumulativeValue: value, + Quote: e.Quote, + TxDBID: s.transactionModel.ID, + processingFeeAsset: *s.processingFeeAsset, + preBalance: *s.preBalance, + userId: userId, + recipientWalletId: *s.recipientWalletId, + } + go t.postProcess(post) + + return model.TransactionReceipt{TxID: txID, TxURL: s.chain.Explorer + "/tx/" + txID}, nil +} + +type transactionSetupData struct { + userId *string + chainId *int + deviceId *string + executor *Executor + processingFeeAsset *model.Asset + transactionModel *model.Transaction + chain *Chain + executionRequest *model.ExecutionRequest + cardAuthorization *AuthorizedCharge + preBalance *float64 + recipientWalletId *string +} + +func (t transaction) transactionSetup(s transactionSetupData, executor Executor) (transactionSetupData, error) { + // get user object + _, err := t.repos.User.GetById(*s.userId) + if err != nil { + return s, common.StringError(err) + } + + // Pull chain info needed for execution from repository + chain, err := ChainInfo(uint64(*s.chainId), t.repos.Network, t.repos.Asset) + s.chain = &chain + if err != nil { + return s, common.StringError(err) + } + + // Create new Tx in repository, populate it with known info + transactionModel, err := t.repos.Transaction.Create(model.Transaction{Status: "Created", NetworkID: chain.UUID, DeviceID: *s.deviceId, PlatformID: t.ids.StringPlatformId}) + s.transactionModel = &transactionModel + if err != nil { + return s, common.StringError(err) + } + updateDB := &model.TransactionUpdates{} - processingFeeAsset, err := t.populateInitialTxModelData(e, updateDB) + processingFeeAsset, err := t.populateInitialTxModelData(*s.executionRequest, updateDB) + s.processingFeeAsset = &processingFeeAsset if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } - err = t.repos.Transaction.Update(db.ID, updateDB) + err = t.repos.Transaction.Update(transactionModel.ID, updateDB) if err != nil { fmt.Printf("\nERROR = %+v", err) - return res, common.StringError(err) + return s, common.StringError(err) } // Dial the RPC and update model status - executor := NewExecutor() err = executor.Initialize(chain.RPC) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } - status := "RPC Dialed" - updateDB.Status = &status - err = t.repos.Transaction.Update(db.ID, updateDB) + + err = t.updateTransactionStatus("RPC Dialed", transactionModel.ID) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } + return s, err +} + +func (t transaction) safetyCheck(s transactionSetupData) (transactionSetupData, error) { + // Test the Tx and update model status - estimateUSD, estimateETH, err := t.testTransaction(executor, e.TransactionRequest, chain, false) + estimateUSD, estimateETH, err := t.testTransaction(*s.executor, s.executionRequest.TransactionRequest, *s.chain, false) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } - status = "Tested and Estimated" - updateDB.Status = &status - err = t.repos.Transaction.Update(db.ID, updateDB) + err = t.updateTransactionStatus("Tested and Estimated", s.transactionModel.ID) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } // Verify the Quote and update model status - _, err = verifyQuote(e, estimateUSD) + _, err = verifyQuote(*s.executionRequest, estimateUSD) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } - status = "Quote Verified" - updateDB.Status = &status - err = t.repos.Transaction.Update(db.ID, updateDB) + err = t.updateTransactionStatus("Quote Verified", s.transactionModel.ID) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } // Get current balance of primary token preBalance, err := executor.GetBalance() + s.preBalance = &preBalance if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } if preBalance < estimateETH { - msg := fmt.Sprintf("STRING-API: %s balance is too low to execute %.2f transaction at %.2f", chain.OwlracleName, estimateETH, preBalance) + msg := fmt.Sprintf("STRING-API: %s balance is too low to execute %.2f transaction at %.2f", s.chain.OwlracleName, estimateETH, preBalance) MessageStaff(msg) - return res, common.StringError(errors.New("hot wallet ETH balance too low")) + return s, common.StringError(errors.New("hot wallet ETH balance too low")) } // Authorize quoted cost on end-user CC and update model status - cardAuthorization, err := t.authCard(e.UserAddress, e.CardToken, e.TotalUSD, processingFeeAsset, db.ID, userId) + cardAuthorization, err := t.authCard(s.executionRequest.UserAddress, s.executionRequest.CardToken, s.executionRequest.TotalUSD, *s.processingFeeAsset, s.transactionModel.ID, *s.userId) + s.cardAuthorization = &cardAuthorization if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } - - status = "Card " + cardAuthorization.Status - updateDB.Status = &status - err = t.repos.Transaction.Update(db.ID, updateDB) + err = t.updateTransactionStatus("Card "+cardAuthorization.Status, s.transactionModel.ID) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } - recipientWalletId, err := t.addWalletInstrumentIdIfNew(e.UserAddress, user.ID) + recipientWalletId, err := t.addWalletInstrumentIdIfNew(s.executionRequest.UserAddress, *s.userId) + s.recipientWalletId = *&s.recipientWalletId if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } // TODO: Determine the output of the transaction (destination leg) with Tracers destinationLeg := model.TxLeg{ - Timestamp: time.Now(), // Required by the db. Should be updated when the tx occurs - Amount: "0", // Required by Unit21. The amount of the asset received by the user - Value: "0", // Default to '0'. The value of the asset received by the user - AssetID: chain.GasTokenID, // Required by the db. the asset received by the user - UserID: user.ID, // the user who received the asset - InstrumentID: recipientWalletId, // Required by the db. the instrument which received the asset (wallet usually) + Timestamp: time.Now(), // Required by the db. Should be updated when the tx occurs + Amount: "0", // Required by Unit21. The amount of the asset received by the user + Value: "0", // Default to '0'. The value of the asset received by the user + AssetID: s.chain.GasTokenID, // Required by the db. the asset received by the user + UserID: *s.userId, // the user who received the asset + InstrumentID: recipientWalletId, // Required by the db. the instrument which received the asset (wallet usually) } destinationLeg, err = t.repos.TxLeg.Create(destinationLeg) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } txLeg := model.TransactionUpdates{DestinationTxLegID: &destinationLeg.ID} - err = t.repos.Transaction.Update(db.ID, txLeg) + err = t.repos.Transaction.Update(s.transactionModel.ID, txLeg) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } if !cardAuthorization.Approved { - err := t.unit21CreateTransaction(db.ID) + err := t.unit21CreateTransaction(s.transactionModel.ID) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } - return res, common.StringError(common.StringError(errors.New("payment: Authorization Declined by Checkout"))) + return s, common.StringError(errors.New("payment: Authorization Declined by Checkout")) } // Validate Transaction through Real Time Rules engine - u21auth, err := t.unit21Evaluate(db.ID) + u21auth, err := t.unit21Evaluate(s.transactionModel.ID) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } if !u21auth { - status = "Failed" - updateDB.Status = &status - err = t.repos.Transaction.Update(db.ID, updateDB) + err = t.updateTransactionStatus("Failed", s.transactionModel.ID) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } - err = t.unit21CreateTransaction(db.ID) + err = t.unit21CreateTransaction(s.transactionModel.ID) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } - return res, common.StringError(errors.New("risk: Transaction Failed Unit21 Real Time Rules Evaluation")) - } - status = "Unit21 Authorized" - updateDB.Status = &status - err = t.repos.Transaction.Update(db.ID, updateDB) - if err != nil { - return res, common.StringError(err) - } - - // Send request to the blockchain and update model status, hash, transaction amount - txID, value, err := t.initiateTransaction(executor, e, processingFeeAsset, db.ID, userId) - if err != nil { - return res, common.StringError(err) + return s, common.StringError(errors.New("risk: Transaction Failed Unit21 Real Time Rules Evaluation")) } - status = "Transaction Initiated" - updateDB.Status = &status - updateDB.TransactionHash = &txID - txAmount := value.String() - updateDB.TransactionAmount = &txAmount - err = t.repos.Transaction.Update(db.ID, updateDB) + err = t.updateTransactionStatus("Unit21 Authorized", s.transactionModel.ID) if err != nil { - return res, common.StringError(err) + return s, common.StringError(err) } - // this Executor will not exist in scope of postProcess - executor.Close() - - // Send required information to new thread and return TxID to the endpoint - post := postProcessRequest{ - TxID: txID, - Chain: chain, - Authorization: cardAuthorization, - UserAddress: e.UserAddress, - CumulativeValue: value, - Quote: e.Quote, - TxDBID: db.ID, - processingFeeAsset: processingFeeAsset, - preBalance: preBalance, - userId: userId, - recipientWalletId: recipientWalletId, - } - go t.postProcess(post) - - return model.TransactionReceipt{TxID: txID, TxURL: chain.Explorer + "/tx/" + txID}, nil + return s, nil } func (t *transaction) getStringInstrumentsAndUserId() { @@ -293,10 +318,10 @@ func (t *transaction) getStringInstrumentsAndUserId() { func (t transaction) populateInitialTxModelData(e model.ExecutionRequest, m *model.TransactionUpdates) (model.Asset, error) { txType := "fiat-to-crypto" m.Type = &txType - // TODO populate db.Tags with key-val pairs for Unit21 - // TODO populate db.DeviceID with info from fingerprint - // TODO populate db.IPAddress with info from fingerprint - // TODO populate db.PlatformID with UUID of customer + // TODO populate transactionModel.Tags with key-val pairs for Unit21 + // TODO populate transactionModel.DeviceID with info from fingerprint + // TODO populate transactionModel.IPAddress with info from fingerprint + // TODO populate transactionModel.PlatformID with UUID of customer // bytes, err := json.Marshal() contractParams := pq.StringArray(e.CxParams) @@ -813,5 +838,14 @@ func (t transaction) unit21Evaluate(transactionId string) (evaluation bool, err } return +} + +func (t transaction) updateTransactionStatus(transactionId string, status string) (err error) { + updateDB := &model.TransactionUpdates{Status: &status} + err = t.repos.Transaction.Update(transactionId, updateDB) + if err != nil { + return common.StringError(err) + } + return err } From 0dff507db6408af5ed3470001e6526fa14c9b2d3 Mon Sep 17 00:00:00 2001 From: Ocasta Date: Thu, 9 Feb 2023 18:05:11 -0600 Subject: [PATCH 02/13] abstract card payment steps --- pkg/service/transaction.go | 175 +++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 83 deletions(-) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index a2213578..e467abc0 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -91,15 +91,16 @@ func (t transaction) Quote(d model.TransactionRequest) (model.ExecutionRequest, func (t transaction) Execute(e model.ExecutionRequest, userId string, deviceId string) (res model.TransactionReceipt, err error) { t.getStringInstrumentsAndUserId() - executor := NewExecutor() s := transactionSetupData{executionRequest: &e, userId: &userId, deviceId: &deviceId} - s, err = t.transactionSetup(s, executor) + // Pre-flight transaction setup + s, err = t.transactionSetup(s) if err != nil { return res, common.StringError(err) } + // Run safety checks s, err = t.safetyCheck(s) if err != nil { return res, common.StringError(err) @@ -120,6 +121,7 @@ func (t transaction) Execute(e model.ExecutionRequest, userId string, deviceId s } // this Executor will not exist in scope of postProcess + executor := *s.executor // needed to keep the compiler from complaining executor.Close() // Send required information to new thread and return TxID to the endpoint @@ -155,7 +157,7 @@ type transactionSetupData struct { recipientWalletId *string } -func (t transaction) transactionSetup(s transactionSetupData, executor Executor) (transactionSetupData, error) { +func (t transaction) transactionSetup(s transactionSetupData) (transactionSetupData, error) { // get user object _, err := t.repos.User.GetById(*s.userId) if err != nil { @@ -189,6 +191,7 @@ func (t transaction) transactionSetup(s transactionSetupData, executor Executor) } // Dial the RPC and update model status + executor := NewExecutor() err = executor.Initialize(chain.RPC) if err != nil { return s, common.StringError(err) @@ -237,73 +240,13 @@ func (t transaction) safetyCheck(s transactionSetupData) (transactionSetupData, } // Authorize quoted cost on end-user CC and update model status - cardAuthorization, err := t.authCard(s.executionRequest.UserAddress, s.executionRequest.CardToken, s.executionRequest.TotalUSD, *s.processingFeeAsset, s.transactionModel.ID, *s.userId) - s.cardAuthorization = &cardAuthorization - if err != nil { - return s, common.StringError(err) - } - err = t.updateTransactionStatus("Card "+cardAuthorization.Status, s.transactionModel.ID) - if err != nil { - return s, common.StringError(err) - } - - recipientWalletId, err := t.addWalletInstrumentIdIfNew(s.executionRequest.UserAddress, *s.userId) - s.recipientWalletId = *&s.recipientWalletId - if err != nil { - return s, common.StringError(err) - } - - // TODO: Determine the output of the transaction (destination leg) with Tracers - destinationLeg := model.TxLeg{ - Timestamp: time.Now(), // Required by the db. Should be updated when the tx occurs - Amount: "0", // Required by Unit21. The amount of the asset received by the user - Value: "0", // Default to '0'. The value of the asset received by the user - AssetID: s.chain.GasTokenID, // Required by the db. the asset received by the user - UserID: *s.userId, // the user who received the asset - InstrumentID: recipientWalletId, // Required by the db. the instrument which received the asset (wallet usually) - } - - destinationLeg, err = t.repos.TxLeg.Create(destinationLeg) - if err != nil { - return s, common.StringError(err) - } - - txLeg := model.TransactionUpdates{DestinationTxLegID: &destinationLeg.ID} - - err = t.repos.Transaction.Update(s.transactionModel.ID, txLeg) + s, err = t.authCard(s) if err != nil { return s, common.StringError(err) } - if !cardAuthorization.Approved { - err := t.unit21CreateTransaction(s.transactionModel.ID) - if err != nil { - return s, common.StringError(err) - } - - return s, common.StringError(errors.New("payment: Authorization Declined by Checkout")) - } - // Validate Transaction through Real Time Rules engine - u21auth, err := t.unit21Evaluate(s.transactionModel.ID) - if err != nil { - return s, common.StringError(err) - } - - if !u21auth { - err = t.updateTransactionStatus("Failed", s.transactionModel.ID) - if err != nil { - return s, common.StringError(err) - } - - err = t.unit21CreateTransaction(s.transactionModel.ID) - if err != nil { - return s, common.StringError(err) - } - - return s, common.StringError(errors.New("risk: Transaction Failed Unit21 Real Time Rules Evaluation")) - } - err = t.updateTransactionStatus("Unit21 Authorized", s.transactionModel.ID) + err = t.unit21Evaluate(s.transactionModel.ID) if err != nil { return s, common.StringError(err) } @@ -449,40 +392,90 @@ func (t transaction) addWalletInstrumentIdIfNew(address string, id string) (stri return instrument.ID, nil } -func (t transaction) authCard(userWallet string, cardToken string, usd float64, chargeAsset model.Asset, dbID string, userId string) (AuthorizedCharge, error) { +func (t transaction) authCard(s transactionSetupData) (transactionSetupData, error) { + // userWallet string, cardToken string, usd float64, chargeAsset model.Asset, dbID string, userId string + //s.executionRequest.UserAddress, s.executionRequest.CardToken, s.executionRequest.TotalUSD, *s.processingFeeAsset, s.transactionModel.ID, *s.userId // auth their card - auth, err := AuthorizeCharge(usd, userWallet, cardToken) + + auth, err := AuthorizeCharge(s.executionRequest.TotalUSD, s.executionRequest.UserAddress, s.executionRequest.CardToken) if err != nil { - return auth, common.StringError(err) + return s, common.StringError(err) } // Add Checkout Instrument ID to our DB if it's not there already and associate it with the user - instrumentId, err := t.addCardInstrumentIdIfNew(auth.CheckoutFingerprint, userId, auth.Last4, auth.CardType) + instrumentId, err := t.addCardInstrumentIdIfNew(auth.CheckoutFingerprint, *s.userId, auth.Last4, auth.CardType) if err != nil { - return auth, common.StringError(err) + return s, common.StringError(err) } // Create Origin Tx leg - usdWei := floatToFixedString(usd, int(chargeAsset.Decimals)) + usdWei := floatToFixedString(s.executionRequest.TotalUSD, int(s.processingFeeAsset.Decimals)) origin := model.TxLeg{ Timestamp: time.Now(), Amount: usdWei, Value: usdWei, - AssetID: chargeAsset.ID, - UserID: userId, + AssetID: s.processingFeeAsset.ID, + UserID: *s.userId, InstrumentID: instrumentId, } origin, err = t.repos.TxLeg.Create(origin) if err != nil { - return auth, common.StringError(err) + return s, common.StringError(err) + } + txLegUpdates := model.TransactionUpdates{OriginTxLegID: &origin.ID} + err = t.repos.Transaction.Update(s.transactionModel.ID, txLegUpdates) + if err != nil { + return s, common.StringError(err) + } + + /// + s.cardAuthorization = &auth + if err != nil { + return s, common.StringError(err) } - txLeg := model.TransactionUpdates{OriginTxLegID: &origin.ID} - err = t.repos.Transaction.Update(dbID, txLeg) + err = t.updateTransactionStatus("Card "+auth.Status, s.transactionModel.ID) if err != nil { - return auth, common.StringError(err) + return s, common.StringError(err) + } + + recipientWalletId, err := t.addWalletInstrumentIdIfNew(s.executionRequest.UserAddress, *s.userId) + s.recipientWalletId = *&s.recipientWalletId + if err != nil { + return s, common.StringError(err) } - return auth, nil + // TODO: Determine the output of the transaction (destination leg) with Tracers + destinationLeg := model.TxLeg{ + Timestamp: time.Now(), // Required by the db. Should be updated when the tx occurs + Amount: "0", // Required by Unit21. The amount of the asset received by the user + Value: "0", // Default to '0'. The value of the asset received by the user + AssetID: s.chain.GasTokenID, // Required by the db. the asset received by the user + UserID: *s.userId, // the user who received the asset + InstrumentID: recipientWalletId, // Required by the db. the instrument which received the asset (wallet usually) + } + + destinationLeg, err = t.repos.TxLeg.Create(destinationLeg) + if err != nil { + return s, common.StringError(err) + } + + txLegUpdates = model.TransactionUpdates{DestinationTxLegID: &destinationLeg.ID} + + err = t.repos.Transaction.Update(s.transactionModel.ID, txLegUpdates) + if err != nil { + return s, common.StringError(err) + } + + if !auth.Approved { + err := t.unit21CreateTransaction(s.transactionModel.ID) + if err != nil { + return s, common.StringError(err) + } + + return s, common.StringError(errors.New("payment: Authorization Declined by Checkout")) + } + + return s, nil } func (t transaction) initiateTransaction(executor Executor, e model.ExecutionRequest, chargeAsset model.Asset, txUUID string, userId string) (string, *big.Int, error) { @@ -816,12 +809,12 @@ func (t transaction) unit21CreateTransaction(transactionId string) (err error) { return } -func (t transaction) unit21Evaluate(transactionId string) (evaluation bool, err error) { +func (t transaction) unit21Evaluate(transactionId string) (err error) { //Check transaction in Unit21 txModel, err := t.repos.Transaction.GetById(transactionId) if err != nil { log.Printf("Error getting tx model in Unit21 in Tx Evaluate: %s", err) - return evaluation, common.StringError(err) + return common.StringError(err) } u21Repo := unit21.TransactionRepo{ @@ -831,13 +824,29 @@ func (t transaction) unit21Evaluate(transactionId string) (evaluation bool, err } u21Tx := unit21.NewTransaction(u21Repo) - evaluation, err = u21Tx.Evaluate(txModel) + evaluation, err := u21Tx.Evaluate(txModel) if err != nil { log.Printf("Error evaluating transaction in Unit21: %s", err) - return evaluation, common.StringError(err) + return common.StringError(err) } - return + if !evaluation { + err = t.updateTransactionStatus("Failed", transactionId) + if err != nil { + return common.StringError(err) + } + + err = t.unit21CreateTransaction(transactionId) + if err != nil { + return common.StringError(err) + } + + return common.StringError(errors.New("risk: Transaction Failed Unit21 Real Time Rules Evaluation")) + } + err = t.updateTransactionStatus("Unit21 Authorized", transactionId) + if err != nil { + return common.StringError(err) + } } func (t transaction) updateTransactionStatus(transactionId string, status string) (err error) { From 8d5e951839a655e98a50dc0443ef5df9b6b69dd3 Mon Sep 17 00:00:00 2001 From: Ocasta Date: Fri, 10 Feb 2023 14:10:01 -0600 Subject: [PATCH 03/13] more cleanup --- pkg/service/transaction.go | 338 +++++++++++++++++-------------------- 1 file changed, 156 insertions(+), 182 deletions(-) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index e467abc0..18b2abe0 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -50,6 +50,23 @@ type transaction struct { ids InternalIds } +type transactionProcessingData struct { + userId *string + chainId *int + deviceId *string + executor *Executor + processingFeeAsset *model.Asset + transactionModel *model.Transaction + chain *Chain + executionRequest *model.ExecutionRequest + cardAuthorization *AuthorizedCharge + preBalance *float64 + recipientWalletId *string + txId *string + cumulativeValue *big.Int + trueGas *uint64 +} + func NewTransaction(repos repository.Repositories, redis store.RedisStore) Transaction { return &transaction{repos: repos, redis: redis} } @@ -92,72 +109,37 @@ func (t transaction) Quote(d model.TransactionRequest) (model.ExecutionRequest, func (t transaction) Execute(e model.ExecutionRequest, userId string, deviceId string) (res model.TransactionReceipt, err error) { t.getStringInstrumentsAndUserId() - s := transactionSetupData{executionRequest: &e, userId: &userId, deviceId: &deviceId} + p := transactionProcessingData{executionRequest: &e, userId: &userId, deviceId: &deviceId} // Pre-flight transaction setup - s, err = t.transactionSetup(s) + p, err = t.transactionSetup(p) if err != nil { return res, common.StringError(err) } // Run safety checks - s, err = t.safetyCheck(s) + p, err = t.safetyCheck(p) if err != nil { return res, common.StringError(err) } // Send request to the blockchain and update model status, hash, transaction amount - txID, value, err := t.initiateTransaction(*s.executor, e, *s.processingFeeAsset, s.transactionModel.ID, userId) - if err != nil { - return res, common.StringError(err) - } - - status := "Transaction Initiated" - txAmount := value.String() - updateDB := &model.TransactionUpdates{Status: &status, TransactionHash: &txID, TransactionAmount: &txAmount} - err = t.repos.Transaction.Update(s.transactionModel.ID, updateDB) + p, err = t.initiateTransaction(p) if err != nil { return res, common.StringError(err) } // this Executor will not exist in scope of postProcess - executor := *s.executor // needed to keep the compiler from complaining + executor := *p.executor // needed to keep the compiler from complaining executor.Close() - // Send required information to new thread and return TxID to the endpoint - post := postProcessRequest{ - TxID: txID, - Chain: *s.chain, - Authorization: *s.cardAuthorization, - UserAddress: e.UserAddress, - CumulativeValue: value, - Quote: e.Quote, - TxDBID: s.transactionModel.ID, - processingFeeAsset: *s.processingFeeAsset, - preBalance: *s.preBalance, - userId: userId, - recipientWalletId: *s.recipientWalletId, - } - go t.postProcess(post) - - return model.TransactionReceipt{TxID: txID, TxURL: s.chain.Explorer + "/tx/" + txID}, nil -} + // Send required information to new thread and return txId to the endpoint + go t.postProcess(p) -type transactionSetupData struct { - userId *string - chainId *int - deviceId *string - executor *Executor - processingFeeAsset *model.Asset - transactionModel *model.Transaction - chain *Chain - executionRequest *model.ExecutionRequest - cardAuthorization *AuthorizedCharge - preBalance *float64 - recipientWalletId *string + return model.TransactionReceipt{TxID: *p.txId, TxURL: p.chain.Explorer + "/tx/" + *p.txId}, nil } -func (t transaction) transactionSetup(s transactionSetupData) (transactionSetupData, error) { +func (t transaction) transactionSetup(s transactionProcessingData) (transactionProcessingData, error) { // get user object _, err := t.repos.User.GetById(*s.userId) if err != nil { @@ -205,8 +187,7 @@ func (t transaction) transactionSetup(s transactionSetupData) (transactionSetupD return s, err } -func (t transaction) safetyCheck(s transactionSetupData) (transactionSetupData, error) { - +func (t transaction) safetyCheck(s transactionProcessingData) (transactionProcessingData, error) { // Test the Tx and update model status estimateUSD, estimateETH, err := t.testTransaction(*s.executor, s.executionRequest.TransactionRequest, *s.chain, false) if err != nil { @@ -254,10 +235,6 @@ func (t transaction) safetyCheck(s transactionSetupData) (transactionSetupData, return s, nil } -func (t *transaction) getStringInstrumentsAndUserId() { - t.ids = GetStringIdsFromEnv() -} - func (t transaction) populateInitialTxModelData(e model.ExecutionRequest, m *model.TransactionUpdates) (model.Asset, error) { txType := "fiat-to-crypto" m.Type = &txType @@ -392,11 +369,8 @@ func (t transaction) addWalletInstrumentIdIfNew(address string, id string) (stri return instrument.ID, nil } -func (t transaction) authCard(s transactionSetupData) (transactionSetupData, error) { - // userWallet string, cardToken string, usd float64, chargeAsset model.Asset, dbID string, userId string - //s.executionRequest.UserAddress, s.executionRequest.CardToken, s.executionRequest.TotalUSD, *s.processingFeeAsset, s.transactionModel.ID, *s.userId +func (t transaction) authCard(s transactionProcessingData) (transactionProcessingData, error) { // auth their card - auth, err := AuthorizeCharge(s.executionRequest.TotalUSD, s.executionRequest.UserAddress, s.executionRequest.CardToken) if err != nil { return s, common.StringError(err) @@ -478,156 +452,80 @@ func (t transaction) authCard(s transactionSetupData) (transactionSetupData, err return s, nil } -func (t transaction) initiateTransaction(executor Executor, e model.ExecutionRequest, chargeAsset model.Asset, txUUID string, userId string) (string, *big.Int, error) { +func (t transaction) initiateTransaction(p transactionProcessingData) (transactionProcessingData, error) { call := ContractCall{ - CxAddr: e.CxAddr, - CxFunc: e.CxFunc, - CxReturn: e.CxReturn, - CxParams: e.CxParams, - TxValue: e.TxValue, - TxGasLimit: e.TxGasLimit, - } + CxAddr: p.executionRequest.CxAddr, + CxFunc: p.executionRequest.CxFunc, + CxReturn: p.executionRequest.CxReturn, + CxParams: p.executionRequest.CxParams, + TxValue: p.executionRequest.TxValue, + TxGasLimit: p.executionRequest.TxGasLimit, + } + executor := *p.executor txID, value, err := executor.Initiate(call) if err != nil { - return "", nil, common.StringError(err) + return p, common.StringError(err) } + p.txId = &txID // Create Response Tx leg eth := common.WeiToEther(value) wei := floatToFixedString(eth, 18) - usd := floatToFixedString(e.TotalUSD, int(chargeAsset.Decimals)) + usd := floatToFixedString(e.TotalUSD, int(p.processingFeeAsset.Decimals)) responseLeg := model.TxLeg{ Timestamp: time.Now(), Amount: wei, Value: usd, - AssetID: chargeAsset.ID, - UserID: userId, + AssetID: p.processingFeeAsset.ID, + UserID: *p.userId, InstrumentID: t.ids.StringWalletId, } responseLeg, err = t.repos.TxLeg.Create(responseLeg) if err != nil { - return txID, value, common.StringError(err) + return p, common.StringError(err) } txLeg := model.TransactionUpdates{ResponseTxLegID: &responseLeg.ID} - err = t.repos.Transaction.Update(txUUID, txLeg) + err = t.repos.Transaction.Update(p.transactionModel.ID, txLeg) if err != nil { - return txID, value, common.StringError(err) + return p, common.StringError(err) } - return txID, value, nil -} - -func confirmTx(executor Executor, txID string) (uint64, error) { - trueGas, err := executor.TxWait(txID) - if err != nil { - return 0, common.StringError(err) - } - return trueGas, nil -} - -func (t transaction) chargeCard(userWallet string, authorizationID string, usd float64, chargeAsset model.Asset, txUUID string, userId string) error { - _, err := CaptureCharge(usd, userWallet, authorizationID) - if err != nil { - return common.StringError(err) - } - - // Create Receipt Tx leg - usdWei := floatToFixedString(usd, int(chargeAsset.Decimals)) - receiptLeg := model.TxLeg{ - Timestamp: time.Now(), - Amount: usdWei, - Value: usdWei, - AssetID: chargeAsset.ID, - UserID: t.ids.StringUserId, - InstrumentID: t.ids.StringBankId, - } - receiptLeg, err = t.repos.TxLeg.Create(receiptLeg) - if err != nil { - return common.StringError(err) - } - txLeg := model.TransactionUpdates{ReceiptTxLegID: &receiptLeg.ID} - err = t.repos.Transaction.Update(txUUID, txLeg) + status := "Transaction Initiated" + txAmount := p.cumulativeValue.String() + updateDB := &model.TransactionUpdates{Status: &status, TransactionHash: p.txId, TransactionAmount: &txAmount} + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { - return common.StringError(err) + return p, common.StringError(err) } - return nil + return p, nil } -// TODO: rewrite this transaction to reference the asset(s) received by the user, not what we paid -func (t transaction) tenderTransaction(cumulativeValue *big.Int, cumulativeGas uint64, quotedTotal float64, chain Chain, txUUID string, recipientId string, userWalletId string) (float64, error) { - cost := NewCost(t.redis) - trueWei := big.NewInt(0).Add(cumulativeValue, big.NewInt(int64(cumulativeGas))) - trueEth := common.WeiToEther(trueWei) - trueUSD, err := cost.LookupUSD(chain.CoingeckoName, trueEth) +func confirmTx(executor Executor, txID string) (uint64, error) { + trueGas, err := executor.TxWait(txID) if err != nil { return 0, common.StringError(err) } - profit := quotedTotal - trueUSD - - // Create Receive Tx leg - asset, err := t.repos.Asset.GetName("ETH") - if err != nil { - return profit, common.StringError(err) - } - wei := floatToFixedString(trueEth, int(asset.Decimals)) - usd := floatToFixedString(quotedTotal, 6) - - txModel, err := t.repos.Transaction.GetById(txUUID) - if err != nil { - return profit, common.StringError(err) - } - - now := time.Now() - destinationLeg := model.TxLegUpdates{ - Timestamp: &now, // updated based on *when the transaction occured* not time.Now() - Amount: &wei, // Should be the amount of the asset received by the user - Value: &usd, // The value of the asset received by the user - AssetID: &asset.ID, // the asset received by the user - UserID: &recipientId, // the user who received the asset - InstrumentID: &userWalletId, // the instrument which received the asset (wallet usually) - } - - // We now update the destination leg instead of creating it - err = t.repos.TxLeg.Update(txModel.DestinationTxLegID, destinationLeg) - if err != nil { - return profit, common.StringError(err) - } - - return profit, nil -} - -type postProcessRequest struct { - TxID string - Chain Chain - Authorization AuthorizedCharge - UserAddress string - CumulativeGas uint64 - CumulativeValue *big.Int - Quote model.Quote - TxDBID string - processingFeeAsset model.Asset - preBalance float64 - userId string - recipientWalletId string + return trueGas, nil } -func (t transaction) postProcess(request postProcessRequest) { +func (t transaction) postProcess(p transactionProcessingData) { executor := NewExecutor() - err := executor.Initialize(request.Chain.RPC) + err := executor.Initialize(p.chain.RPC) if err != nil { // TODO: Handle error instead of returning it } updateDB := model.TransactionUpdates{} status := "Post Process RPC Dialed" updateDB.Status = &status - err = t.repos.Transaction.Update(request.TxDBID, updateDB) + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { // TODO: Handle error instead of returning it } // confirm the Tx on the EVM, update db status and NetworkFee - trueGas, err := confirmTx(executor, request.TxID) + trueGas, err := confirmTx(executor, *p.txId) + p.trueGas = &trueGas if err != nil { // TODO: Handle error instead of returning it } @@ -635,7 +533,7 @@ func (t transaction) postProcess(request postProcessRequest) { updateDB.Status = &status networkFee := strconv.FormatUint(trueGas, 10) updateDB.NetworkFee = &networkFee // geth uses uint64 for gas - err = t.repos.Transaction.Update(request.TxDBID, updateDB) + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { // TODO: Handle error instead of returning it } @@ -647,8 +545,8 @@ func (t transaction) postProcess(request postProcessRequest) { } // TODO: store threshold on a per-network basis in the repo threshold := 10.0 - if request.preBalance >= threshold && postBalance < threshold { - msg := fmt.Sprintf("STRING-API: %s balance is < %.2f at %.2f", request.Chain.OwlracleName, threshold, postBalance) + if *p.preBalance >= threshold && postBalance < threshold { + msg := fmt.Sprintf("STRING-API: %s balance is < %.2f at %.2f", p.chain.OwlracleName, threshold, postBalance) MessageStaff(msg) if err != nil { // TODO: handle error instead of returning it @@ -657,7 +555,7 @@ func (t transaction) postProcess(request postProcessRequest) { // compute profit, update db status and processing fees to db // TODO: factor request.processingFeeAsset in the event of crypto-to-usd - profit, err := t.tenderTransaction(request.CumulativeValue, trueGas, request.Quote.TotalUSD, request.Chain, request.TxDBID, request.userId, request.recipientWalletId) + profit, err := t.tenderTransaction(p) if err != nil { // TODO: Handle error instead of returning it } @@ -668,13 +566,13 @@ func (t transaction) postProcess(request postProcessRequest) { processingFee := floatToFixedString(profit, 6) // TODO: set processingFee based on payment method, and location updateDB.StringFee = &stringFee // string fee is always USD with 6 digits updateDB.ProcessingFee = &processingFee - err = t.repos.Transaction.Update(request.TxDBID, updateDB) + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { // TODO: Handle error instead of returning it } // charge the users CC - err = t.chargeCard(request.UserAddress, request.Authorization.AuthID, request.Quote.TotalUSD, request.processingFeeAsset, request.TxDBID, request.userId) + err = t.chargeCard(p) if err != nil { // TODO: Handle error instead of returning it } @@ -682,39 +580,111 @@ func (t transaction) postProcess(request postProcessRequest) { updateDB.Status = &status // TODO: Figure out how much we paid the CC payment processor and deduct it // and use it to populate processing_fee and processing_fee_asset in the table - err = t.repos.Transaction.Update(request.TxDBID, updateDB) + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { // TODO: Handle error instead of returning it } status = "Completed" updateDB.Status = &status - err = t.repos.Transaction.Update(request.TxDBID, updateDB) + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { // TODO: Handle error instead of returning it } executor.Close() // Create Transaction data in Unit21 - err = t.unit21CreateTransaction(request.TxDBID) + err = t.unit21CreateTransaction(p.transactionModel.ID) if err != nil { log.Printf("Error creating Unit21 transaction: %s", err) } // send email receipt - err = t.sendEmailReceipt(request) + err = t.sendEmailReceipt(p) if err != nil { log.Printf("Error sending email receipt to user: %s", err) } } -func (t transaction) sendEmailReceipt(request postProcessRequest) error { - user, err := t.repos.User.GetById(request.userId) +// TODO: rewrite this transaction to reference the asset(s) received by the user, not what we paid +func (t transaction) tenderTransaction(p transactionProcessingData) (float64, error) { + cost := NewCost(t.redis) + trueWei := big.NewInt(0).Add(p.cumulativeValue, big.NewInt(int64(*p.trueGas))) + trueEth := common.WeiToEther(trueWei) + trueUSD, err := cost.LookupUSD(p.chain.CoingeckoName, trueEth) + if err != nil { + return 0, common.StringError(err) + } + profit := p.executionRequest.Quote.TotalUSD - trueUSD + + // Create Receive Tx leg + asset, err := t.repos.Asset.GetName("ETH") + if err != nil { + return profit, common.StringError(err) + } + wei := floatToFixedString(trueEth, int(asset.Decimals)) + usd := floatToFixedString(p.executionRequest.Quote.TotalUSD, 6) + + txModel, err := t.repos.Transaction.GetById(p.transactionModel.ID) + if err != nil { + return profit, common.StringError(err) + } + + now := time.Now() + destinationLeg := model.TxLegUpdates{ + Timestamp: &now, // updated based on *when the transaction occured* not time.Now() + Amount: &wei, // Should be the amount of the asset received by the user + Value: &usd, // The value of the asset received by the user + AssetID: &asset.ID, // the asset received by the user + UserID: p.userId, // the user who received the asset + InstrumentID: p.recipientWalletId, // the instrument which received the asset (wallet usually) + } + + // We now update the destination leg instead of creating it + err = t.repos.TxLeg.Update(txModel.DestinationTxLegID, destinationLeg) + if err != nil { + return profit, common.StringError(err) + } + + return profit, nil +} + +func (t transaction) chargeCard(p transactionProcessingData) error { + _, err := CaptureCharge(p.executionRequest.Quote.TotalUSD, p.executionRequest.UserAddress, p.cardAuthorization.AuthID) + if err != nil { + return common.StringError(err) + } + + // Create Receipt Tx leg + usdWei := floatToFixedString(p.executionRequest.Quote.TotalUSD, int(p.processingFeeAsset.Decimals)) + receiptLeg := model.TxLeg{ + Timestamp: time.Now(), + Amount: usdWei, + Value: usdWei, + AssetID: p.processingFeeAsset.ID, + UserID: t.ids.StringUserId, + InstrumentID: t.ids.StringBankId, + } + receiptLeg, err = t.repos.TxLeg.Create(receiptLeg) + if err != nil { + return common.StringError(err) + } + txLeg := model.TransactionUpdates{ReceiptTxLegID: &receiptLeg.ID} + err = t.repos.Transaction.Update(p.transactionModel.ID, txLeg) + if err != nil { + return common.StringError(err) + } + + return nil +} + +func (t transaction) sendEmailReceipt(p transactionProcessingData) error { + user, err := t.repos.User.GetById(*p.userId) if err != nil { log.Printf("Error getting user from repo: %s", err) return err } - contact, err := t.repos.Contact.GetByUserId(request.userId) + contact, err := t.repos.Contact.GetByUserId(user.ID) if err != nil { log.Printf("Error getting user contact from repo: %s", err) return err @@ -726,22 +696,22 @@ func (t transaction) sendEmailReceipt(request postProcessRequest) error { receiptParams := common.ReceiptGenerationParams{ ReceiptType: "NFT Purchase", // TODO: retrieve dynamically CustomerName: name, - StringPaymentId: request.TxDBID, + StringPaymentId: p.transactionModel.ID, PaymentDescriptor: "String Digital Asset", // TODO: retrieve dynamically TransactionDate: time.Now().Format(time.RFC1123), } receiptBody := [][2]string{ - {"Transaction ID", "" + request.TxID + ""}, - {"Destination Wallet", "" + request.UserAddress + ""}, + {"Transaction ID", "" + *p.txId + ""}, + {"Destination Wallet", "" + p.executionRequest.UserAddress + ""}, {"Payment Descriptor", receiptParams.PaymentDescriptor}, - {"Payment Method", request.Authorization.Issuer + " " + request.Authorization.Last4}, + {"Payment Method", p.cardAuthorization.Issuer + " " + p.cardAuthorization.Last4}, {"Platform", "String Demo"}, // TODO: retrieve dynamically {"Item Ordered", "String Fighter NFT"}, // TODO: retrieve dynamically {"Token ID", "1234"}, // TODO: retrieve dynamically, maybe after building token transfer detection - {"Subtotal", common.FloatToUSDString(request.Quote.BaseUSD + request.Quote.TokenUSD)}, - {"Network Fee:", common.FloatToUSDString(request.Quote.GasUSD)}, - {"Processing Fee", common.FloatToUSDString(request.Quote.ServiceUSD)}, - {"Total Charge", common.FloatToUSDString(request.Quote.TotalUSD)}, + {"Subtotal", common.FloatToUSDString(p.executionRequest.Quote.BaseUSD + p.executionRequest.Quote.TokenUSD)}, + {"Network Fee:", common.FloatToUSDString(p.executionRequest.Quote.GasUSD)}, + {"Processing Fee", common.FloatToUSDString(p.executionRequest.Quote.ServiceUSD)}, + {"Total Charge", common.FloatToUSDString(p.executionRequest.Quote.TotalUSD)}, } err = common.EmailReceipt(contact.Data, receiptParams, receiptBody) if err != nil { @@ -858,3 +828,7 @@ func (t transaction) updateTransactionStatus(transactionId string, status string return err } + +func (t *transaction) getStringInstrumentsAndUserId() { + t.ids = GetStringIdsFromEnv() +} From 98f293c312d935362063bb0e2dc753c2bb39647e Mon Sep 17 00:00:00 2001 From: Ocasta Date: Fri, 10 Feb 2023 14:15:41 -0600 Subject: [PATCH 04/13] s to p --- pkg/service/transaction.go | 124 +++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index 18b2abe0..ad330ffc 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -139,100 +139,100 @@ func (t transaction) Execute(e model.ExecutionRequest, userId string, deviceId s return model.TransactionReceipt{TxID: *p.txId, TxURL: p.chain.Explorer + "/tx/" + *p.txId}, nil } -func (t transaction) transactionSetup(s transactionProcessingData) (transactionProcessingData, error) { +func (t transaction) transactionSetup(p transactionProcessingData) (transactionProcessingData, error) { // get user object - _, err := t.repos.User.GetById(*s.userId) + _, err := t.repos.User.GetById(*p.userId) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } // Pull chain info needed for execution from repository - chain, err := ChainInfo(uint64(*s.chainId), t.repos.Network, t.repos.Asset) - s.chain = &chain + chain, err := ChainInfo(uint64(*p.chainId), t.repos.Network, t.repos.Asset) + p.chain = &chain if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } // Create new Tx in repository, populate it with known info - transactionModel, err := t.repos.Transaction.Create(model.Transaction{Status: "Created", NetworkID: chain.UUID, DeviceID: *s.deviceId, PlatformID: t.ids.StringPlatformId}) - s.transactionModel = &transactionModel + transactionModel, err := t.repos.Transaction.Create(model.Transaction{Status: "Created", NetworkID: chain.UUID, DeviceID: *p.deviceId, PlatformID: t.ids.StringPlatformId}) + p.transactionModel = &transactionModel if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } updateDB := &model.TransactionUpdates{} - processingFeeAsset, err := t.populateInitialTxModelData(*s.executionRequest, updateDB) - s.processingFeeAsset = &processingFeeAsset + processingFeeAsset, err := t.populateInitialTxModelData(*p.executionRequest, updateDB) + p.processingFeeAsset = &processingFeeAsset if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } err = t.repos.Transaction.Update(transactionModel.ID, updateDB) if err != nil { fmt.Printf("\nERROR = %+v", err) - return s, common.StringError(err) + return p, common.StringError(err) } // Dial the RPC and update model status executor := NewExecutor() err = executor.Initialize(chain.RPC) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } err = t.updateTransactionStatus("RPC Dialed", transactionModel.ID) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } - return s, err + return p, err } -func (t transaction) safetyCheck(s transactionProcessingData) (transactionProcessingData, error) { +func (t transaction) safetyCheck(p transactionProcessingData) (transactionProcessingData, error) { // Test the Tx and update model status - estimateUSD, estimateETH, err := t.testTransaction(*s.executor, s.executionRequest.TransactionRequest, *s.chain, false) + estimateUSD, estimateETH, err := t.testTransaction(*p.executor, p.executionRequest.TransactionRequest, *p.chain, false) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } - err = t.updateTransactionStatus("Tested and Estimated", s.transactionModel.ID) + err = t.updateTransactionStatus("Tested and Estimated", p.transactionModel.ID) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } // Verify the Quote and update model status - _, err = verifyQuote(*s.executionRequest, estimateUSD) + _, err = verifyQuote(*p.executionRequest, estimateUSD) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } - err = t.updateTransactionStatus("Quote Verified", s.transactionModel.ID) + err = t.updateTransactionStatus("Quote Verified", p.transactionModel.ID) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } // Get current balance of primary token preBalance, err := executor.GetBalance() - s.preBalance = &preBalance + p.preBalance = &preBalance if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } if preBalance < estimateETH { - msg := fmt.Sprintf("STRING-API: %s balance is too low to execute %.2f transaction at %.2f", s.chain.OwlracleName, estimateETH, preBalance) + msg := fmt.Sprintf("STRING-API: %s balance is too low to execute %.2f transaction at %.2f", p.chain.OwlracleName, estimateETH, preBalance) MessageStaff(msg) - return s, common.StringError(errors.New("hot wallet ETH balance too low")) + return p, common.StringError(errors.New("hot wallet ETH balance too low")) } // Authorize quoted cost on end-user CC and update model status - s, err = t.authCard(s) + p, err = t.authCard(p) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } // Validate Transaction through Real Time Rules engine - err = t.unit21Evaluate(s.transactionModel.ID) + err = t.unit21Evaluate(p.transactionModel.ID) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } - return s, nil + return p, nil } func (t transaction) populateInitialTxModelData(e model.ExecutionRequest, m *model.TransactionUpdates) (model.Asset, error) { @@ -369,53 +369,53 @@ func (t transaction) addWalletInstrumentIdIfNew(address string, id string) (stri return instrument.ID, nil } -func (t transaction) authCard(s transactionProcessingData) (transactionProcessingData, error) { +func (t transaction) authCard(p transactionProcessingData) (transactionProcessingData, error) { // auth their card - auth, err := AuthorizeCharge(s.executionRequest.TotalUSD, s.executionRequest.UserAddress, s.executionRequest.CardToken) + auth, err := AuthorizeCharge(p.executionRequest.TotalUSD, p.executionRequest.UserAddress, p.executionRequest.CardToken) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } // Add Checkout Instrument ID to our DB if it's not there already and associate it with the user - instrumentId, err := t.addCardInstrumentIdIfNew(auth.CheckoutFingerprint, *s.userId, auth.Last4, auth.CardType) + instrumentId, err := t.addCardInstrumentIdIfNew(auth.CheckoutFingerprint, *p.userId, auth.Last4, auth.CardType) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } // Create Origin Tx leg - usdWei := floatToFixedString(s.executionRequest.TotalUSD, int(s.processingFeeAsset.Decimals)) + usdWei := floatToFixedString(p.executionRequest.TotalUSD, int(p.processingFeeAsset.Decimals)) origin := model.TxLeg{ Timestamp: time.Now(), Amount: usdWei, Value: usdWei, - AssetID: s.processingFeeAsset.ID, - UserID: *s.userId, + AssetID: p.processingFeeAsset.ID, + UserID: *p.userId, InstrumentID: instrumentId, } origin, err = t.repos.TxLeg.Create(origin) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } txLegUpdates := model.TransactionUpdates{OriginTxLegID: &origin.ID} - err = t.repos.Transaction.Update(s.transactionModel.ID, txLegUpdates) + err = t.repos.Transaction.Update(p.transactionModel.ID, txLegUpdates) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } /// - s.cardAuthorization = &auth + p.cardAuthorization = &auth if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } - err = t.updateTransactionStatus("Card "+auth.Status, s.transactionModel.ID) + err = t.updateTransactionStatus("Card "+auth.Status, p.transactionModel.ID) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } - recipientWalletId, err := t.addWalletInstrumentIdIfNew(s.executionRequest.UserAddress, *s.userId) - s.recipientWalletId = *&s.recipientWalletId + recipientWalletId, err := t.addWalletInstrumentIdIfNew(p.executionRequest.UserAddress, *p.userId) + p.recipientWalletId = *&p.recipientWalletId if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } // TODO: Determine the output of the transaction (destination leg) with Tracers @@ -423,33 +423,33 @@ func (t transaction) authCard(s transactionProcessingData) (transactionProcessin Timestamp: time.Now(), // Required by the db. Should be updated when the tx occurs Amount: "0", // Required by Unit21. The amount of the asset received by the user Value: "0", // Default to '0'. The value of the asset received by the user - AssetID: s.chain.GasTokenID, // Required by the db. the asset received by the user - UserID: *s.userId, // the user who received the asset + AssetID: p.chain.GasTokenID, // Required by the db. the asset received by the user + UserID: *p.userId, // the user who received the asset InstrumentID: recipientWalletId, // Required by the db. the instrument which received the asset (wallet usually) } destinationLeg, err = t.repos.TxLeg.Create(destinationLeg) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } txLegUpdates = model.TransactionUpdates{DestinationTxLegID: &destinationLeg.ID} - err = t.repos.Transaction.Update(s.transactionModel.ID, txLegUpdates) + err = t.repos.Transaction.Update(p.transactionModel.ID, txLegUpdates) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } if !auth.Approved { - err := t.unit21CreateTransaction(s.transactionModel.ID) + err := t.unit21CreateTransaction(p.transactionModel.ID) if err != nil { - return s, common.StringError(err) + return p, common.StringError(err) } - return s, common.StringError(errors.New("payment: Authorization Declined by Checkout")) + return p, common.StringError(errors.New("payment: Authorization Declined by Checkout")) } - return s, nil + return p, nil } func (t transaction) initiateTransaction(p transactionProcessingData) (transactionProcessingData, error) { @@ -817,6 +817,8 @@ func (t transaction) unit21Evaluate(transactionId string) (err error) { if err != nil { return common.StringError(err) } + + return nil } func (t transaction) updateTransactionStatus(transactionId string, status string) (err error) { From 3228e38e15a70ea3558efd34ad2ede4f5efb04ad Mon Sep 17 00:00:00 2001 From: Ocasta Date: Fri, 10 Feb 2023 14:26:13 -0600 Subject: [PATCH 05/13] cleaned up errors --- pkg/service/transaction.go | 54 +++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index ad330ffc..1a1ba4a5 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -168,7 +168,7 @@ func (t transaction) transactionSetup(p transactionProcessingData) (transactionP } err = t.repos.Transaction.Update(transactionModel.ID, updateDB) if err != nil { - fmt.Printf("\nERROR = %+v", err) + fmt.Printf("\nERROR = %+v", common.StringError(err)) return p, common.StringError(err) } @@ -511,8 +511,10 @@ func confirmTx(executor Executor, txID string) (uint64, error) { func (t transaction) postProcess(p transactionProcessingData) { executor := NewExecutor() + p.executor = &executor err := executor.Initialize(p.chain.RPC) if err != nil { + log.Printf("Failed to initialized executor in postProcess: %s", common.StringError(err)) // TODO: Handle error instead of returning it } updateDB := model.TransactionUpdates{} @@ -520,6 +522,7 @@ func (t transaction) postProcess(p transactionProcessingData) { updateDB.Status = &status err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { + log.Printf("Failed to update transaction repo with status 'Post Process RPC Dialed': %s", common.StringError(err)) // TODO: Handle error instead of returning it } @@ -527,6 +530,7 @@ func (t transaction) postProcess(p transactionProcessingData) { trueGas, err := confirmTx(executor, *p.txId) p.trueGas = &trueGas if err != nil { + log.Printf("Failed to confirm transaction: %s", common.StringError(err)) // TODO: Handle error instead of returning it } status = "Tx Confirmed" @@ -535,20 +539,24 @@ func (t transaction) postProcess(p transactionProcessingData) { updateDB.NetworkFee = &networkFee // geth uses uint64 for gas err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { + log.Printf("Failed to update transaction repo with status 'Tx Confirmed': %s", common.StringError(err)) // TODO: Handle error instead of returning it } // Check and see if balance threshold was crossed postBalance, err := executor.GetBalance() if err != nil { + log.Printf("Failed to get executor balance: %s", common.StringError(err)) // TODO: handle error instead of returning it } // TODO: store threshold on a per-network basis in the repo threshold := 10.0 if *p.preBalance >= threshold && postBalance < threshold { msg := fmt.Sprintf("STRING-API: %s balance is < %.2f at %.2f", p.chain.OwlracleName, threshold, postBalance) - MessageStaff(msg) + err = MessageStaff(msg) if err != nil { + log.Printf("Failed to send staff with low balance threshold message: %s", common.StringError(err)) + // Not seeing any e // TODO: handle error instead of returning it } } @@ -557,6 +565,7 @@ func (t transaction) postProcess(p transactionProcessingData) { // TODO: factor request.processingFeeAsset in the event of crypto-to-usd profit, err := t.tenderTransaction(p) if err != nil { + log.Printf("Failed to tender transaction: %s", common.StringError(err)) // TODO: Handle error instead of returning it } fmt.Printf("PROFIT=%+v", profit) @@ -568,12 +577,14 @@ func (t transaction) postProcess(p transactionProcessingData) { updateDB.ProcessingFee = &processingFee err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { + log.Printf("Failed to update transaction repo with status 'Profit Tendered': %s", common.StringError(err)) // TODO: Handle error instead of returning it } // charge the users CC err = t.chargeCard(p) if err != nil { + log.Printf("Error, failed to charge card: %+v", common.StringError(err)) // TODO: Handle error instead of returning it } status = "Card Charged" @@ -582,6 +593,7 @@ func (t transaction) postProcess(p transactionProcessingData) { // and use it to populate processing_fee and processing_fee_asset in the table err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { + log.Printf("Failed to update transaction repo with status 'Card Charged': %s", common.StringError(err)) // TODO: Handle error instead of returning it } @@ -589,20 +601,20 @@ func (t transaction) postProcess(p transactionProcessingData) { updateDB.Status = &status err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) if err != nil { - // TODO: Handle error instead of returning it + log.Printf("Failed to update transaction repo with status 'Completed': %s", common.StringError(err)) } executor.Close() // Create Transaction data in Unit21 err = t.unit21CreateTransaction(p.transactionModel.ID) if err != nil { - log.Printf("Error creating Unit21 transaction: %s", err) + log.Printf("Error creating Unit21 transaction: %s", common.StringError(err)) } // send email receipt err = t.sendEmailReceipt(p) if err != nil { - log.Printf("Error sending email receipt to user: %s", err) + log.Printf("Error sending email receipt to user: %s", common.StringError(err)) } } @@ -681,13 +693,13 @@ func (t transaction) chargeCard(p transactionProcessingData) error { func (t transaction) sendEmailReceipt(p transactionProcessingData) error { user, err := t.repos.User.GetById(*p.userId) if err != nil { - log.Printf("Error getting user from repo: %s", err) - return err + log.Printf("Error getting user from repo: %s", common.StringError(err)) + return common.StringError(err) } contact, err := t.repos.Contact.GetByUserId(user.ID) if err != nil { - log.Printf("Error getting user contact from repo: %s", err) - return err + log.Printf("Error getting user contact from repo: %s", common.StringError(err)) + return common.StringError(err) } name := user.FirstName // + " " + user.MiddleName + " " + user.LastName if name == "" { @@ -715,8 +727,8 @@ func (t transaction) sendEmailReceipt(p transactionProcessingData) error { } err = common.EmailReceipt(contact.Data, receiptParams, receiptBody) if err != nil { - log.Printf("Error sending email receipt to user: %s", err) - return err + log.Printf("Error sending email receipt to user: %s", common.StringError(err)) + return common.StringError(err) } return nil } @@ -736,7 +748,7 @@ func (t transaction) unit21CreateInstrument(instrument model.Instrument) (err er u21InstrumentId, err := u21Instrument.Create(instrument) if err != nil { fmt.Printf("Error creating new instrument in Unit21") - return + return common.StringError(err) } // Log create instrument action w/ Unit21 @@ -750,7 +762,7 @@ func (t transaction) unit21CreateInstrument(instrument model.Instrument) (err er _, err = u21Action.Create(instrument, "Creation", u21InstrumentId, "Creation") if err != nil { fmt.Printf("Error creating a new instrument action in Unit21") - return + return common.StringError(err) } return @@ -759,8 +771,8 @@ func (t transaction) unit21CreateInstrument(instrument model.Instrument) (err er func (t transaction) unit21CreateTransaction(transactionId string) (err error) { txModel, err := t.repos.Transaction.GetById(transactionId) if err != nil { - log.Printf("Error getting tx model in Unit21 in Tx Postprocess: %s", err) - return + log.Printf("Error getting tx model in Unit21 in Tx Postprocess: %s", common.StringError(err)) + return common.StringError(err) } u21Repo := unit21.TransactionRepo{ @@ -772,18 +784,18 @@ func (t transaction) unit21CreateTransaction(transactionId string) (err error) { u21Tx := unit21.NewTransaction(u21Repo) _, err = u21Tx.Create(txModel) if err != nil { - log.Printf("Error updating Unit21 in Tx Postprocess: %s", err) - return + log.Printf("Error updating Unit21 in Tx Postprocess: %s", common.StringError(err)) + return common.StringError(err) } - return + return nil } func (t transaction) unit21Evaluate(transactionId string) (err error) { //Check transaction in Unit21 txModel, err := t.repos.Transaction.GetById(transactionId) if err != nil { - log.Printf("Error getting tx model in Unit21 in Tx Evaluate: %s", err) + log.Printf("Error getting tx model in Unit21 in Tx Evaluate: %s", common.StringError(err)) return common.StringError(err) } @@ -796,7 +808,7 @@ func (t transaction) unit21Evaluate(transactionId string) (err error) { u21Tx := unit21.NewTransaction(u21Repo) evaluation, err := u21Tx.Evaluate(txModel) if err != nil { - log.Printf("Error evaluating transaction in Unit21: %s", err) + log.Printf("Error evaluating transaction in Unit21: %s", common.StringError(err)) return common.StringError(err) } @@ -828,7 +840,7 @@ func (t transaction) updateTransactionStatus(transactionId string, status string return common.StringError(err) } - return err + return nil } func (t *transaction) getStringInstrumentsAndUserId() { From fddd864d13c9b147699298c78a74fbbf2adae2f0 Mon Sep 17 00:00:00 2001 From: Ocasta Date: Fri, 10 Feb 2023 14:33:53 -0600 Subject: [PATCH 06/13] fixing bugs --- pkg/service/transaction.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index 1a1ba4a5..575c9492 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -209,6 +209,7 @@ func (t transaction) safetyCheck(p transactionProcessingData) (transactionProces } // Get current balance of primary token + executor := *p.executor preBalance, err := executor.GetBalance() p.preBalance = &preBalance if err != nil { @@ -413,7 +414,7 @@ func (t transaction) authCard(p transactionProcessingData) (transactionProcessin } recipientWalletId, err := t.addWalletInstrumentIdIfNew(p.executionRequest.UserAddress, *p.userId) - p.recipientWalletId = *&p.recipientWalletId + p.recipientWalletId = &recipientWalletId if err != nil { return p, common.StringError(err) } @@ -471,7 +472,7 @@ func (t transaction) initiateTransaction(p transactionProcessingData) (transacti // Create Response Tx leg eth := common.WeiToEther(value) wei := floatToFixedString(eth, 18) - usd := floatToFixedString(e.TotalUSD, int(p.processingFeeAsset.Decimals)) + usd := floatToFixedString(p.executionRequest.TotalUSD, int(p.processingFeeAsset.Decimals)) responseLeg := model.TxLeg{ Timestamp: time.Now(), Amount: wei, From 1d53069a91652306c8f0c7049f5370889d915727 Mon Sep 17 00:00:00 2001 From: Ocasta Date: Fri, 10 Feb 2023 14:58:25 -0600 Subject: [PATCH 07/13] working as expected --- pkg/internal/common/sign_test.go | 2 +- pkg/service/transaction.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/internal/common/sign_test.go b/pkg/internal/common/sign_test.go index 30c8be7d..6ef0cd7b 100644 --- a/pkg/internal/common/sign_test.go +++ b/pkg/internal/common/sign_test.go @@ -16,7 +16,7 @@ func TestSignAndValidateString(t *testing.T) { err := godotenv.Load("../../../.env") assert.NoError(t, err) - encodedMessage := "Your base64 encoded String Here" + encodedMessage := "VGhhbmsgeW91IGZvciB1c2luZyBTdHJpbmchIEJ5IHNpZ25pbmcgdGhpcyBtZXNzYWdlIHlvdSBhcmU6CgoxKSBBdXRob3JpemluZyBTdHJpbmcgdG8gaW5pdGlhdGUgb2ZmLWNoYWluIHRyYW5zYWN0aW9ucyBvbiB5b3VyIGJlaGFsZiwgaW5jbHVkaW5nIHlvdXIgYmFuayBhY2NvdW50LCBjcmVkaXQgY2FyZCwgb3IgZGViaXQgY2FyZC4KCjIpIENvbmZpcm1pbmcgdGhhdCB0aGlzIHdhbGxldCBpcyBvd25lZCBieSB5b3UuCgpUaGlzIHJlcXVlc3Qgd2lsbCBub3QgdHJpZ2dlciBhbnkgYmxvY2tjaGFpbiB0cmFuc2FjdGlvbiBvciBjb3N0IGFueSBnYXMuCgpOb25jZTogejhQVk4wSzlNZW5hcGNOSnY0V2xvNFhkM1gxV2lCSVE5UE94b0hPc1ZuWFFjN0tCOEV2NTZzOTAvZU1OR25kWE03S2JtblZiMU9EZDdLc3VuekZEZW9SWGdwcTBaYTliek94VGJ0dVdzQnpnYnZsb3RjQ2V5NWx3VzRHMm5uTT0=" // decode decoded, err := b64.URLEncoding.DecodeString(encodedMessage) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index 575c9492..50c5500d 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -52,7 +52,6 @@ type transaction struct { type transactionProcessingData struct { userId *string - chainId *int deviceId *string executor *Executor processingFeeAsset *model.Asset @@ -147,7 +146,7 @@ func (t transaction) transactionSetup(p transactionProcessingData) (transactionP } // Pull chain info needed for execution from repository - chain, err := ChainInfo(uint64(*p.chainId), t.repos.Network, t.repos.Asset) + chain, err := ChainInfo(uint64(p.executionRequest.ChainID), t.repos.Network, t.repos.Asset) p.chain = &chain if err != nil { return p, common.StringError(err) @@ -174,6 +173,7 @@ func (t transaction) transactionSetup(p transactionProcessingData) (transactionP // Dial the RPC and update model status executor := NewExecutor() + p.executor = &executor err = executor.Initialize(chain.RPC) if err != nil { return p, common.StringError(err) @@ -464,6 +464,7 @@ func (t transaction) initiateTransaction(p transactionProcessingData) (transacti } executor := *p.executor txID, value, err := executor.Initiate(call) + p.cumulativeValue = value if err != nil { return p, common.StringError(err) } @@ -834,7 +835,7 @@ func (t transaction) unit21Evaluate(transactionId string) (err error) { return nil } -func (t transaction) updateTransactionStatus(transactionId string, status string) (err error) { +func (t transaction) updateTransactionStatus(status string, transactionId string) (err error) { updateDB := &model.TransactionUpdates{Status: &status} err = t.repos.Transaction.Update(transactionId, updateDB) if err != nil { From ceba54fe5252709c73d1ca39d592712800b4c563 Mon Sep 17 00:00:00 2001 From: Ocasta Date: Fri, 10 Feb 2023 15:00:21 -0600 Subject: [PATCH 08/13] remove sign_test changes --- pkg/internal/common/sign_test.go | 2 +- pkg/service/checkout.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/internal/common/sign_test.go b/pkg/internal/common/sign_test.go index 6ef0cd7b..30c8be7d 100644 --- a/pkg/internal/common/sign_test.go +++ b/pkg/internal/common/sign_test.go @@ -16,7 +16,7 @@ func TestSignAndValidateString(t *testing.T) { err := godotenv.Load("../../../.env") assert.NoError(t, err) - encodedMessage := "VGhhbmsgeW91IGZvciB1c2luZyBTdHJpbmchIEJ5IHNpZ25pbmcgdGhpcyBtZXNzYWdlIHlvdSBhcmU6CgoxKSBBdXRob3JpemluZyBTdHJpbmcgdG8gaW5pdGlhdGUgb2ZmLWNoYWluIHRyYW5zYWN0aW9ucyBvbiB5b3VyIGJlaGFsZiwgaW5jbHVkaW5nIHlvdXIgYmFuayBhY2NvdW50LCBjcmVkaXQgY2FyZCwgb3IgZGViaXQgY2FyZC4KCjIpIENvbmZpcm1pbmcgdGhhdCB0aGlzIHdhbGxldCBpcyBvd25lZCBieSB5b3UuCgpUaGlzIHJlcXVlc3Qgd2lsbCBub3QgdHJpZ2dlciBhbnkgYmxvY2tjaGFpbiB0cmFuc2FjdGlvbiBvciBjb3N0IGFueSBnYXMuCgpOb25jZTogejhQVk4wSzlNZW5hcGNOSnY0V2xvNFhkM1gxV2lCSVE5UE94b0hPc1ZuWFFjN0tCOEV2NTZzOTAvZU1OR25kWE03S2JtblZiMU9EZDdLc3VuekZEZW9SWGdwcTBaYTliek94VGJ0dVdzQnpnYnZsb3RjQ2V5NWx3VzRHMm5uTT0=" + encodedMessage := "Your base64 encoded String Here" // decode decoded, err := b64.URLEncoding.DecodeString(encodedMessage) diff --git a/pkg/service/checkout.go b/pkg/service/checkout.go index 916a9bd2..95648fce 100644 --- a/pkg/service/checkout.go +++ b/pkg/service/checkout.go @@ -71,10 +71,10 @@ func AuthorizeCharge(amount float64, userWallet string, tokenId string) (auth Au // Generate a payment token ID in case we don't yet have one in the front end // For testing purposes only card := tokens.Card{ - Type: checkoutCommon.Card, - Number: "4242424242424242", // Success + Type: checkoutCommon.Card, + // Number: "4242424242424242", // Success // Number: "4273149019799094", // succeed authorize, fail capture - // Number: "4544249167673670", // Declined - Insufficient funds + Number: "4544249167673670", // Declined - Insufficient funds // Number: "5148447461737269", // Invalid transaction ExpiryMonth: 2, ExpiryYear: 2024, From c407b40c26c170d4319a85c6dedeab51bb3ac43b Mon Sep 17 00:00:00 2001 From: Ocasta Date: Fri, 10 Feb 2023 15:04:01 -0600 Subject: [PATCH 09/13] revert card changes --- pkg/service/checkout.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/service/checkout.go b/pkg/service/checkout.go index 95648fce..62e1f847 100644 --- a/pkg/service/checkout.go +++ b/pkg/service/checkout.go @@ -71,10 +71,10 @@ func AuthorizeCharge(amount float64, userWallet string, tokenId string) (auth Au // Generate a payment token ID in case we don't yet have one in the front end // For testing purposes only card := tokens.Card{ - Type: checkoutCommon.Card, - // Number: "4242424242424242", // Success + Type: checkoutCommon.Card, + Number: "4242424242424242", // Success // Number: "4273149019799094", // succeed authorize, fail capture - Number: "4544249167673670", // Declined - Insufficient funds + // Number: "4544249167673670", // Declined - Insufficient funds // Number: "5148447461737269", // Invalid transaction ExpiryMonth: 2, ExpiryYear: 2024, From 2842e9f2a484247f0525c127bc97360a8e38f655 Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 10 Feb 2023 14:46:30 -0700 Subject: [PATCH 10/13] Added more comments to postProcess and moved it closer to the top of transaction.go --- pkg/service/transaction.go | 231 ++++++++++++++++++++----------------- 1 file changed, 122 insertions(+), 109 deletions(-) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index 50c5500d..e1415ca7 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -138,6 +138,128 @@ func (t transaction) Execute(e model.ExecutionRequest, userId string, deviceId s return model.TransactionReceipt{TxID: *p.txId, TxURL: p.chain.Explorer + "/tx/" + *p.txId}, nil } +func (t transaction) postProcess(p transactionProcessingData) { + // Reinitialize Executor + executor := NewExecutor() + p.executor = &executor + err := executor.Initialize(p.chain.RPC) + if err != nil { + log.Printf("Failed to initialized executor in postProcess: %s", common.StringError(err)) + // TODO: Handle error instead of returning it + } + + // Update TX Status + updateDB := model.TransactionUpdates{} + status := "Post Process RPC Dialed" + updateDB.Status = &status + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) + if err != nil { + log.Printf("Failed to update transaction repo with status 'Post Process RPC Dialed': %s", common.StringError(err)) + // TODO: Handle error instead of returning it + } + + // confirm the Tx on the EVM + trueGas, err := confirmTx(executor, *p.txId) + p.trueGas = &trueGas + if err != nil { + log.Printf("Failed to confirm transaction: %s", common.StringError(err)) + // TODO: Handle error instead of returning it + } + + // Update DB status and NetworkFee + status = "Tx Confirmed" + updateDB.Status = &status + networkFee := strconv.FormatUint(trueGas, 10) + updateDB.NetworkFee = &networkFee // geth uses uint64 for gas + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) + if err != nil { + log.Printf("Failed to update transaction repo with status 'Tx Confirmed': %s", common.StringError(err)) + // TODO: Handle error instead of returning it + } + + // Get new string wallet balance after executing the transaction + postBalance, err := executor.GetBalance() + if err != nil { + log.Printf("Failed to get executor balance: %s", common.StringError(err)) + // TODO: handle error instead of returning it + } + + // If threshold was crossed, notify devs + // TODO: store threshold on a per-network basis in the repo + threshold := 10.0 + if *p.preBalance >= threshold && postBalance < threshold { + msg := fmt.Sprintf("STRING-API: %s balance is < %.2f at %.2f", p.chain.OwlracleName, threshold, postBalance) + err = MessageStaff(msg) + if err != nil { + log.Printf("Failed to send staff with low balance threshold message: %s", common.StringError(err)) + // Not seeing any e + // TODO: handle error instead of returning it + } + } + + // compute profit + // TODO: factor request.processingFeeAsset in the event of crypto-to-usd + profit, err := t.tenderTransaction(p) + if err != nil { + log.Printf("Failed to tender transaction: %s", common.StringError(err)) + // TODO: Handle error instead of returning it + } + stringFee := floatToFixedString(profit, 6) + processingFee := floatToFixedString(profit, 6) // TODO: set processingFee based on payment method, and location + + // update db status and processing fees to db + updateDB.StringFee = &stringFee // string fee is always USD with 6 digits + updateDB.ProcessingFee = &processingFee + status = "Profit Tendered" + updateDB.Status = &status + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) + if err != nil { + log.Printf("Failed to update transaction repo with status 'Profit Tendered': %s", common.StringError(err)) + // TODO: Handle error instead of returning it + } + + // charge the users CC + err = t.chargeCard(p) + if err != nil { + log.Printf("Error, failed to charge card: %+v", common.StringError(err)) + // TODO: Handle error instead of returning it + } + + // Update status upon success + status = "Card Charged" + updateDB.Status = &status + // TODO: Figure out how much we paid the CC payment processor and deduct it + // and use it to populate processing_fee and processing_fee_asset in the table + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) + if err != nil { + log.Printf("Failed to update transaction repo with status 'Card Charged': %s", common.StringError(err)) + // TODO: Handle error instead of returning it + } + + // Transaction complete! Update status + status = "Completed" + updateDB.Status = &status + err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) + if err != nil { + log.Printf("Failed to update transaction repo with status 'Completed': %s", common.StringError(err)) + } + + // Close EVM executor + executor.Close() + + // Create Transaction data in Unit21 + err = t.unit21CreateTransaction(p.transactionModel.ID) + if err != nil { + log.Printf("Error creating Unit21 transaction: %s", common.StringError(err)) + } + + // send email receipt + err = t.sendEmailReceipt(p) + if err != nil { + log.Printf("Error sending email receipt to user: %s", common.StringError(err)) + } +} + func (t transaction) transactionSetup(p transactionProcessingData) (transactionProcessingData, error) { // get user object _, err := t.repos.User.GetById(*p.userId) @@ -511,115 +633,6 @@ func confirmTx(executor Executor, txID string) (uint64, error) { return trueGas, nil } -func (t transaction) postProcess(p transactionProcessingData) { - executor := NewExecutor() - p.executor = &executor - err := executor.Initialize(p.chain.RPC) - if err != nil { - log.Printf("Failed to initialized executor in postProcess: %s", common.StringError(err)) - // TODO: Handle error instead of returning it - } - updateDB := model.TransactionUpdates{} - status := "Post Process RPC Dialed" - updateDB.Status = &status - err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) - if err != nil { - log.Printf("Failed to update transaction repo with status 'Post Process RPC Dialed': %s", common.StringError(err)) - // TODO: Handle error instead of returning it - } - - // confirm the Tx on the EVM, update db status and NetworkFee - trueGas, err := confirmTx(executor, *p.txId) - p.trueGas = &trueGas - if err != nil { - log.Printf("Failed to confirm transaction: %s", common.StringError(err)) - // TODO: Handle error instead of returning it - } - status = "Tx Confirmed" - updateDB.Status = &status - networkFee := strconv.FormatUint(trueGas, 10) - updateDB.NetworkFee = &networkFee // geth uses uint64 for gas - err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) - if err != nil { - log.Printf("Failed to update transaction repo with status 'Tx Confirmed': %s", common.StringError(err)) - // TODO: Handle error instead of returning it - } - - // Check and see if balance threshold was crossed - postBalance, err := executor.GetBalance() - if err != nil { - log.Printf("Failed to get executor balance: %s", common.StringError(err)) - // TODO: handle error instead of returning it - } - // TODO: store threshold on a per-network basis in the repo - threshold := 10.0 - if *p.preBalance >= threshold && postBalance < threshold { - msg := fmt.Sprintf("STRING-API: %s balance is < %.2f at %.2f", p.chain.OwlracleName, threshold, postBalance) - err = MessageStaff(msg) - if err != nil { - log.Printf("Failed to send staff with low balance threshold message: %s", common.StringError(err)) - // Not seeing any e - // TODO: handle error instead of returning it - } - } - - // compute profit, update db status and processing fees to db - // TODO: factor request.processingFeeAsset in the event of crypto-to-usd - profit, err := t.tenderTransaction(p) - if err != nil { - log.Printf("Failed to tender transaction: %s", common.StringError(err)) - // TODO: Handle error instead of returning it - } - fmt.Printf("PROFIT=%+v", profit) - status = "Profit Tendered" - updateDB.Status = &status - stringFee := floatToFixedString(profit, 6) - processingFee := floatToFixedString(profit, 6) // TODO: set processingFee based on payment method, and location - updateDB.StringFee = &stringFee // string fee is always USD with 6 digits - updateDB.ProcessingFee = &processingFee - err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) - if err != nil { - log.Printf("Failed to update transaction repo with status 'Profit Tendered': %s", common.StringError(err)) - // TODO: Handle error instead of returning it - } - - // charge the users CC - err = t.chargeCard(p) - if err != nil { - log.Printf("Error, failed to charge card: %+v", common.StringError(err)) - // TODO: Handle error instead of returning it - } - status = "Card Charged" - updateDB.Status = &status - // TODO: Figure out how much we paid the CC payment processor and deduct it - // and use it to populate processing_fee and processing_fee_asset in the table - err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) - if err != nil { - log.Printf("Failed to update transaction repo with status 'Card Charged': %s", common.StringError(err)) - // TODO: Handle error instead of returning it - } - - status = "Completed" - updateDB.Status = &status - err = t.repos.Transaction.Update(p.transactionModel.ID, updateDB) - if err != nil { - log.Printf("Failed to update transaction repo with status 'Completed': %s", common.StringError(err)) - } - executor.Close() - // Create Transaction data in Unit21 - - err = t.unit21CreateTransaction(p.transactionModel.ID) - if err != nil { - log.Printf("Error creating Unit21 transaction: %s", common.StringError(err)) - } - - // send email receipt - err = t.sendEmailReceipt(p) - if err != nil { - log.Printf("Error sending email receipt to user: %s", common.StringError(err)) - } -} - // TODO: rewrite this transaction to reference the asset(s) received by the user, not what we paid func (t transaction) tenderTransaction(p transactionProcessingData) (float64, error) { cost := NewCost(t.redis) From 14db6ebf3b08af22fabb0569d0a29082bf25d90e Mon Sep 17 00:00:00 2001 From: akfoster Date: Fri, 10 Feb 2023 15:59:24 -0600 Subject: [PATCH 11/13] Update pkg/service/transaction.go --- pkg/service/transaction.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index e1415ca7..feb2f80a 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -525,7 +525,6 @@ func (t transaction) authCard(p transactionProcessingData) (transactionProcessin return p, common.StringError(err) } - /// p.cardAuthorization = &auth if err != nil { return p, common.StringError(err) From 71c4589cf97b5cca4f8c34399af1c026378eb006 Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 10 Feb 2023 15:02:41 -0700 Subject: [PATCH 12/13] close executor earlier --- pkg/service/transaction.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index feb2f80a..64cd6308 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -184,6 +184,9 @@ func (t transaction) postProcess(p transactionProcessingData) { // TODO: handle error instead of returning it } + // We can close the executor because we aren't using it after this + executor.Close() + // If threshold was crossed, notify devs // TODO: store threshold on a per-network basis in the repo threshold := 10.0 @@ -244,9 +247,6 @@ func (t transaction) postProcess(p transactionProcessingData) { log.Printf("Failed to update transaction repo with status 'Completed': %s", common.StringError(err)) } - // Close EVM executor - executor.Close() - // Create Transaction data in Unit21 err = t.unit21CreateTransaction(p.transactionModel.ID) if err != nil { From b9d9f23f5d43d5972cb69eb9023e100f9facfc09 Mon Sep 17 00:00:00 2001 From: Ocasta Date: Fri, 10 Feb 2023 16:06:53 -0600 Subject: [PATCH 13/13] clean up executor calls --- pkg/service/transaction.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index feb2f80a..0a46d208 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -129,8 +129,7 @@ func (t transaction) Execute(e model.ExecutionRequest, userId string, deviceId s } // this Executor will not exist in scope of postProcess - executor := *p.executor // needed to keep the compiler from complaining - executor.Close() + (*p.executor).Close() // Send required information to new thread and return txId to the endpoint go t.postProcess(p) @@ -331,8 +330,7 @@ func (t transaction) safetyCheck(p transactionProcessingData) (transactionProces } // Get current balance of primary token - executor := *p.executor - preBalance, err := executor.GetBalance() + preBalance, err := (*p.executor).GetBalance() p.preBalance = &preBalance if err != nil { return p, common.StringError(err) @@ -583,8 +581,8 @@ func (t transaction) initiateTransaction(p transactionProcessingData) (transacti TxValue: p.executionRequest.TxValue, TxGasLimit: p.executionRequest.TxGasLimit, } - executor := *p.executor - txID, value, err := executor.Initiate(call) + + txID, value, err := (*p.executor).Initiate(call) p.cumulativeValue = value if err != nil { return p, common.StringError(err)