VAS Use Cases

Eclipse has integrations to a number of VAS providers covering a wide range of services from airtime, electricity, bill payments, etc. Typically the process for purchasing VAS involves 4 steps:

  1. Retrieve the VAS catalog
  2. Get network partner ID for mobile number - this is optional and relevant only for airtime VAS. Here you retrieve the partner ID for the provided mobile number, this queries the relevant mobile number portability database.
  3. Prepare catalog query - this is optional and the entire catalog can be retrieved.
  4. Initiate VAS payment - typically the payment data consists of the partnerId and productId of a product in the VAS catalog separated with a "_" e.g. 2_ABC123
  5. Complete VAS Payment

Here are details for specific providers.

South Africa VAS providers

The following VAS providers are specific to tenants operating in South Africa.

Purchase VAS from Scan to Pay

Introduction

Scan to Pay is a QR code payment solution for seamless and contactless transactions. Scan to Pay revolutionizes the way merchants and consumers interact financially.

For merchants, Scan to Pay offers the flexibility to create and manage static or dynamic QR codes, enabling them to accept payments effortlessly. Whether you're a local store, restaurant, or an online business, our platform ensures a smooth payment experience for your customers.

Consumers benefit from the convenience, safety, and security of Scan to Pay. Consumers can simply scan the QR code, confirm the transaction, and they're done! No need to worry about carrying cash or cards.

One feature of Scan to Pay is to efficiently enable consumers to purchase VAS products of their choosing within the application.

Prerequisites

  • A valid JWT for API calls.
  • tenant config mobilerecharge.providers=com.ukheshe.services.mobilerecharge.provider.MasterpassRechargeProvider
  • tenant config oltio.config.username={scan_to_pay_username}
  • tenant config oltio.config.password={password}
  • tenant config destination.wallet.config.VAS.1={destination walletId}

Step 1 - Verify mobile number (MSISDN) network

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/{msisdn}
📘

Note

Currently only MTN And Vodacom are supported networks

Step 2 - Get the list of VAS partners & products

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs

Step 3 - Filter the catalog

Once the catalog is returned, the channel must filter it based on the MSISDN provided in step 1.
Select the productId from the catalog to proceed with the payment process (e.g., "productId": "VD11").

Below is an example of a filtered catalog response:

[
  {
    "partnerId": 5,
    "partnerName": "Vodacom",
    "providerId": 1,
    "products": [
      { "partnerId": 5, "productTypeName": "SMS",     "productId": "VS02", "productName": "SMS20"     },
      { "partnerId": 5, "productTypeName": "SMS",     "productId": "VS03", "productName": "SMS50"     },
      { "partnerId": 5, "productTypeName": "SMS",     "productId": "VS04", "productName": "SMS100"    },
      { "partnerId": 5, "productTypeName": "DATA",    "productId": "VD11", "productName": "DATA75MB"  },
      { "partnerId": 5, "productTypeName": "DATA",    "productId": "VD12", "productName": "DATA240MB" },
      { "partnerId": 5, "productTypeName": "DATA",    "productId": "VD13", "productName": "DATA400MB" },
      { "partnerId": 5, "productTypeName": "DATA",    "productId": "VD14", "productName": "DATA600MB" },
      { "partnerId": 5, "productTypeName": "DATA",    "productId": "VD50", "productName": "DATA2.5GB" },
      { "partnerId": 5, "productTypeName": "AIRTIME", "productId": "01",   "productName": "100 Minutes" },
      { "partnerId": 5, "productTypeName": "AIRTIME", "productId": "03",   "productName": "500 Minutes" },
      { "partnerId": 5, "productTypeName": "AIRTIME", "productId": "05",   "productName": "250 Minutes" }
    ]
  }
]

Initiating a VAS Payment

For FrontierWeb Services, the POST to /payments performs both the pre-vend (validation) and the vend (fulfilment) steps in a single call. The response is returned synchronously once both have completed. There is no separate PUT step required for FrontierWeb (unlike Scan to Pay or ACS).

The paymentData field follows one of two formats depending on the product:

  • Airtime (fixed-price products): {partnerId}_{productId}_0 — e.g. 20_501_0
  • Two-stage products (utility token, DSTV): {partnerId}_{vendProductId}_{prevendProductId} — e.g. 20_1248_1247 (Utility Token + Utility Pre-Vend)

PartnerId for FrontierWeb Services is 20.

Use Case 1 — Utility Token (Electricity)

The purpose of this integration is to process utility token purchases. The pre-vend validates the meter number against the upstream utility (Conlog Botswana); the vend issues the token. The token and customer receipt are returned in the extraInfo field.

Prerequisites

  • A valid JWT for API calls.
  • Tenant config fees.amount.config.Pay.GLOBAL_DEFERRED.WALLET.{partnerId}.{productCategory}=feeAmount
  • Tenant config fees.wallet.config.Pay.GLOBAL_DEFERRED.WALLET=destinationWalletId (fee destination)
  • Tenant config destination.wallet.config.VAS.{partnerId}.{productCategory}=destinationWalletId (VAS settlement destination)

Initiate Payment

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
{
    "externalUniqueId": "{{$randomUUID}}",
    "paymentData": "20_1248_1247",
    "type": "GLOBAL_VAS",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        },
        {
            "id": "meterNumber",
            "value": "21500123456"
        }
    ],
    "amount": 180,
    "currency": "BWP",
    "walletId": 2536583
}

Example Response

{
    "paymentId": 4007245,
    "externalUniqueId": "2c6d4298-d9c8-4616-82ef-c1f7e446cec7",
    "status": "SUCCESSFUL",
    "amount": 13.000000000,
    "description": "VAS purchase  PartnerId-20 : Utility Token",
    "currency": "BWP",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        },
        {
            "id": "meterNumber",
            "value": "21500123456"
        }
    ],
    "paymentType": "WALLET",
    "created": "2025-12-08T11:09:49.000Z",
    "lastModified": "2025-12-08T11:09:50.124Z",
    "paymentData": "20_1248_1247",
    "extraInfo": "[{\"callbackId\":72628017,\"result\":\"...token, customerReceipt and electricityResponse payload...\"}]",
    "fee": 4,
    "paymentReference": "1248_1247",
    "walletId": 2536583,
    "customerId": 8586330,
    "gatewayTransactionId": "Payment-4007245"
}
📘

Note

Sandbox test meter numbers: 00300123452 or 21500123456 (Conlog Botswana fixture). In sandbox, invalid meter numbers may still return status: SUCCESSFUL as pre-vend validation against the live utility provider is bypassed in non-production environments.

Use Case 2 — DSTV Recharge

The purpose of this integration is to allow customers to pay DSTV subscriptions. The pre-vend validates the DSTV account number; the vend recharges the account.

Prerequisites

  • A valid JWT for API calls.
  • Tenant config fees.amount.config.Pay.GLOBAL_DEFERRED.WALLET.{partnerId}.{productCategory}=feeAmount
  • Tenant config fees.wallet.config.Pay.GLOBAL_DEFERRED.WALLET=destinationWalletId
  • Tenant config destination.wallet.config.VAS.{partnerId}.{productCategory}=destinationWalletId

Initiate Payment

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
{
    "externalUniqueId": "{{$randomUUID}}",
    "paymentData": "20_1260_1259",
    "type": "GLOBAL_VAS",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        },
        {
            "id": "accountNumber",
            "value": "12345678"
        }
    ],
    "amount": 33,
    "currency": "BWP",
    "walletId": 2536583
}

In this use case the paymentData field is set to {partnerId}_{vendProductId}_{prevendProductId} i.e. 20_1260_1259

PartnerId = 20

VendProductId = 1260 (Bots DSTV Recharge)

PrevendProductId = 1259 (Bots DSTV Balance)

The response shape mirrors the Utility Token response above.

Use Case 3 — Airtime Purchase

LFSB wallet holders can purchase airtime from Orange, BE Mobile, and Mascom. Airtime products are fixed-price, so the amount value is determined by the selected productId and does not need to be supplied on the request.

Prerequisites

  • A valid JWT for API calls.
  • Tenant config fees.amount.config.Pay.GLOBAL_DEFERRED.WALLET.{partnerId}.{productCategory}=feeAmount
  • Tenant config fees.wallet.config.Pay.GLOBAL_DEFERRED.WALLET=destinationWalletId
  • Tenant config destination.wallet.config.VAS.{partnerId}.{productCategory}=destinationWalletId

Initiate Payment

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
{
    "externalUniqueId": "{{$randomUUID}}",
    "paymentData": "20_501_0",
    "type": "GLOBAL_VAS",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        }
    ],
    "currency": "BWP",
    "walletId": 2536583
}

In this example 20_501_0 purchases BE MOBILE 20 (productId 501). The trailing _0 indicates no pre-vend product is required for fixed-price airtime.

Example Response

{
    "paymentId": 4007260,
    "externalUniqueId": "59802001-ac18-4695-9e9f-a0e3a359826b",
    "status": "SUCCESSFUL",
    "amount": 20.000000000,
    "description": "VAS purchase  PartnerId-20 : BE MOBILE 20",
    "currency": "BWP",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        }
    ],
    "paymentType": "WALLET",
    "created": "2025-12-08T11:12:39.000Z",
    "paymentData": "20_501_0",
    "extraInfo": "[{\"callbackId\":72628059,\"result\":\"...saleItemList containing serialNumber, pinNumber, expiryDate...\"}]",
    "fee": 5,
    "paymentReference": "501_0",
    "walletId": 2536583,
    "customerId": 8586330,
    "gatewayTransactionId": "Payment-4007260"
}

For pinned airtime products (e.g. BE MOBILE), the voucher serial and PIN are returned inside extraInfo.result.saleItemList. Channels should parse these and surface them to the customer.

