跳到正文

API 参考

The Sonco Pay API lets your server create crypto payments, follow them until they are paid, read your balance and withdraw it. Every request and response is JSON over HTTPS.

基础 URL
https://soncopay.example

接口列表

接口描述
POST/v1/payments创建付款
GET/v1/payments列出付款
GET/v1/payments/{id}查询付款
POST/v1/payments/{id}/method选择支付方式
POST/v1/payments/{id}/simulate模拟付款(测试模式)
GET/v1/payments/{id}/timeline付款时间线
GET/v1/methods可用的支付方式、资产和网络
GET/v1/rates当前美元汇率
GET/v1/balances各资产余额
GET/v1/ledger余额变动
GET/v1/statements账户对账单
GET/v1/statements.csv账户对账单(CSV)
POST/v1/payouts创建提现
GET/v1/payouts列出提现
GET/v1/payouts/{id}查询提现
GET/v1/payouts/{id}/timeline提现时间线
GET/v1/payout-fees网络费用和最低提现额
GET/v1/payout-destinations列出提现目的地
POST/v1/payout-destinations保存提现目的地
GET/v1/payout-destinations/{id}查询提现目的地
PATCH/v1/payout-destinations/{id}重命名提现目的地
DELETE/v1/payout-destinations/{id}停用提现目的地
GET/v1/settlement结算模式与目的地
GET/v1/settlement/items待结算付款
PUT/v1/settlement/routes选择结算目的地
GET/v1/fees您的费用和限额
POST/v1/customers客户、发票、订阅
GET/v1/customers发票和订阅
POST/v1/invoices创建发票
GET/v1/invoices发票和订阅
POST/v1/invoices/{id}/send发票和订阅
POST/v1/invoices/{id}/remind发票和订阅
POST/v1/invoices/{id}/void发票和订阅
POST/v1/subscriptions创建订阅
POST/v1/subscriptions/{id}/pause发票和订阅
POST/v1/subscriptions/{id}/cancel发票和订阅
GET/v1/billing/summary账单摘要和测试时钟

SDK

Official libraries for Node.js, PHP and Python call the API for you: authentication with your key, JSON errors turned into exceptions, and webhook signature checks.

Version 2.2.0 adds customers, invoices and subscriptions (customers, plans, invoices, subscriptions and billing resources, invoice.* and subscription.* events). It is backward compatible with 2.0.0, the first release under the Sonco Pay name.

安装
npm install https://soncopay.example/assets/sdk/soncopay-node-2.2.0.tgz
快速开始
import SoncoPay from 'soncopay-node';
// const { SoncoPay } = require('soncopay-node');

const sp = new SoncoPay(process.env.SONCOPAY_API_KEY);

const payment = await sp.payments.create({
  amount: '25.00',
  currency: 'USD',
  order_id: 'demo-1042',
  return_url: 'https://shop.example/thanks',
});
res.redirect(303, payment.checkout_url);

验证 Webhook

先测试Use a sp_test_… key while you build: the SDKs work the same way in both modes.
验证 Webhook
app.post('/webhooks/soncopay', express.raw({ type: 'application/json' }), (req, res) => {
  let event;
  try {
    event = SoncoPay.webhooks.constructEvent(req.body, req.headers, process.env.SONCOPAY_WEBHOOK_SECRET);
  } catch (err) {
    return res.status(400).send('invalid signature');
  }
  if (event.event === 'payment.paid') {
    // deliver event.data.order_id
  }
  res.sendStatus(200);
});

身份验证

Send your API key in the X-Api-Key header. Create keys in your dashboard under Developers.

sp_live_…

Moves real money. Use it only on your production server. Live keys work once your business verification is approved.

sp_test_…

Works on separate test data: no real payment is taken and payments can be simulated.

请妥善保管您的密钥Keep your keys on your server. Never put them in a web page or a mobile app.
请求
curl https://soncopay.example/v1/balances -H "X-Api-Key: sp_test_…"

付款流程

  1. Your server creates a payment with the amount and your order ID.
  2. You redirect your customer to checkout_url, or you choose the method yourself and display the details on your own page (white-label).
  3. The customer pays in crypto on the chosen network (the exact amount), with Binance Pay, by mobile money or by bank card where it is available.
  4. We confirm the payment, send you a payment.paid webhook and credit your balance, minus the fee.
