Testing Cross-border Transfers
Test sender creation, collection, CNY payout and recovery in sandbox.
Use sandbox credentials and https://api-v2.honeycoin.app/api/sandbox/b2b/cross-border/requests as {crossBorderBaseUrl}. Generate a sandbox bearer token at POST https://api-v2.honeycoin.app/api/sandbox/b2b/auth/generate-bearer-token. Sandbox simulates payments; no real money is charged or paid out.
Create a sandbox sender, create a request, and then initiate its charge. Use a new request externalReference for each separate scenario. The sender and request must belong to the same sandbox account.
Sandbox still validates sender data, beneficiary identity, payment purpose, source currency, collection availability and operator details. Include a beneficiary with type, idType, idCountry, and nested payoutDetails. Both senders and beneficiaries use idNumber for individuals or registrationNumber for businesses; the other number field is forbidden. Test values choose an outcome only after the request is accepted; they do not activate an unavailable currency, country or method. The source currency must be KES or NGN and the receiver currency must be CNY.
Mobile money to CNY
Use the collection country's numeric calling code followed by the scenario digits. Send the number as a string without +, spaces or a local leading zero. Match chargeDetails.country, chargeCurrency, and momoOperatorId to the same available collection market.
| Outcome | chargeDetails.phoneNumber |
|---|---|
| Collection success | <CALLING_CODE>712345678 |
| Collection failure | <CALLING_CODE>787654321 |
For example, the Kenya success fixture is 254712345678 with country: KE, chargeCurrency: KES, and momoOperatorId: mpesa. The sender's separate phoneNumber still uses the + format required by the sender API.
For bank and Alipay payouts, use these beneficiary account numbers as strings:
| Outcome | beneficiary.payoutDetails.payoutMethod.accountNumber |
|---|---|
| Payout success | 1234567890 |
| Payout failure | 0987654321 |
This sandbox Alipay account number is a test fixture. For production, supply the real recipient account identifier. Alipay requires an individual sender in both environments.
Success example
Replace senderId with your sandbox individual sender ID. Include the recipient identity fields in beneficiary; use idType: business-registration-number and registrationNumber for a business recipient instead of the individual identity example below. Adjust the illustrative amount to one allowed for your account.
{
"senderId": "sender_example_001",
"purpose": "family_support",
"amount": 1000,
"chargeCurrency": "KES",
"receiverCurrency": "CNY",
"externalReference": "sandbox-cny-success-001",
"requestType": "transfer",
"chargeDetails": {
"method": "momo",
"firstName": "Amina",
"lastName": "Otieno",
"country": "KE",
"email": "[email protected]",
"phoneNumber": "254712345678",
"momoOperatorId": "mpesa"
},
"beneficiary": {
"type": "individual",
"idType": "national-id",
"idCountry": "CN",
"idNumber": "EXAMPLE-CN-ID-001",
"payoutDetails": {
"method": "alipay",
"country": "CN",
"payoutMethod": {
"accountName": "Example Recipient",
"accountNumber": "1234567890"
}
}
}
}Send this body to POST {crossBorderBaseUrl}, save data.id, and initiate POST {crossBorderBaseUrl}/charges/{requestId} with no body. Save the returned transaction ID.
The sandbox automatically initiates a simulated payout after the charge succeeds. Check the charge's chargeStatus: successful separately from request status: payout_successful. Production payout processing follows HoneyCoin review and does not have the same timing as sandbox simulation.
Sandbox bank collections
Sandbox accepts chargeDetails.method: bank on new requests. Production currently accepts only momo for new requests, so successful bank testing does not establish production availability.
Replace the full chargeDetails object with the example below. Keep the sender, purpose, currencies, amount, reference and beneficiary fields required by the create-request API. Omit phoneNumber and momoOperatorId from bank chargeDetails; those fields are forbidden for bank charges. firstName, lastName, country, and email remain required.
{
"method": "bank",
"firstName": "Amina",
"lastName": "Otieno",
"country": "NG",
"email": "[email protected]"
}For this example use chargeCurrency: NGN and an account with the bank collection route available. email: [email protected] simulates collection success; [email protected] simulates failure. These email fixtures apply to bank collections, not mobile money outcomes.
After charge initiation, read the charge transaction to inspect its virtualAccount. The sandbox charge response contains the transaction ID; do not require virtual account details to appear in the initial response, and do not send real funds to sandbox bank details.
Scenarios to verify
| Test | Setup | Expected behavior |
|---|---|---|
| End-to-end success | Success collection fixture and success beneficiary account | Charge succeeds; request reaches payout_successful |
| Collection failure | Failure collection fixture | Request reaches charge_failed; no successful beneficiary payout |
| Payout failure | Success collection fixture and beneficiary 0987654321 | Collection succeeds; request reaches payout_failed; do not recharge |
| Duplicate request reference | Repeat an existing reference with changed request details | 409 DUPLICATE_EXTERNAL_REFERENCE; an identical recent call may replay its original response |
| Business sender with Alipay | Business senderId, Alipay payout | 422 UNSUPPORTED_METHOD |
| Missing purpose or required sub-purpose | Omit purpose, or omit subPurpose for goods/services | 400 VALIDATION_ERROR |
| Missing beneficiary | Supply only the former top-level payoutDetails | 400 VALIDATION_ERROR; beneficiary is required |
| Wrong sender or beneficiary identity number | Omit the number field for the selected type, or include the other type's field | 400 VALIDATION_ERROR; use idNumber for an individual and registrationNumber for a business |
| Duplicate charge | Charge a request that already has a pending or successful deposit | A replay or state conflict; no second active charge |
| Close unpaid request | Create a request and close it before charging | closed with pendingChargesClosed: 0 |
| Close paid or active request | Attempt to close while a deposit is pending/successful or a payout exists | 409 CROSS_BORDER_REQUEST_CANNOT_BE_CLOSED |
| Retry a closed request | Initiate its charge | Rejected; no new charge |
| Sender deletion | Delete an unused sender and fetch it | 404 SENDER_NOT_FOUND; it disappears from active listings |
| Pagination | Create enough records for multiple pages | Use each endpoint's documented cursor envelope and preserve filters |
A retry on a failed request reuses its saved charge details, so a failure test number or email continues to select failure. Create a separate request with a new external reference to test success with different collection details.
Use request and transaction lookups to verify persisted outcomes, and test webhook handling for both transaction_updated and crossborder_updated. Do not infer completion from the initiation response or from elapsed time.
Updated about 14 hours ago