Response Field Reference

  • paymentId — Eclipse-generated unique identifier for the payment. Use this for status enquiries via GET /payments/{paymentId}.
  • externalUniqueId — The idempotency key supplied by the channel on the request. Eclipse rejects duplicate externalUniqueId values within the tenant.
  • status — Terminal or intermediate state of the payment. See Transaction Status Codes below.
  • amount — Amount transacted, in major currency units. Determined by productId for fixed-price products.
  • description — Human-readable description, populated by Eclipse based on the partner and product.
  • currency — ISO 4217 currency code. Must match the wallet currency.
  • additionalFields — Echoes the additional fields supplied on the request (msisdn, meterNumber, accountNumber).
  • paymentType — The payment instrument used. For wallet-funded VAS this will be WALLET.
  • paymentData — Echoes the paymentData value from the request.
  • extraInfo — Provider-specific payload as a stringified JSON. For utility tokens contains the token number, customer receipt, KWh units, and tariff details. For airtime contains the voucher serial and PIN. Channels must parse and surface this content to the customer.
  • paymentReference — Reference used for reconciliation. Composed of {vendProductId}_{prevendProductId}.
  • fee — Transaction fee applied per tenant config.
  • gatewayTransactionId — Eclipse internal gateway reference.
  • tracingContext — W3C trace context. Useful when raising support tickets to correlate with Eclipse server logs.
  • errorDescription — Populated only on status ERROR_PERM or ERROR_TEMP.

Transaction Status Codes

  • SUCCESSFUL — Pre-vend and vend both completed. Token, voucher or recharge has been delivered. Terminal state. Action expected from channel: Render token, PIN, or receipt to the customer from extraInfo.
  • PENDING — Eclipse has accepted the request but the upstream provider has not yet confirmed completion. Action expected from channel: Wait for callback (if callbackUrl was supplied) or poll GET /payments/{paymentId}. Do not retry the POSTexternalUniqueId will reject duplicates.
  • BUILDING — Intermediate state seen only when a separate PUT is required to finalise the payment (does not apply to FrontierWeb single-shot flows). Action expected from channel: N/A for FrontierWeb.
  • ERROR_PERM / ERROR_TEMP — The transaction did not complete. Funds have been reversed on the source wallet. Action expected from channel: Inspect errorDescription and the application error envelope (code, traceId). Surface a user-facing error and allow retry with a new externalUniqueId.

For the full Eclipse application error code list, see the Application Error Codes page.

The error envelope returned by Eclipse on a failed call follows this shape:

[
    {
        "type": "BUSINESS",
        "severity": "INFO",
        "description": "Invalid voucher for the users network. User is on network id 0 but voucher is for network id 20",
        "code": "IAE001",
        "traceId": "9099797c1668796c2ba0b371b76a45df",
        "spanId": "c06f2d77d914b3fb"
    }
]

Asynchronous Callbacks

In the standard happy path, FrontierWeb VAS payments complete synchronously and the POST returns status: SUCCESSFUL with the token or voucher in extraInfo. However, channels should still implement callback handling to cover cases where the upstream provider is slow or unavailable and Eclipse returns status: PENDING.

To receive a callback when a PENDING payment finalises, supply a callbackUrl on the original POST:

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
{
    "externalUniqueId": "{{$randomUUID}}",
    "paymentData": "20_1248_1247",
    "type": "GLOBAL_VAS",
    "callbackUrl": "https://channel.tenant.example.com/eclipse/vas/callback",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        },
        {
            "id": "meterNumber",
            "value": "21500123456"
        }
    ],
    "amount": 180,
    "currency": "BWP",
    "walletId": 2536583
}

Eclipse will POST the full payment object (same shape as the synchronous response above) to callbackUrl once the payment reaches a terminal state (SUCCESSFUL, FAILED, or ERROR).

📘

Note

All callbacks from Eclipse to the channel carry an Eclipse-Signature header. Channels must verify this signature before processing the payload. See the Encryption and Integrity page for the verification procedure.

Source IP allow-list

Channels that whitelist inbound traffic should permit calls from the following Eclipse source IPs:

  • Production: 79.125.40.127, 54.220.185.240, 18.134.2.182, 3.11.195.109
  • Sandbox: 34.255.93.65

Custom headers

Additional headers can be configured on the tenant via the webhookAdditionalHeaders property (comma-delimited key1,value1,key2,value2).

Retries

Eclipse will retry callbacks on non-2xx responses. Retry behaviour is documented on the CallbackURL Retries page.

Polling alternative

If callback infrastructure is not available, channels can instead poll GET /eclipse-conductor/rest/v1/tenants/{tenantId}/payments/{paymentId} until the payment reaches a terminal status. Polling should be capped (e.g. 30 seconds at 2-second intervals) to avoid hot loops.

Settlement

On successful VAS payment, Eclipse triggers a notification for funds to move from the customer's source wallet to the configured VAS destination wallet (destination.wallet.config.VAS.{partnerId}.{productCategory}) and the fee destination wallet (fees.wallet.config.Pay.GLOBAL_DEFERRED.WALLET). For tenants where the source wallet is fronted by an external bank trust account (e.g. Access Bank), Eclipse emits a settlement notification to trigger the corresponding bank-side movement.

Step 4 - Initiate Making a Payment by a Customer

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
{
  "additionalFields": [
    {
      "id": "msisdn",
      "value": "value"
    }
  ],
  "landingUrl": "https://www.google.com/",
  "callbackUrl": "http://www.callback.com",
  "externalUniqueId": "abcd1234",
  "paymentData": "5_VD50",
  "type": "GLOBAL_VAS",
  "currency": "ZAR",
  "walletId": XXX
}

In this use case the paymentData field is set to the partnerId and productId (i.e. 5_VD50)

PartnerId = 5

productId = VD50

Step 5 - Update Necessary Fields to complete the Payment

PUT /eclipse-conductor/rest/v1/tenants/{tenantId}/payments/{paymentId}
Passed tenantId and paymentId for update payment along with below request payload.
{
  "amount": 155,
  "walletId":XXX,
  "landingUrl": "https://www.google.com/"
}

Scan to Pay returns a success response string once the payment is completed successfully.

Purchase VAS from ACS

Introduction

ACS services can be integrated with Eclipse as an open feature platform. It provides a comprehensive catalog of APIs that tenants can use to manage airtime, data, and electricity bundles.

Prerequisites

  • A valid JWT for API calls.
  • tenant config mobilerecharge.providers=com.ukheshe.services.mobilerecharge.provider.AcsMobileRecharge
  1. tenant config destination.wallet.config.VAS.1={destination walletId}

Step 1 - Prepare MSISDN

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/{msisdn}

Step 2 - List of VAS partners & products

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs

Step 3 - Channel to filter the catalog

Once the catalog is returned, the channel filters it based on the MSISDN provided in step 1.
Select the productId from the catalog to proceed with the payment process (e.g., "productId": "M511").

Below is a list from one of the providers:

[
  {
    "partnerId": 4,
    "partnerName": "MTN",
    "providerId": 1,
    "products": [
      { "partnerId": 4, "productTypeName": "SMS",     "productId": "M502", "productName": "SMS30"      },
      { "partnerId": 4, "productTypeName": "SMS",     "productId": "M504", "productName": "SMS100"     },
      { "partnerId": 4, "productTypeName": "DATA",    "productId": "M511", "productName": "DATA350MB"  },
      { "partnerId": 4, "productTypeName": "DATA",    "productId": "M512", "productName": "DATA500MB"  },
      { "partnerId": 4, "productTypeName": "DATA",    "productId": "M513", "productName": "DATA1GB"    },
      { "partnerId": 4, "productTypeName": "DATA",    "productId": "M514", "productName": "DATA2GB"    },
      { "partnerId": 4, "productTypeName": "DATA",    "productId": "M550", "productName": "DATA100MB"  },
      { "partnerId": 4, "productTypeName": "AIRTIME", "productId": "01",   "productName": "MTN R30 Call"    },
      { "partnerId": 4, "productTypeName": "AIRTIME", "productId": "03",   "productName": "MTN R60 Call"    },
      { "partnerId": 4, "productTypeName": "AIRTIME", "productId": "05",   "productName": "MTN R180 Call"   },
      { "partnerId": 4, "productTypeName": "AIRTIME", "productId": "11",   "productName": "MTN R10 Recharge"},
      { "partnerId": 4, "productTypeName": "AIRTIME", "productId": "M500", "productName": "MTN Pinless"     }
    ]
  }
]

Step 4 - Initiate Making a Payment by a Customer

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
{
  "additionalFields": [
    {
      "id": "msisdn",
      "value": "xxxxxxxxxxxxx"
    }
  ],
  "landingUrl": "https://www.google.com/",
  "callbackUrl": "http://www.blankwebsite.com",
  "externalUniqueId": "abcd1234a",
  "paymentData": "4_M512",
  "type": "GLOBAL_VAS",
  "currency": "ZAR",
  "walletId": XXX
}

In this use case the paymentData field is set to the partnerId and productId (i.e. 4_VD50)

PartnerId = 4

productId = VD50

Step 5 - Update Necessary Fields for Payment

PUT /eclipse-conductor/rest/v1/tenants/{tenantId}/payments/{paymentId}
Passed tenantId and paymentId for update payment along with below request payload.
{
  "amount": 49,
  "walletId":xxxxx,
  "landingUrl": "https://www.google.com/"
}

ACS returns a success response string once the payment is completed successfully.

📘

Electricity VAS purchases via ACS

Once a user has purchased an electricity voucher via ACS, a dummy code is visible in the Sandbox environment.
In production, this voucher code is called an UnPin.

The UnPin is sent to the user via SMS and should also appear on the successful electricity purchase screen in the channel.
This UnPin cannot be used until the user dials the number below.

USSD String

The meter number can be found on the user's prepaid meter.
After dialing the number, the user will receive another SMS containing a PIN, which must be entered into the prepaid meter.