精确金额匹配On networks, every open payment receives a unique exact amount (for example 25.0137 USDT). That is how a deposit is matched to its order: tell your customer to send exactly this amount.

创建付款

POST/v1/payments

字段描述
amount必填字符串或数字Amount to collect.
currency必填字符串USD, EUR, XOF, XAF, or a crypto asset (USDT, USDC, BTC, ETH, BNB, TRX, LTC). Fiat amounts are converted at the current rate when the customer chooses a method.
order_id字符串Your reference. Sending the same order_id again returns the same payment, so retries are safe.
description字符串Shown to the customer on the payment page.
customer_email字符串Your customer's email, for your records.
return_url字符串Where the customer goes back after paying.
callback_url字符串Webhook URL for this payment only, as a public HTTPS address. By default, the URL of your account is used.
lifetime_minutes整数Between 10 and 1440. By default, the lifetime set by Sonco Pay.
methods数组Restrict the choices, for example ["USDT"], ["binance_pay"], ["USDT:TRON"], ["mobile_money"] or a method code such as ["orange_ci"].
metadata对象Any JSON object, returned as is.
country字符串Customer country (ISO alpha-2, for example CI, SN or CM): only the methods of this country are offered.
method字符串A mobile money method code (see Mobile money): the request is sent to the customer right away.
phone字符串With method: the mobile money number, in international format.
operator字符串With method: the operator, when the method needs one.
payer对象Information about the payer, all optional: name, email, phone (international format), country (ISO alpha-2) and reference (your own customer reference). See Payer information.

付款人信息

In the dashboard (Settings), choose for each field whether it is not collected, optional or required. A required field that your call does not send is asked to the customer on the payment page, before they choose how to pay. An email address also receives the payment receipt. Only the fields you collect are stored; they are kept for a limited time and never shown to anyone else.

The payment object carries payer (the collected fields, or null) in every response and in the signed webhooks. If you choose the method yourself with the API while a required field is missing, the call fails with 422 and the code payer_required (with fields); an invalid value fails with payer_invalid (with field).

请求
curl -X POST https://soncopay.example/v1/payments \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{"amount":"25","currency":"USD","order_id":"1042","return_url":"https://shop.example/thanks",
       "payer":{"name":"Demo Customer","email":"[email protected]","reference":"client-42"}}'
响应201
{
  "id": "pay_3f9a5c0d21e4b7a6c8d1",
  "object": "payment",
  "livemode": false,
  "status": "new",
  "amount": "25", "currency": "USD",
  "order_id": "1042",
  "checkout_url": "…/pay/pay_3f9a5c0d21e4b7a6c8d1",
  "payer": { "name": "Demo Customer", "email": "[email protected]", "reference": "client-42" },
  "expires_at": "2026-10-01T15:30:00+00:00",
  …
}

选择支付方式

Skip this step if you use checkout_url: the customer chooses on our page. To build your own page (white-label), list the available methods, then choose one.

GET/v1/methods

Each entry of methods has a method (crypto, binance_pay, mobile_money or card), a code and a kind. Mobile money entries add operator, countries, currency, redirect and fields (what to send: phone, sometimes operator). The payment_methods list gives the methods usable by your account, with your effective fees.

请求
curl https://soncopay.example/v1/methods -H "X-Api-Key: sp_test_…"

POST/v1/payments/{id}/method

字段描述
method必填字符串crypto, binance_pay, card, mobile_money (with code) or a mobile money method code.
asset字符串For crypto: the asset to receive, for example USDT.
network字符串For crypto: TRON, BSC, ETH, POLYGON, BTC or LTC.
code字符串With mobile_money: the method code, for example orange_ci or mtn_cm.
phone字符串For mobile money: the customer number, in international format.
operator字符串For mobile money: the operator, when the method needs one.

The bank card is offered where it is available: GET /v1/methods then lists it with kind set to card.

The QR code of any payment is available at /pay/{id}/qr.svg.

请求
curl -X POST https://soncopay.example/v1/payments/pay_3f9a5c0d21e4b7a6c8d1/method \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{"method":"crypto","asset":"USDT","network":"TRON"}'
{ "method": "crypto", "asset": "USDT", "network": "TRON" }

# the payment now carries:
# pay_address, pay_amount (send exactly this amount), network, expires_at

