Connect customer quotations and payment execution to Sendico (#50)
This commit is contained in:
1 parent
5d67c25c8e
commit
7a6510d1a8
45 files changed
+2696
-45
No files matched your search
@@ -0,0 +1,103 @@
|
||||
package sendico
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.sendico.io/tech/sxchnge/product/payments"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPaymentPublicWireAndExactMoney(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ledger := "507f1f77bcf86cd799439015"
|
||||
method := "507f1f77bcf86cd799439016"
|
||||
calls := 0
|
||||
acknowledge := true
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.NotEmpty(t, r.Header.Get("Authorization"))
|
||||
var result any
|
||||
switch r.URL.Path {
|
||||
case "/api/v1/payments/fx/currencies/" + org:
|
||||
result = map[string]any{"currencies": []any{map[string]any{"code": "EUR", "accountingScale": 2}}}
|
||||
case "/api/v1/ledger_accounts/" + org:
|
||||
result = map[string]any{"accounts": []any{
|
||||
map[string]any{"ledgerAccountRef": source, "organizationRef": org, "ownerRef": source, "currency": "EUR", "accountType": "ACCOUNT_TYPE_ASSET", "role": "ACCOUNT_ROLE_OPERATING", "status": "ACCOUNT_STATUS_ACTIVE"},
|
||||
map[string]any{"ledgerAccountRef": ledger, "organizationRef": org, "currency": "EUR", "accountType": "ACCOUNT_TYPE_ASSET", "role": "ACCOUNT_ROLE_OPERATING", "status": "ACCOUNT_STATUS_ACTIVE", "metadata": map[string]string{"sxchnge_subject_ref": subject, "sxchnge_payout_method_ref": method, "sxchnge_payout_label": "Saved bank ending 1234"}},
|
||||
}}
|
||||
case "/api/v1/ledger_accounts/" + org + "/" + ledger + "/balance":
|
||||
result = map[string]any{"balance": map[string]any{"ledgerAccountRef": ledger, "balance": moneyDTO{"75.00", "EUR"}}}
|
||||
case "/api/v1/payments/quote/" + org:
|
||||
var request quoteRequestDTO
|
||||
require.NoError(t, json.NewDecoder(r.Body).Decode(&request))
|
||||
require.Equal(t, subject, request.Intent.Subject)
|
||||
require.Equal(t, "20.01", request.Intent.Amount.Amount)
|
||||
require.Equal(t, ledger, request.Intent.Source.Data["account_ref"])
|
||||
require.Equal(t, method, request.Intent.Destination.Data["ref"])
|
||||
confirmedSubject := subject
|
||||
if !acknowledge {
|
||||
confirmedSubject = ""
|
||||
}
|
||||
result = map[string]any{"externalSubjectRef": confirmedSubject, "quote": paymentQuoteDTO{ID: "quote:1", State: "executable", Executable: true, Expires: time.Now().Add(time.Minute), Amounts: quoteAmountsDTO{Principal: moneyDTO{"20.01", "EUR"}, Debit: moneyDTO{"21.01", "EUR"}, Credit: moneyDTO{"20.01", "EUR"}}}}
|
||||
case "/api/v1/payments/by-quote/" + org:
|
||||
calls++
|
||||
var payload map[string]string
|
||||
require.NoError(t, json.NewDecoder(r.Body).Decode(&payload))
|
||||
require.Equal(t, "stable-execution", payload["idempotencyKey"])
|
||||
require.Equal(t, "quote:1", payload["quoteRef"])
|
||||
result = map[string]any{"payment": paymentDTO{ID: "payment:1", State: "orchestration_state_needs_attention", Quote: paymentQuoteDTO{ID: "quote:1"}, Created: time.Now()}}
|
||||
case "/api/v1/payments/by-ref/payment:1":
|
||||
result = map[string]any{"payment": paymentDTO{ID: "payment:1", State: "orchestration_state_settled", Quote: paymentQuoteDTO{ID: "quote:1"}, Created: time.Now()}}
|
||||
default:
|
||||
t.Errorf("unexpected path: %s", r.URL.Path)
|
||||
w.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
require.NoError(t, json.NewEncoder(w).Encode(map[string]any{"status": "success", "data": result}))
|
||||
}))
|
||||
defer server.Close()
|
||||
client, err := Open(config(t, server.URL))
|
||||
require.NoError(t, err)
|
||||
accounts, err := client.Accounts(ctx, subject)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, accounts, 1)
|
||||
require.EqualValues(t, 7500, accounts[0].Available)
|
||||
q, err := client.Quote(ctx, subject, payments.Request{Kind: "payout", Source: ledger, Amount: 2001, Recipient: method}, accounts)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, 100, q.Fee)
|
||||
require.EqualValues(t, 2101, q.Debit)
|
||||
require.Equal(t, "Saved bank ending 1234", q.Request.Recipient)
|
||||
_, err = client.Quote(ctx, subject, payments.Request{Kind: "payout", Source: source, Amount: 2001, Recipient: method}, accounts)
|
||||
require.ErrorIs(t, err, payments.NotFound)
|
||||
_, err = client.Quote(ctx, subject, payments.Request{Kind: "payout", Source: ledger, Amount: 2001, Recipient: "foreign-method"}, accounts)
|
||||
require.ErrorIs(t, err, payments.InvalidRecipient)
|
||||
acknowledge = false
|
||||
_, err = client.Quote(ctx, subject, payments.Request{Kind: "payout", Source: ledger, Amount: 2001, Recipient: method}, accounts)
|
||||
require.ErrorIs(t, err, payments.Unavailable)
|
||||
acknowledge = true
|
||||
op, err := client.Execute(ctx, q.ID, "stable-execution")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "pending", op.Status)
|
||||
require.Equal(t, 1, calls)
|
||||
op, err = client.Payment(ctx, op.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "completed", op.Status)
|
||||
}
|
||||
func TestPaymentMappingRejectsUnsafeOrUnexecutableValues(t *testing.T) {
|
||||
for _, amount := range []string{"1e3", "0.001", "-2", "NaN", "90071992547409.92", ""} {
|
||||
_, err := minorMoney(moneyDTO{amount, "EUR"}, "EUR", 2)
|
||||
require.Error(t, err, amount)
|
||||
}
|
||||
_, err := minorMoney(moneyDTO{"10.00", "USD"}, "EUR", 2)
|
||||
require.Error(t, err)
|
||||
_, err = mapPayment(paymentDTO{ID: "p", State: "new_unknown_state", Quote: paymentQuoteDTO{ID: "q"}, Created: time.Now()})
|
||||
require.Error(t, err)
|
||||
_, err = mapPaymentQuote(paymentQuoteDTO{State: "blocked", BlockReason: "limit_blocked"}, payments.Request{}, payments.Account{}, payments.Account{})
|
||||
require.ErrorIs(t, err, payments.LimitExceeded)
|
||||
_, err = mapPaymentQuote(paymentQuoteDTO{State: "executable", Executable: false}, payments.Request{}, payments.Account{}, payments.Account{})
|
||||
require.Error(t, err)
|
||||
}
|
||||
Reference in new issue
Block a user