The channel should display both the UnPin and the USSD number string above the successful electricity purchase screen.

Purchase VAS from Kganya

Introduction

Kganya services can be integrated with Eclipse as an open feature platform. It provides a comprehensive catalog of APIs that tenants can use to manage bills and data bundles.

Prerequisites

  • A valid JWT for API calls.
  • Tenant config mobilerecharge.providers=com.ukheshe.services.mobilerecharge.provider.KganyaMobileRecharge
  • Tenant config kganya.config.baseUrl={baseUrl}
  • Tenant config kganya.config.apiKey={apiKey}
  • Tenant config destination.wallet.config.VAS.16={destination walletId}

Step 1 - Prepare catalogQuery

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs?catalogQuery=16_0310564280_T089775335322

Prepare a catalog query - e.g. 16_0310564280_T089775335322

  • PartnerId - 16 for Kganya
  • Mobille number (0) if not available - 0310564280
  • ID Number - T089775335322

Select the productId from the catalog to proceed with the payment process (e.g., "productId": "0310564280_T089775335322_1").

Step 2 - Initiate Making a Payment by a Customer

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
{
  "additionalFields": [
    {
      "id": "msisdn",
      "value": "xxxxxxxxxxxxx"
    }
  ],
  "landingUrl": "https://www.google.com/",
  "callbackUrl": "http://www.blankwebsite.com",
  "externalUniqueId": "abcd1234a",
  "paymentData": "16_0310564280_T089775335322_3",
  "type": "GLOBAL_VAS",
  "currency": "ZAR",
  "walletId": XXX
}

In this use case the paymentData field is set to the partnerId and productId i.e. 16_0310564280_T089775335322_3

PartnerId - 16

ProductId - 0310564280_T089775335322_3

Step 3 - Update Necessary Fields for Payment

PUT /eclipse-conductor/rest/v1/tenants/{tenantId}/payments/{paymentId}
{
  "amount": xxx,
  "walletId": xxx,
  "landingUrl": "https://www.google.com/"
}

Kganya returns a success response string once the payment is completed successfully.

Purchase VAS from Pay@

Pay@ services can be integrated with Eclipse as an open feature platform. The Pay@ system supports real-time transaction flows, reconciliation, and fund settlement between networks/retailers and bill payment authorizers or issuers.
It provides a comprehensive catalog of APIs that tenants can use to manage various bill payments.

Prerequisites

  • A valid JWT for API calls.
  • Tenant config mobilerecharge.providers=com.ukheshe.services.mobilerecharge.provider.PayAtRecharge
  • Tenant config destination.wallet.config.VAS.14={destinationWalletId}

Step 1 – Fetch Catalog

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs

Filter based on Pay@ Partner ID 14 and select the productId from the catalog to proceed with the payment process.

Step 2- Initiate a Payment

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments  
{ 
      "additionalFields": [ 
        { 
          "id": "accountNumber", 
          "value": "xxxxxxxxxxxx" 
        } 
      ], 
      "callbackUrl": "http://www.blankwebsite.com", 
      "externalUniqueId": "9e60398d-6ddd-4f46-b6cd-2081c872b2ea", 
      "paymentData": "14_14577", 
      "type": "GLOBAL_VAS" 
}

In this use case the paymentData field is set to the partnerId and productId i.e. 14_14577

PartnerId - 14

ProductId - 14577

Step 3- Update Fields for Payment

PUT /eclipse-conductor/rest/v1/tenants/{tenantId}/payments/{paymentId} 
{ 
      "amount": 150, 
      "walletId": XXX, 
      "externalUniqueId":"9e60398d-6ddd-4f46-b6cd-2081c872b2ea" 
}

Pay@ returns a success response string once the payment is completed successfully.

Purchase VAS from EasyPay

EasyPay services can be integrated with Eclipse as an open feature platform.

It provides a comprehensive catalog of APIs that tenants can use to manage bill payments, vend airtime, SMS packs, and data bundles.

Prerequisites

  • A valid JWT for API calls.
  • Tenant config mobilerecharge.providers=com.ukheshe.services.mobilerecharge.provider.EasyPayMobileRecharge
  • Tenant config destination.wallet.config.VAS.15=${destinationWalletId}

Step 1 – Fetch Catalog

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs

Filter based on EasyPay Partner ID 15 and select the productId from the catalog to proceed with the payment process.

Step 2 - Initiate a Payment

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments  

{ 
  "additionalFields": [ 
    { 
      "id": "msisdn", 
      "value": "xxxxxxxxxxxxx" 
    } 
  ], 
  "landingUrl": "https://www.google.com/", 
  "callbackUrl": "http://www.blankwebsite.com", 
  "externalUniqueId": "29agd6z317d519e87211", 
  "paymentData": "15_1764", 
  "type": "GLOBAL_VAS", 
  "phone": "xxxxxxxxxxxxx", 
  "walletId": XXX 
}

In this use case the paymentData field is set to the partnerId and productId i.e. 15_1764

PartnerId - 15

ProductId - 1764

Step 3 - Update Fields for Payment

PUT/eclipse-conductor/rest/v1/tenants/{tenantId}/payments/{paymentId} 

Passed tenantId and paymentId for update payment along with below request payload. 
{ 
      "amount": 150, 
      "walletId": XXX, 
      "externalUniqueId":"29agd6z317d519e87211" 
}

EasyPay returns a success response string once the payment is completed successfully.

Kenya VAS providers

The following VAS providers are specific to tenants operating in Kenya.

Pay Tax through Diamond Trust Bank

DTB integrations allow the ability to forcustomer to query their Payment Registration Number (PRN) against the Kenya Revenue Authority (KRA) and then pay the tax amount.

Prerequisites

  • A valid JWT for API calls.

Below are the tenant configs that need to be configured when paying tax through Diamond Trust Bank:

  1. mobilerecharge.providers=com.ukheshe.services.mobilerecharge.provider.DTBQueryUtilityRecharge
  2. destination.wallet.config.VAS.19=${destinationWalletId}

Step 1 – Validate PRN and get amount owed

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs?19_ETAX_10202300000582380

The catalog query is partnerId_taxUtilitCode_prn. Partner ID for DTB Tax payments is 19 and the utility code is ETAX.