移动支付

Mobile money works in several countries through local operators (Wave, Orange Money, MTN MoMo, Moov Money, Free Money and others). Each method has a code made of the operator and the country (for example orange_ci or mtn_cm), the countries where it works and the currency it collects: XOF or XAF.

国家、运营商和货币

国家/地区运营商(方式代码)货币
Côte d'Ivoire CIWave wave_ci, Orange Money orange_ci, MTN Mobile Money mtn_ci, Moov Money moov_ciXOF
Senegal SNWave wave_sn, Orange Money orange_sn, Free Money free_snXOF
Benin BJMTN Mobile Money mtn_bj, Moov Money moov_bjXOF
Burkina Faso BFOrange Money orange_bf, Moov Money moov_bfXOF
Cameroon CMMTN Mobile Money mtn_cm, Orange Money orange_cmXAF

GET /v1/methods returns the methods open to your account right now, with kind set to mobile_money, code, operator, countries and currency.

  1. Choose the customer's country with country (ISO alpha-2, for example CI or CM) and the operator with method (its code), plus phone in international format. You can also choose the method later with POST /v1/payments/{id}/method.
  2. The customer approves the request on their phone. Show them instructions. When the method works in redirect mode, pay_url is filled in: send the customer there.
  3. When the operator confirms, the payment becomes paid and you receive the payment.paid webhook. Your balance is credited in the collected currency: XOF or XAF.

付款字段

字段描述
method_codeThe chosen method: crypto, binance_pay, card or a mobile money code.
providerFamily of the chosen method: crypto, binance_pay, mobile_money or card, and null before the choice. It never names an internal processor. Base your logic on method, method_code and status.
pay_urlBinance Pay: the Binance Pay link. Mobile money in redirect mode and bank card: a link to /pay/{id}/redirect on our domain, which sends the customer to the secure payment page while the payment waits for it, then back to the payment page.
customer_phoneThe number the request was sent to.
operatorThe mobile money operator, when known.
countryThe customer country (ISO alpha-2).
instructionsText to show the customer while they approve the request.
last_errorWhy the last request failed, for example a request declined by the customer.
一次一个请求While a request waits on the customer's phone, a new choice is refused with 409. A declined request puts the payment back to new with last_error: the customer can try again, up to 5 attempts, then 429.
XOF 和 XAF 金额These currencies have no decimals: the amount to pay is rounded up to the unit, and a mobile money payout amount must be a whole number.
测试模式 The number decides the outcome: ending in 99, the request is declined; ending in 98, it stays pending; any other number is paid after a few seconds, or at once with POST /v1/payments/{id}/simulate.
请求
# Côte d'Ivoire: Orange Money, amount in XOF (no decimals)
curl -X POST https://soncopay.example/v1/payments \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{"amount":"5000","currency":"XOF","country":"CI","order_id":"demo-1043","method":"orange_ci","phone":"+2250700000000"}'

# Cameroon: MTN Mobile Money, amount in XAF (no decimals)
curl -X POST https://soncopay.example/v1/payments \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{"amount":"5000","currency":"XAF","country":"CM","order_id":"demo-1044","method":"mtn_cm","phone":"+237670000000"}'

移动支付提现

Send method (the method code of the country, for example wave_sn or orange_cm), phone and amount to POST /v1/payouts, plus operator when the method needs one. The currency is the one of the method: XOF or XAF, in whole amounts.

请求
# Senegal: Wave, XOF
curl -X POST https://soncopay.example/v1/payouts \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{"method":"wave_sn","phone":"+221770000000","amount":"10000","reference":"demo-payout-8"}'

# Cameroon: Orange Money, XAF
curl -X POST https://soncopay.example/v1/payouts \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{"method":"orange_cm","phone":"+237690000000","amount":"15000","reference":"demo-payout-9"}'

查询与列表

GET/v1/payments/{id}

GET/v1/payments

参数描述
statusOnly payments with this status, for example paid.
qSearch by payment ID, order ID or customer email.
limitFrom 1 to 200. Default: 50.
offsetNumber of payments to skip. The response says has_more when there are more.
测试模式 POST /v1/payments/{id}/simulate marks a payment as paid with its exact amount, once a method is chosen, so you can test your webhook handler.
请求
curl https://soncopay.example/v1/payments/pay_3f9a5c0d21e4b7a6c8d1 -H "X-Api-Key: sp_test_…"

