diff --git a/pkg/model/request.go b/pkg/model/request.go index 9f3ff64a..ca56c48e 100644 --- a/pkg/model/request.go +++ b/pkg/model/request.go @@ -1,6 +1,7 @@ package model import ( + "database/sql" "time" "github.com/jmoiron/sqlx/types" @@ -32,6 +33,17 @@ type TransactionUpdates struct { StringFee *string `json:"stringFee" db:"string_fee"` } +type InstrumentUpdates struct { + Type *string `json:"type" db:"type"` + Status *string `json:"status" db:"status"` + Tags *StringMap `json:"tags" db:"tags"` + Network *string `json:"network" db:"network"` + PublicKey *string `json:"publicKey" db:"public_key"` + Last4 *string `json:"last4" db:"last_4"` + UserID *string `json:"userId" db:"user_id"` + LocationID *sql.NullString `json:"locationId" db:"location_id"` +} + type TxLegUpdates struct { Timestamp *time.Time `json:"timestamp" db:"timestamp"` Amount *string `json:"amount" db:"amount"` diff --git a/pkg/repository/instrument.go b/pkg/repository/instrument.go index 6a816bc6..c6e17c30 100644 --- a/pkg/repository/instrument.go +++ b/pkg/repository/instrument.go @@ -13,9 +13,10 @@ import ( type Instrument interface { Transactable Create(model.Instrument) (model.Instrument, error) - GetById(id string) (model.Instrument, error) - GetWallet(addr string) (model.Instrument, error) Update(ID string, updates any) error + GetById(id string) (model.Instrument, error) + GetWalletByAddr(addr string) (model.Instrument, error) + GetCardByFingerprint(fingerprint string) (m model.Instrument, err error) GetWalletByUserId(userId string) (model.Instrument, error) GetBankByUserId(userId string) (model.Instrument, error) WalletAlreadyExists(addr string) (bool, error) @@ -48,7 +49,7 @@ func (i instrument[T]) Create(insert model.Instrument) (model.Instrument, error) return m, nil } -func (i instrument[T]) GetWallet(addr string) (model.Instrument, error) { +func (i instrument[T]) GetWalletByAddr(addr string) (model.Instrument, error) { m := model.Instrument{} err := i.store.Get(&m, fmt.Sprintf("SELECT * FROM %s WHERE public_key = $1", i.table), addr) if err != nil && err == sql.ErrNoRows { @@ -59,6 +60,10 @@ func (i instrument[T]) GetWallet(addr string) (model.Instrument, error) { return m, nil } +func (i instrument[T]) GetCardByFingerprint(fingerprint string) (m model.Instrument, err error) { + return i.GetWalletByAddr(fingerprint) +} + func (i instrument[T]) GetWalletByUserId(userId string) (model.Instrument, error) { m := model.Instrument{} err := i.store.Get(&m, fmt.Sprintf("SELECT * FROM %s WHERE user_id = $1 AND type = 'Crypto Wallet'", i.table), userId) @@ -82,7 +87,7 @@ func (i instrument[T]) GetBankByUserId(userId string) (model.Instrument, error) } func (i instrument[T]) WalletAlreadyExists(addr string) (bool, error) { - wallet, err := i.GetWallet(addr) + wallet, err := i.GetWalletByAddr(addr) if err != nil && errors.Cause(err).Error() != "not found" { // because we are wrapping error and care about its value return true, common.StringError(err) diff --git a/pkg/service/auth.go b/pkg/service/auth.go index e500d038..1eed666a 100644 --- a/pkg/service/auth.go +++ b/pkg/service/auth.go @@ -97,7 +97,7 @@ func (a auth) VerifySignedPayload(request model.WalletSignaturePayloadSigned) (U } // Verify user is registered to this wallet address - instrument, err := a.repos.Instrument.GetWallet(payload.Address) + instrument, err := a.repos.Instrument.GetWalletByAddr(payload.Address) if err != nil { return resp, common.StringError(err) } @@ -204,7 +204,7 @@ func (a auth) RefreshToken(refreshToken string, walletAddress string) (UserCreat // verify wallet address // Verify user is registered to this wallet address - instrument, err := a.repos.Instrument.GetWallet(walletAddress) + instrument, err := a.repos.Instrument.GetWalletByAddr(walletAddress) if err != nil { if strings.Contains(err.Error(), "not found") { return resp, common.StringError(errors.New("wallet address not associated with this user: " + walletAddress)) diff --git a/pkg/service/checkout.go b/pkg/service/checkout.go index 62e1f847..62d8910f 100644 --- a/pkg/service/checkout.go +++ b/pkg/service/checkout.go @@ -59,10 +59,11 @@ type AuthorizedCharge struct { CardType string } -func AuthorizeCharge(amount float64, userWallet string, tokenId string) (auth AuthorizedCharge, err error) { +func AuthorizeCharge(p transactionProcessingData) (transactionProcessingData, error) { + auth := AuthorizedCharge{} config, err := getConfig() if err != nil { - return auth, common.StringError(err) + return p, common.StringError(err) } client := payments.NewClient(*config) @@ -75,7 +76,7 @@ func AuthorizeCharge(amount float64, userWallet string, tokenId string) (auth Au Number: "4242424242424242", // Success // Number: "4273149019799094", // succeed authorize, fail capture // Number: "4544249167673670", // Declined - Insufficient funds - // Number: "5148447461737269", // Invalid transaction + // Number: "5148447461737269", // Invalid transaction (debit card) ExpiryMonth: 2, ExpiryYear: 2024, Name: "Customer Name", @@ -83,17 +84,17 @@ func AuthorizeCharge(amount float64, userWallet string, tokenId string) (auth Au } paymentToken, err := CreateToken(&card) if err != nil { - return auth, common.StringError(err) + return p, common.StringError(err) } paymentTokenID = paymentToken.Created.Token - if tokenId != "" { - paymentTokenID = tokenId + if p.executionRequest.CardToken != "" { + paymentTokenID = p.executionRequest.CardToken } } else { - paymentTokenID = tokenId + paymentTokenID = p.executionRequest.CardToken } - usd := convertAmount(amount) + usd := convertAmount(p.executionRequest.TotalUSD) capture := false request := &payments.Request{ @@ -104,7 +105,7 @@ func AuthorizeCharge(amount float64, userWallet string, tokenId string) (auth Au Amount: usd, Currency: "USD", Customer: &payments.Customer{ - Name: userWallet, + Name: p.executionRequest.UserAddress, }, Capture: &capture, } @@ -115,7 +116,7 @@ func AuthorizeCharge(amount float64, userWallet string, tokenId string) (auth Au } response, err := client.Request(request, ¶ms) if err != nil { - return auth, common.StringError(err) + return p, common.StringError(err) } // Collect authorization ID and Instrument ID @@ -132,9 +133,9 @@ func AuthorizeCharge(amount float64, userWallet string, tokenId string) (auth Au auth.CheckoutFingerprint = response.Processed.Source.CardSourceResponse.Fingerprint } } - + p.cardAuthorization = &auth // TODO: Create entry for authorization in our DB associated with userWallet - return auth, nil + return p, nil } func CaptureCharge(amount float64, userWallet string, authorizationID string) (capture *payments.CapturesResponse, err error) { diff --git a/pkg/service/transaction.go b/pkg/service/transaction.go index 5341a402..936fe7fa 100644 --- a/pkg/service/transaction.go +++ b/pkg/service/transaction.go @@ -449,8 +449,8 @@ func verifyQuote(e model.ExecutionRequest, newEstimate model.Quote) (bool, error return true, nil } -func (t transaction) addCardInstrumentIdIfNew(fingerprint string, userID string, last4 string, cardType string) (string, error) { - instrument, err := t.repos.Instrument.GetWallet(fingerprint) // temporarily using get wallet and storing it there +func (t transaction) addCardInstrumentIdIfNew(p transactionProcessingData) (string, error) { + instrument, err := t.repos.Instrument.GetCardByFingerprint(p.cardAuthorization.CheckoutFingerprint) if err != nil && !strings.Contains(err.Error(), "not found") { // because we are wrapping error and care about its value return "", common.StringError(err) } else if err == nil && instrument.UserID != "" { @@ -458,12 +458,18 @@ func (t transaction) addCardInstrumentIdIfNew(fingerprint string, userID string, } // We should gather type from the payment processor - instrument_type := "DebitCard" - if cardType == "CREDIT" { - instrument_type = "CreditCard" + instrument_type := "Debit Card" + if p.cardAuthorization.CardType == "CREDIT" { + instrument_type = "Credit Card" } // Create a new instrument - instrument = model.Instrument{Type: instrument_type, Status: "authorized", Last4: last4, UserID: userID, PublicKey: fingerprint} // No locationID until fingerprint + instrument = model.Instrument{ // No locationID until fingerprint + Type: instrument_type, + Status: "created", + Last4: p.cardAuthorization.Last4, + UserID: *p.userId, + PublicKey: p.cardAuthorization.CheckoutFingerprint, + } instrument, err = t.repos.Instrument.Create(instrument) if err != nil { return "", common.StringError(err) @@ -473,7 +479,7 @@ func (t transaction) addCardInstrumentIdIfNew(fingerprint string, userID string, } func (t transaction) addWalletInstrumentIdIfNew(address string, id string) (string, error) { - instrument, err := t.repos.Instrument.GetWallet(address) + instrument, err := t.repos.Instrument.GetWalletByAddr(address) if err != nil && !strings.Contains(err.Error(), "not found") { return "", common.StringError(err) } else if err == nil && instrument.PublicKey == address { @@ -492,13 +498,13 @@ func (t transaction) addWalletInstrumentIdIfNew(address string, id string) (stri func (t transaction) authCard(p transactionProcessingData) (transactionProcessingData, error) { // auth their card - auth, err := AuthorizeCharge(p.executionRequest.TotalUSD, p.executionRequest.UserAddress, p.executionRequest.CardToken) + p, err := AuthorizeCharge(p) if err != nil { 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, *p.userId, auth.Last4, auth.CardType) + instrumentId, err := t.addCardInstrumentIdIfNew(p) if err != nil { return p, common.StringError(err) } @@ -523,11 +529,7 @@ func (t transaction) authCard(p transactionProcessingData) (transactionProcessin return p, common.StringError(err) } - p.cardAuthorization = &auth - if err != nil { - return p, common.StringError(err) - } - err = t.updateTransactionStatus("Card "+auth.Status, p.transactionModel.ID) + err = t.updateTransactionStatus("Card "+p.cardAuthorization.Status, p.transactionModel.ID) if err != nil { return p, common.StringError(err) } @@ -560,7 +562,7 @@ func (t transaction) authCard(p transactionProcessingData) (transactionProcessin return p, common.StringError(err) } - if !auth.Approved { + if !p.cardAuthorization.Approved { err := t.unit21CreateTransaction(p.transactionModel.ID) if err != nil { return p, common.StringError(err) diff --git a/pkg/service/user.go b/pkg/service/user.go index 1d205ac1..303c213e 100644 --- a/pkg/service/user.go +++ b/pkg/service/user.go @@ -150,7 +150,7 @@ func (u user) createUserData(addr string) (model.User, error) { return user, common.StringError(err) } // Create a new wallet instrument and associate it with the new user - instrument := model.Instrument{Type: "crypto-wallet", Status: "verified", Network: "EVM", PublicKey: addr, UserID: user.ID} + instrument := model.Instrument{Type: "Crypto Wallet", Status: "verified", Network: "EVM", PublicKey: addr, UserID: user.ID} instrument, err = u.repos.Instrument.Create(instrument) if err != nil { u.repos.Instrument.Rollback()