[
  {
    "partnerId": 19,
    "partnerName": "dtbQueryUtility",
    "providerId": 19,
    "products": [
      {
        "partnerId": 19,
        "productId": "ETAX_1020230000058238",
        "productName": "ETAX",
        "pinnedProduct": false,
        "additionalFields": [
          {
            "id": "SystemCode",
            "defaultValue": "PG"
          },
          {
            "id": "EslipNumber",
            "defaultValue": "1020230000058238"
          },
          {
            "id": "PaymentAdviceDate",
            "defaultValue": "2023-06-19T13:25:12"
          },
          {
            "id": "TaxpayerPin",
            "defaultValue": "P051094777L"
          },
          {
            "id": "TaxpayerFullName",
            "defaultValue": "Call Fast Services Limited"
          },
          {
            "id": "TotalAmount",
            "defaultValue": "52008620"
          },
          ......

Step 2 - Initiate a Payment

📘

Note

The wallet type of the walletId specified in the body determines if the source of the transaction is an Eclipse wallet or a DTB Bank Account.

paymentData field is {partnerId}{taxUtilitCode} {prn}. Partner ID for DTB Tax payments is 19 and the utility code is ETAX.

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments	
{
    "externalUniqueId": "772021c2-bc1b-4a83-8c26-2ea2fe210836",
    "type": "GLOBAL_VAS",
    "paymentData": "19_ETAX_1020230000060915",
    "amount": 52008620,
    "currency": "KES",
    "additionalFields": [ ],
    "walletId": 830989,
    "customFraudChecks": false
}
{
    "paymentId": 453209,
    "externalUniqueId": "772021c2-bc1b-4a83-8c26-2ea2fe210836",
    "status": "PENDING",
    "amount": 52008620,
    "currency": "KES",
    "additionalFields": [ ],
    "acceptedCardSchemes": [ ],
    "cardPhone": "254729294292",
    "phone": "254729294292",
    "acceptedPaymentMechanisms": [ ],
    "paymentType": "EFT",
    "created": "2023-09-15T09:11:14.000Z",
    "extraInfo": "{"dtbCallingCustomerCif":"65594662-bc39-4c0d-8c7c-bc7cf6012fb9","dtbCallingCustomerId":709193,"dtbCallingCustomerOrganisation":"","dtbSourceBankCode":"","dtbSourceCountryCode":"KE","dtbSourceCurrency":"KES","dtbSourceName":"kuBQcxDilafNzey RQPgcyyEQNZdBLF","dtbSourcePhone":"254729294292","dtbSourceType":"DTB_WALLET","dtbDestinationCurrency":"KES"}",
    "paymentInstrumentInfo": {
        "cardPhone": "254729294292"
    },
    "fee": 0,
    "paymentReference": "1020230000060915",
    "walletId": 830989,
    "customerId": 709193
}

Typically you will immediately receive a PENDING response. If the callbackUrl was populated a callback will be sent to that URL when the payment is finalised (either successful or error). Alternatively a call can be made to the GET payment by paymentId endpoint.

Purchase VAS through Diamond Trust Bank

Eclipse has integrations to DTB to offer a wide range of VAS services where tenants can pay various bills, Vend airtime, SMS and data bundles.

Prerequisites

  • A valid JWT for API calls.

Below are the tenant configs that need to be configured when purchasing VAS through Diamond Trust Bank:

  1. mobilerecharge.providers=com.ukheshe.services.mobilerecharge.provider.DTBQueryUtilityRecharge
  2. destination.wallet.config.VAS.19=${destinationWalletId}

Step 1 – Fetch Catalog

The catalog query is {partnerId}_LOCAL-CATALOG. Partner ID for DTB VAS payments is 1.

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs?catalogQuery=19_LOCAL-CATALOG

API Returned Multiple products with Partner Id 19. Select productid from catalogs to further initiate the payment process (i.e. "productId": "1")

Step 2 - Initiate a Payment

📘

Note

The wallet type of the walletId specified in the body determines if the source of the transaction is an Eclipse wallet or a DTB Bank Account.

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments  	
{
    "externalUniqueId": "4490d40f-f2ef-4ee6-878c-2885fd4d990c",
    "type": "GLOBAL_VAS",
    "paymentData": "19_KE-STARTIMES_1234567890",
    "amount": 100,
    "currency": "KES",
    "additionalFields": [ ],
    "walletId": 836788,
    "description": "VAS KE-STARTIMES payment for 1234567890",
    "customFraudChecks": false
}

Here paymentData is partnerId_productId/utility_code_optional_reference

e.g. 19_KE-STARTIMES_1234567890

{
    "paymentId": 453215,
    "externalUniqueId": "019f240f-163f-43c2-a9b4-273b50e910a9",
    "status": "PENDING",
    "amount": 100,
    "description": "VAS KE-STARTIMES payment for 1234567890",
    "currency": "KES",
    "additionalFields": [ ],
    "acceptedCardSchemes": [ ],
    "cardPhone": "254729294292",
    "phone": "254729294292",
    "acceptedPaymentMechanisms": [ ],
    "paymentType": "EFT",
    "created": "2023-09-15T09:11:16.000Z",
    "extraInfo": "{"dtbCallingCustomerCif":"65594662-bc39-4c0d-8c7c-bc7cf6012fb9","dtbCallingCustomerId":709193,"dtbCallingCustomerOrganisation":"","dtbSourceBankCode":"","dtbSourceCountryCode":"KE","dtbSourceCurrency":"KES","dtbSourceName":"kuBQcxDilafNzey RQPgcyyEQNZdBLF","dtbSourcePhone":"254729294292","dtbSourceType":"DTB_WALLET","dtbDestinationAccountNumber":"KE-STARTIMES","dtbDestinationCurrency":"KES"}",
    "paymentInstrumentInfo": {
        "cardPhone": "254729294292"
    },
    "fee": 0,
    "paymentReference": "1234567890",
    "walletId": 830989,
    "customerId": 709193
}

Typically you will immediately receive a PENDING response. If the callbackUrl was populated a callback will be sent to that URL when the payment is finalised (either successful or error). Alternatively a call can be made to the GET payment by paymentId endpoint.

Uganda VAS Providers

The following VAS providers are specific to tenants operating in Uganda.

Purchase VAS through Diamond Trust Bank

Below are the tenant configs that need to be configured for Diamond Trust Bank tenants so that Astra can make calls to DTB Uganda APIs:

  • dtb.config.oauthEndpoint = /dtbug/oauth/1.0/
  • dtb.config.clientId = VVICUoWyVpyscQdQdNdPFUjI
  • dtb.config.clientSecret = KAPmTTLoTGrw-zbmxBINtDgAQvzuoxGaVeKe

VAS Related Configs

  • mobilerecharge.providers=com.ukheshe.services.mobilerecharge.provider.DTBUGRechargeVasProvider
  • destination.wallet.config.VAS.21=${destinationWalletId}

List of VAS partners & products

Retrieve catalog items for DTB UG.

Prerequisites

  • A valid JWT for API calls.
GET /eclipse-conductor/rest/v1/tenants/6658/vas/catalogs?catalogQuery=21_LOCAL-CATALOG

catalogQuery: 21_LOCAL-CATALOG

Example Response

[
    {
        "partnerId": 21,
        "partnerName": "DTBUGVasProvider",
        "providerId": 21,
        "products": [
            {
                "partnerId": 21,
                "productTypeName": "BILL",
                "productId": "NWSC",
                "productCategory": "BILL",
                "productDescription": "NWSC water payment",
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 21,
                "productTypeName": "BILL",
                "productId": "ETAX",
                "productCategory": "BILL",
                "productDescription": "URA Tax Returns payment",
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 21,
                "productTypeName": "BILL",
                "productId": "UMEME",
                "productCategory": "BILL",
                "productDescription": "Make UMEME Bill payments",
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            }
        ]
    }
]

NWSC Bill Payment

This VAS integration is used to process water bill payments for DTB UG’s tenants. The integration will make calls to these two API’s i.e. the getCustomerDetails which provides customer details and paymentsAPI which handles the final payments processed through the Fiorano payments gateway.

Prerequisites

  • A valid JWT for API calls.
  • Valid customer reference number and area.

Step One: Query Account Details

Fetch Catalog

GET /eclipse-conductor/rest/v1/tenants/11666/vas/catalogs?catalogQuery=21_NWSC_21117391_Kampala

• catalogQuery: 21_NWSC_21117391_Kampala
• 21117391 represents the client’s reference number.
• Kampala represents the client’s area

Example Response

[
    {
        "partnerId": 21,
        "partnerName": "DTBUGVasProvider",
        "providerId": 21,
        "products": [
            {
                "partnerId": 21,
                "productId": "NWSC_21117391",
                "productName": "NWSC",
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "CustRef",
                        "defaultValue": "21117391"
                    },
                    {
                        "id": "PropertyRef",
                        "defaultValue": "21/34/19/175"
                    },
                    {
                        "id": "CustName",
                        "defaultValue": "ROBERT MWESIGYE"
                    },
                    {
                        "id": "Area",
                        "defaultValue": "Kampala"
                    },
                    {
                        "id": "OutstandingBal",
                        "defaultValue": "71683"
                    },
                    {
                        "id": "CustomerError",
                        "defaultValue": "NONE"
                    },
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            }
        ]
    }
]

Step Two: Initiate Payment

📘

Note

The wallet type of the walletId specified in the body determines if the source of the transaction is an Eclipse wallet or a DTB Bank Account.

Initiate Payment

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments

Example Payload

{  
   "externalUniqueId":"{{$guid}}",  
   "type":"GLOBAL_VAS",  
   "paymentData":"21_NWSC_21117391",  
   "amount":71683,  
   "currency":"UGX",  
   "additionalFields":[  
      {  
         "id":"destinationBranchCode",  
         "value":"001"  
      },  
      {  
         "id":"destinationName",  
         "value":"ROBERT MWESIGYE"  
      }  
   ],  
   "walletId":206744,  
   "description":"21117391",  
   "customFraudChecks":false  
}

UMEME - Electricity Bill Payment

The purpose of this integration is to allow clients to query their UMEME account and make payments for electricity bills. This integration utilizes a single endpoint with two methods i.e. the GET method for fetching customer details and the POST method for posting the bill payment and notifying UMEME through Fiorano.

Step One: Query Account Details
Get Account Details

GET /eclipse-conductor/rest/v1/tenants/6658/vas/catalogs?catalogQuery=21_UMEME_200166426

• catalogQuery=21_UMEME_200166426
• 200166426 represents the client’s reference number.

Example Response

[
    {
        "partnerId": 21,
        "partnerName": "DTBUGVasProvider",
        "providerId": 21,
        "products": [
            {
                "partnerId": 21,
                "productId": "UMEME_200166426",
                "productName": "UMEME",
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "customerRef",
                        "defaultValue": "200166426"
                    },
                    {
                        "id": "customerType",
                        "defaultValue": "POSTPAID"
                    },
                    {
                        "id": "customerName",
                        "defaultValue": "BAFUKAWA  RONALD"
                    },
                    {
                        "id": "statusDescription",
                        "defaultValue": "SUCCESS"
                    },
                    {
                        "id": "balance",
                        "defaultValue": "-21103"
                    },
                    {
                        "id": "statusCode",
                        "defaultValue": "0"
                    },
                    {
                        "id": "credit",
                        "defaultValue": "0"
                    },
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            }
        ]
    }
]

Step Two: Initiate Bill Payment
Initiate Payment

📘

Note

The wallet type of the walletId specified in the body determines if the source of the transaction is an Eclipse wallet or a DTB Bank Account.

POST /eclipse-conductor/rest/v1/tenants/6658/customers/184132/payments

Example Payload

{  
   "externalUniqueId":"{{$guid}}",  
   "type":"GLOBAL_VAS",  
   "paymentData":"21_UMEME_200166426",  
   "amount":29182,  
   "currency":"UGX",  
   "additionalFields":[  
      {  
         "id":"destinationBranchCode",  
         "value":"001"  
      },  
      {  
         "id":"destinationName",  
         "value":"BASEKANAKYO DEBULA"  
      },  
      {  
         "id":"destinationType",  
         "value":"UMEME_ACCOUNT"  
      },  
      {  
         "id":"destinationPhone",  
         "value":"704263087"  
      }  
   ],  
   "walletId":206744,  
   "description":"POSTPAID",  
   "customFraudChecks":false  
}

Example Response

{
    "paymentId": 142607,
    "externalUniqueId": "48d9b276-ad03-4ee9-9a4b-6377eb383502",
    "status": "PENDING",
    "amount": 29182.000000000,
    "description": "POSTPAID",
    "currency": "UGX",
    "additionalFields": [
        {
            "id": "destinationBranchCode",
            "value": "001"
        },
        {
            "id": "destinationName",
            "value": "BASEKANAKYO DEBULA"
        },
        {
            "id": "destinationType",
            "value": "UMEME_ACCOUNT"
        },
        {
            "id": "destinationPhone",
            "value": "704263087"
        }
    ],
    "acceptedCardSchemes": [],
    "acceptedPaymentMechanisms": [],
    "paymentType": "EFT",
    "created": "2024-01-26T15:14:53.000Z",
    "extraInfo": "{\"dtbCallingCustomerCif\":\"b74f7ecb-5730-4718-ab26-32cb31f9b19f\",\"dtbCallingCustomerId\":184132,\"dtbCallingCustomerOrganisation\":\"\",\"dtbSourceAccountNumber\":\"8000020000016\",\"dtbSourceBankCode\":\"\",\"dtbSourceCountryCode\":\"UG\",\"dtbSourceCurrency\":\"UGX\",\"dtbSourceName\":\"Daphine Kajoina\",\"dtbSourcePhone\":\"256775555466\",\"dtbSourceType\":\"DTB_WALLET\",\"dtbDestinationType\":\"UMEME_ACCOUNT\",\"dtbDestinationAccountNumber\":\"UMEME\",\"dtbDestinationBankCode\":\"001\",\"dtbDestinationCountryCode\":\"KE\",\"dtbDestinationCurrency\":\"UGX\",\"dtbDestinationName\":\"BASEKANAKYO DEBULA\",\"dtbDestinationPhone\":\"704263087\"}",
    "paymentInstrumentInfo": {},
    "fee": 0E-9,
    "paymentReference": "200166426",
    "walletId": 206744,
    "customerId": 184132,
    "gatewayTransactionId": "fa5acd83-9d3f-4571-b290-7f0f29c2fc8e"
}

URA Tax Payments

Step 1 – Validate PRN and get the amount owed

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs?21_ETAX_2240000621955

The catalog query is partnerId_taxUtilitCode_prn. The partner ID for DTB UG Tax payments is 21 and the utility code is ETAX.

[
    {
        "partnerId": 21,
        "partnerName": "DTBUGVasProvider",
        "providerId": 21,
        "products": [
            {
                "partnerId": 21,
                "productId": "ETAX_2240000621955",
                "productName": "ETAX",
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "Prn",
                        "defaultValue": "2240000621955"
                    },
                    {
                        "id": "Tin",
                        "defaultValue": "1001307092"
                    },
                    {
                        "id": "TaxpayerName",
                        "defaultValue": "DAMBAK ENTERPRISES LIMITED"
                    },
                    {
                        "id": "Amount",
                        "defaultValue": "25000"
                    },
                    {
                        "id": "ExpiryDt",
                        "defaultValue": "08-FEB-24"
                    },
                    {
                        "id": "StatusCode",
                        "defaultValue": "T"
                    },
                    {
                        "id": "StatusDesc",
                        "defaultValue": "RECEIVED AND CREDITED"
                    },
                    {
                        "id": "Currency"
                    },
                    {
                        "id": "PaymentRegDt",
                        "defaultValue": "20-NOV-23"
                    },
                    {
                        "id": "IsSentToBank",
                        "defaultValue": "Y"
                    },
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            }
        ]
    }
]

Step 2 - Initiate a Payment

📘

Note

The wallet type of the walletId specified in the body determines if the source of the transaction is an Eclipse wallet or a DTB Bank Account.

paymentData field is {partnerId}{taxUtilitCode} {prn}. The partner ID for DTB UG Tax payments is 21 and the utility code is ETAX.

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments	
{
   "externalUniqueId":"{{$guid}}",
   "type":"GLOBAL_VAS",
   "paymentData":"21_ETAX_1001307092"
   "amount":25000,
   "currency":"UGX",
   "additionalFields":[
      {
         "id":"destinationBranchCode",
         "value":"001"
      },
      {
         "id":"destinationName",
         "value":"DAMBAK ENTERPRISES LIMITED"
      }
   ],
   "walletId":207016,
   "description":"2240000621955",
   "customFraudChecks":false

}
{
    "paymentId": 150417,
    "externalUniqueId": "6797d402-1734-46f3-8066-73a3a2bc3860",
    "status": "PENDING",
    "amount": 25000.000000000,
    "description": "2240000621955",
    "currency": "UGX",
    "additionalFields": [
        {
            "id": "destinationBranchCode",
            "value": "001"
        },
        {
            "id": "destinationName",
            "value": "DAMBAK ENTERPRISES LIMITED"
        }
    ],
    "acceptedCardSchemes": [],
    "acceptedPaymentMechanisms": [],
    "paymentType": "EFT",
    "errorDescription": ": ",
    "created": "2024-02-02T13:00:16.000Z",
    "extraInfo": "{\"dtbCallingCustomerCif\":\"a7cfe6d1-dc34-4e56-b537-0a0ac21b3bde\",\"dtbCallingCustomerId\":723689,\"dtbCallingCustomerOrganisation\":\"\",\"dtbSourceAccountNumber\":\"7053960002\",\"dtbSourceBranchCode\":\"001\",\"dtbSourceBankCode\":\"\",\"dtbSourceCountryCode\":\"UG\",\"dtbSourceCurrency\":\"UGX\",\"dtbSourceName\":\"James Madison\",\"dtbSourcePhone\":\"27987654321\",\"dtbSourceType\":\"DTB_BANK_ACCOUNT\",\"dtbDestinationBankCode\":\"001\",\"dtbDestinationCountryCode\":\"UG\",\"dtbDestinationCurrency\":\"UGX\",\"dtbDestinationName\":\"DAMBAK ENTERPRISES LIMITED\"}",
    "paymentInstrumentInfo": {},
    "fee": 0E-9,
    "paymentReference": "1001307092",
    "walletId": 207016,
    "customerId": 723689
}

Typically you will immediately receive a PENDING response. If the callback URL was populated a callback will be sent to that URL when the payment is finalised (either successful or error). Alternatively, a call can be made to the GET payment by paymentId endpoint.

Fund your MTN wallet through DTB Uganda

Prerequisites

A valid JWT for API calls.

Property
Below are the tenant configs that need to be configured for transferring funds to an MTM Mobile Money Wallet through Diamond Trust Bank:

  • mobilerecharge.providers=com.ukheshe.services.mobilerecharge.provider.DTBUGRechargeVasProvider
  • destination.wallet.config.VAS.XX=${destinationWalletId}

Step1 Validate the MTN number

	GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs?21_MTN_256779999703

The catalog query is partnerId_taxUtilitCode_customerRef. Partner ID for DTB MTN wallet topup is 21 and the utility code is MTN. 21_MTN_256779999703

Step2 Initiate a Payment

POST: /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
{
  "externalUniqueId": "9c04cede-e105-488b-9211-ab21bf3e1db9",
  "type": "GLOBAL_VAS",
  "paymentData": "21_MTN_256779999703",
  "amount": 29182,
  "currency": "UGX",
  "additionalFields": [
    {
      "id": "destinationBranchCode",
      "value": "001"
    },
    {
      "id": "destinationName",
      "value": "Martha N"
    },
    {
      "id": "destinationType",
      "value": "MTN_WALLET"
    },
    {
      "id": "destinationPhone",
      "value": "256779999703"
    }
  ],
  "walletId": 204142,
  "description": "Transfer from bank to wallet",
  "customFraudChecks": false
}
{
    "paymentId": 172565,
    "externalUniqueId": "2b204a21-d686-489e-980c-9c1ff3c1ca1a",
    "status": "PENDING",
    "amount": 29182.000000000,
    "description": "Transfer from bank to wallet",
    "currency": "UGX",
    "additionalFields": [
        {
            "id": "destinationBranchCode",
            "value": "001"
        },
        {
            "id": "destinationName",
            "value": "Martha N"
        },
        {
            "id": "destinationType",
            "value": "MTN_WALLET"
        },
        {
            "id": "destinationPhone",
            "value": "256779999703"
        }
    ],
    "acceptedCardSchemes": [],
    "acceptedPaymentMechanisms": [],
    "paymentType": "EFT",
    "errorDescription": ": ",
    "created": "2024-02-21T07:31:33.000Z",
    "extraInfo": "{\"dtbCallingCustomerCif\":\"a7cfe6d1-dc34-4e56-b537-0a0ac21b3bde\",\"dtbCallingCustomerId\":723689,\"dtbCallingCustomerOrganisation\":\"\",\"dtbSourceAccountNumber\":\"7115418418\",\"dtbSourceBranchCode\":\"001\",\"dtbSourceBankCode\":\"\",\"dtbSourceCountryCode\":\"UG\",\"dtbSourceCurrency\":\"UGX\",\"dtbSourceName\":\"James Madison\",\"dtbSourcePhone\":\"27987654321\",\"dtbSourceType\":\"DTB_WALLET\",\"dtbDestinationType\":\"MTN_WALLET\",\"dtbDestinationAccountNumber\":\"MTN\",\"dtbDestinationBankCode\":\"001\",\"dtbDestinationCountryCode\":\"UG\",\"dtbDestinationCurrency\":\"UGX\",\"dtbDestinationName\":\"Martha N\",\"dtbDestinationPhone\":\"256779999703\"}",
    "paymentInstrumentInfo": {},
    "fee": 0E-9,
    "paymentReference": "256779999703",
    "walletId": 204142,
    "customerId": 723689,
    "paymentTerminalData": {}
}

Global VAS providers

The following VAS providers are not specific to any region.

Purchase VAS from Cellulant

Introduction

Cellulant services to integrate with Eclipse as an open feature platform. It has an extensive catalog of API’s available for tenants to manage the Transfer money, Refunds, Pay various bills, Vend airtime and data bundles. Cellulant supported multiple services in many countries/regions.

Prerequisites

  • A valid JWT for API calls.
  • Eclipse provides global vas catalogs for tenants.

It is recommended to search service code from catalogs within the tenant.

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs.

Step 1 – Create Json
First create a json file based on provided service codes for a particular country. Added those json data in the property table against mobilerecharge.cellulant.bundles.{tenantId} key. Defined country code for selected tenant. (i.e. cellulant.config.countryCode={countryCode})

Step 2 - Fetch vas Catalogs

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs.

Based on tenantId return all the service codes as a catalogs from configured json data. Choose a voucher from catalogs list and prepare one catalog query for selected services.

Step 3 - Prepare CatalogQuery

GET /eclipse-conductor/rest/v1/tenants/242/vas/catalogs?catalogQuery=17_254707720002_042381176772_KE-DSTV

17 - partnerId for cellulant
254707720002 - Mobile number, If not available then pass 0
042381176772 - Account number
KE-DSTV - Service code

Based on catalogQuery they returned products for defined service. Select productid from catalogs to further initiate the payment process (i.e., "productid": "254707720002_042381176772_KE-DSTV_DSTVTESTKE")

Step 4 - Initiate Making a Payment by a Customer

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
{
  "additionalFields": [
    {
      "id": "msisdn",
      "value": "xxxxxxxxxxxxx"
    }
  ],
  "landingUrl": "https://www.google.com/",
  "callbackUrl": "http://www.blankwebsite.com",
  "externalUniqueId": "abcd1234a",
  "paymentData": "17_254707720002_042381176772_KE-DSTV_DSTVTESTKE",
  "type": "GLOBAL_VAS",
  "currency": "ZAR",
  "walletId": XXX
}

So here paymentData it could be (i.e. 17_254707720002_042381176772_KE-DSTV_DSTVTESTKE)
17 - PartnerId
254707720002_042381176772_KE-DSTV_DSTVTESTKE - productId

In response getting paymentId & amount along with all required extra information.

Step 5 - Update Necessary Fields for Payment

PUT /eclipse-conductor/rest/v1/tenants/{tenantId}/payments/{paymentId}
Passed tenantId and paymentId for update payment along with below request payload.
{
  "amount": 517,
  "walletId":476,
  "landingUrl": "https://www.google.com/"
}

Cellulant returns a success response string once successful payment.

Botswana VAS providers

Purchase VAS from FrontierWeb Services

Introduction

FrontierWeb services provides an extensive catalog for tenants' clients to pay for digital subscriptions, airtime, data bundles, and electricity tokens. These services have been integrated into Eclipse.

Prerequisites

  • A valid JWT for API calls.
  • Eclipse provides global VAS catalogs for tenants.

Below are the tenant configs to be configured for FrontierWeb Services:

  1. mobilerecharge.providers=com.ukheshe.services.mobilerecharge.provider.FrontierVasProvider
  2. destination.wallet.config.VAS.1={destination walletId}

Below are the global configs required for FrontierWeb Services:

mobilerecharge.config

  • frontierProviderId={frontierProviderId}
  • frontierNetworkName={frontierNetworkName}
  • frontierNetworkId={frontierNetworkId}

frontier.vas.config

  • merchantNumber={merchantNumber}
  • merchantPassword={merchantPassword}
  • terminalNumber={terminalNumber}
  • floatingUser={floatingUser}
  • userCode={userCode}
  • userPassword={userPassword}
  • userLevel={userLevel}
  • applicationID={applicationID}
  • processorNumber={processorNumber}
  • soapActionUrl={soapActionUrl}

sd.external.services

PartnerId for FrontierWeb Services is 20.

Get List of VAS products

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/vas/catalogs?catalogQuery=20
[
    {
        "partnerId": 20,
        "partnerName": "FrontierWebServices",
        "providerId": 20,
        "products": [
            {
                "partnerId": 20,
                "productId": "500",
                "productName": "BE MOBILE 10",
                "productDescription": "BE MOBILE 10",
                "fixedPriceIncl": 10.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "501",
                "productName": "BE MOBILE 20",
                "productDescription": "BE MOBILE 20",
                "fixedPriceIncl": 20.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "504",
                "productName": "BE MOBILE 100",
                "productDescription": "BE MOBILE 100",
                "fixedPriceIncl": 100.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "502",
                "productName": "BE MOBILE 30",
                "productDescription": "BE MOBILE 30",
                "fixedPriceIncl": 30.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "503",
                "productName": "BE MOBILE 50",
                "productDescription": "BE MOBILE 50",
                "fixedPriceIncl": 50.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "1259",
                "productName": "Bots DSTV Balance",
                "productDescription": "Bots DSTV Balance",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "1260",
                "productName": "Bots DSTV Recharge",
                "productDescription": "Bots DSTV Recharge",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "1261",
                "productName": "Box Office Payment",
                "productDescription": "Box Office Payment",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "2069",
                "productName": "BTC Check",
                "productDescription": "BTC Check",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "1446",
                "productName": "BTC Sefalana Topup",
                "productDescription": "BTC Sefalana Topup",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "491",
                "productName": "MASCOM 10",
                "productDescription": "MASCOM 10",
                "fixedPriceIncl": 10.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "492",
                "productName": "MASCOM 20",
                "productDescription": "MASCOM 20",
                "fixedPriceIncl": 20.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "494",
                "productName": "MASCOM 100",
                "productDescription": "MASCOM 100",
                "fixedPriceIncl": 100.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "493",
                "productName": "MASCOM 60",
                "productDescription": "MASCOM 60",
                "fixedPriceIncl": 60.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "1574",
                "productName": "Mascom Direct Ideal Topup",
                "productDescription": "Mascom Direct Ideal Topup",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "1910",
                "productName": "Mascom Direct Ideal Pre-Vend",
                "productDescription": "Mascom Direct Ideal Pre-Vend",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "496",
                "productName": "ORANGE 20",
                "productDescription": "ORANGE 20",
                "fixedPriceIncl": 20.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "498",
                "productName": "ORANGE 100",
                "productDescription": "ORANGE 100",
                "fixedPriceIncl": 100.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "497",
                "productName": "ORANGE 50",
                "productDescription": "ORANGE 50",
                "fixedPriceIncl": 50.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "495",
                "productName": "ORANGE 10",
                "productDescription": "ORANGE 10",
                "fixedPriceIncl": 10.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "2066",
                "productName": "Orange Pre-vend Ideal",
                "productDescription": "Orange Pre-vend Ideal",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "2065",
                "productName": "Orange Topup Ideal",
                "productDescription": "Orange Topup Ideal",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "1247",
                "productName": "Utility Pre Vend",
                "productDescription": "Utility Pre Vend",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "1290",
                "productName": "Reprint Token",
                "productDescription": "Reprint Token",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            },
            {
                "partnerId": 20,
                "productId": "1248",
                "productName": "Utility Token",
                "productDescription": "Utility Token",
                "fixedPriceIncl": 0.00,
                "pinnedProduct": false,
                "additionalFields": [
                    {
                        "id": "msisdn",
                        "title": "Phone number",
                        "type": "NUMERIC",
                        "regex": "^[0-9]{8,16}$"
                    }
                ]
            }
        ]
    }
]

Initiating a VAS Payment

For FrontierWeb Services, the POST to /payments initiates both the pre-vend (validation) and the vend (fulfilment), but the call is asynchronous end-to-end. Eclipse responds immediately with status: SUCCESSFUL and deducts funds from the source wallet, but the extraInfo payload (token, voucher serial/PIN, customer receipt) is not populated on the POST response. Channels must poll GET /payments/{paymentId} for up to 2 minutes after the POST to retrieve extraInfo once Eclipse has confirmed the final state with the provider.

This async model was introduced to handle scenarios where the FrontierVasProvider times out on Eclipse's outbound call but the underlying transaction is still in progress at the upstream utility/network. Eclipse internally calls the provider's transaction-check API to confirm the true terminal state, then either populates extraInfo on the payment record or issues a reversal on the source wallet (status flips to FAILED).

All requests use the standard Eclipse authentication and content-type headers:

  • Header Authorization: Bearer <JWT> — see the Authentication and Authorisation page for JWT issuance.

  • Header Content-Type: application/json.
    The paymentData field follows one of two formats depending on the product:

  • Airtime (fixed-price products): {partnerId}_{productId}_0 — e.g. 20_501_0

  • Two-stage products (utility token, DSTV): {partnerId}_{vendProductId}_{prevendProductId} — e.g. 20_1248_1247 (Utility Token + Utility Pre-Vend)

PartnerId for FrontierWeb Services is 20.

Retrieving the token / voucher (GET polling)

Because extraInfo is never populated on the synchronous POST response, channels must retrieve it via GET against the paymentId returned by the POST.

HTTP

GET /eclipse-conductor/rest/v1/tenants/{tenantId}/payments/{paymentId}
Authorization: Bearer <JWT>
Accept: application/json

JSON

The GET returns the full payment object. The below is an example response for a Utility Token purchase against Conlog Botswana — note the populated extraInfo field, which is a stringified JSON array.

{
    "paymentId": 4304995,
    "externalUniqueId": "2c6d4298-d9c8-4616-82ef-c1f7e446cec7",
    "status": "SUCCESSFUL",
    "amount": 100.000000000,
    "description": "VAS purchase  PartnerId-20 : Utility Token",
    "currency": "BWP",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "75757459"
        },
        {
            "id": "meterNumber",
            "value": "21500123456"
        }
    ],
    "acceptedCardSchemes": [],
    "cardPhone": "26775757459",
    "phone": "26775757459",
    "acceptedPaymentMechanisms": [
        "WALLET",
        "AMT",
        "SECURE_CODE"
    ],
    "paymentType": "WALLET",
    "created": "2026-05-18T11:34:04.000Z",
    "lastModified": "2026-05-18T11:34:04.000Z",
    "paymentData": "20_1248_1247",
    "extraInfo": "[{\"att\":\"preValidationResponse\",\"val\":\"{\\\"requestId\\\":\\\"d8d61e07-3b5d-4931-b17c-f60520d915ee\\\",\\\"transactionNumber\\\":\\\"28937440802605181334\\\",\\\"itemId\\\":1247,\\\"instructionSet\\\":\\\"\\\",\\\"merchantReceipt\\\":\\\"METER: 21500123456\\\\n\\\\nTT:02  SGC:990471  KRN:1   TI:01\\\\n\\\\nNAME: Conlog Botswana\\\\nADDRESS: 1 TEST BLOCK\\\\n\\\",\\\"customerReceipt\\\":\\\"\\\",\\\"printFinancialInfo\\\":false,\\\"responseCode\\\":0,\\\"thirdPartyResponse\\\":{\\\"thirdPartyErrorResponse\\\":null,\\\"prevendResponse\\\":{\\\"utilityDetails\\\":{\\\"utilityName\\\":\\\"\\\",\\\"address\\\":\\\"\\\",\\\"contactDetails\\\":\\\"\\\",\\\"taxRef\\\":\\\"\\\",\\\"doUpdate\\\":false},\\\"processorID\\\":215,\\\"processorDescription\\\":\\\"Conlog - Botswana\\\",\\\"address\\\":\\\"1 TEST BLOCK\\\",\\\"customerName\\\":\\\"Conlog Botswana\\\",\\\"meterNumber\\\":\\\"21500123456\\\",\\\"customerIDNumber\\\":null,\\\"standNumber\\\":\\\"////\\\",\\\"town\\\":\\\"\\\",\\\"remaningQuota\\\":\\\"\\\",\\\"oustandingCharges\\\":\\\"\\\",\\\"maxVendAmount\\\":1000.00,\\\"minVendAmount\\\":0,\\\"electricityExtensions\\\":{\\\"tariffsDescription\\\":\\\"\\\",\\\"tariffs\\\":\\\"\\\",\\\"customerMessage\\\":\\\"\\\",\\\"operatorMessage\\\":\\\"\\\",\\\"chargesDetails\\\":[],\\\"arrearsDetails\\\":[],\\\"tariffBlocks\\\":[],\\\"utilityDetails\\\":{\\\"utilityName\\\":\\\"\\\",\\\"address\\\":\\\"\\\",\\\"contactDetails\\\":\\\"\\\",\\\"taxRef\\\":\\\"\\\",\\\"doUpdate\\\":false}},\\\"linkedReferenceNum\\\":\\\"\\\",\\\"balance\\\":null,\\\"availableBalance\\\":null,\\\"creditLimit\\\":null},\\\"electricityResponse\\\":null,\\\"uniPinResponse\\\":null,\\\"carwashResponse\\\":null,\\\"billPaymentResponse\\\":null,\\\"airtimeTopUpResponse\\\":null,\\\"voucherResponse\\\":null},\\\"cellMessage\\\":null,\\\"receiptFormat\\\":0,\\\"receiptFormatVersion\\\":0,\\\"receiptFormatMerchant\\\":null,\\\"receiptFormatCustomer\\\":null}\"}]",
    "paymentInstrumentInfo": {
        "cardPhone": "26775757459"
    },
    "fee": 4,
    "paymentReference": "1248_1247",
    "walletId": 2536583,
    "customerId": 8586330,
    "gatewayTransactionId": "Payment-4304995",
    "paymentTerminalData": {},
    "tracingContext": "00-a023de9869f046ac653f4658116a369c-220c2a5e9df30572-03"
}

