Zoorya Pay Developers

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.

http
POST https://sandbox.eclipse.et/eclipse-conductor/rest/v1/tenants/{tenantId}/organisations/{organisationId}/payments
Authorization: Bearer <token from Log in>
Content-Type: application/json

Every method has its own page with a Try it button and the body already filled in — click the method name below.

MethodtypeadditionalFieldsCustomer experience
M-PesaMPESA_USSD_TOPUPphoneNumber (7XXXXXXXX)PIN prompt on the phone
Dashen BankDASHEN_TOP_UPphoneNumberPush prompt
Nib BankNIB_TOP_UPphoneNumberPush prompt
telebirrETTA_TELEBIRR_TOP_UPphoneNumber (9XXXXXXXX)PIN prompt
telebirr QRTELE_QR_C2B—You show a QR; they scan with telebirr
EthSwitchETTA_ETH_SWITCH_TOP_UP—You show a QR; they scan with their bank app
Zemen Bank: verify account → pay with SMS codeZEMEN_TOP_UPaccountNumber, otpSMS code, then payment
Payment linkGLOBAL_PAYMENT_LINKHMACKey, callbackUrlYou send them a link

M-Pesa#

Request
{
  "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" }
  ]
}
Response
{
  "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.

Request
{
  "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:

typeValue in the response additionalFieldsCustomer scans with
TELE_QR_C2BqrUrlThe telebirr app
ETTA_ETH_SWITCH_TOP_UPbarcodeTheir bank app
Request
{
  "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. 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. 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 new externalUniqueId.

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.

Request
{
  "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" }
  ]
}
Response
{
  "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 completionUrl to the payer.
  • For links, callbackUrl goes inside additionalFields, not at the top level.
  • landingUrl is where the payer's browser goes after paying.
  • Bring the HMACKey from the merchant portal (Payment links → HMACKey).

After every payment#

  1. Wait for the callback on your callbackUrl.
  2. Call Get a payment with the paymentId.
  3. Deliver only when status is SUCCESSFUL and amount matches.
statusMeaning
PENDINGWaiting for the customer
SUCCESSFULPaid
ERROR_TEMPTemporary error — may still succeed
ERROR_PERMFailed for good
TIMEOUTThe customer did not approve in time
CANCELLEDCancelled before completing
REVERSEDReversed after success