curl "https://soncopay.example/v1/payments?status=paid&limit=50" -H "X-Api-Key: sp_test_…"

# test mode
curl -X POST https://soncopay.example/v1/payments/pay_3f9a5c0d21e4b7a6c8d1/simulate -H "X-Api-Key: sp_test_…"

付款状态

状态含义
newCreated; the customer has not chosen a method yet.
waitingMethod chosen; waiting for the funds.
confirmingDeposit detected; waiting for confirmations.
Confirmed and credited to your balance. Deliver the order.
expiredNot paid in time. A deposit that arrives within 24 hours of expiry still marks it paid, with late: true in the webhook.
failedPaid with a wrong amount through Binance Pay.

余额

GET/v1/balances

Returns your available balance per asset, in the mode of the key you use.

reserved gives, per asset, the amount and the fee of your payouts that are still open. They are already deducted from balances.

请求
curl https://soncopay.example/v1/balances -H "X-Api-Key: sp_test_…"
响应
{
  "livemode": false,
  "balances": { "USDT": "124.5", "XOF": "35000" },
  "reserved": { "USDT": "51" }
}

GET/v1/ledger

Lists every movement of your balance: payments, fees and payouts. Filter with asset, page with limit (up to 500) and offset.

流水记录 IDWhen the platform moves its books to double entry, /v1/ledger keeps its format but each id becomes the ID of the accounting line: IDs from before and after the switch cannot be compared. Use statements to reconcile.
请求
curl "https://soncopay.example/v1/ledger?asset=USDT&limit=100" -H "X-Api-Key: sp_test_…"

费用

GET/v1/fees

Returns the fee applied to your payments, as a percentage plus a fixed amount in USD, for each method you can use in this mode, with the payout fees and the limits. Each part comes from your own rate first, then from the method, then from the platform default: percent_source and fixed_source say which one applies.

The values above are examples: your dashboard and this endpoint show the fees that apply to you.

请求
curl https://soncopay.example/v1/fees -H "X-Api-Key: sp_test_…"
响应
{
  "livemode": false,
  "payment": { "percent": "0.5", "fixed_usd": "0", "percent_source": "global", "fixed_source": "global" },
  "per_method": [ { "code": "orange_ci", "name": "Orange Money", "kind": "mobile_money", "percent": "1.5", … } ],
  "payout_fees": { "USDT:TRON": "1", … },
  "limits": { "min_payment_usd": 1, "min_payout_usd": 5 }
}

各网络的最低提现金额和费用

每笔提现(包括自动结算)都必须达到其网络的最低金额;网络费用在金额之外另计,从您的余额中扣除。

当某个网络上的应付总额达到其最低金额时才会自动结算。未达到时,金额在您的余额中累计,并以一笔提现一起发送。

币种和网络每笔最低提现费率至少从余额扣除
加载中…

这些数值来自实时配置,可能随网络变化。GET /v1/fees 会返回适用于您账户的数值(payout_limits)。

提现

POST/v1/payouts

Withdraws from your balance to a crypto address, a mobile money number or a saved payout destination, bank account included. For crypto, the network fee from /v1/payout-fees is added to the amount; the same call returns the minimum payout.

字段描述
asset字符串Crypto: the asset to withdraw, for example USDT (required). Mobile money: the currency, by default the one of the method.
network字符串Crypto (required): the network to send on, for example TRON.
amount必填字符串或数字Amount the address receives.
address字符串Crypto (required): the destination address, checked against the network format.
method字符串Mobile money: the method code, for example wave_sn or orange_cm.
phone字符串Mobile money: the number to pay, in international format.
operator字符串Mobile money: the operator, when the method needs one.
memo字符串Memo or tag, when the destination needs one.
reference字符串Your reference. Sending the same reference again returns the same payout.
destination_id字符串A saved and confirmed payout destination. Its details replace asset, network, address, method and phone; asset, when sent, must equal its currency. It is the only way to pay out to a bank account.
请求
curl -X POST https://soncopay.example/v1/payouts \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{ "asset": "USDT", "network": "TRON", "amount": "150",
  "address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE", "reference": "withdraw-77" }'