Decoded extraInfo

The extraInfo field is a JSON-stringified array. Each entry has an att (attribute name identifying the phase) and a val (the phase response, itself a stringified JSON). After un-escaping the outer string and parsing the inner val, the structure looks like this:

[
    {
        "att": "preValidationResponse",
        "val": {
            "requestId": "d8d61e07-3b5d-4931-b17c-f60520d915ee",
            "transactionNumber": "28937440802605181334",
            "itemId": 1247,
            "instructionSet": "",
            "merchantReceipt": "METER: 21500123456\n\nTT:02  SGC:990471  KRN:1   TI:01\n\nNAME: Conlog Botswana\nADDRESS: 1 TEST BLOCK\n",
            "customerReceipt": "",
            "printFinancialInfo": false,
            "responseCode": 0,
            "thirdPartyResponse": {
                "thirdPartyErrorResponse": null,
                "prevendResponse": {
                    "processorID": 215,
                    "processorDescription": "Conlog - Botswana",
                    "address": "1 TEST BLOCK",
                    "customerName": "Conlog Botswana",
                    "meterNumber": "21500123456",
                    "maxVendAmount": 1000.00,
                    "minVendAmount": 0,
                    "electricityExtensions": {
                        "tariffsDescription": "",
                        "tariffs": "",
                        "customerMessage": "",
                        "operatorMessage": "",
                        "chargesDetails": [],
                        "arrearsDetails": [],
                        "tariffBlocks": []
                    }
                },
                "electricityResponse": null,
                "voucherResponse": null
            }
        }
    }
]

