Skip to content

Commit

Permalink
[SDP-962] Change SEP-24 Flow to display different verifications based…
Browse files Browse the repository at this point in the history
… on Disbursement's verification type (#116)

Modify the SEP-24 flow to perform verification for an entered phone number based on the latest verification type.

The current SEP-24 flow is hardcoded to only accept date of birth but we will have disbursement files that will include pin and national id, and the front-end will need to change to be able to parse those values.

Release 1.0.1 to develop (#129)

[Release 1.0.1](#127) to develop

To sync the `main` branch hotfixes:
- #125
- #126

changes:

fix tests

migrate
  • Loading branch information
ziyliu committed Dec 21, 2023
1 parent 9424bf7 commit 5a7e1cd
Show file tree
Hide file tree
Showing 24 changed files with 339 additions and 66 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

- Add sorting to `GET /users` endpoint [#104](https://github.com/stellar/stellar-disbursement-platform-backend/pull/104)

## [1.0.1](https://github.com/stellar/stellar-disbursement-platform-backend/compare/1.0.0...1.0.1)

### Changed

- Update log message for better debugging. [#125](https://github.com/stellar/stellar-disbursement-platform-backend/pull/125)

### Fixed

- Fix client_domain from the Viobrant Assist wallet. [#126](https://github.com/stellar/stellar-disbursement-platform-backend/pull/126)

## [1.0.0](https://github.com/stellar/stellar-disbursement-platform-backend/compare/1.0.0-rc2...1.0.0)

### Added
Expand Down
4 changes: 2 additions & 2 deletions cmd/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
// Test the two wallets
assert.Equal(t, "Vibrant Assist", vibrantAssist.Name)
assert.Equal(t, "https://vibrantapp.com/vibrant-assist", vibrantAssist.Homepage)
assert.Equal(t, "api.vibrantapp.com", vibrantAssist.SEP10ClientDomain)
assert.Equal(t, "vibrantapp.com", vibrantAssist.SEP10ClientDomain)
assert.Equal(t, "https://vibrantapp.com/sdp", vibrantAssist.DeepLinkSchema)

assert.Equal(t, "Vibrant Assist RC", vibrantAssistRC.Name)
Expand All @@ -244,7 +244,7 @@ func Test_DatabaseCommand_db_setup_for_network(t *testing.T) {
"Name: Vibrant Assist",
"Homepage: https://vibrantapp.com/vibrant-assist",
"Deep Link Schema: https://vibrantapp.com/sdp",
"SEP-10 Client Domain: api.vibrantapp.com",
"SEP-10 Client Domain: vibrantapp.com",
}

logs := buf.String()
Expand Down
2 changes: 1 addition & 1 deletion helmchart/sdp/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v2
name: stellar-disbursement-platform
description: A Helm chart for the Stellar Disbursement Platform Backend (A.K.A. `sdp`)
version: 0.9.3
appVersion: "1.0.0"
appVersion: "1.0.1"
type: application
maintainers:
- name: Stellar Development Foundation
Expand Down
20 changes: 11 additions & 9 deletions internal/data/disbursement_instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import (
)

type DisbursementInstruction struct {
Phone string `csv:"phone"`
ID string `csv:"id"`
Amount string `csv:"amount"`
VerificationValue string `csv:"verification"`
Phone string `csv:"phone"`
ID string `csv:"id"`
Amount string `csv:"amount"`
VerificationValue string `csv:"verification"`
ExternalPaymentId *string `csv:"paymentID"`
}

type DisbursementInstructionModel struct {
Expand Down Expand Up @@ -194,11 +195,12 @@ func (di DisbursementInstructionModel) ProcessAll(ctx context.Context, userID st
for _, instruction := range instructions {
receiver := receiverMap[instruction.Phone]
payment := PaymentInsert{
ReceiverID: receiver.ID,
DisbursementID: disbursement.ID,
Amount: instruction.Amount,
AssetID: disbursement.Asset.ID,
ReceiverWalletID: receiverWalletsMap[receiver.ID],
ReceiverID: receiver.ID,
DisbursementID: disbursement.ID,
Amount: instruction.Amount,
AssetID: disbursement.Asset.ID,
ReceiverWalletID: receiverWalletsMap[receiver.ID],
ExternalPaymentID: instruction.ExternalPaymentId,
}
payments = append(payments, payment)
}
Expand Down
29 changes: 29 additions & 0 deletions internal/data/disbursement_instructions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package data

import (
"context"
"database/sql"
"testing"

"github.com/stellar/stellar-disbursement-platform-backend/internal/db"
Expand Down Expand Up @@ -47,16 +48,19 @@ func Test_DisbursementInstructionModel_ProcessAll(t *testing.T) {
VerificationValue: "1990-01-02",
}

externalPaymentID := "abc123"
instruction3 := DisbursementInstruction{
Phone: "+380-12-345-673",
Amount: "100.03",
ID: "123456783",
VerificationValue: "1990-01-03",
ExternalPaymentId: &externalPaymentID,
}
instructions := []*DisbursementInstruction{&instruction1, &instruction2, &instruction3}
expectedPhoneNumbers := []string{instruction1.Phone, instruction2.Phone, instruction3.Phone}
expectedExternalIDs := []string{instruction1.ID, instruction2.ID, instruction3.ID}
expectedPayments := []string{instruction1.Amount, instruction2.Amount, instruction3.Amount}
expectedExternalPaymentIDs := []string{*instruction3.ExternalPaymentId}

disbursementUpdate := &DisbursementUpdate{
ID: disbursement.ID,
Expand Down Expand Up @@ -91,6 +95,9 @@ func Test_DisbursementInstructionModel_ProcessAll(t *testing.T) {
actualPayments := GetPaymentsByDisbursementID(t, ctx, dbConnectionPool, disbursement.ID)
assert.Equal(t, expectedPayments, actualPayments)

actualExternalPaymentIDs := GetExternalPaymentIDsByDisbursementID(t, ctx, dbConnectionPool, disbursement.ID)
assert.Equal(t, expectedExternalPaymentIDs, actualExternalPaymentIDs)

// Verify Disbursement
actualDisbursement, err := di.disbursementModel.Get(ctx, dbConnectionPool, disbursement.ID)
require.NoError(t, err)
Expand Down Expand Up @@ -304,3 +311,25 @@ func GetPaymentsByDisbursementID(t *testing.T, ctx context.Context, dbConnection
require.NoError(t, err)
return payments
}

func GetExternalPaymentIDsByDisbursementID(t *testing.T, ctx context.Context, dbConnectionPool db.DBConnectionPool, disbursementID string) []string {
query := `
SELECT
p.external_payment_id
FROM
payments p
WHERE p.disbursement_id = $1
`
var externalPaymentIDRefs []sql.NullString
err := dbConnectionPool.SelectContext(ctx, &externalPaymentIDRefs, query, disbursementID)
require.NoError(t, err)

var externalPaymentIDs []string
for _, v := range externalPaymentIDRefs {
if v.String != "" {
externalPaymentIDs = append(externalPaymentIDs, v.String)
}
}

return externalPaymentIDs
}
6 changes: 3 additions & 3 deletions internal/data/disbursements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,9 +437,9 @@ func Test_DisbursementModel_Update(t *testing.T) {
})

disbursementFileContent := CreateInstructionsFixture(t, []*DisbursementInstruction{
{"1234567890", "1", "123.12", "1995-02-20"},
{"0987654321", "2", "321", "1974-07-19"},
{"0987654321", "3", "321", "1974-07-19"},
{"1234567890", "1", "123.12", "1995-02-20", nil},
{"0987654321", "2", "321", "1974-07-19", nil},
{"0987654321", "3", "321", "1974-07-19", nil},
})

t.Run("update instructions", func(t *testing.T) {
Expand Down
10 changes: 5 additions & 5 deletions internal/data/fixtures_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ func Test_Fixtures_CreateInstructionsFixture(t *testing.T) {

t.Run("writes records correctly", func(t *testing.T) {
instructions := []*DisbursementInstruction{
{"1234567890", "1", "123.12", "1995-02-20"},
{"0987654321", "2", "321", "1974-07-19"},
{"1234567890", "1", "123.12", "1995-02-20", nil},
{"0987654321", "2", "321", "1974-07-19", nil},
}
buf := CreateInstructionsFixture(t, instructions)
lines := strings.Split(string(buf), "\n")
Expand All @@ -116,9 +116,9 @@ func Test_Fixtures_UpdateDisbursementInstructionsFixture(t *testing.T) {
})

instructions := []*DisbursementInstruction{
{"1234567890", "1", "123.12", "1995-02-20"},
{"0987654321", "2", "321", "1974-07-19"},
{"0987654321", "3", "321", "1974-07-19"},
{"1234567890", "1", "123.12", "1995-02-20", nil},
{"0987654321", "2", "321", "1974-07-19", nil},
{"0987654321", "3", "321", "1974-07-19", nil},
}

t.Run("update instructions", func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion internal/data/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func NewModels(dbConnectionPool db.DBConnectionPool) (*Models, error) {
Payment: &PaymentModel{dbConnectionPool: dbConnectionPool},
Receiver: &ReceiverModel{},
DisbursementInstructions: NewDisbursementInstructionModel(dbConnectionPool),
ReceiverVerification: &ReceiverVerificationModel{},
ReceiverVerification: &ReceiverVerificationModel{dbConnectionPool: dbConnectionPool},
ReceiverWallet: &ReceiverWalletModel{dbConnectionPool: dbConnectionPool},
DisbursementReceivers: &DisbursementReceiverModel{dbConnectionPool: dbConnectionPool},
Message: &MessageModel{dbConnectionPool: dbConnectionPool},
Expand Down
22 changes: 14 additions & 8 deletions internal/data/payments.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type Payment struct {
ReceiverWallet *ReceiverWallet `json:"receiver_wallet,omitempty" db:"receiver_wallet"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
ExternalPaymentID *string `json:"external_payment_id,omitempty" db:"external_payment_id"`
}

type PaymentStatusHistoryEntry struct {
Expand All @@ -50,11 +51,12 @@ var (
)

type PaymentInsert struct {
ReceiverID string `db:"receiver_id"`
DisbursementID string `db:"disbursement_id"`
Amount string `db:"amount"`
AssetID string `db:"asset_id"`
ReceiverWalletID string `db:"receiver_wallet_id"`
ReceiverID string `db:"receiver_id"`
DisbursementID string `db:"disbursement_id"`
Amount string `db:"amount"`
AssetID string `db:"asset_id"`
ReceiverWalletID string `db:"receiver_wallet_id"`
ExternalPaymentID *string `db:"external_payment_id"`
}

type PaymentUpdate struct {
Expand Down Expand Up @@ -150,6 +152,7 @@ func (p *PaymentModel) Get(ctx context.Context, id string, sqlExec db.SQLExecute
p.status_history,
p.created_at,
p.updated_at,
p.external_payment_id,
d.id as "disbursement.id",
d.name as "disbursement.name",
d.status as "disbursement.status",
Expand Down Expand Up @@ -227,6 +230,7 @@ func (p *PaymentModel) GetAll(ctx context.Context, queryParams *QueryParams, sql
p.status_history,
p.created_at,
p.updated_at,
p.external_payment_id,
d.id as "disbursement.id",
d.name as "disbursement.name",
d.status as "disbursement.status",
Expand Down Expand Up @@ -341,18 +345,20 @@ func (p *PaymentModel) InsertAll(ctx context.Context, sqlExec db.SQLExecuter, in
asset_id,
receiver_id,
disbursement_id,
receiver_wallet_id
receiver_wallet_id,
external_payment_id
) VALUES (
$1,
$2,
$3,
$4,
$5
$5,
$6
)
`

for _, payment := range inserts {
_, err := sqlExec.ExecContext(ctx, query, payment.Amount, payment.AssetID, payment.ReceiverID, payment.DisbursementID, payment.ReceiverWalletID)
_, err := sqlExec.ExecContext(ctx, query, payment.Amount, payment.AssetID, payment.ReceiverID, payment.DisbursementID, payment.ReceiverWalletID, payment.ExternalPaymentID)
if err != nil {
return fmt.Errorf("error inserting payment: %w", err)
}
Expand Down
44 changes: 37 additions & 7 deletions internal/data/receiver_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package data

import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"time"
Expand All @@ -24,7 +26,9 @@ type ReceiverVerification struct {
FailedAt *time.Time `db:"failed_at"`
}

type ReceiverVerificationModel struct{}
type ReceiverVerificationModel struct {
dbConnectionPool db.DBConnectionPool
}

type ReceiverVerificationInsert struct {
ReceiverID string `db:"receiver_id"`
Expand All @@ -48,7 +52,7 @@ func (rvi *ReceiverVerificationInsert) Validate() error {
}

// GetByReceiverIDsAndVerificationField returns receiver verifications by receiver IDs and verification type.
func (m ReceiverVerificationModel) GetByReceiverIDsAndVerificationField(ctx context.Context, sqlExec db.SQLExecuter, receiverIds []string, verificationField VerificationField) ([]*ReceiverVerification, error) {
func (m *ReceiverVerificationModel) GetByReceiverIDsAndVerificationField(ctx context.Context, sqlExec db.SQLExecuter, receiverIds []string, verificationField VerificationField) ([]*ReceiverVerification, error) {
receiverVerifications := []*ReceiverVerification{}
query := `
SELECT
Expand All @@ -74,7 +78,7 @@ func (m ReceiverVerificationModel) GetByReceiverIDsAndVerificationField(ctx cont
}

// GetAllByReceiverId returns all receiver verifications by receiver id.
func (m ReceiverVerificationModel) GetAllByReceiverId(ctx context.Context, sqlExec db.SQLExecuter, receiverId string) ([]ReceiverVerification, error) {
func (m *ReceiverVerificationModel) GetAllByReceiverId(ctx context.Context, sqlExec db.SQLExecuter, receiverId string) ([]ReceiverVerification, error) {
receiverVerifications := []ReceiverVerification{}
query := `
SELECT
Expand All @@ -91,8 +95,35 @@ func (m ReceiverVerificationModel) GetAllByReceiverId(ctx context.Context, sqlEx
return receiverVerifications, nil
}

// GetLatestByPhoneNumber returns the latest updated receiver verification for some receiver that is associated with a phone number.
func (m *ReceiverVerificationModel) GetLatestByPhoneNumber(ctx context.Context, phoneNumber string) (*ReceiverVerification, error) {
receiverVerification := ReceiverVerification{}
query := `
SELECT
rv.*
FROM
receiver_verifications rv
JOIN receivers r ON rv.receiver_id = r.id
WHERE
r.phone_number = $1
ORDER BY
rv.updated_at DESC
LIMIT 1
`

err := m.dbConnectionPool.GetContext(ctx, &receiverVerification, query, phoneNumber)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrRecordNotFound
}
return nil, fmt.Errorf("fetching receiver verifications for phone number %s: %w", phoneNumber, err)
}

return &receiverVerification, nil
}

// Insert inserts a new receiver verification
func (m ReceiverVerificationModel) Insert(ctx context.Context, sqlExec db.SQLExecuter, verificationInsert ReceiverVerificationInsert) (string, error) {
func (m *ReceiverVerificationModel) Insert(ctx context.Context, sqlExec db.SQLExecuter, verificationInsert ReceiverVerificationInsert) (string, error) {
err := verificationInsert.Validate()
if err != nil {
return "", fmt.Errorf("error validating receiver verification insert: %w", err)
Expand All @@ -111,7 +142,6 @@ func (m ReceiverVerificationModel) Insert(ctx context.Context, sqlExec db.SQLExe
`

_, err = sqlExec.ExecContext(ctx, query, verificationInsert.ReceiverID, verificationInsert.VerificationField, hashedValue)

if err != nil {
return "", fmt.Errorf("error inserting receiver verification: %w", err)
}
Expand All @@ -120,7 +150,7 @@ func (m ReceiverVerificationModel) Insert(ctx context.Context, sqlExec db.SQLExe
}

// UpdateVerificationValue updates the hashed value of a receiver verification.
func (m ReceiverVerificationModel) UpdateVerificationValue(ctx context.Context,
func (m *ReceiverVerificationModel) UpdateVerificationValue(ctx context.Context,
sqlExec db.SQLExecuter,
receiverID string,
verificationField VerificationField,
Expand Down Expand Up @@ -148,7 +178,7 @@ func (m ReceiverVerificationModel) UpdateVerificationValue(ctx context.Context,
}

// UpdateVerificationValue updates the hashed value of a receiver verification.
func (m ReceiverVerificationModel) UpdateReceiverVerification(ctx context.Context, receiverVerification ReceiverVerification, sqlExec db.SQLExecuter) error {
func (m *ReceiverVerificationModel) UpdateReceiverVerification(ctx context.Context, receiverVerification ReceiverVerification, sqlExec db.SQLExecuter) error {
query := `
UPDATE
receiver_verifications
Expand Down
Loading

0 comments on commit 5a7e1cd

Please sign in to comment.