状态含义
pending_reviewA large payout waits for our team's review before it is sent.
queuedAccepted and waiting to be sent.
submittingBeing handed over for sending. If the answer is lost, the same request is retried safely: a payout is never sent twice.
sentSent; waiting for confirmation on the network.
unknownNo clear answer after sending: our team checks before anything else happens. The payout is never sent again automatically.
awaiting_manualBank transfer: waiting for our team to execute it. It then becomes completed with bank_reference.
completedDone: tx_hash (crypto) or bank_reference (bank transfer) is filled in.
failedThe payout failed; the amount and the fee are back in your balance.
rejectedRefused after review; the amount and the fee are back in your balance.

提现字段

字段描述
railcrypto, momo (mobile money) or bank.
destination_idThe saved destination used, or null.
destinationCopy of the destination at the time of the request (kind, label, currency, masked details), or null.
bank_referenceReference of the bank transfer, once it is executed.
tx_hashTransaction hash of a crypto payout.
errorWhy the payout failed, when it did.
请求
curl https://soncopay.example/v1/payouts/po_8b2d… -H "X-Api-Key: sp_test_…"

curl https://soncopay.example/v1/payout-fees -H "X-Api-Key: sp_test_…"

提现目的地

Save where your money goes once, confirm it in your dashboard, then pay out with destination_id only. A stolen API key cannot add an address and empty your balance to it.

接口描述
GET/v1/payout-destinationsYour destinations (filters kind and status) and the settings that apply to them.
POST/v1/payout-destinationsSaves a destination. It starts as pending_confirmation.
GET/v1/payout-destinations/{id}查询提现目的地
PATCH/v1/payout-destinations/{id}Changes the label only. New details make a new destination, to confirm again.
DELETE/v1/payout-destinations/{id}Disables a destination. It is kept for your history, never erased.

按类型划分的字段

字段描述
kind必填字符串crypto, mobile_money or bank.
label字符串Your own name for this destination, up to 80 characters.
asset
network
address
Crypto (required): the asset, the network and the address, checked against the network format.
memoCrypto: memo or tag, when the network needs one.
method
phone
Mobile money (required): the method code, for example wave_sn or orange_cm, and the number in international format.
operatorMobile money: the operator, when the method needs one.
holder
bank_name
Bank (required): the account holder and the name of the bank.
iban
account_number
Bank: either an IBAN, which is checked, or a local account number (8 to 34 letters and digits).
swift_bicBank, optional: the SWIFT/BIC code.
countryBank: two-letter country code. With an IBAN, it is taken from the IBAN.
currencyBank (required): one of settings.bank_currencies. Mobile money: by default the currency of the method. Payouts are never converted.
仅可在控制台中确认A new destination is pending_confirmation. A team member confirms it in the dashboard, under Payout destinations, by typing their password again. An API key can never confirm a destination. Depending on the platform settings, the confirmation must come from another team member than the one who added it.
请求
# 1. save it: status 'pending_confirmation'
curl -X POST https://soncopay.example/v1/payout-destinations \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{"kind":"crypto","asset":"USDT","network":"TRON","address":"TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE","label":"Treasury"}'

# 2. a team member confirms it in the dashboard (the password is asked again)
# 3. pay out to it once usable is true
curl -X POST https://soncopay.example/v1/payouts \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{"destination_id":"pd_5c1e…","amount":"50","reference":"demo-payout-9"}'

安全等待期

In live mode, a confirmed destination can be used from usable_from, at the end of a security waiting period that starts at confirmation. usable tells you whether a payout can use it now. There is no waiting period in test mode.

状态含义
pending_confirmationSaved, waiting for confirmation in the dashboard.
activeConfirmed. Usable from usable_from.
disabledDisabled by you or by our team, kept for your history.
rejectedRefused by our team.

列表返回的设置

GET /v1/payout-destinations returns items and settings. These settings are set by the platform and can change: read them rather than writing them in your code.

设置含义
cooldown_hoursWaiting period after confirmation, in hours (0 in test mode).
require_saved_destinationWhen true, every payout must use destination_id.
second_confirmerWhen true, another team member must confirm a destination you added.
api_keys_can_createWhen false, destinations can only be added from the dashboard.
bank_currenciesCurrencies accepted for bank transfers.
bank_payout_fees
bank_payout_min
Fee and minimum amount of a bank transfer, per currency.
encryption_readyWhen false, mobile money and bank destinations cannot be saved right now.