The array can contain multiple entries — one per phase of the transaction (pre-validation, vend response, etc.). The phase carrying the token or voucher is identified by att:

  • Utility token (Conlog Botswana): look for thirdPartyResponse.electricityResponse containing the token, KWh units, and tariff details.
  • Pinned airtime (e.g. BE MOBILE): look for thirdPartyResponse.voucherResponse containing the saleItemList with serialNumber, pinNumber, and expiryDate.
  • DSTV recharge: look for thirdPartyResponse.billPaymentResponse containing the recharge confirmation.
    Channels must un-escape the outer extraInfo string, parse it as JSON, iterate the array, and read the appropriate thirdPartyResponse.* field for the product type.

Recommended polling pattern:

  • Begin polling 5 seconds after the POST returns.
  • Poll at 5-second intervals.
  • Cap total polling duration at 2 minutes (24 attempts).
  • Stop polling as soon as extraInfo is present on the response or status transitions to FAILED / ERROR.
    Note that the status field will return SUCCESSFUL on the POST and throughout the polling window even before the provider has confirmed — do not treat status: SUCCESSFUL alone as a signal that the token has been issued. The presence of a populated extraInfo field is the authoritative signal. If polling expires without extraInfo being populated, raise a support ticket with the tracingContext value; do not retry the POST with the same externalUniqueId.

