# Introduction

The EllyPay platform enables merchants (businesses and online platforms) to process payments across multiple payment options. The document describes the current version of the API.

Welcome to the EllyPay Gateway API. This API enables merchants (businesses and other online platforms) to integrate online payments into their business processes and support payments for goods and services. Our developer friendly API gives you access to mobile money in Uganda on MTN and Airtel networks. More options to be enabled in due course. With this platform, merchants are able to request payments from valid mobile money subscribers and visa/mastercard holders.

## Platform Features

### Sandbox Environment

This is a utility platform built to allow merchants simulate the workflows of the production/live environment. The sandbox environment offers an API that the merchant integrates first to test the platform and perform UATs. It offers test accounts that the merchant can use to try out the different operations of the API without affecting customer funds in the real world. When all tests are done to the satisfaction of the merchant and the EllyPay support staff, the merchant can then be approved to go live.

{% hint style="info" %}
We intend to make your migration from sandbox to production very smooth. You'll strictly need to change the API base URL and obtain a new set of credentials (security keys). Everything else will work as expected.
{% endhint %}

### Production Environment

Under this environment, real funds can be requested from real customers. In order for the merchant to be accepted to use the production environment, they will have completed the compliance/KYC phase and also integrated the sandbox API for all the required tests.

### Async Processing

The EllyPay API is asynchronous; meaning the merchant doesn't need to wait on the same request thread to obtain the new status of the transaction. The confirmation of the transaction status will be via a callback/webhook/IPN POST request. We will therefore require that you share the **https** URL for your callback endpoint and it will be configured on the merchant account accordingly.

### Transparency

EllyPay provides a management portal to all merchants. This is the central point to manage your business. It allows for creation of multiple merchant accounts and provides reports for all the activity on the different accounts. It also offers accounts statements to reflect how the merchant balances have changed over time. For every merchant account, API settings can be configured to support the integration.

### API Endpoints

The EllyPay API can be accessed using the following base URLs. Both URLs will only be accessible when the merchant account is created and approved on either environment.

<table><thead><tr><th width="151.77069091796875">Environment</th><th>API Base URL</th></tr></thead><tbody><tr><td>Sandbox/Test</td><td>https://gwapisdbx.ellypayapp.com/</td></tr><tr><td>Production/Live</td><td>https://payments.ellypayapp.com/</td></tr></tbody></table>


# Registration

Any entity interested in the EllyPay  API needs to register and fulfil the compliance/KYC requirements before they are granted access to the platform.

## Required Documents

Please organize and share copies of the following items for account approval;