遮盖的账户信息

Full phone numbers, IBANs and account numbers are stored encrypted and never returned. details shows what you need to recognise a destination: address for crypto, phone_masked for mobile money, iban_masked or account_masked for a bank account.

响应
{
  "id": "pd_5c1e…", "object": "payout_destination", "livemode": true,
  "kind": "mobile_money", "label": "Demo payouts", "currency": "XOF",
  "status": "active", "usable": false, "usable_from": "2026-10-03T09:00:00+00:00",
  "details": { "method_code": "wave_sn", "country": "SN", "phone_masked": "+221•••••000" },
  "created_via": "api", "confirmed_at": "2026-10-02T09:00:00+00:00", …
}

错误代码

These errors carry a stable code in the body, next to detail.

代码HTTP含义
destination_not_confirmed409The destination was not confirmed in the dashboard yet.
destination_cooling_down409Confirmed, but the waiting period is not over: the body carries usable_from.
destination_disabled409The destination is disabled.
destination_mode_mismatch409A test destination with a live key, or the reverse.
destination_duplicate409The same details are already saved: the body carries destination_id.
destination_limit40950 open destinations at most per mode.
destination_currency_mismatch422asset differs from the currency of the destination: payouts are not converted.
destination_required422Your account only pays out to saved destinations: send destination_id.
destination_invalid422Invalid details: the body carries field, the field to fix.
destination_not_found404Unknown destination.
api_key_not_allowed403Destinations can only be added from the dashboard.
data_key_missing503Mobile money and bank destinations cannot be saved right now.
响应409
{
  "detail": "…",
  "code": "destination_cooling_down",
  "usable_from": "2026-10-03T09:00:00+00:00"
}

结算模式

Your account is in one of two settlement modes. You choose it in the dashboard (Settings, password required), unless Sonco Pay has locked the mode of your account:

  • Custody (default): each paid payment is credited to your balance, minus the fee. You withdraw when you want.
  • Automatic: each paid payment is credited, then its net amount is sent automatically to your settlement destination for that currency, as a normal payout (payout fee, review above a threshold, same statuses and webhooks). One payment gives at most one automatic payout; small amounts add up until they reach the minimum payout.

Choose a settlement destination for each currency (USDT, XOF…) or for a currency on one network (USDT:TRON, which wins over USDT) among your confirmed payout destinations, in the dashboard or with PUT /v1/settlement/routes. Without a usable destination, or when a limit or a block applies, the amount stays in your balance and the settlement waits; the reason is given in GET /v1/settlement and in the payment timeline.

GET /v1/payments/{id} carries settlement: mode, status (pending, held, settled, failed, cancelled), reason and payout_id. The payment.paid webhook carries settlement.mode; when the payout is created you receive payment.settled, then the usual payout.* webhooks. Payouts carry origin: request or settlement.

GET /v1/settlement also tells whether you can change the mode yourself: locked, can_change and blocked_by (locked or platform). Turning on automatic settlement needs a usable settlement destination; the change applies to payments paid after it, and the account owners receive a security email.

请求
curl -X PUT https://soncopay.example/v1/settlement/routes \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{"scope":"USDT","destination_id":"pd_3c1e…"}'
payment.settled
{
  "event": "payment.settled",
  "data": {
    "id": "pay_3f9a…", "status": "paid", "net_amount": "24.8886",
    "settlement": {
      "mode": "auto", "status": "settled",
      "payout_id": "po_7b2d…", "payout_amount": "23.8886",
      "payout_fee": "1", "asset": "USDT"
    }, …
  }
}

时间线

GET/v1/payments/{id}/timeline

GET/v1/payouts/{id}/timeline

The steps of a payment or a payout, in order, with their time and the status after each step. A timeline shows no internal detail: give its trace_id to our support when you ask about an operation.

step is a stable code: translate it in your interface. label is an English text, status the status after the step and data the useful public references (amounts, method, transaction hash).

付款步骤

步骤含义
created创建时间
method_selected已选择支付方式
awaiting_customer等待客户操作
attempt_failed尝试被拒绝
confirming已检测到转账,确认中
deposit_attributed转账已由我们的团队匹配
paid已支付并入账
expired已过期
failed失败
webhook_sent通知已送达
webhook_failed通知未送达

