# Amounts, wallets and payment states

## Live-payment availability

New LIVE collections and payouts require Fondeka to enable live API payments. Before launch or during a pause, new LIVE intents return HTTP 403 with `errorCode: BUSINESS_API_LIVE_PAYMENTS_DISABLED`; use SANDBOX for integration testing. Existing accepted payments continue processing, and their status reads, idempotent replays and webhooks remain available. A launch rejection does not consume the idempotency key. The gate applies to LIVE credentials in every server profile, including local development.

## Financial ownership

An API key belongs to one Business. The Business's owning account owns the LIVE fiat wallets. Multiple Businesses under that account share those balances; their credentials, payments and idempotency scopes remain separate. Sandbox wallets are isolated virtual account wallets, also shared by that account's Businesses.

Business API payments are excluded from the client app's general transaction history and detail routes (`/customer-api/transactions`), including LIVE payments. Read them through the merchant API or the portal's dedicated `/customer-api/businesses/{businessId}/api-transactions` routes, with the appropriate environment. LIVE payments remain real financial transactions and are included in admin reporting and applicable loan-eligibility rules. SANDBOX payments remain in separate tables: they do not appear in admin transaction views and do not contribute to financial reports, analytics, real balances or loan eligibility. Sandbox history and virtual balances remain available through the dedicated sandbox/portal APIs.

## Fiat and crypto amounts

Request `amount` and `currency` always describe fiat, even for a crypto method. `10 USD` with `USDT/TRC20` is a USD-priced payment. Use the returned `cryptoDetails.amountInCrypto`, network and address for the actual crypto transfer. A BTC quantity is not the same as the transaction's fiat amount. Monetary requests permit at most two decimal places; compute money with decimal arithmetic rather than binary floating point.

`cryptoDetails.exchangeRate` is denominated in `exchangeRateCurrency` per token. LIVE quotes use USD/token even when the wallet is in CDF or another fiat currency. SANDBOX uses a synthetic 1:1 rate in the payment's fiat currency. Network aliases such as TRX/TRON are returned as TRC20, and ETH/ETHEREUM as ERC20. Always use the returned token amount rather than deriving it from the fiat wallet amount and a rate in another currency.

| Field | Meaning |
| --- | --- |
| `fondekaReference` | Opaque Fondeka payment identifier; use it in the existing `/transactions/{reference}` path |
| `orderReference` | Your optional order/invoice correlation value |
| `service`, `action` | BUSINESS_API and the collection/payout operation |
| `amount` | Net fiat amount; quote before completion, reconciled wallet credit for a completed collection |
| `fee` | One consolidated fee, without internal/provider components |
| `grossAmount` | `amount + fee`; payout wallet debit, or reconciled gross for a completed collection |
| `currency`, `environment`, `status` | Fiat denomination, LIVE/SANDBOX and public payment state |
| `cryptoDetails` | Crypto instructions only when available; omitted for mobile money and while queued |
| `failureCode`, `failureMessage` | Optional sanitized failure details when available |

The payment object omits Business/financial transaction IDs, fee splits, duplicate settlement fields and the refund flag. The Business is selected by the authenticated key and request path. Never use an internal numeric transaction ID for payment lookup.

EXCLUSIVE requests net and adds fees. INCLUSIVE requests gross and deducts fees. For example, with total fees of 0.30, EXCLUSIVE amount 10 produces net 10/gross 10.30; INCLUSIVE amount 10 produces gross 10/net 9.70. Actual LIVE percentages, flat fees, minimums and rounding depend on the route. An omitted/null mode uses the applicable route default; sandbox defaults to EXCLUSIVE.

For LIVE EXCLUSIVE collections, tariff bands are selected using the gross amount sent to the provider. A fee can move the payment into the next band. Inconsistent bands that cannot produce a stable quote are rejected before any financial transaction is created. Accepted payments retain their original quote; later fee configuration changes do not reprice them.

Internal/admin USD reporting for Business API payments retains up to 18 decimal places using the acceptance-time exchange rate. Aggregate those decimal values before rounding for display. Merchant payment fields above remain in the transaction's actual fiat currency.

Crypto under/overpayments can produce different settlement from the original quote. On COMPLETED collections, `amount`, `grossAmount` and `fee` show that reconciled settlement; use `amount` and `currency` for your order-fulfillment amount checks. Before completion these fields remain the accepted quote. No accepted quote, wallet posting or provider request is repriced by this response projection.

## Lifecycle and wallet effects

| State | Your integration should do |
| --- | --- |
| `ACCEPTED` | API submission is queued for execution; preserve its reference and poll with backoff |
| `PROCESSING` | Execution started or a provider/reconciliation outcome remains unresolved; poll with backoff and await the terminal event |
| `COMPLETED` | Reconcile success; completed collections credit net once, completed payouts do not debit again |
| `FAILED` | Reconcile failure, including cancellation or expiration; never infer a payout refund from status |

These are the only four payment statuses in both LIVE and SANDBOX, including the authenticated portal's payment snapshots. A new HTTP `202 Accepted` intent returns a payment with `status: "ACCEPTED"`; an idempotent replay returns its current state. Both ACCEPTED and PROCESSING are pending states; PENDING is not a separate public value. A payment awaiting reconciliation remains PROCESSING until its result is established. Keep its reference and original idempotency key; contact support if it remains unresolved rather than creating another payment automatically.

An accepted payout debits/reserves gross immediately. A completed collection credits only after payment confirmation. Failed/canceled payouts can require manual refunds when automatic refund policy is disabled; the status alone does not promise a refund. The compact payment object does not expose a refund flag. Reconcile wallet records or contact support for an uncertain refund; do not create a replacement payout automatically. Webhooks remain immutable snapshots. Unknown outcomes retain reserved payout funds until reconciliation.

## Request identity and retries

Use `Idempotency-Key` on collections, payouts, sandbox top-ups and sandbox conversions. Allowed values are 1–128 ASCII letters, digits, `.`, `:`, `_` or `-`. Payment keys are scoped to Business+environment across keys and collection/payout directions. Sandbox funding and conversion keys have account-level scopes for their respective operations. Converting between a name and its matching legacy ID does not create a new payment.

After a timeout or retryable HTTP failure, retry the same logical operation with the same key and payload. A 409 means that key already belongs to different normalized content; recover the original operation before deciding to create a new intent. `orderReference` is your correlation field, not a substitute for idempotency.

Each merchant HTTP attempt returns a fresh `X-Request-Id`, also present as `requestId` in errors. Preserve it when asking support for help. Webhook event IDs are separate identifiers and stay stable across delivery retries.