1. Registered & Certified Certificate of Incorporation (for registered companies) OR Certificate of Registration (for registred business names).
2. TIN Certificate (for registered companies)
3. Statement of Particulars of the members.
4. Valid Trading License.
5. Identity Documents of Primary and related parties (National ID (for nationals) OR Passport (for Foreigners) OR Refugee ID and Refugee attestations (for refugees).
6. Identity documents of all UBOs (Ultimate Beneficial Owners) of the company.
7. Proof of company address (tenancy agreement or utility bill etc)
8. Brief company profile including the website details

{% hint style="info" %}
Please write an email to us at **<info@ellypayapp.com>** to request for the API connection and share all the requirements as described in this section. Our compliance department will do due-diligence and once approved, the API account will be created and details shared accordingly.
{% endhint %}


# Error Handling

The EllyPay API uses HTTP status codes to indicate the success or failure of the API requests. The section describes the summary of these codes and what they mean in our context.

## Summary

* Codes in the 2XX range mean that the API request was processed successfully.
* Codes in the 4XX range mean that something was wrong with the data that you sent. For example, you might have missed some required parameters/headers, or you might be using the wrong API credentials.
* Codes in the 5XX range indicate an error in processing on our end

## Common HTTP Codes

<table><thead><tr><th width="145.57142857142856">Code</th><th>Description</th></tr></thead><tbody><tr><td>200</td><td>OK - Request was successful</td></tr><tr><td>202</td><td>Accepted - Request has been accepted and acknowledged. We will now go ahead to process the request and notify you of the status afterwards.</td></tr><tr><td>400</td><td>Bad Request - Malformed request or missing required parameters</td></tr><tr><td>401</td><td>Unauthorized - Missing required headers, wrong Public or Secret Key etc</td></tr><tr><td>403</td><td>Forbidden - You are trying to access a resource for which you don't have proper access rights.</td></tr><tr><td>404</td><td>Not Found - You are trying to access a resource that does not exist</td></tr><tr><td>422</td><td>Unprocessable Entity - You provided all the required parameters but they are not proper for the request</td></tr><tr><td>500</td><td>Internal Server Error - We had a glitch in our servers. Retry the request in a little while or contact support</td></tr><tr><td>503</td><td>Service Unavailable – We are temporarily offline for maintenance. Please try again later.</td></tr></tbody></table>


# Authentication

The EllyPay API uses a key pair (Public and Secret Keys) for authentication. To make an authenticated request to our API, you need to pass custom headers as described in this section.

## Obtain the Keys

The Public and Secret keys can be copied from the merchant dashboard under the specific merchant account details as described [here](/getting-started/merchant-account-credentials). These keys should be sent as headers with the header names; **public-key** and **secret-key**

{% hint style="info" %}
Please ensure that your keys are stored safely and not shared with the public. In the event your keys are compromised, please contact us immediately for assistance. You an alternatively regenerate the secret key from within your merchant account dashboard
{% endhint %}

## Request Headers

For most of the API requests, only the public-key header is required. Scenarios that require the secret-key header will be described accordingly;

<table><thead><tr><th width="169.33333333333331">Header Name</th><th width="461">Value</th><th>Required</th></tr></thead><tbody><tr><td>Content-Type</td><td>application/json</td><td>YES</td></tr><tr><td>x-api-version</td><td>API version (currently 1)</td><td>YES</td></tr><tr><td>public-key</td><td>The Public Key as obtained from the merchant account settings</td><td>YES</td></tr><tr><td>secret-key</td><td>The Secret Key as obtained from the merchant account settings. Required for specific situations as the document will guide</td><td>NO</td></tr></tbody></table>


# Merchant Account Credentials

For every approved merchant account, a set of credentials is automatically generated and stored on the record. This section describes how the merchant can manage credentials after the account creation

As described [here](/getting-started/authentication), the API requires that certain headers are sent for the various requests. To be specific, the `public-key`header is mandatory for all the API requests and the `secret-key` is required only in a few situations as the documentation will advise along the way. The other critical variable is the **signing key**, a randomly generated string stored on the merchant accout record, whose sole purpose is to support the generation of the HMAC Hash signature sent alongside the merchant callbacks.

When the merchant account is created and approved by the admins, all the above 3 mentioned values are generated and securely stored. The public-key (as its name suggests) is dislayed among the merchant account details in plain text. The signing key too is displayed in plain text for the merchant to view it. The secret key on the other hand is stored as a strongly hashed value and the gateway team never gets to see the plain text version of this secret key. The merchant should therefore generate a new secret key from their dashboard in order to temporarily see the secret key in plain text and get a chance to store is somewhere safe.

The merchant is at liberty to re-generate just the secret key OR all the keys mentioned above, that way in case of compromise, the merchant has full control of the keys change without needing the gateway support team. The table below advises further.

|                                                                                      |                                                                                                    |
| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| If you wish to generate only the secret key and the other keys remain the same       | [Generate Secret Key](/getting-started/merchant-account-credentials/generate-secret-key)           |
| If you wish to generate a fresh set of credentials and replace all the existing ones | [Regenerate Security Keys](/getting-started/merchant-account-credentials/regenerate-security-keys) |

{% hint style="info" %}
We strongly recommend routine change of the credentials as a security measure especially in situations where integration work is outsourced to parties external to the merchant/organization.
{% endhint %}


# Generate Secret Key

The section below describes (with screenshots) how to generate and store a new secret key. The process assumes that you're already logged in to the merchant dashboard.

#### Step 1: Click "**Merchant Profile**" Menu.

#### Step 2: Edit the Merchant Profile

#### Step 3: Click the "**Edit**" button on the Merchant Account of your choice

#### Step 4: Click "**Generate New Secret Key**" button.

#### Step 5: From the popup dialog, click "**Request New Secret Key**" Button.

#### Step 6: Close the dialog after copying the secret key


# Regenerate Security Keys

The section below describes (with screenshots) how to generate a new set of security keys. The process assumes that you're already logged in to the merchant dashboard.

#### Step 1: Click "**Merchant Profile**" Menu.

#### Step 2: Edit the Merchant Profile

#### Step 3: Click the "**Edit**" button on the Merchant Account of your choice

#### Step 4: Click "**Generate New Keys**" button.

#### Step 5: From the popup dialog, click "**Generate a set of New Keys**" Button.

#### Step 6: Copy the keys and click "**Close, I have Saved my keys**" button.


# Supported Countries

The EllyPay platform supports multiple payment methods, transaction types and currencies across a number of countries or regions. Merchant accounts will be enabled for only what they request.

Below is a list of the supported countries/regions, the acceptable transaction methods and types plus the official transaction currencies that apply to the respective regions.

<table><thead><tr><th width="97">Code</th><th width="150">Name</th><th width="324">Transaction Method(s)</th><th>Trans. Currency</th></tr></thead><tbody><tr><td>UG</td><td>Uganda</td><td>MOBILE_MONEY (Collection, Payout)</td><td>UGX</td></tr><tr><td>UG</td><td>Uganda</td><td>BANK (Payout)</td><td>UGX</td></tr></tbody></table>


# Transaction Limits

For some of the transaction currencies supported, there are limits that control what a single transaction account is able to do per transaction and in a 24hr period.

The tables below describe the accepted amount limits for the respective currencies for both collections and payouts.

### Collection Limits

<table><thead><tr><th width="108">Country</th><th width="153">Payment Option</th><th width="95">Currency</th><th data-type="number">Min. Trans. Amount</th><th data-type="number">Max. Trans. Amount</th><th data-type="number">Max Day Limit</th></tr></thead><tbody><tr><td>UG</td><td>MTN Mobile Money</td><td>UGX</td><td>3000</td><td>5000000</td><td>7000000</td></tr><tr><td>UG</td><td>Airtel Money</td><td>UGX</td><td>3000</td><td>5000000</td><td>7000000</td></tr></tbody></table>

### Payout Limits

<table><thead><tr><th width="108">Country</th><th width="153">Payment Option</th><th width="95">Currency</th><th data-type="number">Min. Trans. Amount</th><th data-type="number">Max. Trans. Amount</th><th data-type="number">Max Day Limit</th></tr></thead><tbody><tr><td>UG</td><td>MTN Mobile Money</td><td>UGX</td><td>3000</td><td>2000000</td><td>7000000</td></tr><tr><td>UG</td><td>Airtel Money</td><td>UGX</td><td>3000</td><td>2000000</td><td>7000000</td></tr><tr><td>UG</td><td>Bank Transfer</td><td>UGX</td><td>10000</td><td>10000000</td><td>50000000</td></tr></tbody></table>


# Sandbox Test Accounts

The table below describes the test account numbers that can be used for tests on the sandbox to simulate transactions. Two scenarios (success and failure) can be simulated based on the account number.

### Mobile Money

<table><thead><tr><th width="107">Country</th><th width="159">Payment Option</th><th>Success</th><th>Failure</th></tr></thead><tbody><tr><td>UG</td><td>MTN Mobile Money</td><td>Any valid MTN Uganda phone number</td><td>256777000002</td></tr><tr><td>UG</td><td>Airtel Money</td><td>Any valid Airtel Uganda phone number</td><td>256752000002</td></tr></tbody></table>

### Bank (Supported Payout Banks)

| Country/Region | Success       | Failure       |
| -------------- | ------------- | ------------- |
| Uganda         | 9042015124058 | 9042015124060 |


# RSA Public Keys

This section shares the two public keys used for the two environments (Sandbox and Production). The keys can be copied and stored as files to later be used in transaction processing

The EllyPay public key currently plays one critical role of verifying the callback signatures. Callbacks from the platform carry a header named `rsa-signature` and with the public key, the signature can be verified as described [here](/callbacks/rsa-signature-verification) to confirm authenticity.

### Sandbox Public Key

```
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw6cmFcTe9tf0Z+I6l/KB
y8gUPEwLQgwtPkMTL19/uG+3z6LLkwHPoGNnLjicUI9cgb2797AVXPaokFsTRLrZ
krPlxCXYXN79fuDOnsJPGwpkvehUeov5jsYroB67pObcloouRbuf2PoEQHUsK/K2
z1Vzl4jNNVkp29U5ke6rwb7+0+gMDX/mW5RQZnTs1maR0Vx1PhTXgkoYs4x/M/TN
CIttYj+UOBz4Gb+i5zWn7v1uu0EAzEzngpBvPaaWeZGpvzWOpr3KpCEGAeh8lQuu
v91DWZAL/KqObMam/pV2uZ5jTGGFguwRYcsos98hNo2xDa/kLHAMrGyDzYWjhH8o
AjKJXd65PuB3RxJCJBvP9gM8IGKYl9aKXgOnGVcrzg7aZZxn7A1gtSXMTGh15tib
YsSlJtl7VkN9e6LMgJFk6YyNPSMxIEa0m8DXEebcfgJmHXrgJie4nM4SEexkhBSn
+BklGf1oRlC1km0G9TgXYgxrGv7e3Ssyl6l+hEz54vDlkeO6vS49yqd46Lh+2bbR
soCr3U5fX3uEYVctDlPqNnorhw34XwfU8FKAMsFvN4sytw7E+jzPxTFmOrYyrxUQ
yCExCPmk2TwkIUy6/8bIWJIJ3aNH8dpKQwrZ210W86yUNoT2bdxPrWFIAoNVS5RO
oGtT3vC9S3WAXtykJG83CPECAwEAAQ==
-----END PUBLIC KEY-----

```

### Production Public Key

```
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1JM8K8yd1U3e6ZjnSYB7
9EthjhKLNdi3WiQ8d5AjQ1ktgPanAIsZERDJi4LvIcfhpRMuBC6nsIAXcO/mzrGo
OYiqEr+cGSYnA3EiyS520uxX1P7VjJCQSuAZlrRqaNQgBE9px2IJxWBJTlQVk2cI
R3UETOI+NyI8ZUtzf4pY+nj7cbB6Ab0/whtHBODwxRuOZ29iLaBi27ogNhIxQaP9
2DUnD6cHum9gVzzfy19FfVjCRMIkx2FCVxkDplIMRyro1qNd2jQfO10UHF8Wqz+T
XHSuCXvBkp4LzlOvLOZnv0nbt70OTmghv1X9FrtlAk8a0gLiG1rs9/ew0e0utc7R
1qv+f9geztueKsK4sCuEoQex/dyTtObr0emVBgJYCY3zWJ+OY1FzUFYefs5XbPQ+
uHxnSyp+RpEOA7IaqaiFp9W8mM61q+mB02fOf0eTl8IBFg4LxTOc2+kz9uGCbSjy
IPiJg3scoXf9ioWWXfCTidZ6xbSWurjaRfxnkx6kiaxvmH3QdkpOZ2eACIN83yEo
GiNiooDaQGOg4A9ewst+FDvvAhba+da1p05vbKJac22Cs31T2l3mkzexpvDFjo1u
kHq0YGKNyO3FdzBx0gmNa85YuGLBJiVYoxo/lmsktJuv3JUNC4wzK4GL770nDW93
07omDYnkvTTUpRBePZd9ZFcCAwEAAQ==
-----END PUBLIC KEY-----

```

{% hint style="info" %}
We recommend that for each environment you're working with, the key content is copied and stored in your workflow based on how you see fit. In this documentation however, we will assume the content is stored in a file named `ellypay.public.key.pem` and referenced from there for all described usage.
{% endhint %}


# Balance Inquiry

The API allows the merchant to check the balance(s) for the different currencies they might hold.

<mark style="color:blue;">`GET`</mark> `https://gwapisdbx.ellypayapp.com/data/wallet-balances`

The endpoint retrieves the balance(s) by currency. It return currency wallets belonging to the merchant account

#### Query Parameters

<table><thead><tr><th width="149">Name</th><th width="97">Type</th><th>Description</th></tr></thead><tbody><tr><td>currency</td><td>String</td><td>The currency whose balances should be retrieved (Optional)</td></tr></tbody></table>

#### Headers

<table><thead><tr><th>Name</th><th width="119">Type</th><th>Description</th></tr></thead><tbody><tr><td>public-key<mark style="color:red;">*</mark></td><td>String</td><td>The merchant account Public Key</td></tr><tr><td>secret-key<mark style="color:red;">*</mark></td><td>String</td><td>The merchant account Secret Key</td></tr></tbody></table>

### Sample Balance Inquiry Request

```powershell
curl https://gwapisdbx.ellypayapp.com/data/wallet-balances \
   -H "Accept: application/json" \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key" \
   -H "secret-key: your-secret-key"
```

{% tabs %}
{% tab title="200: OK Returns array of wallet balances based on the currency" %}

```json
{
    "code": 200,
    "status": "success",
    "message": "Request completed successfully.",
    "data": {
        "merchant_wallets": [
            {
                "available_balance": 40000,
                "actual_balance": 40000,
                "currency": "UGX",
                "uncleared_balance": 40000,
                "reserve_balance": 0,
                "wallet_type": "MERCHANT_COLLECTION"
            },
            {
                "available_balance": 0,
                "actual_balance": 0,
                "currency": "UGX",
                "uncleared_balance": 0,
                "reserve_balance": 0,
                "wallet_type": "MERCHANT_PAYOUT"
            }
        ]
    }
}

```

{% endtab %}

{% tab title="400: Bad Request When the request isn't formed as expected" %}

```javascript
{
  "code": 400,
  "status": "error",
  "message": "secret-key is required",
  "data": {}
}
```

{% endtab %}

{% tab title="401: Unauthorized When one of the keys is invalid" %}

```javascript
{
  "code": 401,
  "status": "error",
  "message": "Unauthorized API access. Invalid Merchant Secret Key",
  "data": {}
}
```

{% endtab %}
{% endtabs %}

### Wallet Types

<table><thead><tr><th width="280">Wallet Type</th><th>Description</th></tr></thead><tbody><tr><td>MERCHANT_COLLECTION</td><td>This wallet holds funds accumulated from funds collection of a specific currency. Funds directly deposited into the merchant account for a gievn currency would also live in a wallet of this type.</td></tr><tr><td>MERCHANT_PAYOUT</td><td>All the funds the merchant accout wishes to designate for disbursements will be stored in a special wallet of this type. The merchant will be allowed to provision funds here by transferrig from the collection account to this one.</td></tr><tr><td>SERVICE_PAYMENTS</td><td>All the funds the merchant accout wishes to designate for service/biller payments (e.g. Utilities. PayTV, Airtime etc) will be stored in a special wallet of this type. The merchant will be allowed to provision funds here by transferrig from the collection account to this one.</td></tr></tbody></table>

### Balance Descriptions

<table><thead><tr><th width="246">Balance Name</th><th>Description</th></tr></thead><tbody><tr><td>available_balance</td><td>The amount/balance available for use. This the amount the merchant account has access to for any other platform operations</td></tr><tr><td>actual_balance</td><td>This is the total balance of a given currency for a certain merchant account wallet. It's the summation of all the balances in the given wallet.</td></tr><tr><td>uncleared_balance</td><td>This is the amount yet to be made available to the merchant account for use.</td></tr><tr><td>reserve_balance</td><td>In the event transactions are done using payment options for which a rolling reserve is implemented (e.g. card options), the accumulated rolling reserve amounts would be deposited under this balance</td></tr></tbody></table>


# Payment Options

Before any transaction request is performed, it's recommended that the payment options available to the merchant account are known. This endpoint returns that list.

The API description below shows how the list of payment options can be obtained. The request requires that the merchant filters by currency and transaction type to get the appropriate list of the payment options.

## Get Payment Options List

<mark style="color:blue;">`GET`</mark> `https://gwapisdbx.ellypayapp.com/data/payment-providers`

Returns the list of payment options available to the merchant account based on currency and transaction type

#### Query Parameters

<table><thead><tr><th width="180">Name</th><th width="82">Type</th><th>Description</th></tr></thead><tbody><tr><td>currency<mark style="color:red;">*</mark></td><td>String</td><td>The 3 character ISO currency code e.g. UGX</td></tr><tr><td>transaction_type<mark style="color:red;">*</mark></td><td>String</td><td>The transaction type. Allowed types at the moment are; <strong>COLLECTION</strong> and <strong>PAYOUT</strong></td></tr></tbody></table>

#### Headers

<table><thead><tr><th width="147">Name</th><th width="114">Type</th><th>Description</th></tr></thead><tbody><tr><td>public-key<mark style="color:red;">*</mark></td><td>String</td><td>The merchant account Public Key</td></tr></tbody></table>

### Sample Payment Options Request

```powershell
curl https://gwapisdbx.ellypayapp.com/data/payment-providers?currency=UGX&transaction_type=COLLECTION \
   -H "Accept: application/json" \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key"
```

{% tabs %}
{% tab title="200: OK Returns the list of payment options matching the query" %}

```json
{
    "code": 200,
    "status": "success",
    "message": "Request completed successfully.",
    "data": {
        "payment_providers": [
            {
                "provider_name": "MTN Mobile Money Uganda",
                "provider_code": "mtn_momo_ug",
                "transaction_method": "MOBILE_MONEY",
                "transaction_method_name": "Mobile Money",
                "transaction_type": "Collection",
                "transaction_currency": "UGX",
                "min_amount": 500,
                "max_amount": 3000000,
                "max_daily_amount": 7000000,
                "is_active": true,
                "option_prefixes": [
                    "77",
                    "78",
                    "76",
                    "39"
                ]
            },
            {
                "provider_name": "Airtel Money Uganda",
                "provider_code": "airtel_money_ug",
                "transaction_method": "MOBILE_MONEY",
                "transaction_method_name": "Mobile Money",
                "transaction_type": "Collection",
                "transaction_currency": "UGX",
                "min_amount": 500,
                "max_amount": 3000000,
                "max_daily_amount": 7000000,
                "is_active": true,
                "option_prefixes": [
                    "74",
                    "75",
                    "70"
                ]
            }
        ]
    }
}

```

{% tabs %}
{% tab title="Response Description" %}

<table><thead><tr><th width="211">Parameter</th><th width="90">Type</th><th>Description</th></tr></thead><tbody><tr><td>provider_code</td><td>string</td><td>The unique code for the payment option. It is required for the transaction requests</td></tr><tr><td>provider_name</td><td>string</td><td>The name of the option</td></tr><tr><td>transaction_type</td><td>string</td><td>The transaction type</td></tr><tr><td>transaction_method</td><td>string</td><td>The transaction method supported by this option</td></tr><tr><td>transaction_currency</td><td>string</td><td>The currency in which the option transacts</td></tr><tr><td>min_amount</td><td>float</td><td>The minimum amount allowed per transaction when using this option</td></tr><tr><td>max_amount</td><td>float</td><td>The maximum amount allowed per transaction when using this option</td></tr><tr><td>max_daily_amount</td><td>float</td><td>The maximum amount a single account number is allowed to transact in 24hrs</td></tr><tr><td>option_prefixes</td><td>array</td><td>String prefixes that validate the phone numbers. Applies to mobile money options</td></tr></tbody></table>
{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="400: Bad Request When the request is not formed as expected" %}

```javascript
{
  "code": 400,
  "status": "error",
  "message": "transaction_type is required",
  "data": {}
}
```

{% endtab %}
{% endtabs %}


# Payout Bank Codes

For all countries/currencies where EllyPay supports bank payouts, there's need to specify the unique code for the bank to which the funds are to be sent. These codes are available via the API

The bank codes are attached to the respective payment providers designated for paying out funds to the supported banks. Therefore, a provider code, obtained from [here](#get-payment-options-list) can be used to query the list of supported banks. Any provider that supports BANK payouts will have the list of banks and respective codes.

## Get Payout Banks List

<mark style="color:blue;">`GET`</mark> `https://gwapisdbx.ellypayapp.com/data/payout-bank-codes`

Returns the list of payout banks based on the provider code

#### Query Parameters

<table><thead><tr><th width="180">Name</th><th width="82">Type</th><th>Description</th></tr></thead><tbody><tr><td>provider_code<mark style="color:red;">*</mark></td><td>String</td><td>The payment provider code for the provider whose bank codes are required</td></tr></tbody></table>

#### Headers

<table><thead><tr><th width="147">Name</th><th width="114">Type</th><th>Description</th></tr></thead><tbody><tr><td>public-key<mark style="color:red;">*</mark></td><td>String</td><td>The merchant account Public Key</td></tr></tbody></table>

### Sample Banks List Request

```powershell
curl https://gwapisdbx.ellypayapp.com/data/payout-bank-codes?provider_code=bank_ug
   -H "Accept: application/json" \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key"
```

{% tabs %}
{% tab title="200: OK Returns the list of payment options matching the query" %}

```json
{
    "code": 200,
    "status": "success",
    "message": "Request completed successfully.",
    "data": {
        "payout_banks": [
            {
                "bank_name": "Stanbic Bank Uganda",
                "bank_code": "stanbic_bank_ug",
                "is_active": true
            },
            {
                "bank_name": "Guaranty Trust Bank",
                "bank_code": "gtbank_ug",
                "is_active": true
            }
        ]
    }
}
```

{% tabs %}
{% tab title="Response Description" %}

<table><thead><tr><th width="146">Parameter</th><th width="80">Type</th><th>Description</th></tr></thead><tbody><tr><td>bank_code</td><td>string</td><td>The unique code for the payout bank. It is required for the bank payout transaction requests</td></tr><tr><td>bank_name</td><td>string</td><td>The name of the bank</td></tr><tr><td>is_active</td><td>string</td><td>Whether or not the bank is active for transactions</td></tr></tbody></table>
{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="400: Bad Request When the request is not formed as expected" %}

```javascript
{
  "code": 400,
  "status": "error",
  "message": "provider_code is required",
  "data": {}
}
```

{% endtab %}
{% endtabs %}


# Transaction Status Verification

In some situations, the merchant might wish to confirm the status of the transaction. This document resource describes the process of retrieving the transaction details (status inclusive)

Whenever we get the final transaction status from the processor (telecom/bank etc), we'll send the notifications to your designated callback URLs and these requests will be retried using the exponential back-off strategy. This guarantees that even when your URL is unreachable OR the http request fails, there will be automated retries at different intervals. Our belief is that; if we don't have the final status yet, checking status from the merchant side doesn't help. However, if status check is crucial for your workflow, please note the following.

1. Status check requests will be denied if they're done less than **2 minutes** from the time the transaction was initiated.
2. We recommend that an interval of **5 minutes** is implemented between status check requests for the same transaction.

### Get Transaction Status

<mark style="color:blue;">`GET`</mark> `https://gwapisdbx.ellypayapp.com/data/transaction/verify/{reference}`

Returns the transaction details and the parameter `data.transaction_status` holds the current status of the transaction. Replace `{reference}` with the merchant reference that was used prior when initiating the transaction.

#### Headers

<table><thead><tr><th width="182.5">Name</th><th width="119">Type</th><th>Description</th></tr></thead><tbody><tr><td>public-key<mark style="color:red;">*</mark></td><td>String</td><td>The merchant account Public Key</td></tr></tbody></table>

### Sample Transaction Status Request

```powershell
curl https://gwapisdbx.ellypayapp.com/data/transaction/verify/MCTREFT2WMNWZ23SBN6Y \
   -H "Accept: application/json" \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key"
```

{% tabs %}
{% tab title="200: Request Successful" %}

```json
{
    "code": 200,
    "status": "success",
    "message": "Request completed successfully.",
    "data": {
        "id": 20760,
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "PAYOUT",
        "request_currency": "UGX",
        "transaction_amount": 10000,
        "transaction_currency": "UGX",
        "transaction_charge": 1000,
        "transaction_account": "256777000001",
        "charge_customer": false,
        "total_debit": 11000,
        "provider_code": "mtn_momo_ug",
        "request_amount": 10000,
        "customer_name": "JOHN DOE",
        "transaction_status": "COMPLETED",
        "status_message": "Transaction Completed Successfully"
    }
}
```

{% endtab %}

{% tab title="404: Transaction Not Found" %}

```json
{
    "code": 404,
    "status": "error",
    "message": "Transaction with reference: MCTREF2BUBWGEVDZ47DQFH1 Not Found. Contact Support",
    "data": {}
}
```

{% endtab %}

{% tab title="403: Request before 2mins grace period" %}

```javascript
{
    "code": 403,
    "status": "error",
    "message": "Status check denied. Try again after 2 minutes.",
    "data": {}
}
```

{% endtab %}

{% tab title="429: Violation of recommended interval" %}

```javascript
{
    "code": 429,
    "status": "error",
    "message": "Request Failed. Too many requests in a short time.",
    "data": {}
}
```

{% endtab %}
{% endtabs %}

### Transaction Status Descriptions

<table><thead><tr><th width="166">Transaction Status</th><th>Description</th></tr></thead><tbody><tr><td>PENDING</td><td>The transaction has been logged on the EllyPay platform, awaiting escalation to the last mile processor (telecom, bank etc)</td></tr><tr><td>PROCESSING</td><td>The transaction is currently being processed and the EllyPay platform is waiting for the final status from the last mile processor</td></tr><tr><td>FAILED</td><td>The transaction is failed. Reason for failure will always be in the <code>data.status_message</code> parameter.</td></tr><tr><td>COMPLETED</td><td>The transaction was processed successfully.</td></tr><tr><td>CANCELLED</td><td>The transaction was cancelled - either by the customer or an admin process</td></tr></tbody></table>


# Handling Notifications/Callbacks

Whenever the status of the transaction changes (to processing, failure or success), we notify your service via callbacks. Below are the hints to take note of as regards these payment notifications.

* All callbacks are sent to the callback URL you provide during registration.
* A secure callback URL is required and notifications will ONLY be sent to **https** URLs. If you don't receive these notifications, ensure to check this.
* The callback request is a POST with JSON serialized data.
* We expect that your service will acknowledge the callback request by responding with HTTP code **200** otherwise we will retry a few more times (if the response code is below the 500 range) and then stop.
* The callback request includes two extra headers; **hmac-signature** and **rsa-signature**, and these can optionally be used to verify that the callback request originated from our servers. Verification can be done for any of these two signatures, NOT necessarily both of them. If the signature is valid, you can proceed with your business logic.
* The HMAC signature can be verified using [this](/callbacks/hmac-signature-verification) procedure while the RSA signature can be verified using [this](/callbacks/rsa-signature-verification) procedure. The merchant can choose which procedure to work with.

{% hint style="info" %}
All **production** notifications/callbacks will originate from this IP address below. Whenever more IPs are enabled, you'll be notified, and the document updated accordingly. We recommend that the IP (s) are whitelisted on your platform and only accept callback requests originating from them.

**51.195.105.111**
{% endhint %}


# Callback Events

Every callback payload has a parameter named event and its purpose is to communicate the reason for the callback. The section below describes the various values the parameter can take.

Depending on the workflow (collection, invoice generation/payment, disbursement, service payment etc) being used, the gateway will send appropriate events to the appropriate callback URLs. For every workflow type, the applicable events are described below;

### Collections

<table><thead><tr><th width="228">Event</th><th width="179">Transaction Status</th><th>Description</th></tr></thead><tbody><tr><td>request.failed</td><td>FAILED</td><td>Request failed and could not be processed by the gateway. Failure reason is part of the callback payload</td></tr><tr><td>transaction.charges</td><td>PENDING</td><td>Communicates success of the first collection leg (initialization) and the charges to be levied. The collection confirmation request should be sent after this event is received</td></tr><tr><td>transaction.processing</td><td>PROCESSING</td><td>Notifies the merchant that transaction processing is ongoing. Returns mobile money instructions (when payment method is MOBILE_MONEY) or the card payment URL (when payment method is CARD)</td></tr><tr><td>transaction.failed</td><td>FAILED</td><td>The gateway tried to process the transaction but failed e.g. insufficient balance on the account. Failure reason is part of the callback payload</td></tr><tr><td>transaction.completed</td><td>COMPLETED</td><td>The gateway processed the collection successfully.</td></tr></tbody></table>

### Invoices (Generation and Payment)

<table><thead><tr><th width="186">Event</th><th>Description</th></tr></thead><tbody><tr><td>request.failed</td><td>Request failed and could not be processed by the gateway. Failure reason is part of the callback payload</td></tr><tr><td>invoice.generated</td><td>Event sent at successful generation of the invoice. Details of the invoice would be shared as part of the payload</td></tr><tr><td>invoice.paid</td><td>Event sent when the invoice is paid for. Part of the payload is the reference for the collection that fulfilled the invoice</td></tr></tbody></table>

### Payouts/Disbursements

<table><thead><tr><th width="214">Event</th><th width="180">Transaction Status</th><th>Description</th></tr></thead><tbody><tr><td>request.failed</td><td>FAILED</td><td>Request failed and could not be processed by the gateway. Failure reason is part of the callback payload</td></tr><tr><td>transaction.failed</td><td>FAILED</td><td>The gateway tried to process the transaction but failed. Failure reason is part of the callback payload</td></tr><tr><td>transaction.completed</td><td>COMPLETED</td><td>The gateway processed the payout successfully.</td></tr></tbody></table>

### Service Payments

<table><thead><tr><th width="197">Event</th><th width="185">Transaction Status</th><th>Description</th></tr></thead><tbody><tr><td>payment.failed</td><td>FAILED</td><td>The gateway tried to process the service payment but failed. Failure reason is part of the callback payload</td></tr><tr><td>payment.completed</td><td>COMPLETED</td><td>The gateway processed the service payment successfully.</td></tr></tbody></table>


# Getting Started

Collections are payments that the merchant receives into their account. The sections here will highlight how the EllyPay platform supports these.

The API supports funds collection via Mobile Money for now and many other methods will be supported along the way. This is to allow for the flexibility of customer payments, such that customers can make payments in the ways that are most convenient. The funds collection section will describe the process flows for every available payment method. However, every collection will follow the steps described below, in the order of occurrence.&#x20;

## Prerequisites

Before funds collection can start,

1. The merchant needs to have an approved merchant account on the live platform. This is only achieved after the go-live requirements are met (being on sandbox and clearing the UAT tests).
2. The payment channels/options required by the merchant need to be configured on the designated merchant account. The support teams will be available to assist with this.
3. The merchant needs to have added a collection callback URL on their profile. This is critical because along the timeline of any transaction, notifications are done to this configured URL.

## Collection API Request

The merchant will form the collection request payload (based on the selected payment method) and send it to the API for processing. If there are any errors with the parameters, these would be communicated immediately in the response otherwise the API would respond with an acknowledgement. Take the following the mobile money request and a sample acknowledgement.

```json
{
    "merchant_reference": "auto",
    "transaction_method": "MOBILE_MONEY",
    "currency": "UGX",
    "amount": 1000,
    "provider_code": "mtn_momo_ug",
    "msisdn": "256777000001",
    "customer_name": "JOHN DOE",
    "description": "Test Collection"
}

/*sample acknowledgement would be something like the following*/
{
    "code": 202,
    "status": "accepted",
    "message": "Request Accepted",
    "data": {
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y"
    }
}
```

However, should this process fail in any way even after the acknowledgement e.g. the initialization process doesn't not happen as expected throws an error, the following failure callback will be sent to the collection callback URL.

```json
{
    "event": "request.failed",
    "payload": {
        "id": 20760,
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "COLLECTION",
        "request_currency": "UGX",
        "request_amount": 1000,
        "transaction_status": "FAILED",
        "status_message": "Multiple attempts in a short time"
    }
} 
```

## Final Status Notification

This is the notification that communicates the very final status of the transaction, and this would mark the close of the transaction process. Samples of completed and failed transaction callbacks/notifications shared below.

```json
/*sample completed transaction callback*/
{
    "event": "transaction.completed",
    "payload": {
        "id": 20760,
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "COLLECTION",
        "request_currency": "UGX",
        "transaction_amount": 2000000,
        "transaction_currency": "UGX",
        "transaction_charge": 60000,
        "transaction_account": "256787008803",
        "charge_customer": false,
        "total_credit": 1940000,
        "provider_code": "mtn_momo_ug",
        "request_amount": 2000000,
        "institution_name": "MTN Mobile Money Uganda",
        "customer_name": "JOHN DOE",
        "transaction_status": "COMPLETED",
        "status_message": "Transaction Completed Successfully"
    }
}

/*sample failed transaction callback*/
{
      "event": "transaction.failed",
      "payload": {
        "id": 26609,
        "merchant_reference": "MCTREFNRFRTQA6SCWT5X",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "COLLECTION",
        "request_currency": "UGX",
        "transaction_amount": 2000000,
        "transaction_currency": "UGX",
        "transaction_charge": 0,
        "transaction_account": "256777000002",
        "charge_customer": false,
        "total_credit": 0,
        "provider_code": "mtn_momo_ug",
        "request_amount": 2000000,
        "institution_name": "MTN Mobile Money Uganda",
        "customer_name": "JOHN DOE",
        "transaction_status": "FAILED",
        "status_message": "Balance Insufficient for the transaction"
      }
    }
```

## Handling Other Callbacks - Optional

Between the time of the collection API request and the final status notification, the platform will optionally send other notifications to communicate progress and the state of the transaction. These notifications are also sent to the configured collection callback URL and the merchant is listen to them if they so wish. Sample payloads and descriptions are shared below;

### Transaction Charges Notification

Shortly after the collection request is acknowledged, if the request matches all the desired conditions, the platform will perform billing and share details with the merchant. The payload in this case therefore carries information about the transaction charges and the expected amount to be deposited to the merchant currency account in the event the transaction is successful. Sample payload below;

```json
{
    "event": "transaction.charges",
    "payload": {
        "id": 11832,
        "merchant_reference": "MCTREFNGKLP5VQCQSBH2",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "COLLECTION",
        "request_currency": "UGX",
        "transaction_amount": 100000,
        "transaction_currency": "UGX",
        "transaction_charge": 4000,
        "transaction_account": "256777000001",
        "charge_customer": false,
        "total_credit": 96000,
        "provider_code": "mtn_momo_ug",
        "request_amount": 100000,
        "institution_name": "MTN Mobile Money Uganda",
        "customer_name": "JOHN DOE",
        "transaction_status": "PENDING",
        "status_message": "Collection initialized successfully. Confirm charges"
    }
}
```

### Payment Instructions Notification

This notification normally comes after the request has been escalated to the last mile processor (e.g. the telecom mobile money platform). Its purpose is to communicate what the customer might expect in order to approve the transaction e.g. mobile money customers should expect a PIN approval prompt.  Below is a sample payload.

```json
{
    "event": "transaction.processing",
    "payload": {
        "id": 27594,
        "merchant_reference": "MCTREF9WVCPGFRHYCWSK",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "COLLECTION",
        "request_currency": "UGX",
        "transaction_amount": 5000,
        "transaction_currency": "UGX",
        "transaction_charge": 150,
        "transaction_account": "256777000001",
        "charge_customer": false,
        "total_credit": 4850,
        "provider_code": "mtn_momo_ug",
        "request_amount": 5000,
        "institution_name": "MTN Mobile Money Uganda",
        "customer_name": "JOHN DOE",
        "transaction_status": "PROCESSING",
        "status_message": "Transaction Initiated successfully. Approve Payment by entering the PIN",
        "payment_instructions": "<p>You will receive a prompt on the mobile number <b>256777000001</b>.<br/> Enter your PIN to authorize your payment of <b>UGX 5,000</b></p>"
    }
}
```

{% hint style="info" %}
All the callbacks/notifications sent for collections will come with signature headers as described in [this](/utility-functions/handling-notifications-callbacks) section of the documentation. Verification of those signatures is also doumented there.
{% endhint %}

{% hint style="info" %}
**Cross-currency Collections**

If the request currency is different e.g. USD from the payment provider currency (transaction currency) e.g. UGX, the API will convert the USD to UGX and at the end of the transaction, your UGX balance will be credited with the converted amount. More details [here](/knowledge-base/cross-currency-transactions).
{% endhint %}


# Mobile Money Collection

The API supports mobile money as described in the Supported Countries section. The section below will guide you through the process of accepting Mobile Money

## Overview

The currently supported mobile money channels are listed [here](/getting-started/supported-countries) (to be updated from time to time). Test mobile money phone numbers are also described in [this](/getting-started/sandbox-test-accounts) section. We equally recommend that you go through the [Getting Started](/funds-collection/getting-started) section to have a high-level understanding of the funds collection process.

## Workflow Overview

Study this diagram below to understand the customer journey workflow and how the API request/response happens

```mermaid
sequenceDiagram
    actor Customer
    participant Merchant Portal
    participant EllyPay Gateway
    participant Telecom

    Customer->>Merchant Portal: Initiates collection request
    Merchant Portal->>EllyPay Gateway: POST /collections/initialize
    EllyPay Gateway-->>Merchant Portal: 202 Accepted
    EllyPay Gateway->>Telecom: Collection request for amount
    Telecom->>Telecom: Confirm customer eligibility + balance
    Telecom->>Customer: Send approval PIN prompt
    Customer-->>Telecom: PIN approval
    Telecom->>EllyPay Gateway: Notification of transaction completion
    EllyPay Gateway->>Merchant Portal: Merchant callback notification
    Merchant Portal->>Customer: Display success message
```

## Step 1: Form the collection request payload

The table below describes the request parameters that are used for the collection request. Most will be collected from the paying customer and some are custom configurations based on your wishes (e.g. who bears the transaction charge).

<table><thead><tr><th width="201">Parameter</th><th width="98">Type</th><th width="98">Required</th><th>Description</th></tr></thead><tbody><tr><td>merchant_reference</td><td>String</td><td>true</td><td>The unique reference for this request. Alternatively, the value <strong>auto</strong> can be passed, and a unique reference will be created for you by the API</td></tr><tr><td>transaction_method</td><td>String</td><td>true</td><td>The transaction method to be used. This will be <em><strong>MOBILE_MONEY</strong></em> for this request</td></tr><tr><td>currency</td><td>String</td><td>true</td><td>The 3-character ISO currency code for the request currency</td></tr><tr><td>amount</td><td>Number</td><td>true</td><td>The amount being requested</td></tr><tr><td>provider_code</td><td>String</td><td>true</td><td>The provider code as obtained from the payment options <a href="/pages/E8qh5wAQIPuCQxEHFHxg">list</a></td></tr><tr><td>msisdn</td><td>String</td><td>true</td><td>The phone number from which the payment is being requested. This should be sent in international format e.g. 256777000001 for Ugandan numbers</td></tr><tr><td>customer_name</td><td>String</td><td>true</td><td>The name of the customer</td></tr><tr><td>customer_email</td><td>String</td><td>false</td><td>The email of the customer</td></tr><tr><td>description</td><td>String</td><td>false</td><td>The description/narration for the transaction</td></tr><tr><td>charge_customer</td><td>Boolean</td><td>false</td><td>Whether or not the customer should bear the charge for the transaction. By default, this is <strong>false</strong> to mean that the merchant bears the charge</td></tr></tbody></table>

After collecting the necessary mobile money payment information from your customer, prepare your request payload as demonstrated below.

```json
{
  "merchant_reference": "auto",
  "transaction_method": "MOBILE_MONEY",
  "currency": "UGX",
  "amount": 10000,
  "provider_code": "mtn_momo_ug",
  "msisdn": "256777000001",
  "customer_email": "johndoe@gmail.com",
  "customer_name": "JOHN DOE",
  "description": "Test Collection",
  "charge_customer": false
}
```

<mark style="color:green;">`POST`</mark> `https://gwapisdbx.ellypayapp.com/collections/initialize`

The request is sent as a JSON body as demonstrated by the sample request below. Sample responses (acknowledgement and failure) are also shared.

```powershell
curl -X POST "https://gwapisdbx.ellypayapp.com/collections/initialize" \
   -H 'Content-Type: application/json' \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key" \
   -d '{
        "merchant_reference": "auto",
        "transaction_method": "MOBILE_MONEY",
        "currency": "UGX",
        "amount": 10000,
        "provider_code": "mtn_momo_ug",
        "msisdn": "256777000001",
        "customer_email": "johndoe@gmail.com",
        "customer_name": "JOHN DOE",
        "description": "Test Collection",
        "charge_customer": false,
    }'
```

{% tabs %}
{% tab title="202: Accepted - Request acknowledged for processing" %}

```json
{
  "code": 202,
  "status": "accepted",
  "message": "Request Accepted",
  "data": {
    "internal_reference": "ELPREFA65BGTFR7NGUXM",
    "merchant_reference": "MCTREFNGKLP5VQCQSBH2"
  }
}
```

{% endtab %}

{% tab title="400: Bad Request - Request is not formed as expected" %}

```json
{
  "code": 400,
  "status": "error",
  "message": "256752000001 is not a valid MTN Mobile Money Uganda (mtn_momo_ug) phone number",
  "data": {}
}
```

{% endtab %}
{% endtabs %}

## Step 2: Handle intermediate notifications - Optional

You can optionally handle/listen to the intermediate callbacks that are sent, as some form of tracking the transaction progress. These callbacks/notifications are described in [this](/funds-collection/getting-started#handling-other-callbacks-optional) section of the getting-started manual.

## Step 3: Handle final status notification

Every merchant account is expected to have configured a callback/webhook URL for collections. For all collections that transition to the final state (COMPLETED, FAILED or CANCELLED), a JSON POST request will be made to the callback URL. Sample callback payloads (request bodies) are shared below. Be sure to check out [Handling Notifications](/utility-functions/handling-notifications-callbacks) to see how you should verify the signature(s) in the request headers and how to respond.

{% tabs %}
{% tab title="Successful Mobile Money Collection" %}

```json
{
  "event": "transaction.completed",
  "payload": {
    "id": 11832,
    "merchant_reference": "MCTREFNGKLP5VQCQSBH2",
    "internal_reference": "ELPREFA65BGTFR7NGUXM",
    "transaction_type": "COLLECTION",
    "request_currency": "UGX",
    "transaction_amount": 100000,
    "transaction_currency": "UGX",
    "transaction_charge": 4000,
    "transaction_account": "256777000001",
    "charge_customer": false,
    "total_credit": 96000,
    "provider_code": "mtn_momo_ug",
    "request_amount": 100000,
    "institution_name": "MTN Mobile Money Uganda",
    "customer_name": "JOHN DOE",
    "transaction_status": "COMPLETED",
    "status_message": "Transaction Completed Successfully"
  }
}
```

{% endtab %}

{% tab title="Failed Mobile Money Collection" %}

```json
{
  "event": "transaction.failed",
  "payload": {
    "id": 11832,
    "merchant_reference": "MCTREFNGKLP5VQCQSBH2",
    "internal_reference": "ELPREFA65BGTFR7NGUXM",
    "transaction_type": "COLLECTION",
    "request_currency": "UGX",
    "transaction_amount": 100000,
    "transaction_currency": "UGX",
    "transaction_charge": 0,
    "transaction_account": "256777000001",
    "charge_customer": false,
    "total_credit": 0,
    "provider_code": "mtn_momo_ug",
    "request_amount": 100000,
    "institution_name": "MTN Mobile Money Uganda",
    "customer_name": "JOHN DOE",
    "transaction_status": "FAILED",
    "status_message": "Balance Insufficient for the transaction"
  }
}
```

{% endtab %}
{% endtabs %}


# Getting Started

Payouts, also sometimes called Disbursements, are outbound fund transfers from your merchant account to supported mobile money channels or bank accounts.

The API supports funds payout/disbursement via a number of methods e.g. Mobile Money, Bank Transfer and many other methods will be supported along the way. The API enables you to send payments via a simple, secure process that requires only the bank/phone information of the recipients. This section will describe the process flows for every available payment method. However, every payout will follow the steps described below, in the order of occurrence.

## Workflow Overview

Study this diagram below to understand the customer journey workflow and how the API request/response happens

```mermaid
sequenceDiagram
    actor Customer
    participant Merchant Portal
    participant EllyPay Gateway
    participant Telecom/Bank

    Customer->>Merchant Portal: Initiates payout request
    Merchant Portal->>EllyPay Gateway: POST /payout/send-funds
    EllyPay Gateway-->>Merchant Portal: 202 Accepted
    EllyPay Gateway->>Merchant Portal: GET - Verification Handshake
    Merchant Portal-->>EllyPay Gateway: HTTP 200 Response
    EllyPay Gateway->>Telecom/Bank: Payout request for amount
    Telecom/Bank->>Telecom/Bank: Confirm EllyPay eligibility + balance
    Telecom/Bank->>Customer: Transfer funds to customer account
    Telecom/Bank->>EllyPay Gateway: Notification of transaction completion
    EllyPay Gateway->>Merchant Portal: Merchant callback notification
    Merchant Portal->>Customer: Display success message
```

## Prerequisites

Before payouts can start,

1. The merchant needs to have an approved merchant account on the live platform. This is only achieved after the go-live requirements are met (being on sandbox and clearing the UAT tests).
2. The payment channels/options required by the merchant need to be configured on the designated merchant account. The support teams will be available to assist with this.
3. The merchant is expected to have configured the **payout notification URL** as well as the **payout verification URL** on the merchant account profile. The notification URL will receive webhook/callback requests when the payout is successful/failed. The verification URL on the other hand allows our platform to do a "handshake" with the merchant's platform to verify the authenticity of the request as described in step 2 below.
4. The merchant needs to avail the funds for payout. This can be achieved by requesting transfer of funds collected or deposited prior as described in the [Availing Payout Funds](/knowledge-base/availing-payout-funds) section.

## Step 1: Payout API Request

The merchant will form the payout request payload (based on the selected payment method) and send it to the API for processing. If there are any errors with the parameters, these would be communicated immediately in the response otherwise the API would respond with an acknowledgement. Take the following the mobile money request and a sample acknowledgement.

```json
{
    "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
    "transaction_method": "MOBILE_MONEY",
    "currency": "UGX",
    "amount": 1000,
    "provider_code": "mtn_momo_ug",
    "msisdn": "256777000001",
    "customer_name": "JOHN DOE",
    "description": "Test Payout"
}

/*sample acknowledgement would be something like the following*/
{
    "code": 202,
    "status": "accepted",
    "message": "Request Accepted",
    "data": {
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y"
    }
}
```

However, should this process fail in any way even after the acknowledgement e.g. if the merchant is making too many requests to the same phone number, the following failure callback will be sent to the payout callback URL.

```json
{
  "event": "request.failed",
  "payload": {
    "id": 20760,
    "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
    "internal_reference": "ELPREFA65BGTFR7NGUXM",
    "transaction_type": "PAYOUT",
    "request_currency": "UGX",
    "request_amount": 1000,
    "transaction_status": "FAILED",
    "status_message": "Failed to initialize the transaction. System exception!"
  }
}
```

## Step 2: Payout Verification Request

For every payout request sent to the API, a request will be made to the merchant platform via the configured payout verification URL. This is one of the platform's security measures and the purpose of the request is to ensure the merchant platform (database) is "aware" of the transaction. The merchant simply needs to ensure that the configured URL is operational. When a request is sent to it, query your records to confirm that the reference is among your records and in the status/state to be processed. If this is true, respond with a simple HTTP code **200** strictly, which to the API will be translated as permission to process the transaction. Any other response will be regarded "denial" and the API will not move ahead with the processing.

A sample verification request could be like the following. The GET request will have a single query parameter `reference` that holds the value of the merchant reference sent in the payout request prior. The example below assumes that the `merchant_reference` in the request was ***MCTREFDWWJTDYTEAHEQP.***

```powershell
curl -X GET https://your-payout-verification-url?reference=MCTREFDWWJTDYTEAHEQP
```

{% hint style="info" %}
If the response to that request is HTTP code 200, the API will proceed to queue the transaction for processing and after processing, a notification will be sent to the payout notification URL to communicate the final status of the transaction as described in step 3
{% endhint %}

## Step 3: Final Status Notification

This is the notification that communicates the very final status of the transaction, and this would mark the close of the transaction process. Sample completed and failed transaction callbacks/notifications shared below.

```json
/*sample completed transaction callback*/
{
    "event": "transaction.completed",
    "payload": {
        "id": 20760,
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "PAYOUT",
        "request_currency": "UGX",
        "transaction_amount": 10000,
        "transaction_currency": "UGX",
        "transaction_charge": 1000,
        "transaction_account": "256787008803",
        "charge_customer": false,
        "total_debit": 11000,
        "provider_code": "mtn_momo_ug",
        "request_amount": 10000,
        "customer_name": "JOHN DOE",
        "transaction_status": "COMPLETED",
        "status_message": "Transaction Completed Successfully"
    }
}

/*sample failed transaction callback*/
{
    "event": "transaction.failed",
    "payload": {
        "id": 26609,
        "merchant_reference": "MCTREFNRFRTQA6SCWT5X",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "PAYOUT",
        "request_currency": "UGX",
        "transaction_amount": 10000,
        "transaction_currency": "UGX",
        "transaction_charge": 0,
        "transaction_account": "256777000002",
        "charge_customer": false,
        "total_debit": 0,
        "provider_code": "mtn_momo_ug",
        "request_amount": 10000,
        "customer_name": "JOHN DOE",
        "transaction_status": "FAILED",
        "status_message": "Balance Insufficient for the transaction"
    }
}
```

{% hint style="info" %}
**Cross-currency Transfers**

If the request currency is different e.g. USD from the payment provider currency (transaction currency) e.g. UGX, the API will convert the USD to UGX and your UGX balance will be debited with the converted amount. More details [here](/knowledge-base/cross-currency-transactions).
{% endhint %}


# Mobile Money Payouts

The API enables you to transfer funds directly from your available balance to a mobile money account. The documentation below explains further.

{% hint style="info" %}
We recommend checking out the [Getting Started](/funds-payout/getting-started) section to understand the basics of payouts first and the general workflow. This guide assumes that you have read that
{% endhint %}

## Overview

The currently supported mobile money channels are listed [here](/getting-started/supported-countries) (to be updated from time to time). Test mobile money phone numbers are also described in [this](/getting-started/sandbox-test-accounts) section. It's very important that you track the available balance on your PAYOUT wallet before initiating these transactions.

## Step 1: Obtain the required data for the payment request

The table below describes the request parameters that are used for the payout/disbursement request. Most/all will be collected from the paying customer.

<table><thead><tr><th width="197">Parameter</th><th width="93">Type</th><th width="98">Required</th><th>Description</th></tr></thead><tbody><tr><td>merchant_reference</td><td>String</td><td>true</td><td>The unique reference for this request. It must be at least 8 characters long. </td></tr><tr><td>transaction_method</td><td>String</td><td>true</td><td>The transaction method to be used. This will be <em><strong>MOBILE_MONEY</strong></em> for this request</td></tr><tr><td>currency</td><td>String</td><td>true</td><td>The 3-character ISO currency code for the request currency</td></tr><tr><td>amount</td><td>Number</td><td>true</td><td>The amount to be transferred</td></tr><tr><td>provider_code</td><td>String</td><td>true</td><td>The provider code as obtained from the payment options <a href="/pages/E8qh5wAQIPuCQxEHFHxg">list</a> </td></tr><tr><td>account_number</td><td>String</td><td>true</td><td>The phone number of the recipient. This should be sent in international format e.g. 256777000001 for Ugandan numbers</td></tr><tr><td>customer_name</td><td>String</td><td>true</td><td>The name of the customer</td></tr><tr><td>description</td><td>String</td><td>true</td><td>The description/narration for the transaction</td></tr></tbody></table>

After collecting the necessary mobile money payment information from your customer, prepare your request payload as demonstrated below.

```json
{
	"merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
	"transaction_method": "MOBILE_MONEY",
	"currency": "UGX",
	"amount": 4000,
	"provider_code": "mtn_momo_ug",
	"account_number": "256777000001",
	"customer_name": "JOHN DOE",
	"description": "Test Payout"
}
```

<mark style="color:green;">`POST`</mark> `https://gwapisdbx.ellypayapp.com/payout/send-funds`

The request is sent as a JSON body as demonstrated by the sample request below. Sample responses (acknowledgement and failure) are also shared.

```powershell
curl -X POST "https://gwapisdbx.ellypayapp.com/payout/send-funds" \
   -H 'Content-Type: application/json' \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key" \
   -H "secret-key: your-secret-key" \
   -d '{
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "transaction_method": "MOBILE_MONEY",
        "currency": "UGX",
        "amount": 10000,
        "provider_code": "mtn_momo_ug",
        "account_number": "256777000001",
        "customer_name": "JOHN DOE",
        "description": "Test Payout"
    }'
```

{% tabs %}
{% tab title="202: Accepted - Request acknowledged for processing" %}

```json
{
    "code": 202,
    "status": "accepted",
    "message": "Request Accepted",
    "data": {
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y"
    }
}
```

{% endtab %}

{% tab title="400: Bad Request - Request is not formed as expected" %}

```json
{
    "code": 400,
    "status": "error",
    "message": "256752000001 is not a valid MTN Mobile Money Uganda (mtn_ug) phone number",
    "data": {}
}
```

{% endtab %}
{% endtabs %}

## Step 2: Handshake - Verify transaction

{% hint style="info" %}
The API will attempt to verify the transaction from your platform as described in [this](/funds-payout/getting-started#step-2-payout-verification-request) section. Ensure that the correct URL is configured on your merchant account and that you're handling the verification request appropriately.
{% endhint %}

## Step 3: Handle the final status webhook

Every merchant account is expected to have configured a callback/webhook URL for payouts. For all payouts that transition to the final state (COMPLETED or FAILED), a JSON POST request will be made to the callback URL. Sample callback payloads (request bodies) are shared below. Be sure to check out [Handling Notifications](/utility-functions/handling-notifications-callbacks) to see how you should verify the signature(s) in the request headers and how to respond.

{% tabs %}
{% tab title="Successful Mobile Money Payout" %}

```json
{
    "event": "transaction.completed",
    "payload": {
        "id": 20760,
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "PAYOUT",
        "request_currency": "UGX",
        "transaction_amount": 10000,
        "transaction_currency": "UGX",
        "transaction_charge": 1000,
        "transaction_account": "256777000001",
        "charge_customer": false,
        "total_debit": 11000,
        "provider_code": "mtn_momo_ug",
        "request_amount": 10000,
        "customer_name": "JOHN DOE",
        "transaction_status": "COMPLETED",
        "status_message": "Transaction Completed Successfully"
    }
}
```

{% endtab %}

{% tab title="Failed Mobile Money Payout" %}

```json
{
    "event": "transaction.failed",
    "payload": {
        "id": 20760,
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "PAYOUT",
        "request_currency": "UGX",
        "transaction_amount": 10000,
        "transaction_currency": "UGX",
        "transaction_charge": 0,
        "transaction_account": "256777000002",
        "charge_customer": false,
        "total_debit": 0,
        "provider_code": "mtn_momo_ug",
        "request_amount": 10000,
        "customer_name": "JOHN DOE",
        "transaction_status": "FAILED",
        "status_message": "Balance Insufficient for the transaction"
    }
}
```

{% endtab %}
{% endtabs %}


# Bank Account Transfers

The API enables you to transfer funds directly from your available balance to supported bank accounts. The documentation below explains further.

{% hint style="info" %}
We recommend checking out the [Getting Started](/funds-payout/getting-started) section to understand the basics of payouts first and the general workflow. This guide assumes that you have read that
{% endhint %}

## Overview

The currently supported payout bank channels are listed [here](/getting-started/supported-countries) (to be updated from time to time). Test bank accounts are also described in [this](/getting-started/sandbox-test-accounts#bank-supported-payout-banks) section. It's very important that you track the available balance on your PAYOUT wallet before initiating these transactions.

## Step 1: Obtain the required data for the payment request

The table below describes the request parameters that are used for the payout/disbursement request. Most/all will be collected from the paying customer.

<table><thead><tr><th width="235">Parameter</th><th width="93">Type</th><th width="98">Required</th><th>Description</th></tr></thead><tbody><tr><td>merchant_reference</td><td>String</td><td>true</td><td>The unique reference for this request. It must be at least 8 characters long. </td></tr><tr><td>transaction_method</td><td>String</td><td>true</td><td>The transaction method to be used. This will be <em><strong>BANK</strong></em> for this request</td></tr><tr><td>currency</td><td>String</td><td>true</td><td>The 3-character ISO currency code for the request currency</td></tr><tr><td>amount</td><td>Number</td><td>true</td><td>The amount to be transferred</td></tr><tr><td>provider_code</td><td>String</td><td>true</td><td>The provider code as obtained from the payment options <a href="/pages/E8qh5wAQIPuCQxEHFHxg">list</a> </td></tr><tr><td>account_number</td><td>String</td><td>true</td><td>The bank account of the recipient.</td></tr><tr><td>customer_name</td><td>String</td><td>true</td><td>The name of the customer</td></tr><tr><td>description</td><td>String</td><td>true</td><td>The description/narration for the transaction</td></tr><tr><td>extra_params.bank_code</td><td>String</td><td>true</td><td>The bank code of the recipient bank as obtained from the payout bank <a href="/pages/ZSh9aB3wgEGU3gbm5GiI">list</a></td></tr></tbody></table>

After collecting the necessary bank transfer information from your customer, prepare your request payload as demonstrated below.

```json
{
    "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
    "transaction_method": "BANK",
    "currency": "UGX",
    "amount": 10000,
    "provider_code": "bank_ug",
    "account_number": "9030015124058",
    "customer_name": "JOHN DOE",
    "description": "Test Payout",
    "extra_params": {
        "bank_code": "stanbic_bank_ug"
    }
}
```

<mark style="color:green;">`POST`</mark> `https://gwapisdbx.ellypayapp.com/payout/send-funds`

The request is sent as a JSON body as demonstrated by the sample request below. Sample responses (acknowledgement and failure) are also shared.

```powershell
curl -X POST "https://gwapisdbx.ellypayapp.com/payout/send-funds" \
   -H 'Content-Type: application/json' \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key" \
   -H "secret-key: your-secret-key" \
   -d '{
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "transaction_method": "BANK",
        "currency": "UGX",
        "amount": 10000,
        "provider_code": "bank_ug",
        "account_number": "9030015124058",
        "customer_name": "JOHN DOE",
        "description": "Test Payout",
        "extra_params": {
             "bank_code": "stanbic_bank_ug"
         }
    }'
```

{% tabs %}
{% tab title="202: Accepted - Request acknowledged for processing" %}

```json
{
    "code": 202,
    "status": "accepted",
    "message": "Request Accepted",
    "data": {
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y"
    }
}
```

{% endtab %}

{% tab title="400: Bad Request - Request is not formed as expected" %}

```json
{
    "code": 400,
    "status": "error",
    "message": "bank_gu is not a valid provider code",
    "data": {}
}
```

{% endtab %}
{% endtabs %}

## Step 2: Handshake - Verify transaction

{% hint style="info" %}
The API will attempt to verify the transaction from your platform as described in [this](/funds-payout/getting-started#step-2-payout-verification-request) section. Ensure that the correct URL is configured on your merchant account and that you're handling the verification request appropriately.
{% endhint %}

## Step 3: Handle the final status webhook

Every merchant account is expected to have configured a callback/webhook URL for payouts. For all payouts that transition to the final state (COMPLETED or FAILED), a JSON POST request will be made to the callback URL. Sample callback payloads (request bodies) are shared below. Be sure to check out [Handling Notifications](/utility-functions/handling-notifications-callbacks) to see how you should verify the signature(s) in the request headers and how to respond.

{% tabs %}
{% tab title="Successful Bank Payout" %}

```json
{
    "event": "transaction.completed",
    "payload": {
        "id": 20760,
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "PAYOUT",
        "request_currency": "UGX",
        "transaction_amount": 10000,
        "transaction_currency": "UGX",
        "transaction_charge": 1000,
        "transaction_account": "9030015124058",
        "charge_customer": false,
        "total_debit": 11000,
        "provider_code": "bank_ug",
        "request_amount": 10000,
        "customer_name": "JOHN DOE",
        "transaction_status": "COMPLETED",
        "status_message": "Transaction Completed Successfully"
    }
}
```

{% endtab %}

{% tab title="Failed Bank Payout" %}

```json
{
    "event": "transaction.failed",
    "payload": {
        "id": 20760,
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "PAYOUT",
        "request_currency": "UGX",
        "transaction_amount": 10000,
        "transaction_currency": "UGX",
        "transaction_charge": 0,
        "transaction_account": "9030015124058",
        "charge_customer": false,
        "total_debit": 0,
        "provider_code": "bank_ug",
        "request_amount": 10000,
        "customer_name": "JOHN DOE",
        "transaction_status": "FAILED",
        "status_message": "Balance Insufficient for the transaction"
    }
}
```

{% endtab %}
{% endtabs %}


# Getting Started

Service payments is a term EllyPay uses to describe outbound (mostly) payments from your merchant account to supported service accounts e.g. UMEME, URA, mobile services etc.

The API supports payments/transactions for a number of service categories e.g. Mobile Money, Internet, Tax, Utilities, Airtime, PayTV and other methods may be supported along the way. The API enables you to send payments via a simple, secure process that requires only the account information of the recipients. This section will describe the process flow for service payment as well as the other supporting data endpoints. However, every service payment will follow the steps described below, in the order of occurrence.

## Prerequisites

Before service payments can start,

1. The merchant needs to have an approved merchant account on the live platform. This is only achieved after the go-live requirements are met (being on sandbox and clearing the UAT tests).
2. The service options required by the merchant need to be configured on the designated merchant account. The support teams will be available to assist with this.
3. The merchant is expected to have configured the **service payment notification URL** on the merchant account profile. The notification URL will receive webhook/callback requests when the payment is successful/failed.
4. The merchant needs to avail the funds for service payments. This can be achieved by requesting transfer of funds collected or deposited prior as described in the [Availing Service Payment Funds](/knowledge-base/availing-service-payment-funds) section. This is a requirement for outbound service payments. For those that are inbound (increase merchant account balance) e.g. the Mobile Money Withdraw services, this step may not be a requirement so it depends on the merchant's needs.

## Workflow Overview

Study this diagram below to understand the customer journey workflow and how the API request/response happens

```mermaid
sequenceDiagram
    actor Customer
    participant Merchant Portal
    participant EllyPay Gateway
    participant Service Provider
    Customer->>Merchant Portal: Initiates account validation request
    Merchant Portal->>EllyPay Gateway: POST /service-payments/validate-account (Account validation)
    EllyPay Gateway-->>Merchant Portal: HTTP 200 response with account details
    Merchant Portal->>EllyPay Gateway: POST /service-payments/process-payment (Payment confirmation)
    EllyPay Gateway-->>Merchant Portal: 202 Accepted
    EllyPay Gateway->>Service Provider: Payment request for amount
    Service Provider->>Customer: Effect service payment for customer and send SMS
    Service Provider->>EllyPay Gateway: Notification of transaction completion
    EllyPay Gateway->>Merchant Portal: Merchant callback notification
    Merchant Portal->>Customer: Display success message
```

## Step 1: Account Validation Request

This is a requirement prior to the actual payment request. The purpose of this step is to get confirmation of the service account name as well as other details as the service may deem necessary e.g. PRN amount for URA or the balance for NWSC and UMEME YAKA.

The merchant will form the validation request payload (based on the selected service) and send it to the API for processing. If there are any errors with the parameters, these would be communicated immediately in the response otherwise the API would respond with the details of the account number. Take the following the airtime validation request and a sample response.

```json
{
    "merchant_reference": "auto",
    "account_number": "2567770123456",
    "service_code": "MTNAirtime",
    "currency": "UGX",
    "amount": 10000
}

/*sample response would be something like the following*/
{
    "code": 200,
    "status": "success",
    "message": "Request completed successfully",
    "data": {
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "account_number": "2567770123456",
        "request_currency": "UGX",
        "request_amount": 10000,
        "customer_name": "SANDBOX CUSTOMER",
        "transaction_charge": 0,
        "transaction_amount": 10000,
        "service_name": "MTN Airtime",
        "balance_due": 0
    }
}
```

## Step 2: Payment Confirmation Request

After the account validation has successfully responded with the account details, you can proceed to confirm the service payment. In the validation response, the value returned in the `data.internal_reference` parameter will be critical in the confirmation. It's the only parameter that will be sent for the payment confirmation request as demonstrated below.

The API will respond with an acknowledgement and then go ahead to process the payment in the background, sending a notification for the final status when processing is complete/failed.

```json
{
    "internal_reference": "ELPREFA65BGTFR7NGUXM"
}

/*sample acknowledgement would be something like the following*/
{
    "code": 202,
    "status": "accepted",
    "message": "Request Accepted",
    "data": {
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y"
    }
}
```

## Step 3: Final Status Notification

This is the notification that communicates the very final status of the transaction, and this would mark the close of the transaction process. Sample completed and failed transaction callbacks/notifications shared below.

```json
/*sample completed transaction callback*/
{
    "event": "payment.completed",
    "payload": {
        "merchant_reference": "MCTPAY7JAKLM2CGHUAUK",
        "internal_reference": "ELPREFAJJXPPALHUWKFY",
        "transaction_type": "SERVICE_PAYMENT",
        "request_currency": "UGX",
        "request_amount": 1000,
        "transaction_status": "COMPLETED",
        "account_number": "256772007180",
        "transaction_charge": 0,
        "transaction_amount": 1000,
        "customer_name": "SANDBOX CUSTOMER",
        "service_name": "MTN Airtime",
        "status_message": "Transaction Completed Successfully",
        "additional_details": {}
    }
}

/*sample failed transaction callback*/
{
    "event": "payment.failed",
    "payload": {
        "merchant_reference": "MCTPAYWQXZML3EQR7TKT",
        "internal_reference": "ELPREFNWSN5RFEBWHHWK",
        "transaction_type": "SERVICE_PAYMENT",
        "request_currency": "UGX",
        "request_amount": 1000,
        "transaction_status": "FAILED",
        "account_number": "256772000004",
        "transaction_charge": 0,
        "transaction_amount": 0,
        "customer_name": "SANDBOX CUSTOMER",
        "service_name": "MTN Airtime",
        "status_message": "Insufficient funds to complete transaction",
        "additional_details": {}
    }
}
```

{% hint style="info" %}
**Handling of transaction charges**

There's a charge attached when paying for some of the services (the ones in [this](/service-payments/services-list) list and the parameter `is_billable` is **true**). The amount plus the calculated charge would be deducted for the merchant account's service payment wallet and therefore the wallet should have sufficient funds to support the transaction.
{% endhint %}


# Services List

This section of the documentation describes how the list of services can be retrieved. Services are categorised and can be retrieved based on the category

### Get Services List

<mark style="color:blue;">`GET`</mark> `https://gwapisdbx.ellypayapp.com/service-payments/services-list?category=`

Returns the list of services based on the category. Supported categories are `AIRTIME`, `INTERNET`, `WATER`, `ELECTRICITY`, `TAXES`, `MMONEY`

#### Query Parameters

<table><thead><tr><th width="180">Name</th><th width="82">Type</th><th>Description</th></tr></thead><tbody><tr><td>category<mark style="color:red;">*</mark></td><td>String</td><td>The category of services to be retrieved</td></tr></tbody></table>

#### Headers

<table><thead><tr><th width="147">Name</th><th width="114">Type</th><th>Description</th></tr></thead><tbody><tr><td>public-key<mark style="color:red;">*</mark></td><td>String</td><td>The Public Key as shared in the approval email</td></tr></tbody></table>

### Sample Services Request

```powershell
curl https://gwapisdbx.ellypayapp.com/service-payments/services-list?category=AIRTIME \
   -H "Accept: application/json" \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key"
```

{% tabs %}
{% tab title="200: OK Returns the list of services matching the query" %}

```json
{
    "code": 200,
    "status": "success",
    "message": "Request completed successfully.",
    "data": [
        {
            "code": "MTNAirtime",
            "name": "MTN Airtime",
            "category_code": "AIRTIME",
            "category": "Airtime",
            "has_packages": false,
            "has_choices": false,
            "is_billable": false,
            "account_field_label": "MTN Phone Number",
            "account_length": 12,
            "min_amount": 500,
            "max_amount": 1500000,
            "is_active": true
        },
        {
            "code": "MTNVoice",
            "name": "MTN Voice Bundles",
            "category_code": "AIRTIME",
            "category": "Airtime",
            "has_packages": true,
            "has_choices": false,
            "is_billable": false,
            "account_field_label": "MTN Phone Number",
            "account_length": 10,
            "min_amount": 500,
            "max_amount": 2000000,
            "is_active": true
        }
    ]
}

```

{% tabs %}
{% tab title="Response Description" %}

<table><thead><tr><th width="195">Parameter</th><th width="96">Type</th><th>Description</th></tr></thead><tbody><tr><td>code</td><td>string</td><td>The unique code for the service. It is required for other API requests as will be described</td></tr><tr><td>name</td><td>string</td><td>The name of the service</td></tr><tr><td>category_code</td><td>string</td><td>The service category code</td></tr><tr><td>category</td><td>string</td><td>The service category name</td></tr><tr><td>has_packages</td><td>Boolean</td><td>Indicates whether the service has a price list of packages that are subscribed for.</td></tr><tr><td>has_choices</td><td>Boolean</td><td>Indicates wether the service has a list of choices that support its purchase.</td></tr><tr><td>is_billable</td><td>Boolean</td><td>Indicates whether EllyPay charges you for purchasing this service</td></tr><tr><td>account_field_label</td><td>string</td><td>A suggested label to be used on when displaying the payment form on your platform</td></tr><tr><td>account_length</td><td>Number</td><td>The expected length of the account number used for the service payment e.g. a phone number</td></tr><tr><td>min_amount</td><td>Number</td><td>The minimum allowed amount for this service</td></tr><tr><td>max_amount</td><td>Number</td><td>The maximum allowed amount for this service</td></tr><tr><td>is_active</td><td>Boolean</td><td>Whether the service is active to be transacted with.</td></tr></tbody></table>
{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="400: Bad Request When the request is not formed as expected" %}

```javascript
{
  "code": 400,
  "status": "error",
  "message": "unrecognized category",
  "data": {}
}
```

{% endtab %}
{% endtabs %}


# Service Packages List

Some services have packages OR a price list that describes the available items for payment/subscription. This section describes how a service's price/package list can be obtained.

Obtaining a packages list relates to services returned [here](/service-payments/services-list) and the value for `has_packages` is **true**

### Get Service Packages List

<mark style="color:blue;">`GET`</mark> `https://gwapisdbx.ellypayapp.com/service-payments/service-packages?serviceCode=`

Returns the list of service packages based on the service code. The service code is passed as query parameter to retrieve the packages accordingly.

#### Query Parameters

<table><thead><tr><th width="180">Name</th><th width="82">Type</th><th>Description</th></tr></thead><tbody><tr><td>serviceCode<mark style="color:red;">*</mark></td><td>String</td><td>The service code for the service whose packages are to be obtained in the request.</td></tr></tbody></table>

#### Headers

<table><thead><tr><th width="147">Name</th><th width="114">Type</th><th>Description</th></tr></thead><tbody><tr><td>public-key<mark style="color:red;">*</mark></td><td>String</td><td>The Public Key as shared in the approval email</td></tr></tbody></table>

### Sample Service Packages Request

```powershell
curl https://gwapisdbx.ellypayapp.com/service-payments/service-packages?serviceCode=MTNData \
   -H "Accept: application/json" \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key"
```

{% tabs %}
{% tab title="200: OK Returns the list of service packages" %}

```json
{
    "code": 200,
    "status": "success",
    "message": "Request completed successfully.",
    "data": [
        {
            "code": "MTNData_33067121",
            "name": "MTN 70MB Daily Bundle",
            "currency": "UGX",
            "price": 500
        },
        {
            "code": "MTNData_9259874",
            "name": "MTN 25MB Monthly Bundle",
            "currency": "UGX",
            "price": 1500
        },
        {
            "code": "MTNData_40297551",
            "name": "MTN 512MB Monthly Bundle",
            "currency": "UGX",
            "price": 5500
        },
        {
            "code": "MTNData_49894758",
            "name": "MTN 165MB Daily Bundle",
            "currency": "UGX",
            "price": 1000
        }
    ]
}
```

{% tabs %}
{% tab title="Response Description" %}

<table><thead><tr><th width="123">Parameter</th><th width="96">Type</th><th>Description</th></tr></thead><tbody><tr><td>code</td><td>string</td><td>The unique code for the service package. During validation, this would be the service code.</td></tr><tr><td>name</td><td>string</td><td>The name of the service package</td></tr><tr><td>currency</td><td>string</td><td>The currency in which the service payment is done</td></tr><tr><td>price</td><td>Number</td><td>The cost of the service package. This is what the merchant pays for the package</td></tr></tbody></table>
{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="400: Bad Request When the request is not formed as expected" %}

```javascript
{
  "code": 400,
  "status": "error",
  "message": "unrecognized service code",
  "data": {}
}
```

{% endtab %}
{% endtabs %}


# Service Choices List

Some service items have choice lists that support their purchase. This section describes how a service's choice list can be obtained.

Obtaining a service choice list relates to services returned [here](/service-payments/services-list) and the value for `has_choices` is **true**. Every item in the choice list represents a choice a customer would make in order to facilitate their payment. Currently, only NWSC has a defined choice list and it's ID is **NWSC.Area**. Whenever more choice list IDs are created, the document will be updated accordingly

### Get Service Choices List

<mark style="color:blue;">`GET`</mark> `https://gwapisdbx.ellypayapp.com/service-payments/choice-list?listId=`

Returns the list of service choices based on the specified choice list type/ID. The list ID is passed as query parameter to retrieve the choices accordingly.

#### Query Parameters

<table><thead><tr><th width="180">Name</th><th width="82">Type</th><th>Description</th></tr></thead><tbody><tr><td>listId<mark style="color:red;">*</mark></td><td>String</td><td>The ID for the choice list to be obtained in the request.</td></tr></tbody></table>

#### Headers

<table><thead><tr><th width="147">Name</th><th width="114">Type</th><th>Description</th></tr></thead><tbody><tr><td>public-key<mark style="color:red;">*</mark></td><td>String</td><td>The Public Key as shared in the approval email</td></tr></tbody></table>

### Sample Service Choices Request

```powershell
curl https://gwapisdbx.ellypayapp.com/service-payments/choice-list?listId=NWSC.Area \
   -H "Accept: application/json" \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key"
```

{% tabs %}
{% tab title="200: OK Returns the list of choices" %}

```json
{
    "code": 200,
    "status": "success",
    "message": "Request completed successfully.",
    "data": [
        {
            "code": "NWSC.Area:Entebbe",
            "name": "Entebbe",
            "description": "Entebbe",
            "is_active": true
        },
        {
            "code": "NWSC.Area:Iganga",
            "name": "Iganga",
            "description": "Iganga",
            "is_active": true
        },
        {
            "code": "NWSC.Area:Jinja",
            "name": "Jinja",
            "description": "Jinja",
            "is_active": true
        },
        {
            "code": "NWSC.Area:Kajjansi",
            "name": "Kajjansi",
            "description": "Kajjansi",
            "is_active": true
        },
        {
            "code": "NWSC.Area:Kampala",
            "name": "Kampala",
            "description": "Kampala",
            "is_active": true
        },
        {
            "code": "NWSC.Area:Kawuku",
            "name": "Kawuku",
            "description": "Kawuku",
            "is_active": true
        },
        {
            "code": "NWSC.Area:Other",
            "name": "Other NWSC Areas",
            "description": "Other NWSC Areas",
            "is_active": true
        }
    ]
}
```

{% tabs %}
{% tab title="Response Description" %}

<table><thead><tr><th width="141">Parameter</th><th width="96">Type</th><th>Description</th></tr></thead><tbody><tr><td>code</td><td>string</td><td>The unique code for the choice. During validation, this would be sent in the request as one of the additional parameters.</td></tr><tr><td>name</td><td>string</td><td>The name of the choice</td></tr><tr><td>description</td><td>string</td><td>The description of the choice</td></tr><tr><td>is_active</td><td>Boolean</td><td>Whether or not this choice can be used in the payment process</td></tr></tbody></table>
{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="400: Bad Request When the request is not formed as expected" %}

```javascript
{
  "code": 400,
  "status": "error",
  "message": "unrecognized list ID",
  "data": {}
}
```

{% endtab %}
{% endtabs %}

### Supported Choice Lists

<table><thead><tr><th width="180">List ID</th><th>Description</th></tr></thead><tbody><tr><td>NWSC.Area</td><td>Returns the list of the different NWSC areas</td></tr></tbody></table>


# Account Validation

The EllyPay API platform has a pre-requisite that before a payment is made for any of the products, the recipient account needs to be validated to confirm the name. The section describes as follows;

{% hint style="info" %}
We recommend checking out the [Getting Started](/service-payments/getting-started) section to understand the basics of service payments first and the general workflow. This guide assumes that you have read that
{% endhint %}

## Step 1: Obtain the required data for the validation request

The table below describes the request parameters that are used for the account validation request. Most/all will be collected from the paying customer.

<table><thead><tr><th width="197">Parameter</th><th width="93">Type</th><th width="98">Required</th><th>Description</th></tr></thead><tbody><tr><td>merchant_reference</td><td>String</td><td>true</td><td>The unique reference for this request. It must be at least 8 characters long. You can pass the value as <strong>auto</strong> and the API will generate a reference on your behalf</td></tr><tr><td>account_number</td><td>String</td><td>true</td><td>The account number to be validated. This can be the airtime/internet phone number, Yaka Meter Number, NWSC Meter Number, URA PRN or any identifier whose account details are required</td></tr><tr><td>service_code</td><td>String</td><td>true</td><td>The unique service code. For services with price/package lists, use the item code from the <a href="/pages/Tqfh8Fn8v8I2WQnWQRXh">packages list</a> otherwise use the code from the main <a href="/pages/xchW5QFIpbPqLU8Gqn1I">service list</a></td></tr><tr><td>currency</td><td>String</td><td>true</td><td>The 3-character ISO currency code for the request currency</td></tr><tr><td>amount</td><td>Number</td><td>true</td><td>The amount to be transferred</td></tr><tr><td>additional_params</td><td>Object</td><td>false</td><td>This parameter normally used to pass additional parameters that apply to specific services as described in the section below</td></tr></tbody></table>

### Additional Parameters

The table below describes the service codes and the additional parameters required to fulfill the account validation. This list will be updated from time to time depending on the circumstances.

<table><thead><tr><th width="145">Service Code</th><th width="190">Additional Parameter</th><th>Description</th></tr></thead><tbody><tr><td>YAKA</td><td>customer_phone</td><td>The phone number of the customer. It will receive the confirmation SMS</td></tr><tr><td>URA</td><td>customer_phone</td><td>The phone number of the customer. It will receive the confirmation SMS</td></tr><tr><td>GoTV</td><td>customer_phone</td><td>The phone number of the customer. It will receive the confirmation SMS</td></tr><tr><td>DSTV</td><td>customer_phone</td><td>The phone number of the customer. It will receive the confirmation SMS</td></tr><tr><td>NWSC</td><td>customer_phone</td><td>The phone number of the customer. It will receive the confirmation SMS</td></tr><tr><td></td><td>area_code</td><td>The choice code value obtained from the choice list <a href="/pages/5e3qvKKuGSczK5bLQo2w">API</a> with the list_id being NWSC.Area</td></tr></tbody></table>

After collecting the necessary payment information from your customer, prepare your request payload as demonstrated below.

```json
{
    "merchant_reference": "auto",
    "account_number": "2116789901",
    "service_code": "NWSC",
    "currency": "UGX",
    "amount": 12000,
    "additional_params": {
        "customer_phone": "256704878224",
        "area_code": "NWSC.Area:Kampala"
    }
}
```

<mark style="color:green;">`POST`</mark> `https://gwapisdbx.ellypayapp.com/service-payments/validate-account`

The request is sent as a JSON body as demonstrated by the sample request below. Sample responses (success and failure) are also shared.

```powershell
curl -X POST "https://gwapisdbx.ellypayapp.com/service-payments/validate-account" \
   -H 'Content-Type: application/json' \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key" \
   -d '{
        "merchant_reference": "auto",
        "account_number": "2116789901",
        "service_code": "NWSC",
        "currency": "UGX",
        "amount": 12000,
        "additional_params": {
            "customer_phone": "256704878224",
            "area_code": "NWSC.Area:Kampala"
        }
    }'
```

{% tabs %}
{% tab title="200: OK - Request processed successfully" %}

```json
{
    "code": 200,
    "status": "success",
    "message": "Request completed successfully",
    "data": {
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "account_number": "2116789901",
        "request_currency": "UGX",
        "request_amount": 12000,
        "customer_name": "SANDBOX CUSTOMER",
        "transaction_charge": 1000,
        "transaction_amount": 13000,
        "service_name": "NWSC Kampala",
        "balance_due": 2300
    }
}
```

Response parameters are described below

<table><thead><tr><th width="202">Parameter</th><th width="107">Type</th><th>Description</th></tr></thead><tbody><tr><td>internal_reference</td><td>String</td><td>The reference generated by the EllyPay platform. This is very critical for the next step of payment confirmation.</td></tr><tr><td>merchant_reference</td><td>String</td><td>The merchant reference as it was sent in the validation request (or auto generated by the system)</td></tr><tr><td>account_number</td><td>String</td><td>The account number that's been validated</td></tr><tr><td>request_currency</td><td>String</td><td>The currency in which the payment is to be done</td></tr><tr><td>request_amount</td><td>Number</td><td>The amount the customer wishes to pay</td></tr><tr><td>customer_name</td><td>String</td><td>The name attached to the account number</td></tr><tr><td>transaction_charge</td><td>Number</td><td>If the service is billable, this parameter will hold the calculated charge for making a payment of the amount, otherwise it will be zero.</td></tr><tr><td>transaction_amount</td><td>Number</td><td>This is the summation of request amount and tranasaction charge. For outbound service payments, this would be the total amount to be deducted from the merchant account wallet</td></tr><tr><td>service_name</td><td>String</td><td>The name of the service for which payment is being made.</td></tr><tr><td>balance_due</td><td>Number</td><td>Some services (e.g. YAKA, NWSC) accrue a balance on the customer accounts. This parameter would hold the value of that balance</td></tr></tbody></table>
{% endtab %}

{% tab title="400: Bad Request - Request is not formed as expected" %}

```json
{
    "code": 400,
    "status": "error",
    "message": "256752000001 is not a valid MTN Mobile Money Uganda (mtn_ug) phone number",
    "data": {}
}
```

{% endtab %}
{% endtabs %}

## Step 2: Display details to the customer

{% hint style="info" %}
We strongly recommend that details returned from the validation process are displayed to the customer especially the name of the accountholder so that there's confirmation before going forward. In some situations e.g. YAKA and NWSC, the balance owed may also be value greater than zero and this needs to be displayed so that the customer is aware.
{% endhint %}

## Step 3: Confirm the payment

When the validation process is successful, the next step is the confirmation of the payment as described in the payment confirmation section. This confirmation expects the internal reference (returned from the validation) as the only parameter in the request. More on this [here](/service-payments/payment-confirmation)


# Payment Confirmation

After the account validation has successfully responded with the account details. You can proceed to confirm the payment for the service

{% hint style="info" %}
We recommend checking out the [Getting Started](/service-payments/getting-started) section to understand the basics of service payments first and the general workflow. This guide assumes that you have read that
{% endhint %}

## Step 1: Setup the payment confirmation request

The table below describes the request parameters that are used for the payment confirmation request.&#x20;

<table><thead><tr><th width="197">Parameter</th><th width="93">Type</th><th width="98">Required</th><th>Description</th></tr></thead><tbody><tr><td>internal_reference</td><td>String</td><td>true</td><td>The internal reference returned as part of the validation response</td></tr><tr><td>secret_code</td><td>String</td><td>false</td><td>This one is a special parameter that applies to Airtel Money Withdraw transactions. <em><strong>The customer secret code should be Base64 encoded before sending the API request</strong></em>.</td></tr></tbody></table>

Prepare your request payload as demonstrated below.

```json
{
    "internal_reference": "ELPREFQ9ULSYC7KAASWJ"
}
```

<mark style="color:green;">`POST`</mark> `https://gwapisdbx.ellypayapp.com/service-payments/process-payment`

The request is sent as a JSON body as demonstrated by the sample request below. Sample responses (acknowledgement and failure) are also shared.

```powershell
curl -X POST "https://gwapisdbx.ellypayapp.com/service-payments/process-payment" \
   -H 'Content-Type: application/json' \
   -H "x-api-version: 1" \
   -H "public-key: your-public-key" \
   -d '{
        "internal_reference": "ELPREFQ9ULSYC7KAASWJ"
    }'
```

{% tabs %}
{% tab title="200: OK - Request processed successfully" %}

```json
{
    "code": 202,
    "status": "accepted",
    "message": "Request Accepted",
    "data": {
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y"
    }
}
```

{% endtab %}

{% tab title="400: Bad Request - Request is not formed as expected" %}

```json
{
    "code": 400,
    "status": "error",
    "message": "Unknown internal reference",
    "data": {}
}
```

{% endtab %}
{% endtabs %}

## Step 2: Handle final status notification

Every merchant account is expected to have configured a callback/webhook URL for service payments. For all transactions that transition to the final state (COMPLETED or FAILED), a JSON POST request will be made to the callback URL. Sample callback payloads (request bodies) are shared below. Be sure to check out [Handling Notifications](/utility-functions/handling-notifications-callbacks) to see how you should verify the signature(s) in the request headers and how to respond.

{% tabs %}
{% tab title="Successful Service Payment" %}

```json
{
    "event": "payment.completed",
    "payload": {
        "merchant_reference": "MCTPAY7JAKLM2CGHUAUK",
        "internal_reference": "ELPREFAJJXPPALHUWKFY",
        "transaction_type": "SERVICE_PAYMENT",
        "request_currency": "UGX",
        "request_amount": 1000,
        "transaction_status": "COMPLETED",
        "account_number": "256772007180",
        "transaction_charge": 0,
        "transaction_amount": 1000,
        "customer_name": "SANDBOX CUSTOMER",
        "service_name": "MTN Airtime",
        "status_message": "Transaction Completed Successfully",
        "additional_details": {}
    }
}
```

In some situations, the `payload.additional_details` parameter will hold extra details about the payment e.g. the YAKA token etc. The table below decribes some of the expected parameters in the additional\_details object.

<table><thead><tr><th width="171">Parameter</th><th width="101">Type</th><th>Description</th></tr></thead><tbody><tr><td>token</td><td>String</td><td>The YAKA token. Only returned on YAKA payments</td></tr><tr><td>units</td><td>Number</td><td>The total units purchased. Usually returned on YAKA payments</td></tr><tr><td>receipt_number</td><td>String</td><td>The payment receipt. Usually returned on utility or tax payments</td></tr><tr><td>description</td><td>String</td><td>Description of the payment. This usually applies to all service payments</td></tr><tr><td>debt_recovery</td><td>String</td><td>Usually returns on YAKA payments</td></tr><tr><td>fuel</td><td>String</td><td>Usually returns on YAKA payments</td></tr><tr><td>inflation</td><td>String</td><td>Usually returns on YAKA payments</td></tr><tr><td>tax</td><td>String</td><td>Usually returns on YAKA payments</td></tr><tr><td>customer_phone</td><td>String</td><td>Usually returns on utility, tax and digital TV payments</td></tr></tbody></table>
{% endtab %}

{% tab title="Failed Service Payment" %}

```json
{
    "event": "payment.failed",
    "payload": {
        "merchant_reference": "MCTPAYWQXZML3EQR7TKT",
        "internal_reference": "ELPREFNWSN5RFEBWHHWK",
        "transaction_type": "SERVICE_PAYMENT",
        "request_currency": "UGX",
        "request_amount": 1000,
        "transaction_status": "FAILED",
        "account_number": "256772000004",
        "transaction_charge": 0,
        "transaction_amount": 0,
        "customer_name": "SANDBOX CUSTOMER",
        "service_name": "MTN Airtime",
        "status_message": "Insufficient funds to complete transaction",
        "additional_details": {}
    }
}
```

{% endtab %}
{% endtabs %}


# HMAC Signature Verification

The section describes how the hmac signature sent in the callback header can be verified

## Obtain the Signing Key

The signing key is an alpha-numeric string generated by our platform during your merchant account creation and it is stored against your account record. This value can be found under your account details in the merchant dashboard. EllyPay uses this value to create the HMAC signature and the same will be used when verifying the signature. It is recommended that it is copied and stored safely together with the security keys.

Below is the sample callback data to be used for the demonstration;

```json
{
    "event": "transaction.charges",
    "payload": {
        "id": 11832,
        "merchant_reference": "MCTREFNGKLP5VQCQSBH2",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "COLLECTION",
        "request_currency": "UGX",
        "transaction_amount": 100000,
        "transaction_currency": "UGX",
        "transaction_charge": 4000,
        "transaction_account": "256777000001",
        "charge_customer": false,
        "total_credit": 96000,
        "provider_code": "mtn_momo_ug",
        "request_amount": 100000,
        "institution_name": "MTN Mobile Money Uganda",
        "customer_name": "JOHN DOE",
        "transaction_status": "PENDING",
        "status_message": "Collection initialized successfully. Confirm charges"
    }
}
```

## Next Steps

1. Obtain the value of the `hmac-signature` header. The value sent in the signature header takes the format `t=timestamp,s=hmac_hash`
2. Form the string payload to be used in signature verification. This is obtained by concatenating values of the callback data in the format; `event:merchant_reference:internal_reference:transaction_type:transaction_status` and these values are obtained from the callback data. The string payload in this case would therefore be `transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING`
3. Create the hmac hash of the string payload.
4. Compare the resulting hash to the value in the hmac-signature header. Equality means the signature is valid.

{% tabs %}
{% tab title="PHP" %}

```php
<?php

public function isValidSignature() {
    $strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
    $signingKey = "your signing key string";
    $hmacSignature = "value of hmac-signature header";

    try {
      $timestamp = null;
      $hmacHash = null;

      // Split the hmacSignature into key-value pairs
      foreach (explode(",", $hmacSignature) as $sig_part) {
        [$key, $value] = explode("=", $sig_part);
        switch ($key) {
          case "t":
            $timestamp = $value;
            break;
          case "s":
            $hmacHash = $value;
            break;
        }
      }

      // Optional timestamp check based on your logic

      // Calculate the HMAC signature
      $signature = hash_hmac("sha256", $strPayload, $signingKey, false);

      // Compare the calculated and provided signatures
      return $signature === $hmacHash;
    } catch (Exception $e) {
      return false;
    }
}

?>
```

{% endtab %}

{% tab title="NodeJS" %}

```javascript
const crypto = require('crypto');

function isValidSignature() {
    const strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
    const signingKey = "your signing key string";
    const hmacSignature = "value of hmac-signature header";

    try {
        let timestamp, hmacHash;
        for (const sig_part of hmacSignature.split(",")) {
            const [key, value] = sig_part.split("=");
            switch (key) {
                case "t":
                    timestamp = value;
                    break;
                case "s":
                    hmacHash = value;
                    break;
            }
        }

        /*you can optionally check the timestamp. your current timestamp and the timestamp in the header should not be more than 30 seconds apart*/

        const signature = crypto.createHmac("sha256", signingKey).update(strPayload).digest("hex");
        /*true or false*/
        return signature === hmacHash;
    } catch (e) {
        return false;
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

public class SignatureValidator {

  private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";

  public static boolean isValidSignature(String signingKey, String strPayload, String hmacSignature)
      throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
    // Extract timestamp and signature hash from hmacSignature
    Map<String, String> signatureParts = parseSignatureParts(hmacSignature);
    String timestamp = signatureParts.get("t");
    String hmacHash = signatureParts.get("s");

    // Optional timestamp check based on your logic

    // Calculate the HMAC signature
    byte[] calculatedSignature = calculateHmac(signingKey, strPayload);
    String encodedSignature = Base64.getEncoder().encodeToString(calculatedSignature);

    // Compare the calculated and provided signatures
    return encodedSignature.equals(hmacHash);
  }

  private static Map<String, String> parseSignatureParts(String hmacSignature) {
    Map<String, String> parts = new HashMap<>();
    for (String sigPart : hmacSignature.split(",")) {
      String[] keyValue = sigPart.split("=");
      parts.put(keyValue[0], keyValue[1]);
    }
    return parts;
  }

  private static byte[] calculateHmac(String signingKey, String strPayload)
      throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
    byte[] decodedKey = Base64.getDecoder().decode(signingKey);
    Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
    mac.init(new SecretKeySpec(decodedKey, HMAC_SHA256_ALGORITHM));
    return mac.doFinal(strPayload.getBytes("UTF-8"));
  }

  public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
    // Replace these values with your actual data
    String signingKey = "your_signing_key";
    String strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
    String hmacSignature = "value of hmac-signature header";

    // Call the isValidSignature method and print the result
    boolean isValid = isValidSignature(signingKey, strPayload, hmacSignature);
    System.out.println("Signature is valid: " + isValid);
  }
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

public class SignatureValidator
{
    private const string HmacSha256Algorithm = "HmacSha256";

    public static bool IsValidSignature(string signingKey, string strPayload, string hmacSignature)
    {
        try
        {
            // Extract timestamp and signature hash (optional timestamp check not included)
            var signatureParts = SplitAndParseSignatureParts(hmacSignature);
            var hmacHash = signatureParts["s"];

            // Calculate the HMAC signature
            var calculatedSignature = CalculateHmac(signingKey, strPayload);
            var encodedSignature = Convert.ToBase64String(calculatedSignature);

            // Compare the calculated and provided signatures
            return encodedSignature.Equals(hmacHash);
        }
        catch (Exception ex)
        {
            return false;
        }
    }

    private static Dictionary<string, string> SplitAndParseSignatureParts(string hmacSignature)
    {
        var parts = new Dictionary<string, string>();
        foreach (var sigPart in hmacSignature.Split(','))
        {
            var keyValue = sigPart.Split('=');
            parts.Add(keyValue[0], keyValue[1]);
        }
        return parts;
    }

    private static byte[] CalculateHmac(string signingKey, string strPayload)
    {
        using (var hmac = HMACSHA256.Create())
        {
            hmac.Key = Convert.FromBase64String(signingKey);
            return hmac.ComputeHash(Encoding.UTF8.GetBytes(strPayload));
        }
    }

    public static void Main(string[] args)
    {
        // Replace these values with your actual data
        string signingKey = "your_signing_key";
        string strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
        string hmacSignature = "value of hmac-signature header";

        // Call the isValidSignature method and print the result
        bool isValid = IsValidSignature(signingKey, strPayload, hmacSignature);
        Console.WriteLine("Signature is valid: {0}", isValid);
    }
}

```

{% endtab %}

{% tab title="Python" %}

```python
import base64
import hmac
import hashlib

def is_valid_signature(signing_key, str_payload, hmac_signature):
    try:
        # Extract timestamp and signature hash (optional timestamp check not included)
        signature_parts = {key: value for key, value in map(lambda x: x.split("="), hmac_signature.split(","))}
        hmac_hash = signature_parts["s"]

        # Calculate the HMAC signature
        calculated_signature = hmac.new(signing_key.encode(), str_payload.encode(), hashlib.sha256).hexdigest()

        # Compare the calculated and provided signatures
        return calculated_signature == hmac_hash
    except Exception:
        return False

# Replace these values with your actual data
signing_key = "your_signing_key"
str_payload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING"
hmac_signature = "value of hmac-signature header"

# Call the is_valid_signature function and print the result
is_valid = is_valid_signature(signing_key, str_payload, hmac_signature)
print("Signature is valid:", is_valid)
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require 'base64'
require 'digest/hmac'

def is_valid_signature?(signing_key, str_payload, hmac_signature)
  begin
    # Extract timestamp and signature hash (optional timestamp check not included)
    signature_parts = hmac_signature.split(",").map { |part| part.split("=") }.to_h
    hmac_hash = signature_parts["s"]

    # Calculate the HMAC signature
    calculated_signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, signing_key, str_payload))

    # Compare the calculated and provided signatures
    return calculated_signature == hmac_hash
  rescue StandardError
    return false
```

{% endtab %}
{% endtabs %}

Below is a sample signature generated using the signing key: **SGNKYLSPUJKZBKQH5YVU**

```
t=1722416074424,s=a33e2d1b844fad58ab8ca41e3bda4834ef2eece4ac77d857a7c9f06b4b1a4b6b
```


# RSA Signature Verification

The section describes how the RSA signature sent in the callback header can be verified. The signature is generated using an RSA Signing. For verification to succeed, the public key is required.

## Obtain the Public Key - Based on environment

Copy the public key for the environment you're working with from [here](/getting-started/rsa-public-keys). This document assumes that the public key would be stored somewhere on your server under the name `ellypay.public.key.pem`

## Next Steps

Below is the sample callback data for this demonstration

```json
{
    "event": "transaction.charges",
    "payload": {
        "id": 11832,
        "merchant_reference": "MCTREFNGKLP5VQCQSBH2",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "COLLECTION",
        "request_currency": "UGX",
        "transaction_amount": 100000,
        "transaction_currency": "UGX",
        "transaction_charge": 4000,
        "transaction_account": "256777000001",
        "charge_customer": false,
        "total_credit": 96000,
        "provider_code": "mtn_momo_ug",
        "request_amount": 100000,
        "institution_name": "MTN Mobile Money Uganda",
        "customer_name": "JOHN DOE",
        "transaction_status": "PENDING",
        "status_message": "Collection initialized successfully. Confirm charges"
    }
}
```

1. Obtain the value of the `rsa-signature` header.
2. Form the string payload to be used in signature verification. This is obtained by concatenating values of the callback data in the format; `event:merchant_reference:internal_reference:transaction_type:transaction_status` and these values are obtained from the callback data. The string payload in this case would therefore be `transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING`
3. Use the public key obtained above to verify the signature as described in the sample source codes below;&#x20;

{% tabs %}
{% tab title="PHP" %}

```php
<?php

public function isValidSignature() {
    $file = "path-to-file/ellypay.public.key.pem";
    $keyContent = file_get_contents($file);
    $publicKey = openssl_get_publickey($keyContent);
    $strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
    $signature = base64_decode("value-of-rsa-signature");

    /*true or false*/
    return openssl_verify($strPayload, $signature, $publicKey, "sha256") == 1;
}

?>
```

{% endtab %}

{% tab title="NodeJS" %}

```javascript
const crypto = require('crypto');
const fs = require('fs');

function isValidSignature() {
    const strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
    const signature = "value-of-rsa-signature";
    const publicKeyFile = "path-to-file/ellypay.public.key.pem";
    const publicKey = fs.readFileSync(publicKeyFile).toString().replace(/\\n/g, '\n');

    const verify = crypto.createVerify("SHA256");
    verify.write(strPayload);
    verify.end();

    /*true or false*/
    return verify.verify(publicKey, signature, 'base64');
}
```

{% endtab %}

{% tab title="Java" %}

```java
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class SignatureVerifier {

    public boolean isValidSignature() throws Exception {
        // Read public key from file
        Path pathToFile = Paths.get("path-to-file/ellypay.public.key.pem");
        byte[] keyBytes = Files.readAllBytes(pathToFile);

        // Decode public key
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);

        // Signature and payload
        String strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
        byte[] signatureBytes = Base64.getDecoder().decode("value-of-rsa-signature");

        // Verify signature
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(strPayload.getBytes());
        return signature.verify(signatureBytes);
    }

    public static void main(String[] args) throws Exception {
        SignatureVerifier verifier = new SignatureVerifier();
        System.out.println(verifier.isValidSignature());
    }
}

```

{% endtab %}

{% tab title="C#" %}

```csharp
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public class SignatureVerifier
{
    public bool IsValidSignature()
    {
        // Read public key from file
        string pathToFile = @"path-to-file\ellypay.public.key.pem";
        string keyContent = File.ReadAllText(pathToFile);

        // Decode public key
        RSA rsa = RSA.Create();
        rsa.ImportFromPem(keyContent);

        // Signature and payload
        string strPayload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING";
        byte[] signatureBytes = Convert.FromBase64String("value-of-rsa-signature");

        // Verify signature
        var verifier = new RSAPKCS1SignatureDeformatter(rsa);
        verifier.SetHashAlgorithm("SHA256");
        byte[] payloadBytes = Encoding.UTF8.GetBytes(strPayload);
        byte[] sha256Hash;

        using (SHA256 sha256 = SHA256.Create())
        {
            sha256Hash = sha256.ComputeHash(payloadBytes);
        }

        return verifier.VerifySignature(sha256Hash, signatureBytes);
    }

    public static void Main(string[] args)
    {
        SignatureVerifier verifier = new SignatureVerifier();
        Console.WriteLine(verifier.IsValidSignature());
    }
}

```

{% endtab %}

{% tab title="Python" %}

```python
import base64
import hashlib
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.serialization import load_pem_public_key

def is_valid_signature():
    # Read public key from file
    with open("path-to-file/ellypay.public.key.pem", "rb") as key_file:
        key_content = key_file.read()

    # Decode public key
    public_key = load_pem_public_key(key_content, backend=default_backend())

    # Signature and payload
    str_payload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING"
    signature_bytes = base64.b64decode("value-of-rsa-signature")

    # Verify signature
    verifier = public_key.verifier(
        signature_bytes,
        padding.PKCS1v15(),
        hashlib.sha256
    )
    verifier.update(str_payload.encode())

    try:
        verifier.verify()
        return True
    except Exception as e:
        print("Signature verification failed:", e)
        return False

```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require 'openssl'
require 'base64'

def is_valid_signature
  # Read public key from file
  file = File.read("path-to-file/ellypay.public.key.pem")

  # Decode public key
  public_key = OpenSSL::PKey::RSA.new(file)

  # Signature and payload
  str_payload = "transaction.charges:MCTREFNGKLP5VQCQSBH2:ELPREFA65BGTFR7NGUXM:COLLECTION:PENDING"
  signature_bytes = Base64.decode64("value-of-rsa-signature")

  # Verify signature
  sha256 = OpenSSL::Digest::SHA256.new
  result = public_key.verify(sha256, signature_bytes, str_payload)

  result
end

```

{% endtab %}
{% endtabs %}

Below is an example of a generated RSA signature (rsa-signature) on the sandbox environment. Feel free to verify it using the sample payload above and the public key.

{% code overflow="wrap" %}

```
TrvB4sLobRvrrsc1BnXFM08WeJiCkEnQKPYAXuIk31sc/8zosdTLGgyU5jx2Vr2yehGD/vILh3I/8rni5Q7kyLiOwkGC7pA6OioV9B2gTJBFfPkkwNGdZCuAAG2EkAZJAfXNu0WhwhGjO80ffdgxxBpLwodcmoEDCppF0tyXTVp9qhfzJg0Yg7U2w5wBjnxp0Cp3Rc/Q7X8xxMHU7q1cI0WSUZJmq7CkwDjn3RpUtgD/lRMfHdkoDbKPvxWl7lBJHaQc4nq6a0qdIdWLsCh+TjsZTmJybw9GZ0bSof8VkfOrCraKSbj5NDWEHXAMTnIezpSYmc0fM2NjClTIDLiSL9VfcopMG7D7V8igpmoRdem0cGkAM3q1GtxxK0j2aPftpWqfEjZPaW+kAT3hkS/B9w9fu8ju1YmlBeAbPD5iNN+TSOk7ZfYeQm3/tfV0gqD8mydppXIWg5Ex5KiC5EnxlYanNlekbB+hnBIGH8VTf57PxmtJL+WxN5CHymPFvYJXjJsux0E+smHEqTKmCWcgS/U0AgqsvwJARFcYm2ka4hMM2em/PlGth1wXwMTVQmVqGhwtKDKBdAzIknSZN1JR8hwxzjCzsHnGHWru+2m7EQurBfGskpjnW7NJt4cr7lQ7g8yOj9fTfogByHGQ4akcIbYJhLifqIXQajvZhcNJ9eM=
```

{% endcode %}


# API Reference

{% openapi src="/files/sQmrr4RUMoEqqdjVd6HG" path="/data/payment-providers" method="get" %}
[ellypay-swagger.json](https://3185691840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FnTFWAQ7JwWk4uiwRgLxm%2Fuploads%2Fgit-blob-fd46e88c314874cb59413f4511cf3ab03e4a3d96%2Fellypay-swagger.json?alt=media)
{% endopenapi %}

{% openapi src="/files/sQmrr4RUMoEqqdjVd6HG" path="/data/wallet-balances" method="get" %}
[ellypay-swagger.json](https://3185691840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FnTFWAQ7JwWk4uiwRgLxm%2Fuploads%2Fgit-blob-fd46e88c314874cb59413f4511cf3ab03e4a3d96%2Fellypay-swagger.json?alt=media)
{% endopenapi %}

{% openapi src="/files/sQmrr4RUMoEqqdjVd6HG" path="/data/transaction/verify/{merchant\_reference}" method="get" %}
[ellypay-swagger.json](https://3185691840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FnTFWAQ7JwWk4uiwRgLxm%2Fuploads%2Fgit-blob-fd46e88c314874cb59413f4511cf3ab03e4a3d96%2Fellypay-swagger.json?alt=media)
{% endopenapi %}

{% openapi src="/files/sQmrr4RUMoEqqdjVd6HG" path="/collections/initialize" method="post" %}
[ellypay-swagger.json](https://3185691840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FnTFWAQ7JwWk4uiwRgLxm%2Fuploads%2Fgit-blob-fd46e88c314874cb59413f4511cf3ab03e4a3d96%2Fellypay-swagger.json?alt=media)
{% endopenapi %}

{% openapi src="/files/sQmrr4RUMoEqqdjVd6HG" path="/data/payout-bank-codes" method="get" %}
[ellypay-swagger.json](https://3185691840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FnTFWAQ7JwWk4uiwRgLxm%2Fuploads%2Fgit-blob-fd46e88c314874cb59413f4511cf3ab03e4a3d96%2Fellypay-swagger.json?alt=media)
{% endopenapi %}

{% openapi src="/files/sQmrr4RUMoEqqdjVd6HG" path="/payout/send-funds" method="post" %}
[ellypay-swagger.json](https://3185691840-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FnTFWAQ7JwWk4uiwRgLxm%2Fuploads%2Fgit-blob-fd46e88c314874cb59413f4511cf3ab03e4a3d96%2Fellypay-swagger.json?alt=media)
{% endopenapi %}


# Availing Payout Funds

Some merchants wish that collected funds can be used for processing payouts/disbursements. This section what the merchant needs to do to avail balance for handling payouts

The platform requires that the merchant explicitly requests for funds to be availed for payouts/disbursements. This way, the merchant has full control of what their collections/deposits are used for. When the merchant initiates this request on the platform, the finance team handles the request and when approved, the merchant will be notified via email.


# Availing Service Payment Funds

Some merchants wish that collected funds can be used for processing service payments. This section describes what the merchant needs to do to avail balance for handling service payments.

The platform requires that the merchant explicitly requests for funds to be availed for service payments. This way, the merchant has full control of what their collections/deposits are used for. When the merchant initiates this request on the platform, the finance team handles the request and when approved, the merchant will be notified via email.


# Funds Settlement

Some merchants wish that collected fund can be settled every so often. This section describes how the merchant can initiate a funds settlement request to the finance team for handling.

##


# Cross Currency Transactions

The API allows for cross currency transactions whereby the request currency does not match the currency of the selected payment option. The platform handles the conversion appropriately.

The payment options provided by the EllyPay API have specific transaction currencies as seen from the response [here](/utility-functions/payment-options). These currencies therefore correspond to the currencies in which EllyPay is able to hold balances. The platform however allows the merchant to send any currency in the collection/payout requests for example the request could be for USD 100 but the selected payment option has transaction currency as UGX etc.&#x20;

The platform has a robust workflow to ensure the conversion happens to the transaction currency/amount since that's the currency the payment provider supports. When the transaction completes successfully, the merchant balance would be updated in the transaction currency. The API uses the prevailing market exchange rates to do the conversion.

Consider a sample Mobile Money collection request where the requested currency is USD and the payment option is mtn\_momo\_ug (MTN Mobile Money Uganda). In the example, we assume the conversion rate to be 1 USD = 3700 UGX and that the transaction charge is 1%

```json
{
    "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
    "transaction_method": "MOBILE_MONEY",
    "currency": "USD",
    "amount": 100,
    "provider_code": "mtn_momo_ug",
    "msisdn": "256777000001",
    "customer_name": "JOHN DOE",
    "description": "Test Collection"
}
```

On completion, the callback would look like the following. Take critical note on the callback parameters; `request_currency`, `request_amount`, `transaction_currency` and `transaction_amount`

```json
{
    "event": "transaction.completed",
    "payload": {
        "id": 20760,
        "merchant_reference": "MCTREFT2WMNWZ23SBN6Y",
        "internal_reference": "ELPREFA65BGTFR7NGUXM",
        "transaction_type": "COLLECTION",
        "request_currency": "USD",
        "request_amount": 100,
        "transaction_currency": "UGX",
        "transaction_amount": 380000,
        "transaction_charge": 3800,
        "transaction_account": "256777000001",
        "charge_customer": false,
        "total_credit": 376200,
        "provider_code": "mtn_momo_ug",
        "institution_name": "MTN Mobile Money Uganda",
        "customer_name": "JOHN DOE",
        "transaction_status": "COMPLETED",
        "status_message": "Transaction Completed Successfully"
    }
}
```

{% hint style="info" %}
This workflow gives merchants the flexibility to bill their customers in other currencies, while allowing the customers to pay/get paid in the currencies at their disposal, and using the payment options that are most convenient. This workflow applies to both collections and payouts
{% endhint %}