Channels that prefer a push model can supply a callbackUrl on the original POST — see Asynchronous Callbacks below.

Use Case 1 - Utility Token (Electricity)

The purpose of this integration is to process utility token purchases. The pre-vend validates the meter number against the upstream utility (Conlog Botswana); the vend issues the token. The token and customer receipt are returned in the extraInfo field.

Prerequisites

  • A valid JWT for API calls.
  • Tenant config fees.amount.config.Pay.GLOBAL_DEFERRED.WALLET.{partnerId}.{productCategory}=feeAmount
  • Tenant config fees.wallet.config.Pay.GLOBAL_DEFERRED.WALLET=destinationWalletId (fee destination)
  • Tenant config destination.wallet.config.VAS.{partnerId}.{productCategory}=destinationWalletId (VAS settlement destination)

Initiate Payment

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
Authorization: Bearer <JWT>
Content-Type: application/json

{
    "externalUniqueId": "{{$randomUUID}}",
    "paymentData": "20_1248_1247",
    "type": "GLOBAL_VAS",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        },
        {
            "id": "meterNumber",
            "value": "21500123456"
        }
    ],
    "amount": 180,
    "currency": "BWP",
    "walletId": 2536583
}
{
    "paymentId": 4007245,
    "externalUniqueId": "2c6d4298-d9c8-4616-82ef-c1f7e446cec7",
    "status": "SUCCESSFUL",
    "amount": 13.000000000,
    "description": "VAS purchase  PartnerId-20 : Utility Token",
    "currency": "BWP",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        },
        {
            "id": "meterNumber",
            "value": "21500123456"
        }
    ],
    "acceptedCardSchemes": [],
    "cardPhone": "27847919825",
    "phone": "27847919825",
    "acceptedPaymentMechanisms": [
        "WALLET",
        "AMT",
        "SECURE_CODE"
    ],
    "paymentType": "WALLET",
    "created": "2025-12-08T11:09:49.000Z",
    "lastModified": "2025-12-08T11:09:50.124Z",
    "paymentData": "20_1248_1247",
    "extraInfo": "[{\"callbackId\":72628017,\"result\":\"...stringified JSON containing customer receipt, token number, KWh units, tariff details, electricityResponse...\"}]",
    "paymentInstrumentInfo": {
        "cardPhone": "27847919825"
    },
    "fee": 4,
    "paymentReference": "1248_1247",
    "walletId": 2536583,
    "customerId": 8586330,
    "gatewayTransactionId": "Payment-4007245",
    "paymentTerminalData": {},
    "tracingContext": "00-17cf8831a32a43f3370ba75356081445-b51bc51c2a65845b-01"
}

On the POST, the response carries status: SUCCESSFUL and an empty/unpopulated extraInfo. The token (under electricityResponse.tokens), customer receipt, KWh units, and tariff details become available on the GET payment record once Eclipse has confirmed the vend with Conlog Botswana — typically within 5–30 seconds, but allow up to 2 minutes. Channels must poll GET /payments/{paymentId} and parse extraInfo to surface the token to the customer.

📘

Note

Sandbox test meter numbers: 00300123452 or 21500123456 (Conlog Botswana fixture). In sandbox, invalid meter numbers may still return status: SUCCESSFUL as pre-vend validation against the live utility provider is bypassed in non-production environments.

Use Case 2 - DSTV Recharge

The purpose of this integration is to allow customers to pay DSTV subscriptions. The pre-vend validates the DSTV account number; the vend recharges the account.

Prerequisites

  • A valid JWT for API calls.
  • Tenant config fees.amount.config.Pay.GLOBAL_DEFERRED.WALLET.{partnerId}.{productCategory}=feeAmount
  • Tenant config fees.wallet.config.Pay.GLOBAL_DEFERRED.WALLET=destinationWalletId
  • Tenant config destination.wallet.config.VAS.{partnerId}.{productCategory}=destinationWalletId

Initiate Payment

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
Authorization: Bearer <JWT>
Content-Type: application/json