提现步骤

步骤含义
requested已申请提现
under_review审核中
approved已批准
rejected已拒绝
submitted已提交处理
sent已接受发送
unknown我们的团队正在核实
completed已完成
failed失败
webhook_sent通知已送达
webhook_failed通知未送达
请求
curl https://soncopay.example/v1/payouts/po_8b2d…/timeline -H "X-Api-Key: sp_test_…"
# or /v1/payments/{id}/timeline
响应
{
  "object": "timeline",
  "subject": { "type": "payout", "id": "po_8b2d…", "status": "completed" },
  "livemode": true, "trace_id": "4f0c…",
  "events": [
    { "step": "requested", "label": "Payout requested", "status": "queued",
      "occurred_at": "2026-10-02T09:00:00+00:00", "data": { "amount": "50", "fee": "1", "asset": "USDT" } },
    { "step": "submitted", "label": "Sent for processing", "status": "submitting", … },
    { "step": "completed", "label": "Completed", "status": "completed", "data": { "tx_hash": "…" } }
  ]
}

对账单

GET/v1/statements

GET/v1/statements.csv

Your account statement per currency, read from our double entry books: the opening balance, every line with the balance after it, and the closing balance. The opening balance plus the lines always equals the closing balance.

参数描述
currencyOne currency, for example USDT. By default, every currency of your account.
fromStart of the period: YYYY-MM-DD or ISO 8601.
toEnd of the period, included: YYYY-MM-DD or ISO 8601.
limitFrom 1 to 1000. Default: 200.
cursorThe next_cursor of the previous page, while it is not null.

对账单明细

字段描述
idID of the accounting line.
datePosting time, ISO 8601.
typepayment, fee, payout, payout_fee, payout_release or adjustment.
labelStable English label, for example Payment received: translate it in your interface.
descriptionReason of an adjustment, when there is one.
amountSigned amount: positive when it credits your balance, negative when it debits it.
balance_afterBalance of the currency after this line.
operationThe related payment or payout (type and id), or null.

GET /v1/statements.csv takes the same currency, from and to and returns the whole period as CSV, with the columns date, currency, type, label, description, amount, balance_after, operation_type, operation_id and line_id.

请求
curl "https://soncopay.example/v1/statements?currency=USDT&from=2026-10-01&to=2026-10-31" \
  -H "X-Api-Key: sp_test_…"

curl "https://soncopay.example/v1/statements.csv?currency=USDT&from=2026-10-01" \
  -H "X-Api-Key: sp_test_…" -o statement.csv
响应
{
  "livemode": true, "currency": "USDT",
  "from": "2026-10-01T00:00:00+00:00", "to": "2026-11-01T00:00:00+00:00",
  "currencies": ["USDT"],
  "opening": { "USDT": "100" },
  "lines": [
    { "id": 5120, "date": "2026-10-02T09:12:40+00:00", "currency": "USDT",
      "type": "payment", "label": "Payment received", "description": null,
      "amount": "25.0137", "balance_after": "125.0137",
      "operation": { "type": "payment", "id": "pay_3f9a…" } },
    …
  ],
  "closing": { "USDT": "124.8886" },
  "next_cursor": null
}

发票和订阅

POST/v1/invoices

POST /v1/invoices creates a draft (issue: true gives it a number, send: true also sends it). Lines carry description, quantity, unit_amount and an optional tax_rate (percent). The customer pays on hosted_url (/i/… short link in SMS) with every active method; each payment follows the usual flow (credit, commission, settlement).

Statuses: draft, open, partially_paid (with allow_partial), overdue, paid, void. Actions: /issue, /send, /remind (429 reminder_too_soon), /void, /mark-paid (paid outside Sonco Pay), /timeline and /pdf.

创建订阅

POST/v1/subscriptions

A subscription creates one invoice per due date (interval: week, month, quarter, year or custom with interval_days), a few days in advance, then reminds the customer before the due date (reminders.days_before, for example 7, 3 and 1), on the day and after it (after_due). An unpaid invoice makes the subscription unpaid; after dunning.grace_days it is canceled, paused or kept, as you choose.

Automatic card debit is not available yet: auto_debit: true fails with auto_debit_unavailable. Card, crypto, mobile money and Binance Pay are paid from the invoice link. Reminders go by email, and by SMS or WhatsApp only to customers who agreed (consent); a STOP reply opts them out.

