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, ¤cies); 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 struct { Accounts []accountDTO `json:"accounts"` } 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 !validRef(row.ID) || row.ScopeRef != c.config.OrganizationID || row.Owner != "" || row.Status != "ACCOUNT_STATUS_ACTIVE" || row.Type != "ACCOUNT_TYPE_ASSET" || row.LedgerRole != "ACCOUNT_ROLE_OPERATING" { 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) }