{
    "externalUniqueId": "{{$randomUUID}}",
    "paymentData": "20_1260_1259",
    "type": "GLOBAL_VAS",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        },
        {
            "id": "accountNumber",
            "value": "12345678"
        }
    ],
    "amount": 33,
    "currency": "BWP",
    "walletId": 2536583
}

In this use case the paymentData field is set to \{partnerId\}_\{vendProductId\}_\{prevendProductId\} i.e. 20_1260_1259

PartnerId = 20

VendProductId = 1260 (Bots DSTV Recharge)

PrevendProductId = 1259 (Bots DSTV Balance)

The response shape mirrors the Utility Token response above. As with utility tokens, extraInfo is not populated on the POST response and must be retrieved via GET polling.

Use Case 3 - Airtime Purchase

LFSB wallet holders can purchase airtime from Orange, BE Mobile, and Mascom. Airtime products are fixed-price, so the amount value is determined by the selected productId and does not need to be supplied on the request.

Prerequisites

  • A valid JWT for API calls.
  • Tenant config fees.amount.config.Pay.GLOBAL_DEFERRED.WALLET.{partnerId}.{productCategory}=feeAmount
  • Tenant config fees.wallet.config.Pay.GLOBAL_DEFERRED.WALLET=destinationWalletId
  • Tenant config destination.wallet.config.VAS.{partnerId}.{productCategory}=destinationWalletId

Initiate Payment

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
Authorization: Bearer <JWT>
Content-Type: application/json

{
    "externalUniqueId": "{{$randomUUID}}",
    "paymentData": "20_501_0",
    "type": "GLOBAL_VAS",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        }
    ],
    "currency": "BWP",
    "walletId": 2536583
}

In this example 20_501_0 purchases BE MOBILE 20 (productId 501). The trailing _0 indicates no pre-vend product is required for fixed-price airtime.

{
    "paymentId": 4007260,
    "externalUniqueId": "59802001-ac18-4695-9e9f-a0e3a359826b",
    "status": "SUCCESSFUL",
    "amount": 20.000000000,
    "description": "VAS purchase  PartnerId-20 : BE MOBILE 20",
    "currency": "BWP",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        }
    ],
    "acceptedCardSchemes": [],
    "cardPhone": "27847919825",
    "phone": "27847919825",
    "acceptedPaymentMechanisms": [
        "WALLET",
        "AMT",
        "SECURE_CODE"
    ],
    "paymentType": "WALLET",
    "created": "2025-12-08T11:12:39.000Z",
    "paymentData": "20_501_0",
    "extraInfo": "[{\"callbackId\":72628059,\"result\":\"...stringified JSON containing saleItemList with serialNumber, pinNumber, expiryDate...\"}]",
    "paymentInstrumentInfo": {
        "cardPhone": "27847919825"
    },
    "fee": 5,
    "paymentReference": "501_0",
    "walletId": 2536583,
    "customerId": 8586330,
    "gatewayTransactionId": "Payment-4007260",
    "paymentTerminalData": {}
}

As with utility tokens, the POST response returns status: SUCCESSFUL with an unpopulated extraInfo. For pinned airtime products (e.g. BE MOBILE), the voucher serial and PIN inside extraInfo.result.saleItemList become available on the GET payment record once Eclipse has confirmed the vend with the upstream network. Channels must poll GET /payments/{paymentId} and parse saleItemList to surface the voucher to the customer.

Response Field Reference

FieldDescription
paymentIdEclipse-generated unique identifier for the payment. Use this for status enquiries via GET /payments/{paymentId}.
externalUniqueIdThe idempotency key supplied by the channel on the request. Eclipse rejects duplicate externalUniqueId values within the tenant.
statusTerminal or intermediate state of the payment. See Transaction Status Codes below.
amountAmount transacted, in major currency units. Determined by productId for fixed-price products.
descriptionHuman-readable description, populated by Eclipse based on the partner and product.
currencyISO 4217 currency code. Must match the wallet currency.
additionalFieldsEchoes the additional fields supplied on the request (msisdn, meterNumber, accountNumber).
acceptedCardSchemesList of card schemes accepted for this payment. Empty for wallet-funded VAS.
acceptedPaymentMechanismsList of payment mechanisms supported for this payment (e.g. WALLET, AMT, SECURE_CODE).
cardPhone / phoneCustomer contact number populated from the wallet record.
paymentTypeThe payment instrument used. For wallet-funded VAS this will be WALLET.
paymentInstrumentInfoNested object echoing details of the instrument used (e.g. cardPhone for wallet payments).
paymentDataEchoes the paymentData value from the request.
extraInfoProvider-specific payload as a stringified JSON. Not populated on the POST response — retrieved via GET /payments/{paymentId} polling after Eclipse has confirmed the vend with the upstream provider (typically within 5–30 seconds, allow up to 2 minutes). For utility tokens contains the token number, customer receipt, KWh units, tariff details, and electricityResponse object. For pinned airtime contains the voucher serial and PIN inside saleItemList. Channels must parse and surface this content to the customer.
paymentReferenceReference used for reconciliation. Composed of {vendProductId}_{prevendProductId}.
feeTransaction fee applied per tenant config.
walletIdSource wallet from which the payment was funded.
customerIdEclipse customer ID associated with the payment.
gatewayTransactionIdEclipse internal gateway reference.
paymentTerminalDataTerminal data object. Empty for VAS; populated for card-present transactions.
tracingContextW3C trace context. Useful when raising support tickets to correlate with Eclipse server logs.
errorDescriptionPopulated only on status FAILED or ERROR.

Transaction Status Codes

StatusMeaningAction expected from channel
SUCCESSFULEclipse has accepted the request and deducted funds from the source wallet. Note that this status is returned immediately on the POST and does not by itself indicate that the upstream vend has completed — Eclipse asynchronously confirms the final state with the provider.Poll GET /payments/{paymentId} (or wait for a callback) until extraInfo is populated, then render the token/PIN/receipt to the customer. Status may transition to FAILED if Eclipse's async confirmation determines the vend did not complete at the provider, in which case the wallet is reversed.
PENDINGEclipse has accepted the request but the upstream provider has not yet confirmed completion.Wait for callback (if callbackUrl was supplied) or poll GET /payments/{paymentId}. Do not retry the POST — externalUniqueId will reject duplicates.
BUILDINGIntermediate state seen only when a separate PUT is required to finalise the payment. Does not apply to FrontierWeb single-POST flows.N/A for FrontierWeb.
FAILED / ERRORThe transaction did not complete. Funds have been reversed on the source wallet.Inspect errorDescription and the application error envelope (code, traceId). Surface a user-facing error and allow retry with a new externalUniqueId.

For the full Eclipse application error code list, see Application Error Codes.

The error envelope returned by Eclipse on a failed call follows this shape:

[
    {
        "type": "BUSINESS",
        "severity": "INFO",
        "description": "Invalid voucher for the users network. User is on network id 0 but voucher is for network id 20",
        "code": "IAE001",
        "traceId": "9099797c1668796c2ba0b371b76a45df",
        "spanId": "c06f2d77d914b3fb"
    }
]

Asynchronous Callbacks

Every FrontierWeb VAS payment finalises asynchronously — the POST response returns status: SUCCESSFUL with funds deducted but without extraInfo. Channels have two options for retrieving the final extraInfo payload: polling (see Retrieving the token / voucher above) or callbacks.

To receive a callback when the async confirmation completes, supply a callbackUrl on the original POST. Eclipse will POST to this URL once it has confirmed the final state with the upstream provider. The callback body carries the same payment object shape as the GET response and will include the populated extraInfo. Status on the callback may be SUCCESSFUL (token/voucher issued, extraInfo populated) or FAILED / ERROR (wallet reversed).

POST /eclipse-conductor/rest/v1/tenants/{tenantId}/customers/{customerId}/payments
Authorization: Bearer <JWT>
Content-Type: application/json

{
    "externalUniqueId": "{{$randomUUID}}",
    "paymentData": "20_1248_1247",
    "type": "GLOBAL_VAS",
    "callbackUrl": "https://channel.tenant.example.com/eclipse/vas/callback",
    "additionalFields": [
        {
            "id": "msisdn",
            "value": "772216623"
        },
        {
            "id": "meterNumber",
            "value": "21500123456"
        }
    ],
    "amount": 180,
    "currency": "BWP",
    "walletId": 2536583
}

Callback security

Callbacks from Eclipse to the channel are secured under the standard Eclipse webhook security model: TLS 1.2+ on the transport layer, and an Eclipse-Signature HTTP header carrying an HMAC-SHA256 signature of the timestamp and message body, signed with the tenant's HMACOutboundSignatureKey. The 5-minute replay window, optional RSA v2 signature mode, optional webhookAuthorizationHeader, and optional JWE full-payload encryption are all documented on the Encryption and Integrity page.

Source IP allow-list

Channels that whitelist inbound traffic should permit calls from the following Eclipse source IPs:

  • Production: 79.125.40.127, 54.220.185.240, 18.134.2.182, 3.11.195.109
  • Sandbox: 34.255.93.65

Retries

Eclipse will retry callbacks on non-2xx responses. Retry behaviour is documented on the CallbackURL Retries page.

Polling alternative

If callback infrastructure is not available, channels can instead poll GET /eclipse-conductor/rest/v1/tenants/{tenantId}/payments/{paymentId} until extraInfo is populated or the payment reaches a terminal FAILED / ERROR status. Polling should be capped at 2 minutes (e.g. 24 attempts at 5-second intervals) to avoid hot loops.

Settlement

On successful VAS payment, Eclipse triggers a notification for funds to move from the customer's source wallet to the configured VAS destination wallet (destination.wallet.config.VAS.{partnerId}.{productCategory}) and the fee destination wallet (fees.wallet.config.Pay.GLOBAL_DEFERRED.WALLET). For tenants where the source wallet is fronted by an external bank trust account (e.g. Access Bank), Eclipse emits a settlement notification to trigger the corresponding bank-side movement.