Files
sxchnge/backend/product/providers/sendico/payments.go

109 lines
3.5 KiB
Go

package sendico
import (
"context"
"net/http"
"git.sendico.io/tech/sxchnge/product/payments"
"go.mongodb.org/mongo-driver/v2/bson"
)
// Accounts exposes only dedicated, organization-owned customer ledgers tagged by
// trusted platform provisioning. The integration login's personal/pooled ledgers
// never become customer balances. Method bindings are provisioned on that ledger.
func (c *Client) Accounts(ctx context.Context, subject string) ([]payments.Account, error) {
if !validRef(subject) {
return nil, payments.Unavailable
}
var currencies struct {
Rows []currencyDTO `json:"currencies"`
}
if err := c.paymentRequest(ctx, http.MethodGet, "/payments/fx/currencies/"+c.config.OrganizationID, nil, &currencies); err != nil {
return nil, err
}
scales := map[string]int{}
for _, row := range currencies.Rows {
if row.Scale != nil && *row.Scale >= 0 && *row.Scale <= 8 {
scales[row.Code] = *row.Scale
}
}
var data accountListDTO
root := "/ledger_accounts/" + c.config.OrganizationID
if err := c.paymentRequest(ctx, http.MethodGet, root, nil, &data); err != nil {
return nil, err
}
accounts := []payments.Account{}
for _, row := range data.Accounts {
if row.Metadata["sxchnge_subject_ref"] != subject {
continue
}
if !c.validCustomerAccount(row, subject) {
return nil, payments.Unavailable
}
scale, ok := scales[row.Currency]
if !ok {
return nil, payments.Unsupported
}
var balance struct {
Value balanceDTO `json:"balance"`
}
if err := c.paymentRequest(ctx, http.MethodGet, root+"/"+row.ID+"/balance", nil, &balance); err != nil {
return nil, err
}
account, err := mapAccount(row, balance.Value, scale)
if err != nil {
return nil, err
}
accounts = append(accounts, account)
}
return accounts, nil
}
func (c *Client) Quote(ctx context.Context, subject string, r payments.Request, accounts []payments.Account) (payments.Quote, error) {
mapped, err := mapIntent(subject, r, accounts)
if err != nil {
return payments.Quote{}, err
}
var response struct {
Quote paymentQuoteDTO `json:"quote"`
Subject string `json:"externalSubjectRef"`
}
err = c.paymentRequest(ctx, http.MethodPost, "/payments/quote/"+c.config.OrganizationID, quoteRequestDTO{Key: "sxchnge:quote:" + bson.NewObjectID().Hex(), Intent: mapped.wire}, &response)
if err != nil {
return payments.Quote{}, err
}
if response.Subject != subject {
return payments.Quote{}, payments.Unavailable
}
return mapPaymentQuote(response.Quote, mapped.request, mapped.source, mapped.destination)
}
func (c *Client) Execute(ctx context.Context, quote, key string) (payments.RemoteOperation, error) {
if !safePaymentRef(quote) {
return payments.RemoteOperation{}, payments.Unavailable
}
var result struct {
Payment paymentDTO `json:"payment"`
}
payload := struct {
Quote string `json:"quoteRef"`
Key string `json:"idempotencyKey"`
}{quote, key}
err := c.paymentRequest(ctx, http.MethodPost, "/payments/by-quote/"+c.config.OrganizationID, payload, &result)
if err != nil {
return payments.RemoteOperation{}, err
}
return mapPayment(result.Payment)
}
func (c *Client) Payment(ctx context.Context, id string) (payments.RemoteOperation, error) {
if !safePaymentRef(id) {
return payments.RemoteOperation{}, payments.Unavailable
}
var result struct {
Payment paymentDTO `json:"payment"`
}
err := c.paymentRequest(ctx, http.MethodGet, "/payments/by-ref/"+id, nil, &result)
if err != nil {
return payments.RemoteOperation{}, err
}
return mapPayment(result.Payment)
}