Payment methods
The exact request body for each C2B Direct method, and a one-time payment link.
With C2B Direct, your server creates the payment for the method the customer chose, and the customer approves it on their phone or in their bank. Every method uses the same endpoint — only type and additionalFields change.
POST https://sandbox.eclipse.et/eclipse-conductor/rest/v1/tenants/{tenantId}/organisations/{organisationId}/payments
Authorization: Bearer <token from Log in>
Content-Type: application/jsonEvery method has its own page with a Try it button and the body already filled in — click the method name below.
| Method | type | additionalFields | Customer experience |
|---|---|---|---|
| M-Pesa | MPESA_USSD_TOPUP | phoneNumber (7XXXXXXXX) | PIN prompt on the phone |
| Dashen Bank | DASHEN_TOP_UP | phoneNumber | Push prompt |
| Nib Bank | NIB_TOP_UP | phoneNumber | Push prompt |
| telebirr | ETTA_TELEBIRR_TOP_UP | phoneNumber (9XXXXXXXX) | PIN prompt |
| telebirr QR | TELE_QR_C2B | — | You show a QR; they scan with telebirr |
| EthSwitch | ETTA_ETH_SWITCH_TOP_UP | — | You show a QR; they scan with their bank app |
| Zemen Bank: verify account → pay with SMS code | ZEMEN_TOP_UP | accountNumber, otp | SMS code, then payment |
| Payment link | GLOBAL_PAYMENT_LINK | HMACKey, callbackUrl | You send them a link |
M-Pesa#
{
"amount": 2,
"currency": "ETB",
"type": "MPESA_USSD_TOPUP",
"externalUniqueId": "zpmpesa0001",
"destinationWalletId": 123456789,
"callbackUrl": "https://your-domain.et/webhooks/zoorya",
"description": "zoorya test",
"additionalFields": [
{ "id": "phoneNumber", "value": "712345678" }
]
}{
"paymentId": 4581645,
"status": "PENDING",
"amount": 2.0,
"currency": "ETB",
"fee": 0,
"description": "zoorya test",
"externalUniqueId": "zpmpesa0001",
"gatewayTransactionId": "ws_CO_1710202622560822751620",
"organisationId": 12345678,
"phone": "712345678",
"created": "2026-10-17T19:56:07.000Z"
}The customer gets an M-Pesa PIN prompt. status stays PENDING until they approve.
Dashen Bank#
Same as M-Pesa with "type": "DASHEN_TOP_UP". Phone numbers starting with 9 or 7 both work.
Dashen Bank rejects an externalUniqueId longer than 10 characters — for example use D00000001.
{
"amount": 2,
"currency": "ETB",
"type": "DASHEN_TOP_UP",
"externalUniqueId": "D00000001",
"destinationWalletId": 123456789,
"callbackUrl": "https://your-domain.et/webhooks/zoorya",
"description": "Order 1001",
"additionalFields": [
{ "id": "phoneNumber", "value": "912345678" }
]
}Nib Bank#
Same as M-Pesa with "type": "NIB_TOP_UP". Phone numbers starting with 9 or 7 both work.
telebirr#
Same as M-Pesa with "type": "ETTA_TELEBIRR_TOP_UP" and a telebirr number (9XXXXXXXX).
telebirr QR and EthSwitch#
No phone number. The response carries a value for you to show as a QR code:
type | Value in the response additionalFields | Customer scans with |
|---|---|---|
TELE_QR_C2B | qrUrl | The telebirr app |
ETTA_ETH_SWITCH_TOP_UP | barcode | Their bank app |
{
"amount": 2,
"currency": "ETB",
"type": "ETTA_ETH_SWITCH_TOP_UP",
"externalUniqueId": "zpethswitch0001",
"destinationWalletId": 123456789,
"callbackUrl": "https://your-domain.et/webhooks/zoorya",
"description": "Order 1005"
}Turn the value into a QR image with any QR library. Do not open the qrUrl in a browser — it only works when scanned.
Zemen Bank#
Zemen Bank needs two calls:
- 1
Check the account — Zemen sends an SMS code
Call Zemen Bank — Verify account with the customer's account number (10 to 20 digits). If it answers
valid: true, the customer receives an SMS code. - 2
Create the payment with the code
Request{ "amount": 2, "currency": "ETB", "type": "ZEMEN_TOP_UP", "externalUniqueId": "zpzemen0001", "destinationWalletId": 123456789, "callbackUrl": "https://your-domain.et/webhooks/zoorya", "description": "Order 1006", "additionalFields": [ { "id": "accountNumber", "value": "1234567890" }, { "id": "otp", "value": "482913" } ] }A wrong code is rejected with
400— ask the customer again and send a newexternalUniqueId.
One-time payment link#
Create a link for one order and send it to the customer by SMS, Telegram or email. They open it and pay with any method.
{
"amount": 150,
"currency": "ETB",
"type": "GLOBAL_PAYMENT_LINK",
"externalUniqueId": "zplink0001",
"destinationWalletId": 123456789,
"landingUrl": "https://your-domain.et/payment-done",
"additionalFields": [
{ "id": "HMACKey", "value": "your-hmac-key-from-the-merchant-portal" },
{ "id": "callbackUrl", "value": "https://your-domain.et/webhooks/zoorya" }
]
}{
"paymentId": 4581646,
"status": "PENDING",
"paymentType": "LINK",
"amount": 150.0,
"currency": "ETB",
"fee": 0,
"externalUniqueId": "zplink0001",
"completionUrl": "https://sandbox.eclipse.et/t/abc12",
"organisationId": 12345678,
"walletId": 123456789,
"created": "2026-10-17T20:02:11.000Z"
}- Send
completionUrlto the payer. - For links,
callbackUrlgoes insideadditionalFields, not at the top level. landingUrlis where the payer's browser goes after paying.- Bring the
HMACKeyfrom the merchant portal (Payment links → HMACKey).
After every payment#
- Wait for the callback on your
callbackUrl. - Call Get a payment with the
paymentId. - Deliver only when
statusisSUCCESSFULandamountmatches.
status | Meaning |
|---|---|
PENDING | Waiting for the customer |
SUCCESSFUL | Paid |
ERROR_TEMP | Temporary error — may still succeed |
ERROR_PERM | Failed for good |
TIMEOUT | The customer did not approve in time |
CANCELLED | Cancelled before completing |
REVERSED | Reversed after success |