Creations accept the Idempotency-Key header (24 hours; the same key with another body returns 409 idempotency_key_reused) or an external_ref. Lists return items, has_more and next_offset. In test mode, POST /v1/billing/test-clock moves time forward to try due dates and reminders.

创建发票
curl -X POST https://soncopay.example/v1/invoices \
  -H "X-Api-Key: sp_test_…" \
  -H "Idempotency-Key: demo-invoice-1042" \
  -H "Content-Type: application/json" \
  -d '{"customer":{"name":"Demo Customer","email":"[email protected]","phone":"+2250700000000",
        "consent":{"email":true,"sms":true}},"currency":"XOF","due_days":7,
       "lines":[{"description":"Demo service","quantity":1,"unit_amount":"15000"}],"send":true}'
创建订阅
curl -X POST https://soncopay.example/v1/subscriptions \
  -H "X-Api-Key: sp_test_…" \
  -H "Content-Type: application/json" \
  -d '{"customer_id":"cus_…","description":"Demo monthly plan","amount":"10000","currency":"XOF",
       "interval":"month","preferred_method":"mobile_money","reminders":{"days_before":[7,3,1]},
       "dunning":{"grace_days":7,"action":"pause"}}'

Webhooks

Set your webhook URL in the dashboard. We send a POST with a JSON body for each event, and retry for about three days until your server answers with a 2xx status.

事件触发时机
payment.confirmingA deposit was detected and is being confirmed.
payment.paidThe payment is confirmed. Fulfil the order.
payment.settledAutomatic settlement: the payout of this payment was created (settlement.payout_id).
payment.expiredThe payment was not made in time.
payout.completedThe payout is done: tx_hash (crypto) or bank_reference (bank transfer) is filled in.
payout.failedThe payout failed or was rejected; the amount is back in your balance.
invoice.*An invoice was created, sent, reminded, partly paid, paid, became overdue or was voided.
subscription.*A subscription was created, updated, paused, resumed, became unpaid, was canceled or ended.
pingSent when you test your webhook from the dashboard.

请求头

请求头描述
X-SoncoPay-SignatureHMAC SHA512 of the raw body, in hexadecimal. See Verify a signature.
X-SoncoPay-EventThe event name, also present in the body.
Content-Typeapplication/json
payment.paid
{
  "event": "payment.paid",
  "created_at": "2026-10-01T14:31:07+00:00",
  "data": {
    "id": "pay_3f9a…", "order_id": "1042", "status": "paid",
    "received_amount": "25.0137", "asset": "USDT",
    "fee": "0.1251", "net_amount": "24.8886",
    "payer": { "email": "[email protected]" },
    "settlement": { "mode": "custody", "status": null, "payout_id": null },
    "late": false, …
  }
}

验证签名

Each webhook carries X-SoncoPay-Signature: the HMAC SHA512, in hexadecimal, of the raw request body, keyed with your webhook signing secret. Compute it on the raw body, before parsing the JSON, and compare in constant time.

发货之前Always confirm the status by calling GET /v1/payments/{id} before shipping goods of high value.
const crypto = require("crypto");
const expected = crypto.createHmac("sha512", process.env.SP_WEBHOOK_SECRET).update(rawBody).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-soncopay-signature"] || ""));

错误

Errors use standard HTTP codes and a JSON body {"detail": "…"}. Some errors also carry a stable code that you can test in your code: {"detail": "…", "code": "kyb_required"}.

代码含义
401Missing or invalid API key.
403Account suspended, payments or payouts blocked for your account, missing permission (permission_denied), or live mode before your business verification is approved (kyb_required).
404Payment or payout not found in this mode.
409Conflict: payment expired, already paid, or order_id reused with another amount.
429Too many mobile money attempts for this payment.
422Invalid input, unsupported currency, amount below the minimum, insufficient balance.
502A payment provider or the exchange rate is temporarily unavailable. Retry later.
503Payments or payouts are temporarily closed, or the service is under maintenance: the body then carries "maintenance": true and a Retry-After header. Retry later.

Error messages (detail, last_error, the error of a payout) never name our internal processors. Payout destination errors are listed in Payout destinations.

响应403
{
  "detail": "…",
  "code": "kyb_required"
}