# History
Source: https://docs.pawapay.io/dashboard/finances/history
For easy reconciliation and auditability, you can always export detailed financial statements of your wallets. Every single transaction affecting your balance is recorded on the statement.
On the statement history page, you can find all your exported wallet statements.
Only statements generated within the last 90 days are available for download.
### Glossary of data in statements
| Column | Description |
| - | - |
| Time | The date and time at which this statement export was generated. Time is in the UTC time zone. |
| Requester | The user who requested this statement export. |
| Wallet | The wallet for which this statement export was generated. |
| Statement time range | The time period that is included in this statement export. |
| Status | The current status of this statement export. One of:
**Completed**: The statement export is available to download.
**Generating**: The statement export is currently being generated and will be available soon.
**Failed**: The statement export failed to generate. Please try again. |
# Settlements
Source: https://docs.pawapay.io/dashboard/finances/settlements
Choose where, how often and how much we settle from your PawaPay wallets.
PawaPay settles the funds in your wallets to your bank accounts on a schedule you choose. You set this up once on the *Settlements* page under *Finances*, and you can change it at any time.
You need the **Settlements** or **Administrator** role to set up and view settlements. See [User roles](/dashboard/user_management/user_roles).
## Before you start
Have these details ready for each bank account you want to add:
* Account holder name
* Account number or IBAN
* Bank name and SWIFT/BIC code
* Branch name, branch address and, if it has one, branch sort code
* Country and currency of the account
You can settle to two kinds of bank account:
| Account | Currency | Wallets it can take |
| :- | :- | :- |
| Cross-border | EUR, USD or GBP | Wallets from any country, except countries that only allow local settlement |
| Local | The currency of a country where PawaPay processes your payments | Wallets from the same country and currency |
## Set up your settlements
Setup has three steps, then a summary where you can change any step before you confirm.
Add a bank account, then select the wallets that settle to it under *Your wallets*. Select *Add* to save the account.
You can link some or all of your wallets to a cross-border account. Link any other wallet to a local account in its own country. Once you have a cross-border account, *Country* and *Currency* only offer the countries and currencies of your unlinked wallets.
You can add one cross-border account. All your cross-border settlements are paid in its currency.
A local account in XOF, XAF or CDF can also take wallets in other currencies:
| Account currency | Also takes wallets in |
| :- | :- |
| XOF | XOF from other countries, XAF, CDF, USD |
| XAF | XAF from other countries, XOF, CDF, USD |
| CDF | USD |
Choose *Weekly*, *Bi-weekly* or *Monthly*. Settlements always start on a Monday.
| Frequency | When we settle |
| :- | :- |
| Weekly | Every Monday |
| Bi-weekly | Every second Monday |
| Monthly | Every fourth Monday |
A minimum wallet balance is the amount we always leave in a wallet. We never settle it. We only settle the balance above it. For example, if a wallet has a minimum wallet balance of 500,000 XOF and a balance of 1,000,000 XOF, 500,000 XOF can be settled. Each wallet can have its own minimum.
Select *I need to set minimum balances for my wallets* to enter them. If you leave it unticked, every minimum is 0 and we settle the full balance.
Check the summary. To change a section, select its edit icon. When everything is correct, select *Confirm your preferences*. Your first settlement starts on the next Monday.
## When we settle
Settlements start on Mondays, at the frequency you chose. When a settlement starts, we settle the part of each wallet's balance that is above its minimum wallet balance. Funds that arrive after that are included in your next settlement.
The *Settlements* tab shows the date of your upcoming settlement. How long the funds take to reach your bank depends on the country and the account.
## How much we settle
We never settle a wallet's minimum wallet balance. When a settlement starts, only the balance above it can be settled. For example, if a wallet has a minimum wallet balance of 500,000 XOF and a balance of 1,000,000 XOF, 500,000 XOF can be settled.
The total amount being settled must also reach PawaPay's minimum settlement amount:
* **Local account:** the total amount settled to the account must be worth at least 50 USD.
* **Cross-border account:** the total amount settled to the account must be worth at least 100 USD.
A local XOF, XAF or CDF account that takes more than one wallet, or a wallet in another currency, counts as a cross-border account here.
If a wallet doesn't qualify, we skip it for that settlement and its funds stay in the wallet. A wallet is skipped when:
* its balance is 0
* its balance is not above its minimum wallet balance
* the total amount being settled is below PawaPay's minimum settlement amount
* we have temporarily paused settlements in its country
## Track your settlements
The *Settlements* tab lists your previous settlements and shows the date of your upcoming one. Each settlement has one of these statuses:
| Status | What it means |
| :- | :- |
| In progress | We have taken the funds from your wallets and are sending them to your bank. |
| Completed | We have sent the funds. Cross-border settlements show the exchange rate, and you can download the proof of payment once it's attached. |
| Cancelled | We couldn't complete the settlement. All funds were returned to your wallets. |
| Skipped | None of your wallets qualified for this settlement. |
### In progress
An *In progress* settlement shows each wallet included, the amount taken from it and the bank account it goes to. Wallets that were skipped are listed too.
If you settle to more than one bank account, you see how much from each wallet goes to each account, with the bank name and country.
Exchange rates for cross-border settlements are shown once the settlement is complete. To see the rates we used over the past four weeks, select *Historical FX rates*.
To see why a wallet was skipped, select its *Skipped* status.
Our Treasury team may leave comments on a settlement. You'll find them in the top-right corner of the settlement.
### Completed
A completed cross-border settlement shows the amount settled, the exchange rate and the destination bank account. Download the proof of payment (PoP) with *PoP*.
A completed local settlement shows the amount settled, the destination bank account and the proof of payment.
If your bank charged a fee, it is deducted from the amount settled. If you settle to more than one bank account, the fee is shown in the *Bank fee* column.
If you settle to one bank account, the amount next to the date is the total amount settled. When your bank charged a fee, select *Amount Breakdown* under that amount to see the amount, the bank charges and the total amount settled.
### Cancelled
If we cancel a settlement, the funds go back to your wallets straight away. Our Treasury team may leave a comment explaining why.
### Skipped
A settlement is *Skipped* when none of your wallets qualified. See [How much we settle](#how-much-we-settle) for the reasons.
## Change your preferences
You can change your preferences at any time on the *Configuration* tab. Changes apply from the next settlement.
### Bank accounts
To update a bank account's details, or link more wallets to it, select its edit icon. You can't change the *Country* or *Currency* of an existing account, or unlink a wallet that already settles to an account.
### Frequency
To change how often we settle, select the edit icon next to *How often do you want to be settled?*, choose *Weekly*, *Bi-weekly* or *Monthly*, then select *Done*.
### Minimum wallet balances
To change how much we leave in a wallet, select the edit icon next to *Minimum wallet balances*, enter the new amount for each wallet you want to change, then select *Done*. We never settle a wallet's minimum wallet balance, only the balance above it. To settle the full balance of every wallet, untick *I need to set minimum balances for my wallets*.
### New countries
When you start processing payments in a new country, its wallet is not settled until you link it to a bank account. The *Configuration* tab shows which countries we can't settle yet. Select the link in the message to fix it.
You can link the new wallet to your cross-border account, or add a local account for it.
# Statements
Source: https://docs.pawapay.io/dashboard/finances/statements
You always have access to a full financial statement for each of your wallets to be able to audit and reconcile your balance on our platform.
## Search statements
To see every single transaction on your wallet, please start by choosing:
* The time period
* A wallet
Pressing "Apply filters" will display a table with the transactions on this wallet for the selected period. You can also limit the results to specific Mobile Money Operators (MMOs) or transaction types.
You can choose up to a **one-year period at a time**. For some accounts the limit is one month.
A maximum of 10,000 transactions are displayed.
Statement data for the current day may be up to 20 minutes behind the latest transactions.
By default, results are shown starting from the oldest transaction. You can change the order by pressing on the respective column in the header of the table.
## Export statements
After selecting the wallet and time period, press the "Export Statement" button to generate a CSV file with the results.
You will be asked to confirm this action and then taken to the [History](/dashboard/finances/history) page to download the resulting CSV file.
You can choose up to a **one-year period** at a time. For some accounts the limit is one month.
There is no limit to the number of transactions.
The export may take some time to generate. The [History](/dashboard/finances/history) page will refresh itself automatically to show the latest status.
## Data in statements
After exporting or viewing the statement data in our portal, the following data will be included:
| Column | Description |
| - | - |
| Creation Date | The date and time when this transaction was recorded on our platform. The time is in the UTC time zone. |
| MSISDN | The phone number of the mobile money wallet that was involved in the transaction. |
| Transaction ID | The ID of this transaction as specified by you through our API. You can read more about transaction IDs from [our API docs](/v1/api-reference/deposits/request-deposit). |
| Amount | The amount of the transaction. |
| Currency | The currency in which the transaction took place. |
| Country | The country in which this transaction took place. |
| MNO | The Mobile Money Operator (MMO) that fulfilled this transaction. |
| Type | The type of this transaction. See the list of all [transaction types](/dashboard/finances/statements#transaction-types) below. |
| Balance | Your balance after this transaction. |
| Reason | The reason for this transaction, for example, "Initial top-up" for your first top-up of this wallet. |
| Metadata | Metadata in JSON format you attached to the payments for providing additional context about the payment. |
Not all columns are populated for all transactions. For example, transactions that represent a settlement of your balance do not have an MSISDN and MMO specified.
## Transaction types
You may encounter the following transaction types in your statements:
| Transaction type | Description |
| - | - |
| Deposit | Money collected from a customer has been deposited on your wallet. |
| Payout | Money has been paid out to a customer from your wallet. |
| Failed payout refund | Money initially reserved from your wallet for a payout has been returned due to the payout failing. |
| Refund | Money initially collected to your wallet has been refunded to the customer. |
| Failed refund reversal | Money initially reserved from your wallet for a refund has been returned due to the refund failing. |
| Remittance | Money has been sent from your wallet for a remittance. |
| Failed remittance refund | Money initially reserved from your wallet for a remittance has been returned due to the remittance failing. |
| Deposit pass through fees | A fee charged by the Mobile Money Operator (MMO) for a deposit. |
| Payout pass through fees | A fee charged by the Mobile Money Operator (MMO) for a payout. |
| Refund pass-through fees | A fee charged by the Mobile Money Operator (MMO) for a refund. |
| Remittance pass-through fees | A fee charged by the Mobile Money Operator (MMO) for a remittance. |
| Deposit PawaPay fees | A fee charged by PawaPay for a deposit. |
| Payout PawaPay fees | A fee charged by PawaPay for a payout. |
| PawaPay refund fee | A fee charged by PawaPay for a refund. |
| PawaPay remittance fee | A fee charged by PawaPay for a remittance. |
| Top up | Funds sent by you have been added to your wallet. Read more about [Topping up your wallets](/dashboard/topping_up). |
| Top up - Wallet to Wallet transfer | Funds have been moved into this wallet from another of your PawaPay wallets. |
| Withdrawal | Funds withdrawn from your wallet to be settled to your bank. |
| Withdrawal - Wallet to Wallet transfer | Funds have been moved from this wallet to another of your PawaPay wallets. |
| Cancelled withdrawal reversal | A withdrawal was cancelled and the funds have been returned to your wallet. |
### Adjustments and compensations
We record these manually to correct your balance. The Reason column explains each one.
| Transaction type | Description |
| - | - |
| Adjustment - Collections received | Adds successful deposits that were not credited to your wallet. |
| Adjustment - Collections not credited | Deducts deposits that were credited to your wallet in error. |
| Adjustment - Payouts not debited | Deducts successful payouts that were not debited from your wallet. |
| Adjustment - Payouts debited in error | Deducts an extra debit made for a payout, for example when the MMO debited it twice. |
| Adjustment - Payout reversal by MMO | Adds back a payout that the MMO reversed. |
| Adjustment - MNO interest disbursement | Adds interest paid out by the MMO. |
| Adjustment - Pass-through fees - Rebate | Adds a rebate on MMO pass-through fees. |
| Adjustment - Pass-through fee reimbursement | Adds back MMO pass-through fees. |
| Adjustment - Excise tax | Deducts excise tax. |
| Adjustment - Withholding tax | Deducts withholding tax. |
| Adjustment - Bank charges | Deducts bank charges. |
| Adjustment - Other (+) | Adds an amount for a reason not covered by another type. |
| Adjustment - Other | Deducts an amount for a reason not covered by another type. |
| Compensation - Payouts debited in error | Adds back payouts that were debited from your wallet in error. |
| Compensation - PawaPay fees reimbursement | Adds back PawaPay fees. |
| Compensation - Other | Adds compensation for a reason not covered by another type. |
| Manual pass-through fee deduction | Deducts MMO pass-through fees. |
| Manual PawaPay fee deduction | Deducts PawaPay fees. |
# Wallets
Source: https://docs.pawapay.io/dashboard/finances/wallets
On the PawaPay platform, all of your mobile money is stored in wallets. In most cases, wallets are issued per country and hold all your available mobile money in that country.
On the Wallets page, you can see the balances of all your wallets and view a breakdown of the transactions that have affected each wallet.
## Balance overview
In the balance overview section, you can see each of your wallets and their:
* Country
* Current balance
* Currency
* Active providers (MMOs)
Click on a wallet to instantly view its breakdown in the Wallet breakdown panel.
In normal circumstances, wallets are issued per country.\
If a wallet shows **No active provider**, none of your payment flows are currently active for it.
## Wallet breakdown
When you select a wallet, its breakdown is displayed automatically.
Choose a quick filter to quickly view yesterday, last week, or last month. Or select a custom date range using the date picker.
The wallet breakdown is displayed from **midnight to midnight** in the UTC time zone. You can select a period of up to **one month** at a time.
The breakdown shows how your wallet balance has changed over the selected period.
Together with the opening and closing balance, you will see the total value of:
* Deposits
* Split Deposits
* Refunds
* Payouts
* Split Payouts
* Remittances
* Fees
* Withdrawals
* Top ups
* Adjustments
* Compensations
* Uncategorized
Money in is displayed as a positive value in green. Money out is displayed as a negative value in red, in brackets.
### Export wallet breakdown
You can export the breakdown as a CSV or PDF file at any time by selecting "Export statement".
# Global search
Source: https://docs.pawapay.io/dashboard/global_search
Any payment on our platform can be found quickly and easily using our global search which is always at the top left corner of the dashboard.
This functionality is available to users with the Transactions role.
Read more about [User roles](/dashboard/user_management/user_roles).
## How to search
Enter one of the following into the search bar:
* Phone number (MSISDN)
* MMO Transaction ID
* Transaction ID (deposit ID, payout ID, refund ID)
* Full Metadata value
* Metadata is searched based on full value of the metadata field. For example, searching for 12345, to find INV-12345, will not find results.
**Please note!**
Global search finds transactions from the last three months.
# Home screen
Source: https://docs.pawapay.io/dashboard/home
## Overview
The Home screen provides a central place to monitor your account. It brings together payment activity, wallet balances, settlements and live incidents, helping you quickly understand account health and identify anything that needs your attention.
The available modules are:
* **Payments overview**
* **Wallet balances**
* **Next scheduled settlement or Request Settlement** (action)
* **Live incidents**
The Home screen is personalised to your account. The modules and actions displayed depend on:
* Your user role and permissions.
* Your settlement configuration.
* The payment types enabled on your account.
### Payments overview
Displays key payment metrics for the selected period. Filter data by payment type and time range.
If **"All"** is selected in the payment type filters, the visible blocks are:
* **Transactions:** Total number of transactions during the selected period.
* **Payment volume (EUR):** Total value of transactions during the selected period, converted to EUR.
* **Active markets:** Number of markets where at least one successful transaction was processed during the selected period.
If a specific payment type (Deposits / Payouts / Remittances) is selected, the Active market card is replaced by:
* **Payment success rate:** Percentage of \{payment type} transactions that completed successfully during the selected period.
Payment metrics are updated daily and include data up to yesterday.
### Wallet balances
Displays the balances available across all active markets and currencies. Select the "View all wallets" button to view the full Wallets page.
If a specific wallet card is selected, it will redirect to the Wallets page with the specific wallet selected, showing its respective wallet breakdown.
### Settlements
Depending on your settlement configuration, Home displays one of the following modules:
* Next scheduled settlement (scheduled settlements)
* Available to settle (on-request settlements, or both scheduled and on-request)
**Scheduled Settlements**:
Displays the next scheduled settlement, including the settlement frequency, expected settlement date, eligible wallets and most recent completed settlement.
**On-request settlements:**
Displays your current settlement readiness, including eligible wallets, configured settlement destinations and your most recent completed settlement request.
**Both scheduled and on-request settlements:**
### Live incidents
Displays current platform incidents that may affect payment services or markets configured on your account. Navigate to the [Status Page](https://status.pawapay.io/) for more information.
## Role-based Home Screens
The Home screen is personalised to your account. Here's what modules you'll see for each role.
| Module | Admin | Settlements | Batch Disbursement | Finances | Finances + Top Ups | Transactions |
| :- | :- | :- | :- | :- | :- | :- |
| Payments Overview | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Wallet Balances | ✓ | ✓ | ✓ | ✓ | ✓ | — |
| Settlements | ✓ | ✓ | — | — | — | — |
| Live Incidents | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Create New Top Up | ✓ | — | — | — | ✓ | — |
# PawaPay Dashboard
Source: https://docs.pawapay.io/dashboard/introduction
From the PawaPay Dashboard you can manage all your payments and operations in one single place. Learn more:
See how much funds you have across all the different markets you operate in.
Make sure the funds you have are as they are supposed to be.
To provide the best support to your customers, all payments in any market and with any MMO are available in one place along with their live status.
Global search finds any payment from the last three months quickly, regardless of where it was made.
Manage users with sophisticated access controls and high level of security and auditability.
Get everything set up for your integration quickly and easily.
# Audit logs
Source: https://docs.pawapay.io/dashboard/other/audit_log
Key actions taken in our dashboard are tracked and visible in the Audit Log.
You can find here any changes to user permissions, new users invited, who signed into the portal and when, etc.
This functionality is available to users with the Administrator role. Read more about [User roles](/dashboard/user_management/user_roles).
We strongly recommend using only personalised accounts on our platform - all accounts should be accessible to a single person.
This allows for compliant auditability of all actions taken on our dashboard.
## Searching and filtering
Please choose the time period you are interested in and press "Apply".
The following information will be available for each action:
| Column | Description |
| - | - |
| Time | The date and time when this action took place. |
| Action | The type of action that took place. Find below the glossary of all actions tracked. |
| Performed by | The user who took this action. |
| Description | Further details about the action. *For example, for the ROLE CHANGED action, it would include which role was assigned to the user.* |
| Affected user | The user who was affected by this action. *As an example, for whom the Role was changed.* |
You can always further filter down the results from the search bar or by pressing the filter icon in a column header.
As an example, you could search for the name of the user, which would filter the results to show all actions taken by or affecting this particular person during the selected time period.
## Glossary of actions
| Action | Description |
| - | - |
| User login | User has signed into the dashboard. |
| User invited | A new user has been invited into the dashboard. |
| User invite re-sent | A user has been resent the invitation email. |
| User registered | A user has activated their account in the dashboard. |
| User status change | User has been disabled or re-enabled to access the dashboard. |
| Password changed | A user has changed their password. |
| Role changed | A user's role has changed. Read more about [User roles](/dashboard/user_management/user_roles). |
| Country access changed | The countries that the user is able to see in the dashboard have changed. Read more about country-level access restrictions from [User roles](/dashboard/user_management/user_roles). |
| Mobile money API token generated | A new API token has been generated for your account. |
| Revoke API token | An API token has been revoked. |
| Batch disbursement | A new batch disbursement has been initiated. |
| Domain added | A new email domain has been added to the email domain whitelist. |
| Invite cancelled | A pending invitation has been deleted. |
| Enqueued payout cancellation | An enqueued payout has been cancelled. Read more about [cancelling enqueued payouts](/dashboard/transactions/cancel_enqueued). |
| Merchant admin invited | The first administrator of your account has been created at sign-up and sent a confirmation email. |
| Merchant admin confirmed | The first administrator of your account has confirmed their email address. |
# Batch disbursements
Source: https://docs.pawapay.io/dashboard/other/batch_disbursements
You can use our Batch disbursements page to initiate multiple disbursements at the same time directly from our dashboard.
This functionality is available to users with the Batch Disbursements role. Read more about [User roles](/dashboard/user_management/user_roles).
## Creating your batch disbursements file
You can download a template directly from the dashboard to create a new batch disbursement file.
The template contains the following columns to be filled out by you.
| Column | Description |
| - | - |
| Country | The country in which the recipient of the disbursement resides in. |
| Telco | The Mobile Money Operator (MMO) that the recipient's mobile money wallet is registered with. |
| Currency | The currency for this disbursement. |
| MSISDN | The phone number of the recipient of this disbursement. |
| Amount | The amount you wish to disburse. |
| Description | The description of this disbursement. |
The MSISDN must:
* not start with a + sign
* start with the country code
The description must:
* Be at least 4 characters
* Not be longer than 22 characters
* Contain only letters, numbers and spaces
* Not contain any special characters
You can find all the supported Country, Telco, and Currency combinations from [our API docs](/v2/docs/providers).
### Example batch disbursement file
A maximum of 1000 disbursements can be added to a single batch disbursement file.
## Uploading your batch disbursement file
After filling in your file, you can proceed to upload it into our system for validation. Your file will be validated for formatting errors and to ensure your account has configuration in place to disburse to the specified MMOs.
The results of validation will be shown directly in the dashboard.
| Result | Description |
| - | - |
| Ready | The transaction is ready to be disbursed. |
| Failed | Transaction is not valid. You can hover on the "Failed" result to see the failure reason. |
## Fixing validation failures
When encountering failures in your batch disbursement file, you can press the "Download Results" button to retrieve your batch disbursement file together with the failure reasons.
You can fix the errors in this file and attempt to upload again.
You can re-upload the file downloaded by pressing the "Download Results" button to attempt again.
You do not need to remove the failure reasons from this file before re-uploading.
## Initiating the batch disbursements
If every transaction is "Ready", you can press the "Schedule payment" button to initiate the disbursements.
# API tokens
Source: https://docs.pawapay.io/dashboard/other/system_conf/api_tokens
The PawaPay Merchant API can only be used with an authentication token. You can read more about how this token can be used to access our API from our [API docs](/using_the_api#authentication).
This functionality is available to users with the Technical Administration role. Read more about [User roles](/dashboard/user_management/user_roles).
## Generating an API token
Press the "Generate token" button to generate an API token. You can have at most 10 active API tokens at a time.
You can use the "Copy to clipboard" button to easily copy the API token.
**Store your token safely as PawaPay does not store your token after generating it for security reasons.**
**You need to configure your [Callback URLs](/using_the_api#callback-urls) to be able to generate API tokens.**
## Active API tokens
Your active API tokens are listed together with the information about who generated them, when they were generated, and the Token ID.
Note that the Token ID is not usable as the token itself but is only used for identifying a specific token when contacting our support team.
## Revoking tokens
If you need to revoke an API token, you can press the "Revoke" button which is found under the "Actions" column.
**Revoking an API token will immediately stop all payments that use the given token to authenticate API calls!**
## Signed requests
You can enable PawaPay to only accept signed requests for financial calls. You will need to provide us with the public key of the key pair you are signing your requests with.
### Accepted algorithms
We accept 4 kinds of encryption algorithms:
* [RSASSA-PSS Using SHA-512](https://datatracker.ietf.org/doc/html/rfc9421#name-rsassa-pss-using-sha-512)
* [RSASSA-PKCS1-v1\_5 Using SHA-256](https://datatracker.ietf.org/doc/html/rfc9421#name-rsassa-pkcs1-v1_5-using-sha)
* [ECDSA Using Curve P-256 DSS and SHA-256](https://datatracker.ietf.org/doc/html/rfc9421#name-ecdsa-using-curve-p-256-dss)
* [ECDSA Using Curve P-384 DSS and SHA-384](https://datatracker.ietf.org/doc/html/rfc9421#name-ecdsa-using-curve-p-384-dss)
### Adding a public key
You can add your public key by navigating to the API Security tab and pressing on “Add public key”.
You will then be presented a window where you can:
1. Name your public key
2. Enter the public key itself
3. Choose whether you want to immediately start accepting only signed requests (shown when you add your first key)
After successfully adding a key you will be able to view and remove it.
You can also switch this feature on and off by using the toggle. Switching the feature off **will not** remove any existing keys.
## Signed callbacks
Enable this feature to make PawaPay sign all callbacks.
You can then verify those signatures when receiving the callback to ensure they have not been tampered with and are coming from PawaPay.
Read more about signed callbacks in our [API docs](/using_the_api#signatures-in-callbacks).
# Callback URLs
Source: https://docs.pawapay.io/dashboard/other/system_conf/callback_urls
Our platform uses callbacks to notify you of the final statuses of payments. You can read more about how callbacks are used on our platform from [our API docs](/using_the_api#callback-urls).
This functionality is available to users with the Technical Administration role. Read more about [User roles](/dashboard/user_management/user_roles).
## Specifying the callback URLs
Please enter the URL at which you wish to receive notifications about the [final statuses](/dashboard/transactions/transaction_statuses) of payments. You can use the same URL for all types.
Ensure you enter the correct URL.
If you are using a firewall, ensure that you have whitelisted our IPs.
The IPs can be found in our [API docs](/using_the_api#callback-urls).
If you do not plan to use our API (e.g., you are only planning to use only our [Batch disbursements](/dashboard/other/batch_disbursements) or will be polling for final status, please indicate that by checking the below checkbox.
# How to log in
Source: https://docs.pawapay.io/dashboard/signing_in
The PawaPay Dashboard supports two methods of user authentication: traditional password-based authentication and Google-based authentication.
If you opt for Google-based authentication, your organization must already utilize [Google Workspace](https://workspace.google.com/), or a comparable service where Google manages email addresses. This feature facilitates a seamless login process, allowing users to access the Dashboard using their existing Google accounts.
We recommend forcing 2-step verification on your Google Workspace account for increased security.
## Logging into the dashboard
#### Authentication with username and password
Go to our [log in page](https://dashboard.pawapay.io/), enter your email address, and press "Continue with email".
Enter your password and press "Continue with email".
Our dashboard follows [OWASP ASVS Level 3](https://owasp.org/www-pdf-archive/OWASPApplicationSecurityVerificationStandard3.0.pdf) password policy.
If [two-factor authentication](/dashboard/user_management/security) is enabled for your company, you'll be prompted for a verification code (OTP) sent to your email, unless you've selected the "Remember me" checkbox, which remembers your device for 30 days (up to 3 devices). Enter the code and press "Continue" to be redirected to the PawaPay Dashboard.
#### Authentication with Google
Go to our [log in page](https://dashboard.pawapay.io/) and select "Continue with Google". There's no need to enter your email here.
If you've chosen Google as your authentication method, the Dashboard will present you an option to authenticate via signing in with your Google account.
When using multiple Google accounts (i.e., business and personal), make sure you choose the account that was used to invite you to our Dashboard.
### Resetting your password
Enter your email on our [log in page](https://dashboard.pawapay.io/) and press "Continue with email". Then select "Forgot your password?".
Enter your email and press "Reset my password".
You will receive an email with a link to reset your password. Press on the "Reset password" button.
You will be forwarded to the password reset page where you can enter your new password. When done, press on "Change my password".
You will then see a confirmation. Press "Login to the dashboard" to go to our [log in page](https://dashboard.pawapay.io/) and log in with your new password.
## Activating your account
When invited to our dashboard, you will receive an invitation email with an activation link. Activate your account within 30 days.
Pressing "Activate your account" will take you to the account activation page where you are asked to read and accept our Terms and Conditions and Privacy Policy, and choose your authentication method - Google or username and password - if both are available to you.
The password meter at the bottom of the "Password" input helps you choose a strong password to secure your account.
Pressing "Activate" will take you to our Dashboard [log in page](https://dashboard.pawapay.io/).
# Platform and MMO status
Source: https://docs.pawapay.io/dashboard/status
Any relevant information about incidents and maintenance that affect your merchant account will be displayed on the top right corner of the PawaPay Dashboard.
Pressing the button will expand your view and provide more details on the ongoing incidents and upcoming maintenance.
Pressing "Read more" will direct you to the incident page with more information about it.
The "Status Page" button will direct you to our [status page](https://status.pawapay.io/).
On the [status page](https://status.pawapay.io/) you can subscribe to receive updates about incidents through different channels like e-mail, Slack and more.
## Availability statuses of MMOs
| Status | Description |
| - | - |
| Operational | Service is operating as expected. |
| Degraded performance | Degraded performance for some transactions. |
| Partial outage | Degraded performance with intermittent unavailability. |
| Major outage | Service is unavailable and no transactions are accepted for processing. |
| Under maintenance | There is an active maintenance window. Degraded performance or intermittent unavailability might occur. |
Our support and payment operation teams work 24/7 and are aware of, and acting on, any statuses shown on our status page.
We provide regular updates on the progress of resolving all issues that arise.
## More about MMO availability
To provide the best experience for your customers, we suggest implementing our Correspondent Availability Status endpoints.
This allows you to inform your customers proactively about MMO downtime.
Read more from our [API docs](/v1/api-reference/toolkit/availability).
# Contacting our support
Source: https://docs.pawapay.io/dashboard/support
Our dedicated support team operates around the clock, 24/7, to assist you with any queries you may have regarding your transactions or the functionalities of our platform.
You can contact our Support team by signing into the PawaPay Dashboard and selecting the "Payment Support" link from the "Help" menu in the top-right of the Dashboard.
Note: "Payment Support" is only available on Production, not the Sandbox.
Then select the type of query you have.
#### Payments
From the *Payments* form, select an option from the *Category* dropdown — these include:
| Category | Description |
| - | - |
| Missing deposit enquiry | To enquire about deposits which do not reflect in the PawaPay system, but are deemed as successful by the MMO.
*For example, you have a MMO financial ID of a successful deposit transaction that you are unable to locate in the PawaPay Dashboard.* |
| Deposit status enquiry | To enquire about deposits which reflect in the PawaPay system, but you are seeking for further clarification. |
| Payout status enquiry | To enquire about payouts which reflect in the PawaPay system, but you are seeking for further clarification. |
| Other | In case you have any other questions about transactions on your PawaPay wallets. |
##### What to include in a *Payments* request?
To help us resolve your request quickly and effectively, please include as many relevant details as possible. This will allow us to identify, investigate, and address the issue more efficiently.
| Detail | Description |
| - | - |
| Amount | The amount in country specific currency. |
| MMO transaction ID | This is the ID that the customer would see for this transaction.
*Particularly important for reconciling payments that do not appear in our dashboard or are not in a final state. Read more about [Transaction statuses](/dashboard/transactions/transaction_statuses) .* |
| Phone number (MSISDN) | The phone number of the customer.
*For mobile money, this is the equivalent of a bank account or card number.* |
| Date and time | The date and time the problem occurred.
*Particularly important for reconciling payments that do not appear in our dashboard.* |
| Transaction ID | The ID of the deposit, payout or refund as seen in our dashboard. |
| Additional details & attachments | Any additional details about your request, plus any relevant attachments (optional). |
#### Other enquiries
From here you can enquire about:
| Category | Description |
| - | - |
| Financial statement enquiry | Use this if you have any questions about the data displayed on your financial statements. |
| General Finance & Treasury enquiry | In case you have any other questions related to Treasury or Finance. |
| Raise a complaint | Please use this form to submit a formal complaint regarding our service, product or an issue at hand that requires further investigation. We take all feedback seriously and aim to resolve issues promptly. |
| Other | In case you have any other general questions. |
# Topping up your wallets
Source: https://docs.pawapay.io/dashboard/topping_up
How to pre-fund your PawaPay wallet so you can make payouts.
To avoid any delays, please make sure that all funds for payouts/disbursements are sent from an account owned by your company. If you need to send funds through a contractual partner, please go through the [adding a Liquidity Partner](#adding-a-liquidity-partner) journey described below on this page.
In order to make payouts via our API, your wallet must have sufficient funds available. Where a payout request is made and the wallet has insufficient balance to complete the transaction, the transaction will fail.
Accordingly, if you intend to use payouts you will be required to pre-fund (top up) your PawaPay wallet.
In order to streamline the process you are encouraged to follow the instructions below whenever a top up is being made to your PawaPay wallet.
## Send your funds to the correct bank account
When sending funds to top up your wallet in a particular country, you are requested to send those funds to the bank account for the respective market which will be presented to you in the PawaPay Dashboard. Remember to always include your account specific ID to each payment you make. This will also be presented to you in the payment instruction we give.
To use Top ups in the Dashboard your merchant account must have either payouts or remittances enabled.
You must also have either the "Top ups" role, or be an "Administrator".
If you don’t have access, please contact your account administrator or the PawaPay team.
## Creating a top up
You can start a new top up at any time from the **Top ups** page in the Dashboard. Click **Create new top up** and the Dashboard will walk you through five short steps. If your account has both payouts and remittances enabled, an extra step asks what the funds are for. A top up from another PawaPay wallet skips the currency and payment steps.
Select one or more wallets that will receive the money. Each wallet is shown with its country and currency — for example, *Uganda UGX* or *Ghana GHS*. You can tick as many as you need, or use **Select all** to pick them all at once.
If you don't see a wallet you expect to be there, your PawaPay account may not be set up for that market yet.
Pick the method you will use to send us the money. See [Choosing a funding method](#choosing-a-funding-method) below for a plain-English explanation of each option.
Pick the currency you will send. The Dashboard only shows currencies that are valid for the wallets and funding method you chose in the previous steps. If something isn't listed, it isn't currently supported for your selection.
For each wallet you picked in step 1, type in how much you want credited. The Dashboard keeps a running **Total amount to top up** on the right so you can see the full amount you need to transfer.
This is the final step. The Dashboard will show you:
* The **bank account** you should send the funds to.
* Your **unique merchant code** — a short reference you must include with the transfer (see [Your unique merchant code](#your-unique-merchant-code) below).
* An **expected timeline**. We always try to process top ups as soon as possible but the timelines vary country by country.
Once you've made the transfer from your bank, come back to this screen and upload your Proof of Payment — a screenshot or PDF of the SWIFT confirmation or bank receipt. Then hit **Top up**.
## Choosing a funding method
Step 2 of the wizard asks how you want to fund your balance. You will see one or more of the following options — the ones available depend on your account setup:
Send funds from a bank account owned by your company via a standard bank/SWIFT transfer. This is the most common option. The Dashboard will show you the PawaPay bank account you should send the money to at step 5.
Move money you already hold in one PawaPay wallet into another PawaPay wallet. You'll be asked to pick which PawaPay wallet the money should come from.
A Liquidity Partner is a third party — for example, an aggregator or on-the-ground partner — that funds your wallet on your behalf. Before you can use one, they need to be approved by our Treasury team. See [Adding a Liquidity Partner](#adding-a-liquidity-partner) below.
## Your unique merchant code
Every top up comes with a **unique merchant code**, which is a short reference shown on the payment instructions screen.
You **must** include this code in the reference / description / narration field of your bank transfer. Without it, our Treasury team cannot match your payment to your account, which will delay your top up.
The Dashboard has a **Copy** button next to the code so you can paste it straight into your bank's transfer form. The same code needs to be reused across your top ups as it identifies your account, not the individual payment. Some bank accounts need their own reference instead. In that case the Dashboard shows that reference, so always use the code shown.
## Adding a Liquidity Partner
If someone other than your own company is going to send us the funds — for example, a local aggregator or a contractual partner — then they need to be onboarded as a **Liquidity Partner** first. This is a one-time setup per partner.
### How to add one
From the **Top ups** page, click **Add liquidity partner**, then **Add liquidity partner** again on the **Liquidity Partners** page (or use the **Add new Liquidity Partner** link inside step 2 of the top up wizard).
You'll be asked for:
* **Liquidity Partner name** — the legal name of the partner.
* **Your relationship with the partner** — a short description of how you work together.
* **Line of business** — what the partner does.
* **Country coverage** — the countries they're licensed / allowed to operate in.
Attach:
* **Licenses** — relevant financial or operating licenses the partner holds.
* **KYC documents** — incorporation documents, plus identification for shareholders with 5% or more stake and for directors.
You can upload multiple files in PDF, JPEG or PNG format, up to 5 MB each.
Click **Submit**. Your request goes to our Treasury team for review. You can track it on the **Liquidity Partners** page — the status will show as **In review** until it's been approved or rejected. You may be contacted by our team via email for further clarification.
Once a Liquidity Partner is **Approved**, they will appear as a selectable option under *Liquidity Partner* on step 2 of the top up wizard.
## After you submit a top up
Once you've uploaded your Proof of Payment, your top up lands on the **Top ups** page with a status you can track:
| Status | What it means |
| :- | :- |
| **Pending** | We've received your submission but haven't started processing it yet. |
| **In progress** | Our Treasury team is reviewing the payment and crediting your wallet. |
| **Completed** | The funds are in your wallet and ready to use for payouts. |
| **Failed** | Something went wrong with processing — our team will be in touch. |
| **Rejected** | The Proof of Payment couldn't be matched to a payment we've received. |
| **Cancelled** | You (or our team) cancelled the top up before it was processed. |
You can open any top up to see its details per wallet, add a comment for our Treasury team, or download the Proof of Payment you uploaded. While a top up is **Pending**, you can also cancel it.
Need to do another top up that looks just like a past one? Use **Repeat top up** on any completed top up. The Dashboard will pre-fill the wizard with the same wallets, funding method and currency so you can adjust the amounts and submit.
# Callback resend
Source: https://docs.pawapay.io/dashboard/transactions/callback_resend
When a payment is completed, we communicate the final status to your systems by sending a callback.
However, delivery of these callbacks may be unsuccessful due to network instabilities, downtime in the receiving system, or similar issues.
In such scenarios, you have the option to resend the callback through our dashboard.
Callbacks can only be resent for payments initiated within the last 90 days.
Older payments remain visible in the dashboard and their status can still be checked — only the callback resend is no longer available for them.
The option to resend a callback is only available when our initial attempt to deliver it has been unsuccessful. In the event of a problematic transaction, you can navigate to the respective payment details page. Here, you'll find the option to resend the callback.
To resend the callback, click on the 'Resend callback' button.
The process should conclude within a few seconds, and a confirmation message will appear upon its successful completion.
There is also an option to initiate this process via an API call.
Read more about this from our API documentation for [deposits](/v1/api-reference/deposits/resend-deposit-callback), [payouts](/v1/api-reference/payouts/resend-payout-callback) and [refunds](/v1/api-reference/refunds/resend-refund-callback).
# Cancel enqueued payout
Source: https://docs.pawapay.io/dashboard/transactions/cancel_enqueued
During certain incidents involving Mobile Money Operators (MMOs), PawaPay may temporarily suspend the processing of payouts.
We understand the importance of maintaining transaction flow.
Therefore, we continue to accept your payout requests during these periods.
These requests are assigned a status of **Enqueued**.
This status indicates that your payout request has been received and is in line for processing as soon as the network stabilizes.
However, you have the option to cancel these transactions by navigating to the respective payment details page and initiating a cancellation from there.
To cancel an Enqueued transaction, click on the "Cancel transaction" button.
The process should conclude within a few seconds, and a confirmation message will appear upon its successful completion.
There is also an option to initiate this process via an API call.
Read more regarding this functionality from our [API documentation](/v1/api-reference/payouts/cancel-enqueued-payout).
# Deposit details
Source: https://docs.pawapay.io/dashboard/transactions/deposit_details
You can see the details of a deposit by using either our [Global search](/dashboard/global_search) or [Deposits](/dashboard/transactions/deposits) search functionality and clicking on the specific payment.
You can find the glossary of the data found on the deposit details from the [Deposits](/dashboard/transactions/deposits) documentation.
All possible statuses of transactions are documented in [Transaction statuses](/dashboard/transactions/transaction_statuses).
## Successful deposit
On successful deposits, the Provider Transaction ID is available. This ID is also available to the customer on their SMS receipt or their transaction history. When contacted by a customer, their transaction can be found using this ID.
## Failed deposit
We constantly monitor and improve the failure reasons to be as precise and accurate as possible.\
Some Mobile Money Operators (MMO) do not provide specific failure reasons for failed transactions.
# Deposits
Source: https://docs.pawapay.io/dashboard/transactions/deposits
You can find all deposits made on our platform on the Deposits page.
This functionality is available to users with the Transactions role. Read more about [User roles](/dashboard/user_management/user_roles).
## Search for deposits
Please choose the time period you are interested in by clicking on the time period selector. You can also use the quick links next to the selector to quickly see transactions made in the last hour, today, yesterday, or the current week.
If you are interested in transactions of a specific Mobile Money Operator (MMO), you can filter by picking them from the Providers dropdown.
Pressing "Apply filters" will show you the results.
A maximum of 10,000 records will be shown. If your search results in more than that, the oldest 10,000 will be visible in your results.
## Glossary of results
| Column | Description |
| - | - |
| Creation date | The date and time at which this deposit was initiated. Time is in the UTC time zone. |
| Phone number | The phone number of the party being collected from. |
| Provider Transaction ID | The ID for this transaction specified by the MMO.
**Please note!** This ID is visible to your customers in their SMS receipts or Mobile Money applications transaction history. |
| Amount | The amount on the original deposit request. |
| Currency | The currency in which this transaction took place. |
| Flow subtype | The type of deposit. |
| Status | The current status of this transaction. Read more about statuses from [Transaction statuses](/dashboard/transactions/transaction_statuses). |
| Country | The country in which this transaction took place. |
| Provider | The Mobile Money Operator who carried out the transaction. |
| Reason | The reason for this payment specified by you as the `statementDescription` in our API. Read more about our deposits in our API from our [API docs](/v1/api-reference/deposits/request-deposit). |
# Payout details
Source: https://docs.pawapay.io/dashboard/transactions/payout_details
To see payout details, use either our [Global search](/dashboard/global_search) or [Payouts](/dashboard/transactions/payouts) search functionality, and click on the specific payment.
You can find the glossary of the data found on the payout details from the [Payouts](/dashboard/transactions/payouts) documentation.
All possible statuses of transactions are documented in [Transaction statuses](/dashboard/transactions/transaction_statuses).
## Successful payout
On successful payouts, the Provider Transaction ID is available. This ID is also available to the customer on their SMS receipt or their transaction history. When contacted by a customer, their transaction can be found using this ID.
## Failed payout
We constantly monitor and improve the failure reasons to be as precise and accurate as possible.\
Some Mobile Money Operators do not provide specific failure reasons for failed transactions.
### Proof of payment (PoP)
View and download your proof of payment / payout receipt on the transaction details page once your transaction is complete.
# Payouts
Source: https://docs.pawapay.io/dashboard/transactions/payouts
You can find all of your payouts made on our platform on the Payouts page. Both payouts and refunds are available to search.
This functionality is available to users with the Transactions role. Read more about [User roles](/dashboard/user_management/user_roles).
## Search for payouts
Please choose the time period you are interested in by clicking on the time period selector. You can also use the quick links next to the selector to quickly see transactions made in the last hour, today, yesterday, or the current week.
If you are interested in transactions of a specific Mobile Money Operator (MMO), you can filter by picking them from the Providers dropdown.
Pressing "Apply filters" will show you the results.
A maximum of 10,000 records will be shown. If your search results in more than that, the oldest 10,000 will be visible in your results.
## Glossary of results
| Column | Description |
| - | - |
| Creation date | The date and time at which this payout was initiated. Time is in UTC timezone. |
| Phone number | The phone number of the party being paid. |
| Financial Transaction ID | The ID for this transaction specified by the MMO.
**Please note!** This ID is visible to your customers in their SMS receipts or Mobile Money apps transaction history. |
| Amount | The amount paid out. |
| Currency | The currency in which this transaction took place. |
| Flow Subtype | The type of disbursement, for example a payout or a refund. |
| Status | The current status of this transaction. Read more about statuses from [Transaction statuses](/dashboard/transactions/transaction_statuses). |
| Country | The country in which this transaction took place. |
| Provider | The Mobile Money Operator who carried out the transaction. |
| Reason | The reason for this transaction specified by you as the `statementDescription` in our API. Read more about our payouts in our API from our [API docs](/v1/api-reference/payouts/request-payout). |
# Transaction statuses
Source: https://docs.pawapay.io/dashboard/transactions/transaction_statuses
All transactions on our platform can be in one of five possible statuses.
| Status | Description |
| - | - |
| `Completed` | Transaction has been **successfully** processed. This is a **final** status and will not change. |
| `Failed` | Transaction **failed** to process and no funds have been moved. This is a **final** status and will not change. |
| `In reconciliation` | There was a problem processing this transaction, and it has been moved to our reconciliation engine to determine its final status. The transaction will be moved to its final status as soon as it’s been reconciled. |
| `Processing` | The transaction is currently being processed. |
| `Enqueued` | The transaction has been **accepted** by us for processing but is currently **enqueued** and not being shared with the MMO. Transactions with this status can be manually cancelled on the transaction details page. |
Transaction statuses `Completed` and `Failed` are final statuses and will not change.
# Inviting users
Source: https://docs.pawapay.io/dashboard/user_management/inviting_users
Our dashboard uses an invite-based user management system. All users invited will get an email with instructions on how to activate their account.
This functionality is available to users with the User Management role. Read more about [User roles](/dashboard/user_management/user_roles).
## Inviting a new user
All users are invited to the dashboard based on their email address.
You can limit this user to only see transactions and wallets from specific countries by specifying them in the "Limited to" dropdown.
Choose the appropriate roles for the user based on what tasks they need to perform in the dashboard.
Read more about country limits and our [User roles](/dashboard/user_management/user_roles).
You can read more about the activation flow for the user from [Signing into the PawaPay Dashboard](/dashboard/signing_in).
You can only invite other users using roles that are active on your user.
# Managing users
Source: https://docs.pawapay.io/dashboard/user_management/managing_users
Users on the dashboard can be managed by pressing the tab "Manage users" on the User Management page.
This functionality is available to users with the User Management role. Read more about [User roles](/dashboard/user_management/user_roles).
You can navigate between active, pending, and disabled users by pressing on the respective tab.
## Active users
On the **Active** tab, you can see the currently active users on the dashboard.
Your roles might affect which users you are able to see and manage.
### User Information
The following information is available for each user:
| Column | Description |
| - | - |
| Name | The first and last name of the user. |
| E-mail | The email address of the user they were invited into the dashboard with. |
| Role | The roles the user has. |
| Geo Roles | The countries this user is limited to. |
| Actions | The actions that can be done for this user. |
If the user has more than three roles, you can see all of them by hovering on the "+N more" label.
### Changing users' country limitations
You can change the countries a user has access to by opening the "Geo Roles" dropdown and checking the countries they need.
Changes to country limitations take effect immediately.
### Changing users' roles
User roles can be changed by pressing the pencil icon in the user's "Actions" column.
You can then make the changes needed and press "Save changes."
### Disabling users
You can disable a user by pressing the icon in the user's "Actions" column.
You will be asked to confirm this action by pressing "Yes."
You can find the disabled user in the "Disabled" tab.
## Pending users
Users who have yet to activate their account from their invite email are found in the **Pending** tab.
You can change their role and country limitations just like for active users.
In case their invite has expired or is lost, you can resend the invitation email with a new activation link by pressing the "Resend invitation" button.
You will be asked to confirm this action by pressing "Yes."
You may also choose to delete the invitation by clicking on this icon under the Actions column:
## Disabled users
Users who have been disabled are found in the **Disabled** tab.
You can still change country limitations and roles for disabled users.
You can enable users by pressing on the icon in the user's "Actions" column.
You will be asked to confirm this action by pressing "Yes."
You can find the enabled user in the "Active" tab.
# Security
Source: https://docs.pawapay.io/dashboard/user_management/security
The 'Security' tab, located under the 'User Management' page, is visible only to users with both the User Management and Technical Administration [roles](/dashboard/user_management/user_roles). The Administrator role includes both.
## Two-factor authentication
You can enable two-factor authentication for users who do not use Google as a sign-in option. When enabled, users will receive a six-digit numerical code via email with each sign-in attempt.
## Google authentication
If you are using Google Workspace for your emails, you can require all users to authenticate with Google.
This means that any active user who currently signs in with a password will no longer be able to use their credentials and will be directed to authenticate with Google.
Additionally, all new users will only have the option to confirm their registration through Google authentication.
Any active user who currently signs in with a password will no longer be able to use their credentials and will be directed to authenticate with Google!
# User roles
Source: https://docs.pawapay.io/dashboard/user_management/user_roles
To ensure functionality and data is visible to only the people you need, we provide user roles and country-based limitations for users.
## Country based limitations
Users' access in our dashboard can be limited to specific countries.
For example, limiting a user's access to Ghana only as their tasks involve providing support for Ghana customers.
## User roles
Roles limit users to specific functionality in the dashboard. For example, the User Management role could be enabled only for the team leads of your various support teams. They are then able to invite new users independently. They are only able to invite users with all or some of the roles they have for their user.
| Role | Description |
| - | - |
| **Administrator** | Full access to all functionality in the dashboard.
Includes all other roles automatically. |
| **Batch disbursements** | Can use the [Batch disbursements](/dashboard/other/batch_disbursements) functionality of our platform.
Includes Finances and Transactions roles automatically. |
| **Finances** | Has access to [Finances](/dashboard/finances/wallets) to see the financial data in [Wallets](/dashboard/finances/wallets), [Statements](/dashboard/finances/statements), etc.
Includes Transactions role automatically. |
| **Transactions** | Has access to [Transactions](/dashboard/transactions/deposits) to see deposits, payouts, and refunds in the countries they are limited to. |
| **User Management** | Able to invite new and manage existing users with the same or fewer roles that they have.
**Please note!**
This role can only be combined with other roles. |
| **Technical Administration** | Able to see [Developers page](/dashboard/other/system_conf/callback_urls) to configure technical aspects of the platform. |
| **Settlements** | Access to Settlements to see any settlement related information and set preferences. Only available with Administrator role, or combined with Batch disbursements or Finances role. |
| **Top ups** | Can request, view and manage [Top ups](/dashboard/topping_up). Only available with Administrator role, or combined with Batch disbursements or Finances role. |
Dependent roles get assigned automatically. For example, adding the Finances role automatically adds the Transactions role to the user.
# Check checkout status
Source: https://docs.pawapay.io/v2/api-reference/checkouts/check-checkout-status
openapi_v2 get /v2/checkouts/{checkoutId}
Get the current status of a checkout based on its `checkoutId`.
The response returns the overall checkout state together with the latest payment attempt, when available, and the full history of attempts within that checkout.
# Checkout callback
Source: https://docs.pawapay.io/v2/api-reference/checkouts/checkout-callback
If you have configured callbacks, PawaPay will `POST` a callback to your configured callback URL when a checkout reaches a final status (`COMPLETED`, `FAILED`, `EXPIRED` or `CANCELLED`).
Read more about [callbacks](/v2/docs/what_to_know#callbacks).
The callback payload is the checkout in its final state, including the latest payment attempt (`deposit`) and the full history of attempts (`depositsHistory`).
If you have not configured callbacks, you can always call the [Check checkout status](/v2/api-reference/checkouts/check-checkout-status) endpoint to get the latest state of a specific checkout.
A checkout can contain multiple payment attempts, but at most one can complete successfully — PawaPay manages the retries for you.
If you prefer, each attempt also behaves like a standard [deposit](/v2/docs/deposits), so you can rely on the [deposit callback](/v2/api-reference/deposits/deposit-callback) for it.
### Headers
Following headers are only included if you have enabled signed callbacks.
Read more about how to implement [signed callbacks](/v2/docs/signatures#signatures-in-callbacks).
SHA256 or SHA-512 hash of the request body.
Signature of the request according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Signature input according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Timestamp when the signature was created. This is a custom field and is not part of [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Expected signature algorithm of the response according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Expected digest algorithm of the response according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
### Body
# Expire checkout
Source: https://docs.pawapay.io/v2/api-reference/checkouts/expire-checkout
openapi_v2 post /v2/checkouts/{checkoutId}/expire
Expires a checkout that should no longer be used.
* A successful response returns the checkout in its current state together with `expiredAt`.
* When the expiration is applied, the checkout status will be `EXPIRED`.
* After expiry, the customer can no longer make new payment attempts on that checkout.
* If the checkout does not exist, the endpoint returns `NOT_FOUND`.
# Initiate checkout
Source: https://docs.pawapay.io/v2/api-reference/checkouts/initiate-checkout
openapi_v2 post /v2/checkouts
The checkout endpoint creates a checkout and returns a `redirectUrl` for a hosted payment page that you forward the customer to.
Follow the step-by-step guide on how to create, use, and track checkouts.
* This API call is **idempotent**, which means it is safe to submit a request with the same `checkoutId` multiple times.
* Duplicate requests with the same `checkoutId` will be ignored with the `DUPLICATE_IGNORED` status in the response.
* Always check the `status` in the response for each request. When it is `REJECTED`, the `failureReason` describes what went wrong through its `failureCode` and `failureMessage`.
* A checkout is the single reference for the whole payment, including any retries the customer makes.
* One checkout can contain multiple payment attempts, but at most one can complete successfully. The customer can retry within the same checkout — PawaPay manages the attempts for you.
* If the checkout is accepted, redirect the customer to the returned `redirectUrl`.
* Store the `checkoutCode` from the response. It is appended to your `returnUrl` as a query parameter when the customer is returned to your site, so you can identify which checkout they are returning from.
Each request can get one of the statuses on initiation:
| Status | Description |
| - | - |
| `ACCEPTED` | The checkout has been **accepted** and the hosted payment page has been created. |
| `REJECTED` | The checkout has been **rejected**. See `failureReason` for details. |
| `DUPLICATE_IGNORED` | The checkout has been ignored as a duplicate of an already accepted checkout. Deduplication is based on `checkoutId`. |
### How to find out the final status of this checkout?
As the PawaPay Merchant API is an [asynchronous API](/v2/docs/what_to_know#callbacks), a checkout has its own lifecycle. You can find out the final status of an `ACCEPTED` checkout by either:
If you have configured callbacks, the callback with the final status of the checkout will be delivered to your callback URL.
Or poll the `Check Checkout Status` endpoint for the latest checkout and payment status.
If the checkout should no longer be used, you can [expire it](/v2/api-reference/checkouts/expire-checkout) explicitly.
Headers related to [signatures](/v2/docs/signatures) must only be included if you have enabled “Only accept signed requests”.
Read more about it from the [PawaPay Dashboard documentation](/dashboard/other/system_conf/api_tokens#signed-requests).
# Check deposit status
Source: https://docs.pawapay.io/v2/api-reference/deposits/check-deposit-status
openapi_v2 get /v2/deposits/{depositId}
Get the current status of a deposit based on its `depositId`.
# Deposit callback
Source: https://docs.pawapay.io/v2/api-reference/deposits/deposit-callback
If you have configured callbacks, PawaPay will `POST` a callback to your configured callback URL for each deposit when it reaches a final status.
Read more about [callbacks](/v2/docs/what_to_know#callbacks).
If you have not configured callbacks, you can always call the [Check Deposit Status](/v2/api-reference/deposits/check-deposit-status) endpoint to get the latest status of a specific deposit.
### Headers
Following headers are only included if you have enabled signed callbacks.
Read more about how to implement [signed callbacks](/v2/docs/signatures#signatures-in-callbacks).
SHA256 or SHA-512 hash of the request body.
Signature of the request according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Signature input according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Timestamp when the signature was created. This is a custom field and is not part of [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Expected signature algorithm of the response according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Expected digest algorithm of the response according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
### Body
# Initiate deposit
Source: https://docs.pawapay.io/v2/api-reference/deposits/initiate-deposit
v2/api-reference/openapi_v2.yaml POST /v2/deposits
The deposit endpoint allows you to request a payment from a customer.
Follow the step-by-step guide on how to build a payment flow using deposits.
* This API call is **idempotent**, which means it is safe to submit a request with the same `depositId` multiple times.
* Duplicate requests with the same `depositId` will be ignored with the `DUPLICATE_IGNORED` status in the response.
* Since the request can be rejected, you must check the `status` in the response for each submitted request. The `failureReason` in the response will contain information about the reason of the rejection.
Each request can get one of the statuses on initiation:
| Status | Callback | Description |
| - | - | - |
| `ACCEPTED` | Yes | The deposit request has been **accepted** by PawaPay for processing. |
| `REJECTED` | No | The deposit request has been **rejected**. See `failureReason` for details. |
| `DUPLICATE_IGNORED` | No | The deposit request has been ignored as a duplicate of an already accepted deposit request. Duplication logic relies upon `depositId`. |
### How to find out the final status of this deposit?
As the PawaPay Merchant API is an [asynchronous API](/v2/docs/what_to_know#callbacks), you can find out the final status of the `ACCEPTED` deposit by either:
If you have configured callbacks, the callback with the final status of the deposit will be delivered to your callback URL.
Or poll the `Check Deposit Status` endpoint.
Headers related to [signatures](/v2/docs/signatures) must only be included if you have enabled “Only accept signed requests”.
Read more about it from the [PawaPay Dashboard documentation](/dashboard/other/system_conf/api_tokens#signed-requests).
# Resend deposit callback
Source: https://docs.pawapay.io/v2/api-reference/deposits/resend-deposit-callback
openapi_v2 post /v2/deposits/resend-callback/{depositId}
Resends the callback for a deposit to your configured callback URL. The deposit must have reached a final state.
Callbacks can only be resent for deposits initiated within the last 90 days.
The status and details of older deposits remain available through the [check deposit status](/v2/api-reference/deposits/check-deposit-status) endpoint.
Please ensure your implementation of callback handling follows our [guidelines](/v2/docs/what_to_know#callbacks).
# Check statement status
Source: https://docs.pawapay.io/v2/api-reference/finances/check-statement-status
openapi_v2 get /v2/statements/{statementId}
Get the current status of the statement based on its `statementId`
# Statements
Source: https://docs.pawapay.io/v2/api-reference/finances/initiate-statement
openapi_v2 post /v2/statements
The statements endpoint allows you to generate a financial statement for a wallet. The statement can be downloaded as a CSV file and optionally as a compressed GZIP file.
Learn more about statements from our [Dashboard docs](/dashboard/finances/statements).
* Each wallet is defined by its **country** and **currency**.
* If you have provider specific wallet, also specify the **provider** for the wallet.
* The **date range** must not exceed **31 days** per statement.
* The statement can be downloaded from the `downloadUrl`.
* The `downloadUrl` **expires after 1 day**.
* Statement data for the current day may be up to **20 minutes behind** the latest transactions.
* Since the request can be rejected, you must check the `status` code in the response for each submitted request. The `failureReason` in the response will contain information about the reason of the rejection.
Each request can get one of the statuses on initiation:
| Status | Callback | Description |
| - | - | - |
| `ACCEPTED` | Yes | The statement has been **accepted** by PawaPay for generation. |
| `REJECTED` | No | The statement has been **rejected**. See `failureReason` for details. |
### How to know which wallets i have?
You can find all your wallets with their balances from the [Wallet Balances](/v2/api-reference/finances/wallet-balances) endpoint.
You can iterate over all of them and use the `country` and `currency` from the response as parameters for your request.
If the response includes a not empty `provider`, it should be passed into the request as well to generate a provider specific wallets statement.
### How to find out the final status of this statement?
As the PawaPay Merchant API is an [asynchronous API](/v2/docs/what_to_know#callbacks), you can find out the final status of the `ACCEPTED` statement by either:
When a statement has been generated, PawaPay will post a callback to the callbackUrl provided in the statement creation request.
Or poll the `Check Statement Status` endpoint.
# Statement callback
Source: https://docs.pawapay.io/v2/api-reference/finances/statement-callback
When a statement has been generated, PawaPay will `POST` a callback to the `callbackUrl` provided in the statement creation request.
Read more about [handling callbacks](/v2/docs/what_to_know#callbacks).
You can also call our [Check Statement Status](/v2/api-reference/finances/check-statement-status) endpoint to get the latest status of a specific statement request.
### Body
# Wallet balances
Source: https://docs.pawapay.io/v2/api-reference/finances/wallet-balances
openapi_v2 get /v2/wallet-balances
Allows you to get the list of wallets and their balances configured for your PawaPay account.
# Deposit via Payment Page
Source: https://docs.pawapay.io/v2/api-reference/payment-page/deposit-via-payment-page
openapi_v2 post /v2/paymentpage
Initiates a [deposit](/v2/api-reference/deposits/initiate-deposit) using the PawaPay Payment Page.
Follow the step-by-step guide on how to build a payment flow using the payment page.
* The request will return a `redirectURL`.
* The customer should be forwarded to this URL to start the payment process.
* Once the customer completes the payment process on the Payment Page, they will be forwarded to the `returnUrl`.
* The original `depositId` will be passed as a query parameter.
Your page on the `returnUrl` has two options to determine the status of the payment. You can do that by either:
If you have configured callbacks, just wait for it.
Or poll the Check Deposit Status endpoint.
The customer experience of the Payment Page can be changed through the following parameters during initiation.
| Parameter | Behaviour if specified | Behaviour if not specified |
| - | - | - |
| `amountDetails` | The specified amount will be used for the payment. Customer is not able to change it. | The customer is able to enter the amount they wish to pay. For example, when depositing into their eWallet. |
| `phoneNumber` | The payment will be initiated from the specified mobile money wallet. Customer is not able to change it. | The customer can specify the phone number of the mobile money wallet they wish to pay from. |
| `country` | The customer can pay only using a mobile money wallet from the specified country. | The customer can pay with a mobile money wallet from any country that is configured on your PawaPay account. |
| `reason` | The reason will be shown to the customer on the Payment Page | The customer will not be shown what they are paying for. |
# Cancel enqueued payout
Source: https://docs.pawapay.io/v2/api-reference/payouts/cancel-enqueued-payout
openapi_v2 post /v2/payouts/fail-enqueued/{payoutId}
Cancels a payout that is currently in the `ENQUEUED` status.
* The payment will be moved to `FAILED` status.
* The payment will have the failure code `MANUALLY_CANCELLED`.
* If you have configured callbacks, a callback will be sent.
# Check payout status
Source: https://docs.pawapay.io/v2/api-reference/payouts/check-payout-status
openapi_v2 get /v2/payouts/{payoutId}
Get the current status of the payout based on its `payoutId`.
# Initiate bulk payouts
Source: https://docs.pawapay.io/v2/api-reference/payouts/initiate-bulk-payout
v2/api-reference/openapi_v2.yaml POST /v2/payouts/bulk
The bulk payout endpoint allows you to pay multiple customers with a single request.
Follow the step-by-step guide on how to build a payment flow using payouts.
When sending them in bulk, the same logic applies for each individual payout.
* Bulk requests with more than 20 payout transactions may be rejected by PawaPay.
* This API call is idempotent, which means it is safe to submit a request with the same `payoutId` multiple times.
* Duplicate requests with the same `payoutId` will be ignored with the `DUPLICATE_IGNORED` status in the response.
* Since the request can be rejected, you must check the `status` code in the response for each submitted request. The `failureReason` in the response will contain information about the reason of the rejection.
* Callback will be delivered separately for each payout in the bulk request as the payouts are resolved to their final status.
Each individual request can get one of the statuses on initiation:
| Status | Callback | Description |
| - | - | - |
| `ACCEPTED` | Yes | The payout has been **accepted** by PawaPay for processing. |
| `REJECTED` | No | The payout has been **rejected**. See `failureReason` for details. |
| `DUPLICATE_IGNORED` | No | The payout has been **ignored** as a duplicate of an already accepted payout. Duplication logic relies upon `payoutId`. |
### How do i find out the final statuses of these payouts?
As the PawaPay Merchant API is an [asynchronous API](/v2/docs/what_to_know#callbacks), you can find out the final status of the `ACCEPTED` payout by either:
If you have configured callbacks, the callback with the final status of the payout will be delivered to your callback URL.
Or poll the `Check Payout Status` endpoint.
Headers related to [signatures](/v2/docs/signatures) must only be included if you have enabled “Only accept signed requests”.
Read more about it from the [PawaPay Dashboard documentation](/dashboard/other/system_conf/api_tokens#signed-requests).
# Initiate payout
Source: https://docs.pawapay.io/v2/api-reference/payouts/initiate-payout
openapi_v2 post /v2/payouts
The payout endpoint allows you to pay a customer.
Follow the step-by-step guide on how to build a payment flow using payouts.
* This API call is idempotent, which means it is safe to submit a request with the same `payoutId` multiple times.
* Duplicate requests with the same `payoutId` will be ignored with the `DUPLICATE_IGNORED` status in the response.
* Since the request can be rejected, you must check the `status` code in the response for each submitted request. The `failureReason` in the response will contain information about the reason of the rejection.
Each request can get one of the statuses on initiation:
| Status | Callback | Description |
| - | - | - |
| `ACCEPTED` | Yes | The payout has been **accepted** by PawaPay for processing. |
| `REJECTED` | No | The payout has been **rejected**. See `failureReason` for details. |
| `DUPLICATE_IGNORED` | No | The payout has been **ignored** as a duplicate of an already accepted payout. Duplication logic relies upon `payoutId`. |
### How to find out the final status of this payout?
As the PawaPay Merchant API is an [asynchronous API](/v2/docs/what_to_know#callbacks), you can find out the final status of the `ACCEPTED` payout by either:
If you have configured callbacks, the callback with the final status of the payout will be delivered to your callback URL.
Or poll the `Check Payout Status` endpoint.
Headers related to [signatures](/v2/docs/signatures) must only be included if you have enabled “Only accept signed requests”.
Read more about it from the [PawaPay Dashboard documentation](/dashboard/other/system_conf/api_tokens#signed-requests).
# Payout callback
Source: https://docs.pawapay.io/v2/api-reference/payouts/payout-callback
If you have configured callbacks, PawaPay will `POST` a callback to your configured callback URL for each payout when it reaches a final status.
Read more about [callbacks](/v2/docs/what_to_know#callbacks).
If you have not configured callbacks, you can always call our [Check Payout Status](/v2/api-reference/payouts/check-payout-status) endpoint to get the latest status of a specific payout request.
### Headers
Following headers are only included if you have enabled signed callbacks.
Read more about how to implement [signed callbacks](/v2/docs/signatures#signatures-in-callbacks).
SHA256 or SHA-512 hash of the request body.
Signature of the request according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Signature input according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Timestamp when signature was created. This is a custom field and is not part of [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Expected signature algorithm of the response according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Expected digest algorithm of the response according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
### Body
# Resend payout callback
Source: https://docs.pawapay.io/v2/api-reference/payouts/resend-payout-callback
openapi_v2 post /v2/payouts/resend-callback/{payoutId}
Resends the callback for a payout to your configured callback URL. The payout must have reached a final state.
Please ensure your implementation of callback handling follows the guidelines in [Handling callbacks](#section/Implementation/Handling-callbacks).
Resends the callback for a payout to your configured callback URL. The payout must have reached a final state.
Callbacks can only be resent for payouts initiated within the last 90 days.
The status and details of older payouts remain available through the [check payout status](/v2/api-reference/payouts/check-payout-status) endpoint.
Please ensure your implementation of callback handling follows our [guidelines](/v2/docs/what_to_know#callbacks).
# Cancel enqueued refund
Source: https://docs.pawapay.io/v2/api-reference/refunds/cancel-enqueued-refund
openapi_v2 post /v2/refunds/fail-enqueued/{refundId}
Cancels a refund that is currently in the `ENQUEUED` status.
* The refund will be moved to `FAILED` status.
* The refund will have the failure code `MANUALLY_CANCELLED`.
* If you have configured callbacks, a callback will be sent.
# Check refund status
Source: https://docs.pawapay.io/v2/api-reference/refunds/check-refund-status
openapi_v2 get /v2/refunds/{refundId}
Get the current status of the refund based on its `refundId`.
# Initiate refund
Source: https://docs.pawapay.io/v2/api-reference/refunds/initiate-refund
openapi_v2 post /v2/refunds
The refund endpoint allows you to pay a customer.
Follow the step-by-step guide on how to handle refunds.
* This API call is idempotent, which means it is safe to submit a request with the same `refundId` multiple times.
* Duplicate requests with the same `refundId` will be ignored with the `DUPLICATE_IGNORED` status in the response.
* Since the request can be rejected, you must check the `status` code in the response for each submitted request. The `failureReason` in the response will contain information about the reason of the rejection.
Each request can get one of the statuses on initiation:
| Status | Callback | Description |
| - | - | - |
| `ACCEPTED` | Yes | The refund has been **accepted** by PawaPay for processing. |
| `REJECTED` | No | The refund has been **rejected**. See `rejectionReason` for details. |
| `DUPLICATE_IGNORED` | No | The refund has been **ignored** as a duplicate of an already accepted refund. Duplication logic relies upon `refundId`. |
### How to find out the final status of this refund?
As the PawaPay Merchant API is an [asynchronous API](/implementation#asynchronous-api), you can find out the final status of the `ACCEPTED` refund by either:
If you have configured callbacks, the callback with the final status of the refund will be delivered to your callback URL.
Or poll the `Check Rrefund Status` endpoint.
Headers related to [signatures](/v2/docs/signatures) must only be included if you have enabled “Only accept signed requests”.
Read more about it from the [PawaPay Dashboard documentation](/dashboard/other/system_conf/api_tokens#signed-requests).
# Refund callback
Source: https://docs.pawapay.io/v2/api-reference/refunds/refund-callback
If you have configured callbacks, PawaPay will `POST` a callback to your configured callback URL for each refund when it reaches a final status.
Read more about [callbacks](/v2/docs/what_to_know#callbacks).
If you have not configured callbacks, you can always call our [Check Refund Status](/v2/api-reference/refunds/check-refund-status) endpoint to get the latest status of a specific refund request.
### Headers
Following headers are only included if you have enabled signed callbacks.
Read more about how to implement [signed callbacks](/v2/docs/signatures#signatures-in-callbacks).
SHA256 or SHA-512 hash of the request body.
Signature of the request according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Signature input according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Timestamp when signature was created. This is a custom field and is not part of [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Expected signature algorithm of the response according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Expected digest algorithm of the response according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
### Body
# Resend refund callback
Source: https://docs.pawapay.io/v2/api-reference/refunds/resend-refund-callback
openapi_v2 post /v2/refunds/resend-callback/{refundId}
Resends the callback for a refund to your configured callback URL. The refund must have reached a final state.
Callbacks can only be resent for refunds initiated within the last 90 days.
The status and details of older refunds remain available through the [check refund status](/v2/api-reference/refunds/check-refund-status) endpoint.
Please ensure your implementation of callback handling follows our [guidelines](/v2/docs/what_to_know#callbacks).
# Cancel enqueued remittance
Source: https://docs.pawapay.io/v2/api-reference/remittances/cancel-enqueued-remittance
openapi_v2 post /v2/remittances/fail-enqueued/{remittanceId}
Cancels a remittance that is currently in the `ENQUEUED` status.
* The payment will be moved to `FAILED` status.
* The payment will have the failure code `MANUALLY_CANCELLED`.
* If you have configured callbacks, a callback will be sent.
# Check remittance status
Source: https://docs.pawapay.io/v2/api-reference/remittances/check-remittance-status
openapi_v2 get /v2/remittances/{remittanceId}
Get the current status of the remittance based on its `remittanceId`.
# Initiate remittance
Source: https://docs.pawapay.io/v2/api-reference/remittances/initiate-remittance
openapi_v2 post /v2/remittances
The remittance endpoint allows you to pay a customer.
* This API call is idempotent, which means it is safe to submit a request with the same `remittanceId` multiple times.
* Duplicate requests with the same `remittanceId` will be ignored with the `DUPLICATE_IGNORED` status in the response.
* Since the request can be rejected, you must check the `status` code in the response for each submitted request. The `failureReason` in the response will contain information about the reason of the rejection.
Each request can get one of the statuses on initiation:
| Status | Callback | Description |
| - | - | - |
| `ACCEPTED` | Yes | The remittance has been **accepted** by PawaPay for processing. |
| `REJECTED` | No | The remittance has been **rejected**. See `failureReason` for details. |
| `DUPLICATE_IGNORED` | No | The remittance has been **ignored** as a duplicate of an already accepted remittance. Duplication logic relies upon `remittanceId`. |
### How to find out the final status of this remittance?
As the PawaPay Merchant API is an [asynchronous API](/v2/docs/what_to_know#callbacks), you can find out the final status of the `ACCEPTED` remittance by either:
If you have configured callbacks, the callback with the final status of the remittance will be delivered to your callback URL.
Or poll the `Check Remittance Status` endpoint.
Headers related to [signatures](/v2/docs/signatures) must only be included if you have enabled “Only accept signed requests”.
Read more about it from the [PawaPay Dashboard documentation](/dashboard/other/system_conf/api_tokens#signed-requests).
# Remittance callback
Source: https://docs.pawapay.io/v2/api-reference/remittances/remittance-callback
If you have configured callbacks, PawaPay will `POST` a callback to your configured callback URL for each remittance when it reaches a final status.
Read more about [callbacks](/v2/docs/what_to_know#callbacks).
If you have not configured callbacks, you can always call our [Check Remittance Status](/v2/api-reference/remittances/check-remittance-status) endpoint to get the latest status of a specific remittance request.
### Headers
Following headers are only included if you have enabled signed callbacks.
Read more about how to implement [signed callbacks](/v2/docs/signatures#signatures-in-callbacks).
SHA256 or SHA-512 hash of the request body.
Signature of the request according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Signature input according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Timestamp when signature was created. This is a custom field and is not part of [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Expected signature algorithm of the response according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
Expected digest algorithm of the response according to [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
### Body
# Resend remittance callback
Source: https://docs.pawapay.io/v2/api-reference/remittances/resend-remittance-callback
openapi_v2 post /v2/remittances/resend-callback/{remittanceId}
Resends the callback for a remittance to your configured callback URL. The remittance must have reached a final state.
Callbacks can only be resent for remittances initiated within the last 90 days.
The status and details of older remittances remain available through the [check remittance status](/v2/api-reference/remittances/check-remittance-status) endpoint.
Please ensure your implementation of callback handling follows our [guidelines](/v2/docs/what_to_know#callbacks).
# Active Configuration
Source: https://docs.pawapay.io/v2/api-reference/toolkit/active-configuration
openapi_v2 get /v2/active-conf
Allows you to query your configuration including the countries and providers that have been configured for your account.
You can also retrieve supporting information about your configuration:
* Your company name on your PawaPay account.
* The currencies supported by the providers.
* The deposit authorisation types of each provider.
* Minimum and maximum amounts for deposits, payouts and refunds.
* The name of the company that is shown to the customer on the PIN prompt and/or SMS receipt.
* Instructions to show to the customer for different mechanism of payment authorisation
* And more...
# Provider Availability
Source: https://docs.pawapay.io/v2/api-reference/toolkit/availability
openapi_v2 get /v2/availability
Providers sometimes have instability in processing payments.
Read more about [MMO Stability](/v2/docs/what_to_know#mmo-stability).
Our 24/7 payment operations team constantly monitors all the providers available on our platform for any degraded performance or downtime.
From this endpoint you can get the current processing status of each provider.
This endpoint returns all providers grouped by country.
Each operation type (`DEPOSIT`, `PAYOUT`, `REFUND`) has a separate status.
Following statuses are possible for different operations:
| Status | Description |
| - | - |
| `OPERATIONAL` | The provider is operational and open for processing requests. |
| `DELAYED` | The provider is having problems processing payouts. **Payouts are being enqueued** and will be processed once the problems have been resolved. |
| `CLOSED` | The provider is having problems and all requests are being **rejected** by PawaPay. |
# Predict Provider
Source: https://docs.pawapay.io/v2/api-reference/toolkit/predict-provider
openapi_v2 post /v2/predict-provider
Predicts the provider for the specified phone number.
Average misprediction rate when using this endpoint is **0.12%**.
Benin has a relatively high level of number portability and therefore has an average misprediction rate of 6%.
# Public Keys
Source: https://docs.pawapay.io/v2/api-reference/toolkit/public-keys
openapi_v2 get /v2/public-key/http
Provides the public keys that are used to sign callbacks sent by PawaPay.
Read more about how to use [signatures](/using_the_api#signatures).
# Provider brand guidelines
Source: https://docs.pawapay.io/v2/docs/brand_guidelines
When integrating with PawaPay, refer to the provided brand guidelines in this section for different providers.
These documents outline the proper use of logos, color schemes, and typography, ensuring consistent and compliant representation of each providers' brand identity.
| Provider | Link to guidelines |
| - | - |
| MTN | [https://momodeveloper.mtn.com/api-documentation/brand-guidelines](https://momodeveloper.mtn.com/api-documentation/brand-guidelines) |
| Airtel | [https://developers.airtel.africa/brand-guidelines](https://developers.airtel.africa/brand-guidelines) |
| Orange | [https://boosted.orange.com/docs/4.4/about/brand/](https://boosted.orange.com/docs/4.4/about/brand/) |
| MPESA | [https://www.safaricom.co.ke/about/brand-toolkit](https://www.safaricom.co.ke/about/brand-toolkit) |
# Checkouts
Source: https://docs.pawapay.io/v2/docs/checkouts
Checkouts let you forward your customer to a hosted payment page on PawaPay to pay you, and collect the payment through a single checkout reference.
A checkout is the single reference for the whole payment. If a payment fails, the customer can retry within the same checkout — PawaPay keeps track of every attempt for you, so you only ever work with the one checkout. At most one attempt can finish successfully.
If you haven't already, check out the following information to set you up for success with this guide.
Understand the mobile money considerations that affect redirect-based payment flows.
Configure API tokens and callbacks before building your checkout integration.
## How is this different from the Payment Page?
Both the [Payment Page](/v2/docs/payment_page) and checkouts give your customers a hosted, redirect-based payment experience, so a single API call and a redirect is all you need to get paid.
The difference is the level of control and tracking:
| | Payment Page | Checkout |
| - | - | - |
| Hosted, redirect-based experience | Yes | Yes |
| Customer can retry a failed payment | No — you create a new payment page with a new `depositId` for each attempt | Yes — the customer retries within the same checkout, and PawaPay tracks the attempts for you |
| Lifecycle | Follows the [deposit](/v2/docs/deposits) | The checkout has **its own lifecycle** with its own statuses and callbacks |
| Explicit expiry control | Fixed 15-minute session | Configurable `expiresAfter` and an [expire](/v2/api-reference/checkouts/expire-checkout) endpoint |
Use a checkout when you want a single reference for the whole payment — including any retries the customer makes — and control over when the checkout expires.
## What does it look like
When you create a checkout you receive a `redirectUrl`. You forward your customer to that hosted payment page to pay you. It is optimised for mobile money:
* A user experience for your customers that is optimised for mobile money.
* Responsive design that works on desktop and mobile.
* Low code integration supporting all countries and providers.
* The fields shown adapt to how you configure the checkout (see [Different ways to use a checkout](#different-ways-to-use-a-checkout)).
With just a single API call and a redirect, the customer can pay you.
The customer reviews the payment and chooses or confirms the wallet to pay from.
For most providers the customer then authorises the payment with a PIN prompt on their phone. The page shows the exact steps for the provider being used.
Some providers authorise automatically and skip this step. For providers that support it, if the PIN prompt expires it can be re-sent (revived), and the page shows the updated instructions — the customer does not need to start over.
## Creating and using a checkout
Send a request to the [initiate checkout](/v2/api-reference/checkouts/initiate-checkout) endpoint.
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/checkouts
{
"checkoutId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"returnUrl": "https://merchant.example.com/checkout-result",
"returnMethod": "INSTANT",
"defaultLanguage": "en",
"countries": [
"ZMB"
],
"expiresAfter": 60,
"amounts": [
{
"country": "ZMB",
"currency": "ZMW",
"amount": "100"
}
],
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "260973024434",
"provider": "MTN_MOMO_ZMB",
"allowCustomerToOverride": true
}
},
"clientReferenceId": "ORDER-123",
"reason": {
"en": "GOODS PURCHASE"
},
"metadata": [
{
"orderId": "ORDER-123"
},
{
"customerId": "CUST-456",
"isPII": true
}
]
}
```
`checkoutId` must be a UUIDv4 that you generate and store **before** making the request.
This is so that you always have a reference to the checkout you are expecting, even if you do not receive a response from us due to network errors.
This allows you to always **reconcile all payments** between your system and PawaPay.
The minimal valid request only requires `checkoutId` and `returnUrl`.
If you provide `payer`, then `payer.accountDetails.allowCustomerToOverride` is required.
When accepted, the response returns the `redirectUrl` for the hosted payment page that you forward the customer to.
You will receive a response immediately.
```json theme={null}
{
"checkoutId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"status": "ACCEPTED",
"redirectUrl": "https://checkout.sandbox.pawapay.io/7mVk1x8UbTamQ64xGR",
"created": "2026-03-27T10:30:00Z",
"expiresAt": "2026-03-27T11:30:00Z",
"checkoutCode": "7mVk1x8UbTamQ64xGR"
}
```
The response includes the `checkoutCode` — a unique code for the checkout that also forms the path of the `redirectUrl`.
Store it alongside your `checkoutId`: when the customer is sent back to your `returnUrl`, the `checkoutCode` is appended as a query parameter.
It is how you find the checkout — and the `checkoutId` you stored for it — that the customer is returning from.
You can tailor the customer experience through the optional parameters below.
| Parameter | Behaviour if specified | Behaviour if not specified |
| - | - | - |
| `amounts` | The customer can pay only one of the fixed amount, currency and country combinations provided. | The customer can enter the amount they wish to pay. |
| `countries` | The customer can pay only with a mobile money wallet from the specified countries. | The customer can pay with a wallet from any country configured on your account. |
| `payer` | The payer details are pre-filled in the page. Set `allowCustomerToOverride` to control whether the customer can change them. | The customer enters their wallet details. |
| `reason` | The localized reason is shown to the customer on the page. Each value must be 4–22 characters, letters, numbers and spaces only. | The customer is not shown what they are paying for. |
| `defaultLanguage` | The page opens in the specified language. Supported languages are `en` and `fr`. | The page uses its default language. |
| `expiresAfter` | The number of minutes after creation when the checkout expires. Must be between 3 and 60 minutes. | The checkout expires after the default of 15 minutes. |
| `returnMethod` | Controls **how** the customer is returned to your `returnUrl` once the payment is finished or cancelled (see below). | Defaults to returning the customer automatically once the payment is finished. |
The customer is always sent back to your `returnUrl` once the payment is finished or cancelled. The `returnMethod` controls **how** that happens:
| `returnMethod` | Behaviour |
| - | - |
| `INSTANT` | The customer is redirected back immediately, with no extra screen. |
| `COUNTDOWN` | A short countdown is shown on the checkout page, then the customer is redirected automatically. |
| `CUSTOMER_ACTION` | The checkout page waits until the customer presses **Return to merchant**. |
Redirect the customer to `redirectUrl`.
This hosted payment page is where the customer selects or confirms the wallet details and authorises the payment.
Once the checkout flow finishes, the customer will be redirected to the `returnUrl` you provided, according to the `returnMethod`, with the `checkoutCode` appended as a query parameter.
A checkout has its own lifecycle. You can find out its status by either **waiting for a callback** or **polling** the [check checkout status](/v2/api-reference/checkouts/check-checkout-status) endpoint.
When you have configured callbacks, PawaPay will `POST` a [checkout callback](/v2/api-reference/checkouts/checkout-callback) to your callback URL when the checkout reaches a final status.
You can also poll the status at any time. The status response gives you the overall checkout status together with the current payment attempt and the full history of attempts.
```json theme={null}
GET https://api.sandbox.pawapay.io/v2/checkouts/afb57b93-7849-49aa-babb-4c3ccbfe3d79
{
"status": "FOUND",
"data": {
"checkoutId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"status": "COMPLETED",
"redirectUrl": "https://checkout.sandbox.pawapay.io/7mVk1x8UbTamQ64xGR",
"returnUrl": "https://merchant.example.com/checkout-result",
"returnMethod": "INSTANT",
"defaultLanguage": "en",
"countries": [
"ZMB"
],
"expiresAfter": 60,
"amounts": [
{
"country": "ZMB",
"currency": "ZMW",
"amount": "100"
}
],
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "260973024434",
"provider": "MTN_MOMO_ZMB",
"allowCustomerToOverride": true
}
},
"clientReferenceId": "ORDER-123",
"created": "2026-03-27T10:30:00Z",
"providerTransactionId": "PROVIDER-TXN-123",
"depositStatus": "COMPLETED",
"deposit": {
"depositId": "eac4d2f3-cf36-4a24-a9eb-7014c630f8f0",
"status": "COMPLETED",
"created": "2026-03-27T10:35:00Z",
"providerTransactionId": "PROVIDER-TXN-123",
"amount": "100",
"currency": "ZMW",
"country": "ZMB",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "260973024434",
"provider": "MTN_MOMO_ZMB"
}
},
"customerMessage": "To ACME company"
},
"depositsHistory": [
{
"depositId": "eac4d2f3-cf36-4a24-a9eb-7014c630f8f0",
"status": "COMPLETED",
"created": "2026-03-27T10:35:00Z",
"providerTransactionId": "PROVIDER-TXN-123",
"amount": "100",
"currency": "ZMW",
"country": "ZMB",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "260973024434",
"provider": "MTN_MOMO_ZMB"
}
},
"customerMessage": "To ACME company"
}
],
"metadata": {
"orderId": "ORDER-123",
"customerId": "CUST-456"
},
"reason": {
"en": "GOODS PURCHASE"
},
"checkoutCode": "7mVk1x8UbTamQ64xGR"
}
}
```
`deposit` contains the latest payment attempt when one exists.
`depositsHistory` contains the full history of attempts for the checkout. PawaPay records these for you — you normally only need the checkout `status`.
If the checkout should no longer be used, call the [expire checkout](/v2/api-reference/checkouts/expire-checkout) endpoint.
After a checkout has expired, the customer can no longer make new payment attempts on it.
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/checkouts/afb57b93-7849-49aa-babb-4c3ccbfe3d79/expire
{
"checkoutId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"status": "EXPIRED",
"expiredAt": "2026-03-27T10:45:00Z",
"reason": "MANUAL_EXPIRY",
"expiredBy": "API"
}
```
## Different ways to use a checkout
The same checkout endpoint supports a range of payment experiences, controlled by what you provide for `amounts`, `countries` and `payer`. Pick the combination that matches your use case.
The two parameters that shape the experience the most are:
* **`amounts`** — fix the amount(s) the customer can pay, or leave it out to let them enter their own.
* **`payer`** — pre-fill the mobile money wallet, and decide with `allowCustomerToOverride` whether the customer may change it.
**Use this when the customer decides how much to pay** — for example topping up an eWallet, funding an account, or an open-ended donation. You don't know the amount up front, and the customer may pay from any wallet they like.
The minimal checkout only requires `checkoutId` and `returnUrl`.
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/checkouts
{
"checkoutId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"returnUrl": "https://merchant.example.com/checkout-result",
"reason": {
"en": "Wallet top up"
}
}
```
The customer chooses the country, enters the mobile money wallet to pay from and enters the amount.
**Use this when you know the price but not the wallet** — the classic e-commerce order total. You set the amount and the customer simply picks which wallet to pay from. Provide a single entry in `amounts` and the amount is locked.
```json {6-12} theme={null}
POST https://api.sandbox.pawapay.io/v2/checkouts
{
"checkoutId": "1b9c4f80-2c1e-4d0a-9b3a-0c7d1e2f3a4b",
"returnUrl": "https://merchant.example.com/checkout-result",
"amounts": [
{
"country": "ZMB",
"currency": "ZMW",
"amount": "100"
}
],
"reason": {
"en": "Order 123"
}
}
```
Each entry in `amounts` requires `country`, `currency` and `amount` together. The `currency` must be one that is supported in the specified `country`.
If the customer can pay from more than one country, you must include an `amounts` entry for **every country and currency combination** you want to support — the page shows the entry matching the country the customer is paying from. A country can also support more than one currency: for example, in the DRC (`COD`) both the Congolese franc (`CDF`) and US dollar (`USD`) are supported, so to offer USD you must include a `USD` entry as well.
[Providers](/v2/docs/providers) have transaction limits. You can use the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint to validate that the amount is within the transaction limits before creating the checkout.
The page shows the fixed amount and only asks the customer for the wallet to pay from.
**Use this for predefined price points** — tipping, donation tiers, or packages and bundles where the customer chooses from a set of amounts you define. Provide several entries in `amounts`.
```json {6-23} theme={null}
POST https://api.sandbox.pawapay.io/v2/checkouts
{
"checkoutId": "2c8d5e91-3d2f-4e1b-8c4b-1d8e2f3a4b5c",
"returnUrl": "https://merchant.example.com/checkout-result",
"amounts": [
{
"country": "ZMB",
"currency": "ZMW",
"amount": "100"
},
{
"country": "ZMB",
"currency": "ZMW",
"amount": "250"
},
{
"country": "ZMB",
"currency": "ZMW",
"amount": "500"
}
],
"reason": {
"en": "Choose contribution"
}
}
```
The page shows your amounts as options for the customer to choose from. The other details depend on the rest of your configuration.
**Use this when you only collect payments in specific markets** — restricting the countries keeps the page focused on where you actually operate, and stops customers from selecting a country you can't settle in. By default, the customer can pay with a mobile money wallet from any country configured on your PawaPay account; use `countries` to narrow it.
Fix a single country so the customer can only pay from there:
```json {6-8} theme={null}
POST https://api.sandbox.pawapay.io/v2/checkouts
{
"checkoutId": "3d9e6fa2-4e3a-4f2c-9d5c-2e9f3a4b5c6d",
"returnUrl": "https://merchant.example.com/checkout-result",
"countries": [
"ZMB"
]
}
```
Or offer a subset of countries and let the customer choose between them:
```json {6-10} theme={null}
POST https://api.sandbox.pawapay.io/v2/checkouts
{
"checkoutId": "4ea07fb3-5f4b-4a3d-8e6d-3fa04b5c6d7e",
"returnUrl": "https://merchant.example.com/checkout-result",
"countries": [
"ZMB",
"UGA",
"TZA"
]
}
```
If you fix `amounts` as well, make sure the `country` of each amount option falls within the allowed `countries`. The page shows the amount option matching the country the customer is paying from.
The page only offers the countries you allow.
**Use this for returning customers** — pre-filling the wallet they used before removes a step at checkout and speeds up conversion, while `allowCustomerToOverride: true` still lets them pay from a different wallet if they want. Provide `payer` with `allowCustomerToOverride` set to `true`.
```json {6-15} theme={null}
POST https://api.sandbox.pawapay.io/v2/checkouts
{
"checkoutId": "5fb18ac4-6a5c-4b4e-9f7e-4ab15c6d7e8f",
"returnUrl": "https://merchant.example.com/checkout-result",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "260973024434",
"provider": "MTN_MOMO_ZMB",
"allowCustomerToOverride": true
}
}
}
```
When collecting the phone number, we strongly recommend using our [predict provider](/v2/api-reference/toolkit/predict-provider) endpoint.
It validates the phone number and returns it, together with the `provider`, in a format that works for the checkout.
The page opens with the wallet pre-filled, and the customer can still change it.
**Use this when the customer must pay from a specific wallet** — for example loan repayments, payouts of winnings, or any KYC-verified flow where the paying wallet has to match the one on file. This guards against fraud and keeps you compliant. Provide `payer` with `allowCustomerToOverride` set to `false` and the wallet is locked.
```json {12} theme={null}
POST https://api.sandbox.pawapay.io/v2/checkouts
{
"checkoutId": "6ac29bd5-7b6d-4c5f-8a8f-5bc26d7e8f90",
"returnUrl": "https://merchant.example.com/checkout-result",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "260973024434",
"provider": "MTN_MOMO_ZMB",
"allowCustomerToOverride": false
}
}
}
```
When you provide `payer`, `allowCustomerToOverride` is always required.
The page shows the wallet as fixed and the customer cannot change it.
**Use this for the fastest possible checkout** — when both the customer and the amount are known, such as a subscription renewal, an in-app purchase or a repeat order. Combining a fixed `amounts` entry with a fixed `payer` leaves the customer nothing to enter but the authorisation.
```json {6-21} theme={null}
POST https://api.sandbox.pawapay.io/v2/checkouts
{
"checkoutId": "7bd3ace6-8c7e-4d6a-9b90-6cd37e8f9012",
"returnUrl": "https://merchant.example.com/checkout-result",
"amounts": [
{
"country": "ZMB",
"currency": "ZMW",
"amount": "100"
}
],
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "260973024434",
"provider": "MTN_MOMO_ZMB",
"allowCustomerToOverride": false
}
},
"reason": {
"en": "Order 123"
}
}
```
This is the most streamlined experience: the amount and wallet are both fixed, so the customer just confirms and authorises the payment.
The table below summarises how the customer experience changes based on what you provide.
| You provide | Customer experience |
| - | - |
| Neither `amounts` nor `payer` | Customer chooses the country, wallet and amount. |
| `amounts` with one entry | Amount is fixed; customer chooses the wallet. |
| `amounts` with several entries | Customer picks one of the offered amounts. |
| `countries` | Customer can only pay from the listed countries. |
| `payer` with `allowCustomerToOverride: true` | Wallet is pre-filled but the customer can change it. |
| `payer` with `allowCustomerToOverride: false` | Wallet is fixed and cannot be changed. |
| Fixed `amounts` + fixed `payer` | Customer only authorises the payment. |
## The checkout lifecycle
A checkout moves through its own statuses, independently of the individual payment attempts inside it.
Just like [deposits](/v2/docs/deposits), a checkout has two separate kinds of status:
* The **initiation status** returned in the [initiate checkout](/v2/api-reference/checkouts/initiate-checkout) response — `ACCEPTED`, `REJECTED` or `DUPLICATE_IGNORED` — tells you whether the request was accepted for processing.
* The **lifecycle status** below — returned by [check checkout status](/v2/api-reference/checkouts/check-checkout-status) and in callbacks — tracks the checkout itself. Once accepted, a checkout enters its lifecycle at `WAITING_PAYMENT`.
| Checkout `status` | Meaning |
| - | - |
| `WAITING_PAYMENT` | The checkout and its hosted payment page have been created and are waiting for the customer to pay. |
| `PROCESSING` | A payment attempt is in progress and the checkout is still being processed. |
| `COMPLETED` | The checkout has completed successfully. **Final status.** |
| `FAILED` | The checkout has failed. Inspect `deposit` or `depositsHistory` for the latest attempt result. **Final status.** |
| `EXPIRED` | The checkout has expired and can no longer be used. **Final status.** |
| `CANCELLED` | The customer cancelled the payment on the hosted payment page. **Final status.** |
The aggregate `depositStatus` and each entry in `depositsHistory` reflect the payment attempts, which follow the standard payment lifecycle (`ACCEPTED`, `PROCESSING`, `COMPLETED`, `FAILED`).
A single checkout can contain multiple payment attempts, but at most one can complete successfully.
When a customer retries a failed attempt on the hosted payment page, the new attempt is added to `depositsHistory` automatically — you don't need to create or manage anything.
## Cancellation and returning to your site
The customer is sent back to your `returnUrl` with the `checkoutCode` appended as a query parameter:
```
https://merchant.example.com/checkout-result?checkoutCode=7mVk1x8UbTamQ64xGR
```
Use the `checkoutCode` to look up the checkout — and the `checkoutId` you stored for it — in your system, so you know which checkout the customer is returning from.
The customer is returned in these cases:
* **The payment succeeds** — the customer is returned automatically, following the `returnMethod` you set: immediately (`INSTANT`), after a short countdown (`COUNTDOWN`), or when they press **Return to merchant** (`CUSTOMER_ACTION`).
* **The customer cancels** — they can press **Cancel payment** on the hosted payment page. They are asked to confirm (*"Yes, cancel"* / *"No, stay"*), and once they confirm they are redirected back to your `returnUrl`. The checkout status becomes `CANCELLED`.
After a **failed** attempt, the customer stays on the hosted payment page and can retry within the same checkout — they are **not** redirected automatically. Only a successful payment triggers the automatic return. At any time the customer can press **Return to merchant** to go back.
A **failed or expired** checkout does **not** redirect the customer back to your site automatically. If the customer keeps failing or abandons the page, the checkout reaches `EXPIRED` once `expiresAfter` elapses, but the customer is not sent back. Always confirm the final result from the checkout `status` (via callback or [check checkout status](/v2/api-reference/checkouts/check-checkout-status)) rather than relying on the customer returning to your `returnUrl`.
## How to find out the result
When the checkout reaches a final status, you will receive a [checkout callback](/v2/api-reference/checkouts/checkout-callback) with the final state of the checkout, including the latest payment attempt and the full history of attempts.
If you have not configured callbacks, you can poll the [check checkout status](/v2/api-reference/checkouts/check-checkout-status) endpoint.
On your `returnUrl`, use the `checkoutCode` query parameter to find the corresponding `checkoutId` in your system, then validate the final status by either confirming the callback has been received or using the [check checkout status](/v2/api-reference/checkouts/check-checkout-status) endpoint.
A checkout will expire after `expiresAfter` minutes (or the default expiry). If the customer abandons the checkout without a successful payment, it will eventually reach the `EXPIRED` status.
You don't need to manage the individual attempts — the checkout status already tells you the result.
If you prefer, each attempt also behaves like a standard [deposit](/v2/docs/deposits), so you can rely on the [deposit callback](/v2/api-reference/deposits/deposit-callback) and the [check deposit status](/v2/api-reference/deposits/check-deposit-status) endpoint for it.
We now know what happened to the payment and can make sure it's reflected accurately.
Let's now take a look at how to handle failed payments.
## Handling processing failures
If an attempt's `status` is `FAILED`, you can find further information about the failure from its `failureReason`.
It includes the `failureCode` and the `failureMessage` indicating what has gone wrong.
Checkout payments use the same failure codes as standard [deposits](/v2/docs/deposits).
The `failureMessage` from PawaPay API is meant for you and your support and operations teams.
You are free to decide what message to show to the customer.
Find all the [failure codes](/v2/docs/failure_codes#transaction-failure-codes) and implement handling as you choose.
Because the customer can make multiple attempts within a checkout, a failed attempt does not necessarily mean the checkout has failed — they can simply retry within the same checkout, with no action needed from you. Treat the checkout as failed only when its own `status` is `FAILED`, `EXPIRED` or `CANCELLED`.
We have standardised the numerous different failure codes and scenarios with all the different providers.
The quality of the failure codes varies by provider.
The `UNSPECIFIED_FAILURE` code indicates that the provider indicated a failure with the payment, but did not provide any more specifics on the reason of the failure.
In case there is a general failure, the `UNKNOWN_ERROR` failureCode would be returned.
## Ensuring consistency
When working with financial APIs there are some considerations to take to ensure that you never think a payment is failed, when it is actually successful or vice versa.
It is essential to keep systems in sync on the statuses of payments.
Let's take a look at some considerations and pseudocode to ensure consistency.
All statuses should be checked defensively without assumptions.
```json theme={null}
if( checkout.status == "COMPLETED" ) {
myInvoice.setPaymentStatus(COMPLETED);
} else if ( checkout.status == "FAILED" || checkout.status == "EXPIRED" || checkout.status == "CANCELLED" ) {
myInvoice.setPaymentStatus(FAILED);
} else if ( checkout.status == "WAITING_PAYMENT" || checkout.status == "PROCESSING" ) {
//The checkout is still in progress. Keep it pending and check again later.
myInvoice.setPaymentStatus(PENDING);
} else {
//It is unclear what might have happened. Escalate for further investigation.
myInvoice.setPaymentStatus(NEEDS_ATTENTION);
}
```
The key reason we require you to provide a `checkoutId` for each checkout is to ensure that you can always ask us what the status of a checkout is, even if you never get a response from us.
You should always store this `checkoutId` in your system **before** [initiating a checkout](/v2/api-reference/checkouts/initiate-checkout).
Once the checkout has been accepted, you should also store the `checkoutCode` from the response — it is appended to your `returnUrl` as a query parameter and is how you find the right checkout when the customer is redirected back to your site.
```json theme={null}
var checkoutId = new UUIDv4();
//Let's store the checkoutId we will use to ensure we always have it available even if something dramatic happens
myInvoice.setExternalPaymentId(checkoutId).save();
myInvoice.setPaymentStatus(PENDING);
try {
var initiationResponse = PawaPay.initiateCheckout(checkoutId, ...)
//Store the checkoutCode as well, so we can find this checkout
//when the customer is redirected back to our returnUrl
myInvoice.setCheckoutCode(initiationResponse.checkoutCode).save();
} catch (InterruptedException e) {
var checkResult = PawaPay.checkCheckoutStatus(checkoutId);
if ( checkResult.status == "FOUND" ) {
//The checkout reached PawaPay. Check the status of it from the response.
} else if ( checkResult.status == "NOT_FOUND" ) {
//The checkout did not reach PawaPay. Safe to retry creating it.
} else {
//Unable to determine the status. Leave the payment as pending.
//We will create a status recheck cycle later for such cases.
}
}
```
The important thing to notice here is that we only mark a payment as FAILED when there is a clear indication of its failure.
We use the [check checkout status](/v2/api-reference/checkouts/check-checkout-status) endpoint when in doubt whether the checkout was `ACCEPTED` by PawaPay.
Implementing the considerations listed above avoids almost all discrepancies of payment statuses between your system and PawaPay.
When using callbacks to receive the final statuses of payments, issues like network connectivity, system downtime, and configuration errors might cause the callback not to be received by your system.
To avoid keeping your customers waiting, we strongly recommend implementing a status recheck cycle.
This might look something like the following.
```json theme={null}
//Run the job every few minutes.
var pendingInvoices = invoices.getAllPendingForLongerThan15Minutes();
for ( invoice in pendingInvoices ) {
var checkResult = PawaPay.checkCheckoutStatus(invoice.getExternalPaymentId);
if ( checkResult.status == "FOUND" ) {
//Determine if the checkout is in a final status and handle accordingly
handleInvoiceStatus(checkResult.data);
} else if (checkResult.status == "NOT_FOUND" ) {
//The checkout has never reached PawaPay. Can be retried safely.
} else {
//Something must have gone wrong. Leave for next cycle.
}
}
```
Having followed the rest of the guide, with this simple reconciliation cycle, you should not have any inconsistencies between your system and PawaPay.
Having these checks automated will take a load off your operations and support teams as well.
## Payments in reconciliation
When using PawaPay **all payments are reconciled** by default and automatically — we validate every final status to ensure there are no discrepancies.
Sometimes the final status of a payment attempt cannot be determined immediately and the attempt is sent to our automatic reconciliation engine. While this happens, the payment attempt — and the checkout — simply remain in `PROCESSING`; the Checkout API does not surface a separate reconciliation status.
You do not need to take any action — the final status will be determined soon.
The reconciliation time varies by provider. Payments that turn out to be successful are reconciled faster.
## What to do next?
We've made everything easy to test in our sandbox environment before going live.
We have different phone numbers that you can use to test various failure scenarios on your sandbox account.
Make sure all the failure codes are handled.
To ensure your funds are safe even if your API token should leak, you can always implement signatures for financial calls to add another layer of security.
Have a look at what to consider to make sure everything goes well.
# Deposits
Source: https://docs.pawapay.io/v2/docs/deposits
Deposits allow you to request payment from a customer. Funds will be moved from the customer's mobile money wallet to your account in PawaPay.
In this guide, we'll go through step by step on how to approach building a high quality deposit flow.
If you haven't already, check out the following information to set you up for success with this guide.
Understand some consideration to take into account when working with mobile money.
Sort out API tokens and callbacks.
## Initiating a deposit
Let's start by hard-coding everything.
Throughout this guide we will be making the flow more dynamic and improving the customer experience incrementally.
Let's send this payload to the [initiate deposit](/v2/api-reference/deposits/initiate-deposit) endpoint.
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/deposits
{
"depositId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"amount": "100",
"currency": "RWF",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "250783456789",
"provider": "MTN_MOMO_RWA"
}
}
}
```
Let's see what's in this payload.
We ask you to generate a UUIDv4 `depositId` to uniquely identify this deposit.
This is so that you always have a reference to the deposit you are initiating, even if you do not receive a response from us due to network errors.
This allows you to always **reconcile all payments** between your system and PawaPay.
You should store this depositId in your system **before** initiating the deposit with PawaPay.
The `amount` and `currency` should be relatively self-explanatory.
This is the `amount` that will be collected from the customer.
Any fees will be deducted from that amount after the collection has completed.
Then the `provider` and `phoneNumber` specify exactly who is paying.
Mobile money wallets are identified by the phone number, like bank accounts are by their account number.
The `provider` specifies which mobile money operator this wallet is registered at. For example MTN Ghana or MPesa Kenya.
You will receive a response for the initiation.
```json {3} theme={null}
{
"depositId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"status": "ACCEPTED",
"nextStep": "FINAL_STATUS",
"created": "2025-05-15T07:38:56Z"
}
```
The `status` shows whether this deposit was `ACCEPTED` for processing or not.
We will go through failure modes later in the guide.
We will also take a look later how `nextStep` can be used, but for now it's safe to ignore it.
Now that the deposit has been initiated, the customer will receive a PIN prompt on their phone to authorise the payment.
This step only happens when initiating live payments on your production account.
On your sandbox account, this step is skipped!
Now that the customer has approved the payment, you will receive a [callback](/v2/docs/what_to_know#callbacks) from us to your configured callback URL.
```json {3} theme={null}
{
"depositId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"status": "COMPLETED",
"amount": "100.00",
"currency": "RWF",
"country": "RWA",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "250783456789",
"provider": "MTN_MOMO_RWA"
}
},
"customerMessage": "DEMO",
"created": "2025-05-15T07:38:56Z",
"providerTransactionId": "df0e9405-fb17-42c2-a264-440c239f67ed"
}
```
The main thing to focus on here is the `status` which is `COMPLETED` indicating that the deposit has been processed successfully and the funds have been collected to your wallet on your PawaPay account.
If you have not configured callbacks, you can always poll the [check deposit status](/v2/api-reference/deposits/check-deposit-status) endpoint for the final status of the payment.
Congratulations! You have now made your very first deposit with PawaPay.
We made a call to PawaPay to initiate the deposit.
And we found out the final status of that payment.
But there's a little more to a high quality integration. In the next sections, we will go into more details on:
* How to make this easy to expand to new markets and providers
* How to handle failures so that discrepancies between your system and PawaPay don't happen
* How to cover countries with providers that use different authorisations methods for payments
* And more...
## Asking for payment details from the customer
In the first part we harcoded everything. Now let's take a look at how to ask that information from the customer who is paying.
We are focusing on the use case where you know the amount to be paid already.
Later in the guide, we will also cover the case where the customer can choose the amount to pay.
Some providers do not support decimals in amount, so amounts like "100.50" might not be possible.
You can find how providers support decimals in the amount in the [Providers](/v2/docs/providers) section.
The [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint has useful and important information including which providers and markets have been configured on your PawaPay account.
In most cases you already know from which country the customer is from. And since we are implementing deposits, we can fetch the configuration for deposits only.
```json {8,14,16,28} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=RWA&operationType=DEPOSIT
{
"companyName": "Demo",
"countries": [
{
"country": "RWA",
"prefix": "250",
"flag": "https://static-content.pawapay.io/country_flags/rwa.svg",
"displayName": {
"en": "Rwanda",
"fr": "Rwanda"
},
"providers": [
{
"provider": "AIRTEL_RWA",
"displayName": "Airtel",
"logo": "https://static-content.pawapay.io/company_logos/airtel.png",
"currencies": [
{
"currency": "RWF",
"displayName": "R₣",
"operationTypes": ...
}
]
},
{
"provider": "MTN_MOMO_RWA",
"displayName": "MTN",
"logo": "https://static-content.pawapay.io/company_logos/mtn.png",
"currencies": [
{
"currency": "RWF",
"displayName": "R₣",
"operationTypes": ...
}
]
}
]
}
]
...
}
```
As you can see, in `providers` we have a list of all the providers that have been configured on your account for initiating deposits.
The `displayName` contains the name of the provider.
You can show those as options to your customer.
To enable new payment providers being automatically added to your deposit flows, we've also added the logo of the provider as the `logo`.
This way it is possible to implement the deposit flow dynamically, so that when new providers are enabled on your PawaPay account, they become available for your customers without additional effort.
This might look something like this:
Do not have a default provider. For example, as the first selection in a dropdown.
This often gets missed by customers causing payments to fail due to being sent to the wrong provider.
In the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint there is already the `prefix` within the country object.
This the country calling code for that country.
We recommend showing that to the customer in front of the input box for entering the phone number.
This makes it clear that the number they enter should not include the country code.
We have also added `flag` to the country object to make it look a little nicer.
This might look something like this:
To make sure the number entered by the customer is a valid phone number, let's use the [predict provider](/v2/api-reference/toolkit/predict-provider) endpoint.
First, concatenate the `prefix` to what was entered by the customer as the phone number.
You can now send it to validation. We will handle basic things like whitespace, characters etc.
We also make sure the number of digits is correct for the country and handle leading zeros.
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/predict-provider
{
"phoneNumber": "25007 834-56789a"
}
```
If the phone number is not valid, a failure will be returned.
You can show the customer a validation error.
Otherwise, you will get the following response:
```json theme={null}
{
"country": "RWA",
"provider": "MTN_MOMO_RWA",
"phoneNumber": "250783456789"
}
```
The `phoneNumber` is the sanitized MSISDN format of the phone number that you can use to initiate the deposit.
We strongly recommend using this endpoint for validating numbers especially due to some countries not strictly following ITU E.164.
Also, in the response, you will find the `provider`. This is the predicted provider for this `phoneNumber`.
We recommend preselecting the predicted provider for the customer.
In most countries we see very high accuracy for predictions removing another step from the payment experience.
We recommend allowing the customer to override the prediction as the accuracy is not 100%.
You should now have a payment page similar to this:
Now all information is either coming from the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint, the customer and your system (the amount to pay).
We can call the [initiate deposit](/v2/api-reference/deposits/initiate-deposit) endpoint with that information.
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/deposits
{
"depositId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"amount": "100",
"currency": "RWF",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "250783456789",
"provider": "MTN_MOMO_RWA"
}
}
}
```
The response and callback with the final status are the same as in the first part of the guide.
Please keep in mind that not all providers support amounts with decimals.
You can find which providers do and which ones don't from the [Providers](/v2/docs/providers) section.
We now made a real deposit flow collecting information from different sources and avoiding any hard-coded data.
This way, enabling new providers will not cause additional changes for you.
Next, let's take a look at what to do while we are waiting for the customer to authorise the payment.
## After initiating the deposit
After you initiate a deposit, the customer will need to authorise it.
While there are a couple of different ways providers handle payment authorisation, the most common one involves a PIN prompt popping up on the customers phone.
In this step, we will focus on that type of authorisation flow.
Later in the guide, we will cover the rest as well.
We already used the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint to find the providers configured on your PawaPay account.
The type of authorisation used by the provider is also available from that endpoint.
```json {21,22,23} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=SLE&operationType=DEPOSIT
{
"companyName": "Demo",
"countries": [
{
...
"providers": [
{
"provider": "ORANGE_SLE",
"displayName": "Airtel",
"nameDisplayedToCustomer": "SL LIMITED",
"logo": "https://static-content.pawapay.io/company_logos/orange.png",
"currencies": [
{
"currency": "SLE",
"displayName": "Le",
"operationTypes": {
"DEPOSIT": {
...
"authType": "PROVIDER_AUTH",
"pinPrompt": "MANUAL",
"pinPromptRevivable": true
...
}
]
}
]
}
]
...
}
```
There are three properties here that we will focus on.
The `authType` shows the type of authorisation that this provider uses. We are focusing on `PROVIDER_AUTH` currently.
Other `authTypes` will be covered later in the guide.
The `pinPrompt` property is only applicable for providers with `authType` of `PROVIDER_AUTH`.
It indicates whether or not the PIN prompt that is part of the authorisation will pop up on the customers phone automatically (`AUTOMATIC`) or if they need to take some action for that (`MANUAL`).
The `pinPromptRevivable` shows whether it's possible for the customer to get the PIN prompt to pop up again in case it fails on the first attempt.
Now that we know the specifics of the authorisation flow of the provider, let's implement the screen to show the customer after having initiated the deposit.
For `pinPrompt` value of `AUTOMATIC` we can show the customer a screen indicating that they should authorise the payment by entering their mobile money PIN on their phone.
On the PIN prompt, there is usually a reference to the payment provider (PawaPay or one of its local subsidiaries).
You should show that to the customer as part of the message to assure that it's a valid payment authorisation request.
You can find it from the response as `nameDisplayedToCustomer`.
```json {12} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=SLE&operationType=DEPOSIT
{
"companyName": "Demo",
"countries": [
{
...
"providers": [
{
"provider": "ORANGE_SLE",
"displayName": "Airtel",
"nameDisplayedToCustomer": "SL LIMITED",
...
}
]
}
]
...
}
```
Here's an example of what you might show to the customer while waiting for the callback with the final status of this payment.
If `pinPromptRevivable` is true for the provider, it means that the customer can revive the PIN prompt in case something happens on the first attempt.
For example, this might happen when they can't find their phone before the PIN prompt times out, they happen to get a call at the same time or there is a network issue delivering the PIN prompt.
In this case, we recommend waiting about 10-15 seconds after initiation and showing the customer instructions on how they can revive the PIN prompt to try again.
The specific instructions are in the response from [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint as 'pinPromptInstructions'.
```json {} theme={null}
...
"pinPromptInstructions": {
"channels": [
{
"type": "USSD",
"displayName": {
"en": "Did not get a PIN prompt?",
"fr": "N’avez-vous pas reçu la demande de code PIN?"
},
"instructions": {
"en": [
{
"text": "Dial *115#",
"template": "Dial {{shortCode}}",
"variables": {
"shortCode": "*115#"
}
},
{
"text": "Enter PIN",
"template": "Enter PIN",
"variables": {
"shortCode": "*115#"
}
}
],
"fr": [
{
"text": "Composez *115#",
"template": "Composez {{shortCode}}",
"variables": {
"shortCode": "*115#"
}
},
{
"text": "Entrez le code PIN",
"template": "Entrez le code PIN",
"variables": {
"shortCode": "*115#"
}
}
]
}
}
]
},
...
```
The `channel` for reviving the PIN prompt indicates where the customer is able to revive the PIN prompt.
This is predominantly `USSD` indicating they need to dial a USSD code on their phone to revive the PIN prompt.
The `displayName` includes a message on the intent of the instructions. You are free to use it or come up with your own message.
The `quickLink` (if available) includes the href that you can use to predial the USSD shortcode.
You should use it as the `href` of an `` tag or button.\
This way, when the customer is using the same phone to visit your site that they are paying with, they can press to predial the USSD code.
The `instructions` include the exact steps they should take to revive the PIN.
You can iterate over them to show the instructions for reviving the PIN prompt.
You can use the `text` of each instruction directly or if you want to emphasise key properties, you can use the `template` and `variables`.
Just surrond the `variables` of an instruction with the emphasis you need and replace them in the template.
In case of `pinPrompt` value of `MANUAL`, the customer needs to take some action to get to the PIN prompt.
While most customers know what to do and they will get an SMS with instructions anyway, we've seen improved success rates with clear instructions as part of payment experience.
You can get the instructions to show for this exactly the same way as for PIN prompt revival.
```json theme={null}
"pinPromptInstructions": {
"channels": [
{
"type": "USSD",
"displayName": {
"en": "Follow the instructions to authorise the payment",
"fr": "Veuillez suivre les instructions afin d’autoriser le paiement."
},
"instructions": {
"en": [
{
"text": "Dial *555*6#",
"template": "Dial {{shortCode}}",
"variables": {
"shortCode": "*555*6#"
}
},
{
"text": "Enter PIN",
"template": "Enter PIN",
"variables": {
"shortCode": "*555*6#"
}
}
],
"fr": [
{
"text": "Composez *555*6#",
"template": "Composez {{shortCode}}",
"variables": {
"shortCode": "*555*6#"
}
},
{
"text": "Entrez le code PIN",
"template": "Entrez le code PIN",
"variables": {
"shortCode": "*555*6#"
}
}
]
}
}
]
},
```
Now we have made sure that the customer always knows what they need to do to authorise a payment.
Next let's take a look now how to handle failures.
## Avoiding failures on initiation
Payment initiation can fail for various reasons. Let's see how we can best handle those.
Providers may have downtime. We monitor providers performance and availability 24/7.
For your operational and support teams, we have a [status page](https://status.pawapay.io).
From there they can **subscribe** to get updates in email or slack for all the providers they are interested in.
To avoid failed payment attempts, we've also exposed this information over API from both the [provider availability](/v2/api-reference/toolkit/availability) and [active configuration](/v2/api-reference/toolkit/active-configuration) endpoints.
This way you can be up front with the customer before they attempt to pay.
```json {24,41} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=BEN&operationType=DEPOSIT
{
"companyName": "Demo",
"countries": [
{
"country": "BEN",
"displayName": {
"fr": "Bénin",
"en": "Benin"
},
"prefix": "229",
"providers": [
{
"provider": "MOOV_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"DEPOSIT": {
...
"status": "OPERATIONAL",
...
}
}
}
]
},
{
"provider": "MTN_MOMO_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"DEPOSIT": {
...
"status": "CLOSED",
...
}
}
}
]
}
]
}
]
...
}
```
Based on whether the `status` of the specific provider is `OPERATIONAL` or `CLOSED` you can inform the customer up front that this provider is currently not available and they can attempt again later.
Every time you [initiate a deposit](/v2/api-reference/deposits/initiate-deposit) you should confirm that the `status` in the response is `ACCEPTED`.
If the status is `REJECTED` the `failureReason` will contain both the `failureCode` and `failureMessage` indicating what has happened.
Most of those failures are avoidable if handled beforehand.
The `failureMessage` from PawaPay API is meant for you and your support and operations teams.
You are free to decide what message to show to the customer.
```json theme={null}
//Incorrect amount of decimals in amount
{
"depositId": "f4401bd2-1568-4140-bf2d-eb77d2b2b639",
"status": "REJECTED",
"failureReason": {
"failureCode": "INVALID_AMOUNT",
"failureMessage": "The provider MOOV_BEN only supports up to '2' decimal places in amount."
}
}
//Amount larger than transaction limits allow
{
"depositId": "f4401bd2-1568-4140-bf2d-eb77d2b2b639",
"status": "REJECTED",
"failureReason": {
"failureCode": "AMOUNT_OUT_OF_BOUNDS",
"failureMessage": "The amount needs to be more than '100' and less than '2000000' for provider 'MOOV_BEN'."
}
}
//Invalid currency
{
"depositId": "f4401bd2-1568-4140-bf2d-eb77d2b2b639",
"status": "REJECTED",
"failureReason": {
"failureCode": "INVALID_CURRENCY",
"failureMessage": "The currency 'USD' is not supported with provider 'MOOV_BEN'. Please consult API docs for supported currencies."
}
}
```
In the above example, where the provider does not support decimal places in amount, you need to preempt that by rounding the amounts.
Depending on your use case, you might do that preemptively by adjusting local pricing or dynamically before payment.
Second thing to consider is that customers mobile money wallets have limits on how big the payments can be.
Customers are able to get those limits increased on their mobile money wallets by contacting their provider and going through extended KYC.
By default, we have set the transaction limits on your PawaPay account based on the most common wallet limits.
Regarding currencies, the only country available through PawaPay that supports multiple currencies is [DRC](/v2/docs/providers#democratic-republic-of-the-congo-drc).
These are exposed in the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint as an array of `currencies`.
You can access both the decimal places supported and transaction limits from the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint.
```json {17,24-26,36,43-45} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=BEN&operationType=DEPOSIT
{
"companyName": "Demo",
"countries": [
{
"country": "BEN",
"displayName": {
"fr": "Bénin",
"en": "Benin"
},
"prefix": "229",
"providers": [
{
"provider": "MOOV_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"DEPOSIT": {
...
"decimalsInAmount": "NONE",
"minAmount": "100",
"maxAmount": "2000000",
...
}
}
}
]
},
{
"provider": "MTN_MOMO_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"DEPOSIT": {
...
"decimalsInAmount": "NONE",
"minAmount": "100",
"maxAmount": "2000000",
...
}
}
}
]
}
]
}
]
...
}
```
You can use those to ensure inputs to [initiate deposit](/v2/api-reference/deposits/initiate-deposit) are cleaned before initiation and rejections don't happen.
The deposit endpoint expects the `phoneNumber` in the MSISDN format.
You can use the [predict provider](/v2/api-reference/toolkit/predict-provider) endpoint to clean up the input you receive from the customer.
Make sure the input starts with the country code.
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/predict-provider
{
"phoneNumber": "25007 834-56789a"
}
```
If the phone number is not valid, a failure will be returned.
You can show the customer a validation error.
```json theme={null}
{
"country": "RWA",
"provider": "MTN_MOMO_RWA",
"phoneNumber": "250783456789"
}
```
The `phoneNumber` in the response will be in the correct format to [initiate the deposit](/v2/api-reference/deposits/initiate-deposit).
All payments initiated with PawaPay are idempotent based on their ID.
You must always generate a new UUIDv4 for the `depositId`.
When you attempt to use the same `depositId` more than once the payment will be rejected.
```json theme={null}
{
"depositId": "f4401bd2-1568-4140-bf2d-eb77d2b2b639",
"status": "DUPLICATE_IGNORED"
}
```
We already discussed how to show to the customer which payment methods are currently not available due to provider downtime.
There is usually some time between when you fetch and show this information to the customer and when they initiate the payment.
We still need to ensure accurate information is shown in case the provider goes down during that time.
If a payment is initiated during provider downtime, it will be rejected.
```json theme={null}
{
"depositId": "f4401bd2-1568-4140-bf2d-eb77d2b2b639",
"status": "REJECTED",
"failureReason": {
"failureCode": "PROVIDER_TEMPORARILY_UNAVAILABLE",
"failureMessage": "The provider 'MTN_MOMO_BEN' is currently not able to process payments. Please consult our status page for downtime information on all providers. Programmatic access is also available, please consult our API docs."
}
}
```
Many customers will have mobile money wallets with different providers.
It's reasonable to ask them to pay with another provider or to attempt again later.
The `UNKNOWN_ERROR` failureCode indicates that something unexpected has gone wrong when processing the payment.
There is no `status` in the response in this case.
It is **not safe** to assume the initiation has failed for this deposit.
You should verify the status of the deposit using the [check deposit status](/v2/api-reference/deposits/check-deposit-status) endpoint.
Only if the deposit is `NOT_FOUND` should it be considered `FAILED`.
We will take a look later in the guide, how to ensure consistency of payment statuses between your system and PawaPay.
We have now handled different failures that might happen during initiation.
Next, let's take a look at payment failures that can happen during processing.
## Handling processing failures
As the PawaPay API is asynchronous, you will get a [deposit callback](/v2/api-reference/deposits/deposit-callback) with the final status of the deposit.
If the `status` of the deposit is `FAILED` you can find further information about the failure from `failureReason`.
It includes the `failureCode` and the `failureMessage` indicating what has gone wrong.
The `failureMessage` from PawaPay API is meant for you and your support and operations teams.
You are free to decide what message to show to the customer.
Find all the [failure codes](/v2/docs/failure_codes#transaction-failure-codes) and implement handling as you choose.
Operation specific processing failures are also documented in the API reference:
* [Deposit callback](/v2/api-reference/deposits/deposit-callback)
* [Payout callback](/v2/api-reference/payouts/payout-callback)
* [Refund callback](/v2/api-reference/refunds/refund-callback)
We recommend allowing easy retries for customers by taking the customer back to the payment information collection screen and showing the failure reason on that page.
This way they can quickly try again.
We have standardised the numerous different failure codes and scenarios with all the different providers.
The specificity of the failure codes varies by provider.
The `UNSPECIFIED_FAILURE` code indicates that the provider indicated a failure with the payment, but did not provide any more specifics on the reason of the failure.
In case there is a general failure, the `UNKNOWN_ERROR` failureCode will be returned.
We have now also taken care of failures that can happen during payment processing.
This way the customer knows what has happened and can take appropriate action to try again.
Now let's take a look at some markets where the payment authorisation flow is a little different than usual.
## Ensuring consistency
When working with financial APIs there are some considerations to take to ensure that you never think a payment is failed, when it is actually successful or vice versa.
It is essential to keep systems in sync on the statuses of payments.
Let's take a look at some considerations and pseudocode to ensure consistency.
All statuses should be checked defensively without assumptions.
```json theme={null}
if( status == "COMPLETED" ) {
myInvoice.setPaymentStatus(COMPLETED);
} else if ( status == "FAILED" ) {
myInvoice.setPaymentStatus(FAILED);
} else if ( status == "PROCESSING") {
handleRedirectionAuth();
} else {
//It is unclear what might have failed. Escalate for further investigation.
myInvoice.setPaymentStatus(NEEDS_ATTENTION);
}
```
The key reason we require you to provide a `depositId` for each payment is to ensure that you can always ask us what is the status of a payment, even if you never get a response from us.
You should always store this `depositId` in your system **before** [initiating a deposit](/v2/api-reference/deposits/initiate-deposit).
```json theme={null}
var depositId = new UUIDv4();
//Let's store the depositId we will use to ensure we always have it available even if something dramatic happens
myInvoice.setExternalPaymentId(depositId).save();
myInvoice.setPaymentStatus(PENDING);
try {
var initiationResponse = PawaPay.initiateDeposit(depositId, ...)
} catch (InterruptedException e) {
var checkResult = PawaPay.checkDepositStatus(depositId);
if ( result.status == "FOUND" ) {
//The payment reached PawaPay. Check the status of it from the response.
} else if ( result.status == "NOT_FOUND" ) {
//The payment did not reach PawaPay. Safe to mark it as failed.
myInvoice.setPaymentStatus(FAILED);
} else {
//Unable to determine the status. Leave the payment as pending.
//We will create a status recheck cycle later for such cases.
//In case of a system crash, we should also leave the payment in pending status to be handled in the status recheck cycle.
}
}
```
The important thing to notice here is that we only mark a payment as FAILED when there is a clear indication of it's failure.
We use the [check deposit status](/v2/api-reference/deposits/check-deposit-status) endpoint when in doubt whether the payment was `ACCEPTED` by PawaPay.
Implementing the considerations listed above avoids almost all discrepancies of payment statuses between your system and PawaPay.
When using callbacks to receive the final statuses of payments, issues like network connectivity, system downtime, and configuration errors might cause the callback not to be received by your system.
To avoid keeping your customers waiting, we strongly recommend implementing a status recheck cycle.
This might look something like the following.
```json theme={null}
//Run the job every few minutes.
var pendingInvoices = invoices.getAllPendingForLongerThan15Minutes();
for ( invoice in pendingInvoices ) {
var checkResult = PawaPay.checkDepositStatus(invoice.getExternalPaymentId);
if ( checkResult.status == "FOUND" ) {
//Determine if the payment is in a final status and handle accordingly
handleInvoiceStatus(checkResult.data);
} else if (checkResult.status == "NOT_FOUND" ) {
//The payment has never reached PawaPay. Can be failed safely.
invoice.setPaymentStatus(FAILED);
} else {
//Something must have gone wrong. Leave for next cycle.
}
}
```
Having followed the rest of the guide, with this simple reconciliation cycle, you should not have any inconsistencies between your system and PawaPay.
Having these checks automated will take a load off your operations and support teams as well.
## Other payment authorisation flows
Overwhelmingly providers use a PIN prompt to authorise payments.
There are some markets that need support for alternative authorisation flows:
* Burkina Faso
* Ivory Coast
* Senegal
If you are not looking to go live in these markets, you can skip this section of the guide.
In this case, we recommend filtering out all providers that do not have `authType` of `PROVIDER_AUTH` from your calls to [active configuration](/v2/api-reference/toolkit/active-configuration).
Let's implement support for these authorisation flows as well to ensure that all providers can be used.
### Preauthorised flows
Currently, the only provider using preauthorised payments is Orange in Burkina Faso.
Preauthorisation means that the customer needs to authorise the payment *before* initiation the payment.
For Orange in Burkina Faso the customer needs to generate an OTP through Oranges USSD menu.
Let's make sure they know how to do that by showing them step-by-step instructions for that.
Let's get those instructions from the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint.
```json theme={null}
"authTokenInstructions": {
"channels": [
{
"type": "USSD",
"displayName": {
"en": "Preauthorization instructions",
"fr": "Instructions de préautorisation"
},
"instructions": {
"en": [
{
"text": "Dial *144*4*6#",
"template": "Dial {{shortCode}}",
"variables": {
"shortCode": "*144*4*6#"
}
},
{
"text": "Enter amount",
"template": "Enter amount",
"variables": {
"shortCode": "*144*4*6#"
}
},
{
"text": "Enter PIN",
"template": "Enter PIN",
"variables": {
"shortCode": "*144*4*6#"
}
},
{
"text": "Use OTP",
"template": "Use OTP",
"variables": {
"shortCode": "*144*4*6#"
}
}
],
"fr": [
{
"text": "Composez *144*4*6#",
"template": "Composez {{shortCode}}",
"variables": {
"shortCode": "*144*4*6#"
}
},
{
"text": "Entrez le montant",
"template": "Entrez le montant",
"variables": {
"shortCode": "*144*4*6#"
}
},
{
"text": "Entrez le code PIN",
"template": "Entrez le code PIN",
"variables": {
"shortCode": "*144*4*6#"
}
},
{
"text": "Entrez le code à usage unique reçu par SMS",
"template": "Entrez le code à usage unique reçu par SMS",
"variables": {
"shortCode": "*144*4*6#"
}
}
]
}
}
]
},
```
The channel for reviving the PIN prompt indicates where the customer is able to generate the OTP.
For Orange in Burkina Faso, this is USSD indicating they need to dial a USSD code on their phone to revive the PIN prompt.
The displayName includes a message on the intent of the instructions.
You are free to use it or come up with your own message.
The quickLink includes the href that you can use to predial the USSD shortcode. You should use it as the href of an tag or button.
This way, when the customer is using the same phone to visit your site that they are paying with, they can click to predial the USSD code.
The instructions include the exact steps they should take to generate the OTP.
You can iterate over them to show the instructions to generate the OTP.
You can use the text of each instruction directly or if you want to emphasise key properties, you can use the template and variables.
Just surrond the variables of an instruction with the emphasis you need and replace them in the template.
You also need to provide the customer a place to provide the preauthentication OTP.
The customer would then follow the instructions on their phone to generate the OTP.
You must then include this OTP as the `preAuthorisationCode` into the request to [initiate deposit](/v2/api-reference/deposits/initiate-deposit).
```json {10} theme={null}
{
"depositId": "3bd57454-fc43-49ad-9949-54e03f173c85",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "22673394446",
"provider": "ORANGE_BFA"
}
},
"preAuthorisationCode": "367025",
"amount": "100",
"currency": "XOF"
}
```
You can now just tell the customer that the payment is processing and wait for a callback from PawaPay with the final status of the payment.
We can now get paid by customers using Orange in Burkina Faso.
Now let's look at redirection based authorisation flows.
### Redirection based flows
Some providers use redirection based authorisation.
This is slightly more involved as you will need to redirect the customer to a URL where they can authorise the payment.
This is used by Wave in both Senegal and Ivory Coast.
Let's look at how that might look like for a customer using Wave in Senegal.
You can now [initiate the deposit](/v2/api-reference/deposits/initiate-deposit).
After the authorisation is completed, the customer will be forwarded to...
* `successfulUrl` if the payment completed successfully.
* `failedUrl` if the payment failed to complete.
```json {5,6} theme={null}
{
"depositId": "3bd57454-fc43-49ad-9949-54e03f173c85",
"amount": "99",
"currency": "XOF",
"successfulUrl": "https://merchant.com/successfulUrl",
"failedUrl": "https://merchant.com/failedUrl",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "221773456789",
"provider": "WAVE_SEN"
}
}
}
```
You will receive a response for this with information on what to do next.
```json {4} theme={null}
{
"depositId": "3bd57454-fc43-49ad-9949-54e03f173c85",
"status": "ACCEPTED",
"nextStep": "GET_AUTH_URL",
"created": "2025-05-15T07:38:56Z"
}
```
The `nextStep` property indicates that the next thing to do is to retrieve the `authorizationUrl` to forward the customer to.
If you have configured [callbacks](/v2/docs/what_to_know#callbacks), you will receive a callback with the `authorizationUrl`.
```json {3,19} theme={null}
{
"depositId": "241cc3c4-74b8-4fbd-921d-340cf648e2a3",
"status": "PROCESSING",
"nextStep": "REDIRECT_TO_AUTH_URL",
"amount": "99.00",
"currency": "XOF",
"country": "SEN",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "221773456789",
"provider": "WAVE_SEN"
}
},
"customerMessage": "DEMO",
"created": "2025-05-15T07:38:56Z",
"successfulUrl": "https://merchant.com/successfulUrl",
"failedUrl": "https://merchant.com/failedUrl",
"authorizationUrl": "https://provider.com/authorizationUrl"
}
```
If you do not have callbacks configured, you can always poll [check deposit status](/v2/api-reference/deposits/check-deposit-status).
The response will keep returning `GET_AUTH_URL` as the value of `nextStep` until the provider makes the `authorizationUrl` available.
Then the response will include the `authorizationUrl` and the value of `nextStep` will have changed to `REDIRECT_TO_AUTH_URL`.
```json {4,19} theme={null}
{
"depositId": "241cc3c4-74b8-4fbd-921d-340cf648e2a3",
"status": "PROCESSING",
"nextStep": "REDIRECT_TO_AUTH_URL",
"amount": "99.00",
"currency": "XOF",
"country": "SEN",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "221773456789",
"provider": "WAVE_SEN"
}
},
"customerMessage": "DEMO",
"created": "2025-05-15T07:38:56Z",
"successfulUrl": "https://merchant.com/successfulUrl",
"failedUrl": "https://merchant.com/failedUrl",
"authorizationUrl": "https://provider.com/authorizationUrl"
}
```
This is usually very fast, but since the provider needs to return it, polling should be implemented.
You can now use the `authorizationUrl` that you retrieved in the previous step to forward the customer to it.
If they are on desktop, they will see a QR code that they can scan with their phone that has the Wave app installed.
If the customer is using your site from the phone that has Wave app installed, they will be immmediately redirected to the app to confirm the payment.
...they will see a confirmation screen and then depending on the result of the payment, be forwarded back to either the `successfulUrl` or `failedUrl`.
We will of course send you a callback with the final status of the payment, as usual.
We do recommend validating the status of the deposit on your `successfulUrl` or `failedUrl` from the [check deposit status](/v2/api-reference/deposits/check-deposit-status) endpoint.
You have now built the payment experience allowing customers to pay you regardless of which provider they are using.
## Customer chooses the amount
For some use cases, the customer should decide how much they are paying.
Let's take a look at how to support that.
Not all providers support decimals in amount - amounts like 100.50 will fail to process.
This information is available for each provider in [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint.
```json {24,41} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=BEN&operationType=DEPOSIT
{
"companyName": "Demo",
"countries": [
{
"country": "BEN",
"displayName": {
"fr": "Bénin",
"en": "Benin"
},
"prefix": "229",
"providers": [
{
"provider": "MOOV_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"DEPOSIT": {
...
"decimalsInAmount": "NONE",
...
}
}
}
]
},
{
"provider": "MTN_MOMO_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"DEPOSIT": {
...
"decimalsInAmount": "NONE",
...
}
}
}
]
}
]
}
]
...
}
```
The possible values are `NONE` and `TWO_PLACES`. It's easy to provide dynamic validation now that is appropriate for the provider.
Customers' mobile money wallets have limits on how big the payments can be.
Customers are able to get those limits increased on their mobile money wallets by contacting their provider and going through extended KYC.
By default, we have set the transaction limits on your PawaPay account based on the most common wallet limits.
The limits for the provider will be available from the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint.
```json {24,25,42,43} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=BEN&operationType=DEPOSIT
{
"companyName": "Demo",
"countries": [
{
"country": "BEN",
"displayName": {
"fr": "Bénin",
"en": "Benin"
},
"prefix": "229",
"providers": [
{
"provider": "MOOV_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"DEPOSIT": {
...
"minAmount": "100",
"maxAmount": "2000000",
...
}
}
}
]
},
{
"provider": "MTN_MOMO_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"DEPOSIT": {
...
"minAmount": "100",
"maxAmount": "2000000",
...
}
}
}
]
}
]
}
]
...
}
```
It's easy to provide dynamic validation ensuring the amount is between `minAmount` and `maxAmount`.
Now customers can choose the amount to pay without making multiple attempts.
## Payments in reconciliation
When using PawaPay, you might find that a payment status is `IN_RECONCILIATION`.
This means that there was a problem determining the correct final status of a payment.
When using PawaPay **all payments are reconciled** by default and automatically - we validate all final statuses to ensure there are no discrepancies.
When encountering payments that are `IN_RECONCILIATION` you do not need to take any action.
The payment has already been sent to our automatic reconciliation engine and it's final status will be determined soon.
The reconciliation time varies by provider. Payments that turn out to be successful are reconciled faster.
## What to do next?
We've made everything easy to test in our sandbox environment before going live.
We have different phone numbers that you can use to test various failure scenarios on your sandbox account.
Make sure all the failure codes are handled.
To ensure your funds are safe even if your API token should leak, you can always implement signatures for financial calls to add another layer of security.
Have a look at what to consider to make sure everything goes well.
# Failure codes
Source: https://docs.pawapay.io/v2/docs/failure_codes
## Technical failure codes
Technical failures should not happen during live operation and are intended to provide clear information during the development of the integration.
| Failure Code | HTTP status | Operation | Description |
| - | - | - | - |
| NO\_AUTHENTICATION | 401 | All | We did not find the API token in the request headers. |
| AUTHENTICATION\_ERROR | 403 | All | The API token specified for authentication is not valid or missing. Please refer to [Authentication](/v2/docs/how_to_start#how-to-authenticate-calls-to-the-merchant-api). |
| AUTHORISATION\_ERROR | 403 | All | The API token specified for authentication is now authorized for the API call. |
| HTTP\_SIGNATURE\_ERROR | 403 | All | You have enabled signatures for financial calls, but we are unable to verify the signature from the request. |
| INVALID\_INPUT | 400 | All | We were unable to parse the payload of the request. |
| MISSING\_PARAMETER | 400 | All | A mandatory property is missing from the body of the request. The 'failureMessage' contains more information. Consult the API reference. |
| UNSUPPORTED\_PARAMETER | 400 | All | A parameter was found in the body of the request that is not supported by the endpoint. The 'failureMessage' contains more information. Consult the API reference. |
| INVALID\_PARAMETER | 400 | All | The value of a parameter in the body of the request was invalid. The 'failureMessage' contains more information. Consult the API reference. |
| AMOUNT\_OUT\_OF\_BOUNDS | 200 | All | The amount is outside of the transaction limits for this provider and operation. Check [active configuration](/v2/api-reference/toolkit/active-configuration) for effective limits. |
| INVALID\_AMOUNT | 200 | All | The number of decimal places specified in the amount is not supported by the provider. Check [Providers](/v2/docs/providers) for their support for decimals. |
| INVALID\_PHONE\_NUMBER | 200 | All | The phone number specified is not in the MSISDN format. |
| INVALID\_CURRENCY | 200 | All | The currency is not supported by the provider. |
| INVALID\_PROVIDER | 200 | All | The provider is not valid for the request. |
| DUPLICATE\_METADATA\_FIELD | 400 | All | You have multiple instances of the same metadta included in the request. |
| DEPOSITS\_NOT\_ALLOWED | 403 | Deposits | Deposits have not been enabled for the specified 'provider' on your PawaPay account. |
| PAYOUTS\_NOT\_ALLOWED | 403 | Payouts | Payouts have not been enabled for the specified 'provider' on your PawaPay account. |
| REFUNDS\_NOT\_ALLOWED | 403 | Refunds | Refunds have not been enabled for the specified 'provider' on your PawaPay account. |
| PROVIDER\_TEMPORARILY\_UNAVAILABLE | All | The provider specified is currently experiencing an outage and processing of payments has been temporarily halted. Please refer to our [Status Page](https://status.pawapay.io) for live information about MMO availability. | |
| UNKNOWN\_ERROR | 500 | All | An unknown error has occurred while processing this payment. Please check the status of this payment over API before considering failed. |
## Transaction failure codes
Transaction failures are expected to happen during live operation and are intended to provide clear information as to why a particular payment was not successful.
| Failure Code | Operation | Description |
| - | - | - |
| PAYMENT\_NOT\_APPROVED | Deposits | The customer did not authorize the payment. *Example: Customer did not enter their PIN in time.* |
| INSUFFICIENT\_BALANCE | Deposits | The customer does not have enough funds to perform the deposit. |
| PAYMENT\_IN\_PROGRESS | Deposits | The customer is initiating a transaction while an unfinished transaction is still pending. *Note: Some providers only allow a single transaction to be processed at any given time. When the customer does not enter the PIN to authorize a payment in time, it might take up to 10 minutes for them to be able to initiate a new transaction.* |
| PAYER\_NOT\_FOUND | Deposits | The phone number (MSISDN) does not belong to the provider. |
| RECIPIENT\_NOT\_FOUND | Payouts | The phone number (MSISDN) does not belong to the provider. |
| MANUALLY\_CANCELLED | Payouts/Refunds | The payout request was enqueued and subsequently failed manually from the PawaPay Dashboard or through the [Cancel Enqueued Payout](/v2/api-reference/payouts/cancel-enqueued-payout) endpoint. |
| PAWAPAY\_WALLET\_OUT\_OF\_FUNDS | Payouts/Refunds | The balance of your PawaPay wallet does not have the funds to initiate this payout. |
| DEPOSIT\_ALREADY\_REFUNDED | Refunds | The deposit you are refunding has already been refunded in full. |
| AMOUNT\_TOO\_LARGE | Refunds | The amount you are attempting to refund is larger than what is left to be refunded for this deposit including previous partial refunds. |
| REFUND\_IN\_PROGRESS | Refunds | There is already a refund being processed for this deposit. Only one refund can be processed for a single deposit at a time. |
| WALLET\_LIMIT\_REACHED | All | The customer has reached a limit on their wallet. Example: Customer is only allowed to transfer a maximum of 1 000 000 per week. |
| UNSPECIFIED\_FAILURE | All | The provider did not give any information about the reason for this failure. |
| UNKNOWN\_ERROR | All | An unknown error has occurred while processing this payment. Please check the status of this payment over API before considering failed. |
# Going live
Source: https://docs.pawapay.io/v2/docs/going_live
## Overview
When you are ready to move from the sandbox environment to production, make sure you use the correct [base URL](/v2/docs/how_to_start#where-is-the-api) and the correct [authentication token](/v2/docs/how_to_start#how-to-authenticate-calls-to-the-merchant-api).
These are the only parameters that differ between sandbox and production.
We recommend storing those parameters in your environment-specific configuration.
If you use IP whitelisting, ensure that [our IP addresses are whitelisted](/v2/docs/what_to_know#ips-to-whitelist) so we can deliver callbacks to you.
## Go-live testing
We recommend implementing a feature flag for your integration with PawaPay to enable go-live testing in production, before market launch.
This allows you to test the end-to-end flow in production to uncover any environment-specific problems before they impact customers.
To test live payments, you will need the following for each MMO that you use:
* A phone and phone number with the specific MMO.
* An active mobile money wallet on that phone number.
* An available balance on the mobile money wallet to conduct the tests with.
* Be ready to enter the PIN code on the phone approximately 1-20 seconds from initiating the payment through our API.
# How to start?
Source: https://docs.pawapay.io/v2/docs/how_to_start
When [creating an account](https://dashboard.sandbox.pawapay.io/#/merchant-signup) with PawaPay, you will first receive access to our sandbox environment. The sandbox environment is completely isolated from our production environment.
You can safely test your integration with PawaPay without real mobile money wallets and real money involved immediately after creating your PawaPay without real mobile money wallets and real money involved immediately after creating your PawaPay account.
Access to your production account, where live payments can be made, will be granted after completing the onboarding on your sandbox account.
Now that you have an account in our sandbox, let's look at how to start integrating.
## Where is the API?
The base URL for the PawaPay Merchant API is different between our sandbox and production environments.
| Environment | Base URL |
| - | - |
| Sandbox | [https://api.sandbox.pawapay.io/](https://api.sandbox.pawapay.io/) |
| Production | [https://api.pawapay.io/](https://api.pawapay.io/) |
The specific operation can be called by appending the endpoint to the base URL.
*Example: [https://api.sandbox.pawapay.io/v2/payouts](https://api.sandbox.pawapay.io/v2/payouts)*
The base URLs are different between sandbox and production accounts. Please store them in your application's environment-specific configuration.
## Where is the PawaPay Dashboard?
From the PawaPay Dashboard you can:
* Set up callback URLs
* Generate an API token
* See all accepted payments
* See financial statements
* Invite users
* Control access levels
* And much more
As our sandbox environment is completely isolated from our production environment, the URLs to access the PawaPay dashboard are different.
| Environment | Dashboard URL |
| - | - |
| Sandbox | [https://dashboard.sandbox.pawapay.io/](https://dashboard.sandbox.pawapay.io/) |
| Production | [https://dashboard.pawapay.io/](https://dashboard.pawapay.io/) |
## How to authenticate calls to the Merchant API
The PawaPay Merchant API uses a bearer token for authentication. An authorization header is required for all calls to the PawaPay Merchant API uses a bearer token for authentication. An authorization header is required for all calls to the PawaPay Merchant API.
The token can be generated from the PawaPay Dashboard. Instructions on how to do that can be found in the [**PawaPay Dashboard. Instructions on how to do that can be found in the PawaPay Dashboard Docs**](/dashboard/other/system_conf/api_tokens).
Below is an example payout request with curl:
```json theme={null}
curl -i -X POST \
https://api.sandbox.pawapay.io/payouts \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d '{
"payoutId": "33f30946-881d-40bc-8ca2-94aa4cd467ac",
"amount": "15",
"currency": "ZMW",
"recipient": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "260763456789",
"provider": "MTN_MOMO_ZMB"
}
}
}'
```
The API tokens are different between sandbox and production accounts.
When moving from sandbox to production a new API token must be generated from the production PawaPay Dashboard.
Please store the API token in your application's environment specific secure storage.
If you are looking to add a second layer of security, you can implement [Signatures](/v2/docs/signatures) to ensure security even if your API token should leak.
## Setting up callbacks
As the PawaPay API is asynchronous, we recommend implementing a callback handler in your application to find out about the final status of your payments as soon as they are processed.
You can read more about how to do that from the [PawaPay Dashboard](/dashboard/other/system_conf/callback_urls) docs.
When configured, we will send you a callback with the final status of a payment to your configured callback URL. Read more about [callbacks](/v2/docs/what_to_know#callbacks).
## Starting to build the integration
Now you are all set to integrate with the PawaPay Merchant API.
Test and get a feel for the API using our Postman collection.
Find out how to integrate to the API to get paid.
Find out how to integrate to send money.
Find out how to handle refunds.
Find out how to get paid, with an out-of-the-box payment experience.
## Testing the integration
You can test the PawaPay Merchant API and Dashboard with your sandbox account right after signing up. You do not need to go through the onboarding process for that since no real money is involved.
There are special phone numbers available to simulate successful and different failure cases.
It is not possible to see the authorization flows (i.e. entering the PIN prompt) in sandbox.
Here's a few helpful resources to get everything you need to start integrating and testing.
Find all the different providers currently available on PawaPay.
Here's all the test numbers you can use on your sandbox account to test different payment scenarios.
Here's a summary of all the different payment failure codes you might encounter. They are all of course documented in the API reference as well.
Find the brand guidelines on how to use provider logos.
# Payment Page
Source: https://docs.pawapay.io/v2/docs/payment_page
The payment page allows you to request a payment from a customer together with the customer experience for the payment.
Funds will be moved from the customer's mobile money wallet to your account in PawaPay.
In this guide, we will go through some different use cases and make sure the payment statuses are in sync between you and PawaPay.
If you haven't already, check out the following information to set you up for success with this guide.
Understand some considerations to take into account when working with mobile money.
Sort out API tokens and callbacks.
## What does it look like
The PawaPay Payment Page allows you to quickly integrate mobile money into your website or mobile app providing:
* A user experience for your customers that is optimised for mobile money.
* Responsive design that works on desktop and mobile.
* Low code integration supporting all countries and providers.
* Support for both e-commerce and e-wallet use cases.
With just a single API call and a redirect, the customers can pay you.
It is also integrated with the rest of the PawaPay Merchant API, providing benefits such as:
* Phone numbers are validated to be in the correct format.
* The provider to use for the payment is predicted based on the entered phone number.
* Minimum and maximum transaction limits are always up to date and validated.
* When new countries or providers are enabled, they are available for your customers immediately.
* Information about provider downtime is integrated into the user experience.
* And many more improvements to come...
## How to use it
Let's take a look at a couple of different use cases for the Payment page.
Then we will also see how to handle payment results.
The payment page can support accepting a payment from any country in any amount below the transaction limits for the provider.
We do that using the [Deposit via payment page](/v2/api-reference/payment-page/deposit-via-payment-page) endpoint.
```json {} theme={null}
POST https://api.sandbox.pawapay.io/v2/paymentpage
{
"depositId": "695776cf-73ba-42ff-b9cb-2b9acc008e22",
"returnUrl": "https://merchant.com/returnUrl",
"reason": "Demo payment"
}
```
We ask you to generate a UUIDv4 `depositId` to uniquely identify the deposit that will be processed using the payment page.
This is so that you always have a reference to the deposit you are expecting, even if you do not receive a response from us due to network errors.
This allows you to always **reconcile all payments** between your system and PawaPay.
You should store this depositId in your system **before** initiating the deposit with PawaPay.
The `returnUrl` specifies where the customer should be redirected to after they have gone through the payment process.
The `reason` field is optional. It will be shown on the payment page to the customer to indicate what they are paying for.
The payment page allows them to choose the country from the dropdown.
If you have registered users who should only use the number that they signed up with for payments, you can fix the phone number on the payment page.
```json {6} theme={null}
POST https://api.sandbox.pawapay.io/v2/paymentpage
{
"depositId": "375fb9c9-fe34-48fd-95b2-b0aff9928673",
"returnUrl": "https://merchant.com/returnUrl",
"msisdn": "233593456789",
"reason": "Demo payment"
}
```
The `msisdn` fixes the mobile money wallet that can be used for this payment.
When collecting the phone number, we strongly recommend using our [predict provider](/v2/api-reference/toolkit/predict-provider) endpoint.
It validates the phone number and returns it in a format that works for use with the payment page.
The payment page only allows them to choose the amount to pay.
In case you know how much the customer should be paying, but they can choose the mobile money wallet they want to pay from, you can fix the amount as well.
```json {6-7} theme={null}
POST https://api.sandbox.pawapay.io/v2/paymentpage
{
"depositId": "375fb9c9-fe34-48fd-95b2-b0aff9928673",
"returnUrl": "https://merchant.com/returnUrl",
"amount": "100",
"country": "GHA",
"reason": "Demo payment"
}
```
The `amount` specifies the amount that can be used for this payment.
[Providers](/v2/docs/providers) have transaction limits.
You can use the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint to validate the amount is within the transaction limits.
The payment page will fail to initiate if the amount is out of bounds.
It is not possible to fix the `amount` without specifying the `country`.
The payment page will allow the customer to specify the phone number of the mobile money wallet they are paying from.
When you have only registered users and the amount is predetermined, you can initiate the payment page to fix those parameters so the customers cannot change them.
We do that using the [Deposit via payment page](/v2/api-reference/payment-page/deposit-via-payment-page) endpoint.
```json {} theme={null}
POST https://api.sandbox.pawapay.io/v2/paymentpage
{
"depositId": "695776cf-73ba-42ff-b9cb-2b9acc008e22",
"returnUrl": "https://merchant.com/returnUrl",
"msisdn": "233593456789",
"amount": "100",
"reason": "Demo payment"
}
```
In the request we have specified the `msisdn` (phone number) that **must** be used for the payment.
We have also fixed the `amount` to 100 so it cannot be changed by the customer.
In the response you will receive the `redirectUrl`.
```json theme={null}
{
"redirectUrl": "https://sandbox.paywith.pawapay.io/?token=AgV4iTX%2FzQ2Jryg0teMwiVww5uf2OJYyCVbsZO3ERr8vW80AkAADABVhd3MtY3J5cHRvLXB1YmxpYy1rZXkAREFnWjh5OWZ2enVLNXVlZmRhQ3lwaUs4UCsxU3kyZllSanJtdk81Sis1eWxFYmwxR2VubmgwNkJhSmpMa2t2Y1M1QT09AAdwdXJwb3NlAA5jcmVhdGUtc2Vzc2lvbgAFc3RhZ2UAD3NpZ24tY2xvdWRmcm9udAACAAdhd3Mta21zAE5hcm46YXdzOmttczpldS1jZW50cmFsLTE6NDgwMTk5MzI1NDYzOmtleS9hOWRkZTRkMC1iOTAyLTQ5NzgtYjA5NS1hN2M2N2JiM2Y2YWQAuAECAQB4S2upLB%2B%2FYU%2FEVudxFv5jvmTrgfd74VlX4aL%2Bnszo7yIBKG0J%2Fs4QSpOHpiKVsGFhZAAAAH4wfAYJKoZIhvcNAQcGoG8wbQIBADBoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDCDncSOhrmk9d5l6NwIBEIA7vkZSLecrmFtub%2FRif%2F6hHTXTiC9%2Bv98fV%2F4VLtkqKFd0vuZgZaWdQBKsHFyTZarMA4fRKtTffzqHNfcAB2F3cy1rbXMATmFybjphd3M6a21zOmV1LWNlbnRyYWwtMTo0ODAxOTkzMjU0NjM6a2V5L2E5ZGRlNGQwLWI5MDItNDk3OC1iMDk1LWE3YzY3YmIzZjZhZAC4AQICAHhLa6ksH79hT8RW53EW%2FmO%2BZOuB93vhWVfhov6ezOjvIgFj2PbqXlPVVqGVyUyAparTAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMq7enbFjL5gp6GpBDAgEQgDviHiuhaSeHyBkKFWzxjPba%2BTawnP2%2Fa0nVA2fkzrkQS9DULIoLgktu8MRodlDwj38nqiR84qCLy3bUNQIAABAAw6RHjDCLE0oV4Sb5i39layxoxK4W%2FYDFy8Ctn5EnHah%2FewL78joydsqjjsR2duOf%2F%2F%2F%2F%2FwAAAAEAAAAAAAAAAAAAAAEAAAEWF8lZFuQT%2FNGvGwYYILR4k9DRCeHxYgKI%2FiTLjdLdq6PcfxFjdr6dxqvsFr0ntNCXnlGawjbUcMQvwNqBFbM5YGlWQC5SNjblK305ycuH8NOTY4U5j%2BWOKwf%2BlKgrSzT1plmHLk6vDDLxxnE2Wbe1nhQMyyUxIHdKoODaAcQl%2BBsMkSLrkfVIFaTQXtGtWSK24ImD%2BOaTijY8OYg06bprnDe6SujDJx7ZbpBrFQZxtvM9MRfsypAFJe5zn5pn2Xwu4W2goyRlweHbqR%2BufqxijYYAOnCSXr6bxDu%2FQtT763HHAzaBiVCI%2FAXYoy62mp%2B1mdICERxeYSls4eteomyjA7vN8ktOCotSm0HBBmsxtsq6EhTKyFK0cWTJrW6992qJqSUv%2BK%2B7AGcwZQIwL%2BT%2BRduDkmGBMn45cRuvV0Hef4Odd9M5CknNBnz9UsXhqGDqeX55PRoFCfEr4gTvAjEA9xOpVLwF%2F1tmKa2CBSeGf0ckK%2BsMRkEnE8CRhRXPCxV0YsYI1mPAr40ZTlfIoM8U&depositId=6f3ae557-334e-48bb-bd73-ff04767b224f&returnUrl=https%3A%2F%2Fmerchant.com%2FreturnUrl&msisdn=233593456789&amount=100&country=GHA&reason=Demo%20payment&language=en&correspondent=MTN_MOMO_GHA&Expires=1748586499&Key-Pair-Id=K28YQ8X3BNV7W5&Signature=Jm6d1iEKKTO5TC1-t2J5-4d4I6AWtMEeyPiCHyytiDiruMZSMhkcPkMVpGI0CFfXxnQM9qGA9JmxcB4UcPkM3QTCTSPwmhyWp6FUAjhjR98sRfco2UfTtl1o9TjurcandF~Y5by5FqmWZlVF3QnkbXJjV5-tnPQitmQqyBL5vAhSw9JOUnUIn~dKZMO15V5s~-CL1FiqV1R~lXHrHtyiFBmNvukF1FDZaGAjvPhbcQKHOI5lK~6nzaQJDXP~SyXAGpSlOvoRWru0AuDDP9kdYIK8qT6UsCpNZiJrxx0ByR1Qq494d9ncn2viz0Tla8~6G1qTKgm3Z0lXTB-WgAmGEw__"
}
```
You should redirect the customer to the `redirectUrl`.
Once they have completed the payment process, they will be redirected to the `returnUrl` you provided in the request.
We've now created a payment page and redirected the customer to it.
Now let's take a look at how to find out whether the payment was completed successfully.
## How to find out the payment result
When the customer initiates the payment by pressing "Pay" on the payment page, the deposit will be registered in PawaPay with the `depositId` you specified.
When the payment completes you will receive a [deposit callback](/v2/api-reference/deposits/deposit-callback) with the final status of the payment.
If you have not configured callbacks, you can poll the [check deposit status](/v2/api-reference/deposits/check-deposit-status) endpoint.
Please note that the deposit will only be initiated when the customer presses the pay button.
If they abandon the payment page, the deposit will be `NOT_FOUND` and should be considered `FAILED` after 15 minutes.
On your `returnUrl` you should validate the final status of the payment by either confirming the callback has been received or using the [check deposit status](/v2/api-reference/deposits/check-deposit-status) endpoint.
The payment page session will be active for **15 minutes** after which it will expire.
No callback will be delivered on expiration.
Also, if the customer abandons the payment page, no callback will be delivered.
We now know what happened to the payment and can make sure it's reflected accurately.
Let's now take a look at how to handle failed payments.
## Handling processing failures
If the `status` of the deposit is `FAILED` you can find further information about the failure from `failureReason`.
It includes the `failureCode` and the `failureMessage` indicating what has gone wrong.
The `failureMessage` from PawaPay API is meant for you and your support and operations teams.
You are free to decide what message to show to the customer.
Find all the [failure codes](/v2/docs/failure_codes#transaction-failure-codes) and implement handling as you choose.
We recommend showing the customer the failure reason and an easy way to retry the payment in case of failure.
A new payment page needs to be created with a new `depositId` for the retry.
We have standardised the numerous different failure codes and scenarios with all the different providers.
The quality of the failure codes varies by provider.
The `UNSPECIFIED_FAILURE` code indicates that the provider indicated a failure with the payment, but did not provide any more specifics on the reason of the failure.
In case there is a general failure, the `UNKNOWN_ERROR` failureCode would be returned.
We have now also taken care of failures that can happen during payment processing.
This way the customer knows what has happened and can take appropriate action to try again.
Now let's see how to ensure that payment statuses between your system and PawaPay are in sync.
## Ensuring consistency
When working with financial APIs there are some considerations to take to ensure that you never think a payment is failed, when it is actually successful or vice versa.
It is essential to keep systems in sync on the statuses of payments.
Let's take a look at some considerations and pseudocode to ensure consistency.
All statuses should be checked defensively without assumptions.
```json theme={null}
if( status == "COMPLETED" ) {
myInvoice.setPaymentStatus(COMPLETED);
} else if ( status == "FAILED" ) {
myInvoice.setPaymentStatus(FAILED);
} else if ( status == "PROCESSING") {
handleRedirectionAuth();
} else {
//It is unclear what might have failed. Escalate for further investigation.
myInvoice.setPaymentStatus(NEEDS_ATTENTION);
}
```
The key reason we require you to provide a `depositId` for each payment is to ensure that you can always ask us what the status of a payment is, even if you never get a response from us.
You should always store this `depositId` in your system **before** [initiating a deposit](/v2/api-reference/deposits/initiate-deposit).
```json theme={null}
var depositId = new UUIDv4();
//Let's store the depositId we will use to ensure we always have it available even if something dramatic happens
myInvoice.setExternalPaymentId(depositId).save();
myInvoice.setPaymentStatus(PENDING);
try {
var initiationResponse = PawaPay.initiateDeposit(depositId, ...)
} catch (InterruptedException e) {
var checkResult = PawaPay.checkDepositStatus(depositId);
if ( result.status == "FOUND" ) {
//The payment reached PawaPay. Check the status of it from the response.
} else if ( result.status == "NOT_FOUND" ) {
//The payment did not reach PawaPay. Safe to mark it as failed.
myInvoice.setPaymentStatus(FAILED);
} else {
//Unable to determine the status. Leave the payment as pending.
//We will create a status recheck cycle later for such cases.
//In case of a system crash, we should also leave the payment in pending status to be handled in the status recheck cycle.
}
}
```
The important thing to notice here is that we only mark a payment as FAILED when there is a clear indication of its failure.
We use the [check deposit status](/v2/api-reference/deposits/check-deposit-status) endpoint when in doubt whether the payment was `ACCEPTED` by PawaPay.
Implementing the considerations listed above avoids almost all discrepancies of payment statuses between your system and PawaPay.
When using callbacks to receive the final statuses of payments, issues like network connectivity, system downtime, and configuration errors might cause the callback not to be received by your system.
To avoid keeping your customers waiting, we strongly recommend implementing a status recheck cycle.
This might look something like the following.
```json theme={null}
//Run the job every few minutes.
var pendingInvoices = invoices.getAllPendingForLongerThan15Minutes();
for ( invoice in pendingInvoices ) {
var checkResult = PawaPay.checkDepositStatus(invoice.getExternalPaymentId);
if ( checkResult.status == "FOUND" ) {
//Determine if the payment is in a final status and handle accordingly
handleInvoiceStatus(checkResult.data);
} else if (checkResult.status == "NOT_FOUND" ) {
//The payment has never reached PawaPay. Can be failed safely.
invoice.setPaymentStatus(FAILED);
} else {
//Something must have gone wrong. Leave for next cycle.
}
}
```
Having followed the rest of the guide, with this simple reconciliation cycle, you should not have any inconsistencies between your system and PawaPay.
Having these checks automated will take a load off your operations and support teams as well.
## Payments in reconciliation
When using PawaPay, you might find that a payment status is `IN_RECONCILIATION`.
This means that there was a problem determining the correct final status of a payment.
When using PawaPay **all payments are reconciled** by default and automatically - we validate all final statuses to ensure there are no discrepancies.
When encountering payments that are `IN_RECONCILIATION` you do not need to take any action.
The payment has already been sent to our automatic reconciliation engine and it's final status will be determined soon.
The reconciliation time varies by provider. Payments that turn out to be successful are reconciled faster.
## What to do next?
We've made everything easy to test in our sandbox environment before going live.
We have different phone numbers that you can use to test various failure scenarios on your sandbox account.
Make sure all the failure codes are handled.
To ensure your funds are safe even if your API token should leak, you can always implement signatures for financial calls to add another layer of security.
Have a look at what to consider to make sure everything goes well.
# Payouts
Source: https://docs.pawapay.io/v2/docs/payouts
Payouts allow you to send money to a customer. Funds will be moved from your country wallet in PawaPay to the customers mobile money wallet.
In this guide, we’ll walk through how to build a high-quality payout flow step by step.
If you haven't already, check out the following information to set you up for success with this guide.
Understand some considerations to take into account when working with mobile money.
Sort out API tokens and callbacks.
## Initiating a payout
Let's start by hardcoding everything.
Throughout this guide we will be taking care of more and more details to make sure everything is accurate.
Let's send this payload to the [initiate payout](/v2/api-reference/payouts/initiate-payout) endpoint.
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/payouts
{
"payoutId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"amount": "100",
"currency": "RWF",
"recipient": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "250783456789",
"provider": "MTN_MOMO_RWA"
}
}
}
```
Let's see what's in this payload.
We ask you to generate a UUIDv4 `payoutId` to uniquely identify this payout.
This is so that you always have a reference to the payout you are initiating, even if you do not receive a response from us due to network errors.
This allows you to always **reconcile all payments** between your system and PawaPay.
You should store this payoutId in your system **before** initiating the payout with PawaPay.
The `amount` and `currency` should be relatively self-explanatory.
This is the `amount` that will be disbursed to the customer.
Any fees will be deducted from your PawaPay wallet balance on successful completion of the payout.
Principal amount (the amount specified in `amount`) will be reserved from your PawaPay wallet.
If the payout fails, the funds will be returned to your PawaPay wallet immediately.
Then the `provider` and `phoneNumber` specify who is paying exactly.
Mobile money wallets are identified by the phone number, like bank accounts are by their IBAN.
The `provider` specifies which mobile money operator this wallet is registered at. For example MTN Ghana or MPesa Kenya.
You will receive a response for the initiation.
```json {3} theme={null}
{
"payoutId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"status": "ACCEPTED",
"created": "2025-05-15T07:38:56Z"
}
```
The `status` shows whether this payout was `ACCEPTED` for processing or not.
We will go through failure modes later in the guide.
Payouts do not require any authorisation or other action from the customer to receive it.
The payout is usually processed within a few seconds.
The customer will get an SMS receipt informing them that they have received a payout.
You will receive a [callback](/v2/docs/what_to_know#callbacks) from us to your configured callback URL.
```json {3} theme={null}
{
"payoutId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"status": "COMPLETED",
"amount": "100.00",
"currency": "RWF",
"country": "RWA",
"recipient": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "250783456789",
"provider": "MTN_MOMO_RWA"
}
},
"customerMessage": "DEMO",
"created": "2025-05-15T07:38:56Z",
"providerTransactionId": "df0e9405-fb17-42c2-a264-440c239f67ed"
}
```
The main thing to focus on here is the `status` which is `COMPLETED` indicating that the payout has been processed successfully.
If you have not configured callbacks, you can always poll the [check payout status](/v2/api-reference/payouts/check-payout-status) endpoint for the final status of the payment.
Congratulations! You have now made your very first payout with PawaPay.
We made a call to PawaPay to initiate the payout.
And we found out the final status of that payment.
But there's a little more to a high quality integration. In the next sections, we will go into more details on:
* How to make this easy to maintain
* How to handle failures so that discrepancies between your system and PawaPay don't happen
* And much more...
## Getting the phone number from the customer
To make a payout to a customer, we need to have their valid phone number and need to know the provider they are using.
We suggest validating that information when you are receiving that information whether that is during account signup or a separate process.
In the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint there is already the `prefix` within the country object.
This is the country calling code for that country.
We recommend showing that in front of the input box for entering the phone number.
This makes it clear that the number entered should not include the country code.
We have also added `flag` to the country object to make it look a little nicer.
The providers that have been configured on your PawaPay account for payouts are in the `providers` property.
It includes both the `provider` code to use in calls to [initiate a payout](/v2/api-reference/payouts/initiate-payout) and the `displayName` for it.
To enable new providers being automatically added to your payout flows, we've also added the logo of the provider as the `logo`.
This way it is possible to implement the payout flow dynamically, so that when new providers are enabled on your PawaPay account, they become available for your customers without additional effort.
```json {8,14,16,28} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=RWA&operationType=PAYOUT
{
"companyName": "Demo",
"countries": [
{
"country": "RWA",
"prefix": "250",
"flag": "https://static-content.pawapay.io/country_flags/rwa.svg",
"displayName": {
"en": "Rwanda",
"fr": "Rwanda"
},
"providers": [
{
"provider": "AIRTEL_RWA",
"displayName": "Airtel",
"logo": "https://static-content.pawapay.io/company_logos/airtel.png",
"currencies": [
{
"currency": "RWF",
"displayName": "R₣",
"operationTypes": ...
}
]
},
{
"provider": "MTN_MOMO_RWA",
"displayName": "MTN",
"logo": "https://static-content.pawapay.io/company_logos/mtn.png",
"currencies": [
{
"currency": "RWF",
"displayName": "R₣",
"operationTypes": ...
}
]
}
]
}
]
...
}
```
You can use the above information to collect the phone number together with the provider.
Do not have a default provider. For example, as the first selection in a dropdown.
This often gets missed by customers causing payments to fail due to being sent to the wrong provider.
To make sure the phone number is a valid phone number, let's use the [predict provider](/v2/api-reference/toolkit/predict-provider) endpoint.
First, concatenate the `prefix` to what was entered as the phone number.
You can now send it to validation. We will handle basic things like whitespace, characters etc.
We also make sure the number of digits is correct for the country and handle leading zeros.
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/predict-provider
{
"phoneNumber": "25007 834-56789a"
}
```
If the phone number is not valid, a failure will be returned.
You can show the customer a validation error.
Otherwise, you will get the following response:
```json theme={null}
{
"country": "RWA",
"provider": "MTN_MOMO_RWA",
"phoneNumber": "250783456789"
}
```
The `phoneNumber` is the sanitized MSISDN format of the phone number that you can use to initiate the payout.
We strongly recommend using this endpoint for validating numbers especially due to some countries not strictly following ITU E.164.
Also, in the response, you will find the `provider`. This is the predicted provider for this `phoneNumber`.
We recommend preselecting the predicted provider for the customer.
In most countries we see very high accuracy for predictions removing another step from the payment experience.
We recommend making it possible to override the prediction as the accuracy is not 100%.
Now all information is either coming from the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint, the customer and your system (the amount to pay).
We can call the [initiate payout](/v2/api-reference/payouts/initiate-payout) endpoint with that information.
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/payouts
{
"payoutId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"amount": "100",
"currency": "RWF",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "250783456789",
"provider": "MTN_MOMO_RWA"
}
}
}
```
The response and callback with the final status are the same as in the first part of the guide.
We now made a real payout flow that collects information from different sources and avoids any hardcoded data.
This way, enabling new providers will not cause additional changes for you.
Now let's handle the amounts correctly.
## Handling amounts
Depending on your use case, the amount will either be chosen by the customer or determined by you.
As the support for decimals in amount differs by provider and transaction limits apply, let's take a look at how to handle that.
Not all providers support decimals in amount - amounts like 100.50 will fail to process.
This information is available for each provider in [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint.
```json {24,41} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=BEN&operationType=PAYOUT
{
"companyName": "Demo",
"countries": [
{
"country": "BEN",
"displayName": {
"fr": "Bénin",
"en": "Benin"
},
"prefix": "229",
"providers": [
{
"provider": "MOOV_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"PAYOUT": {
...
"decimalsInAmount": "NONE",
...
}
}
}
]
},
{
"provider": "MTN_MOMO_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"PAYOUT": {
...
"decimalsInAmount": "NONE",
...
}
}
}
]
}
]
}
]
...
}
```
The possible values are `NONE` and `TWO_PLACES`.
It's easy to provide dynamic validation now that is appropriate for the provider.
If the amount is determined by your system, you can apply rounding rules based on the above.
If you are preparing the payout amounts beforehand and do not need to do it dynamically, this information is also documented in the [providers](/v2/docs/providers) section.
Customers' mobile money wallets have limits on how big the payments can be.
Customers are able to get those limits increased on their mobile money wallets by contacting their provider and going through extended KYC.
By default, we have set the transaction limits on your PawaPay account based on the most common wallet limits.
The limits for the provider will be available from the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint.
```json {24,25,42,43} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=BEN&operationType=PAYOUT
{
"companyName": "Demo",
"countries": [
{
"country": "BEN",
"displayName": {
"fr": "Bénin",
"en": "Benin"
},
"prefix": "229",
"providers": [
{
"provider": "MOOV_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"PAYOUT": {
...
"minAmount": "100",
"maxAmount": "2000000",
...
}
}
}
]
},
{
"provider": "MTN_MOMO_BEN",
"nameDisplayedToCustomer": "PAWAPAY",
"currencies": [
{
"currency": "XOF",
"displayName": "CFA",
"operationTypes": {
"PAYOUT": {
...
"minAmount": "100",
"maxAmount": "2000000",
...
}
}
}
]
}
]
}
]
...
}
```
It's easy to provide validation ensuring the amount is between `minAmount` and `maxAmount`.
You now have validation that amount is correct for the payout.
Let's take a look at enqueued payouts next.
## Handling enqueued payouts and provider availability
Providers might have downtime in processing payouts.
Let's take a look at how to best handle cases when payout processing may be delayed or temporarily unavailable.
Providers may have downtime. We monitor providers performance and availability 24/7.
For your operational and support teams, we have a [status page](https://status.pawapay.io).
From there they can **subscribe** to get updates in email or slack for all the providers they are interested in.
To avoid the customer getting failed or delayed payouts, we've also exposed this information over API from the both the [provider availability](/v2/api-reference/toolkit/availability) and [active configuration](/v2/api-reference/toolkit/active-configuration) endpoints.
This way you can be up front with the customer that their payout might take more time to be delivered.
Let's use the [provider availability](/v2/api-reference/toolkit/availability) endpoint here as it's more common for payout use cases.
We are filtering it to the specific country and operationType with the query parameters.
```json {10,16} theme={null}
GET https://api.sandbox.pawapay.io//v2/availability?country=BEN&operationType=PAYOUT
[
{
"country": "BEN",
"providers": [
{
"provider": "MOOV_BEN",
"operationTypes": {
"PAYOUT": "OPERATIONAL"
}
},
{
"provider": "MTN_MOMO_BEN",
"operationTypes": {
"PAYOUT": "DELAYED"
}
}
]
}
]
```
The values you might see are:
* `OPERATIONAL` - the provider is available and processing payouts normally.
* `DELAYED` - the provider is having downtime and all payout requests are being enqueued in PawaPay.
* `CLOSED` - the provider is having downtime and PawaPay is rejecting all payout requests.
Based on the above information you can inform the customer that their payout will be delayed (`DELAYED`) or is not possible at the moment and they can try again later (`CLOSED`).
If your payouts are not initiated by customers, you can validate the status of the provider and delay initiating payouts while the provider is unavailable (`CLOSED`).
We rarely close payouts processing. Mostly we switch off processing, but enqueue your payout requests to be processed when the provider is operational again.
All payouts initiated when the status of the providers payouts is `DELAYED` will be `ACCEPTED` on initiation.
They will then be moved to the `ENQUEUED` status.
Our 24/7 payment operations team will be monitoring the provider for when their payout service becomes operational again.
When that happens, the payouts will get processed as usual.
To find out whether a payout is enqueued, you can [check payout status](/v2/api-reference/payouts/check-payout-status).
```json {} theme={null}
GET https://api.sandbox.pawapay.io/v2/payouts/37b250e0-3075-42c8-92a9-cad55b3d86c8
{
"status": "FOUND",
"data": {
"payoutId": "37b250e0-3075-42c8-92a9-cad55b3d86c8",
"status": "ENQUEUED",
"amount": "100.00",
"currency": "XOF",
"country": "BEN",
"recipient": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "22951345789",
"provider": "MTN_MOMO_BEN"
}
},
"customerMessage": "DEMO",
"created": "2025-05-28T06:28:29Z",
}
}
```
You do not need to take any action to leave the payout to be processed once the provider is operational again.
If you want to cancel the payout while it's enqueued, you use the [cancel enqueued payout](/v2/api-reference/payouts/cancel-enqueued-payout) endpoint.
```json theme={null}
GET https://api.sandbox.pawapay.io/v2/payouts/fail-enqueued/37b250e0-3075-42c8-92a9-cad55b3d86c8
RESPONSE:
{
"payoutId": "37b250e0-3075-42c8-92a9-cad55b3d86c8",
"status": "ACCEPTED"
}
```
The request is asynchronous.
The payout must be in `ENQUEUED` status.
If the payout is already in `PROCESSING` status, the request will be rejected.
You will receive a callback with a `FAILED` status when cancellation has been completed.
## Handling failures on initiation
Having implemented the above suggestions payout initiations should never be rejected.
Let's take a look at a couple of edge cases to make sure everything is handled.
The `UNKNOWN_ERROR` failureCode indicates that something unexpected has gone wrong when processing the payment.
There is no `status` in the response in this case.
It is **not safe** to assume the initiation has failed for this payout.
You should verify the status of the payout using the [check payout status](/v2/api-reference/payouts/check-payout-status) endpoint.
Only if the payout is `NOT_FOUND` should it be considered `FAILED`.
We will take a look later in the guide, how to ensure consistency of payment statuses between your system and PawaPay.
Initiating payouts should be handled now.
Next, let's take a look at payment failures that can happen during processing.
## Handling processing failures
As the PawaPay API is asynchronous, you will get a [payout callback](/v2/api-reference/payouts/payout-callback) with the final status of the payout.
If the `status` of the payout is `FAILED` you can find further information about the failure from `failureReason`.
It includes the `failureCode` and the `failureMessage` indicating what has gone wrong.
The `failureMessage` from PawaPay API is meant for you and your support and operations teams.
You are free to decide what message to show to the customer.
Find all the [failure codes](/v2/docs/failure_codes#transaction-failure-codes) and implement handling as you choose.
We have standardised the numerous different failure codes and scenarios with all the different providers.
The quality of the failure codes varies by provider.
The `UNSPECIFIED_FAILURE` code indicates that the provider indicated a failure with the payment, but did not provide any more specifics on the reason of the failure.
In case there is a general failure, the `UNKNOWN_ERROR` failureCode would be returned.
We have now also taken care of failures that can happen during payment processing.
This way the customer knows what has happened and can take appropriate action to try again.
Now let's take a look at how to ensure consistency of statuses between you and PawaPay.
## Ensuring consistency
When working with financial APIs there are some considerations to take to ensure that you never think a payment is failed, when it is actually successful or vice versa.
It is essential to keep systems in sync on the statuses of payments.
Let's take a look at some considerations and pseudocode to ensure consistency.
All statuses should be checked defensively without assumptions.
```json theme={null}
if( status == "COMPLETED" ) {
myOrder.setPaymentStatus(COMPLETED);
} else if ( status == "FAILED" ) {
myOrder.setPaymentStatus(FAILED);
} else if ( status == "PROCESSING") {
waitForProcessingToComplete();
} else if( status == "ENQUEUED" ) {
determineIfShouldBeCancelled();
} else {
//It is unclear what might have failed. Escalate for further investigation.
myOrder.setPaymentStatus(NEEDS_ATTENTION);
}
```
The key reason we require you to provide a `payoutId` for each payment is to ensure that you can always ask us what is the status of a payment, even if you never get a response from us.
You should always store this `payoutId` in your system **before** [initiating a payout](/v2/api-reference/payouts/initiate-payout).
```json theme={null}
var payoutId = new UUIDv4();
//Let's store the payoutId we will use to ensure we always have it available even if something dramatic happens
myOrder.setExternalPaymentId(payoutId).save();
myOrder.setPaymentStatus(PENDING);
try {
var initiationResponse = PawaPay.initiatePayout(payoutId, ...)
} catch (InterruptedException e) {
var checkResult = PawaPay.checkPayoutStatus(payoutId);
if ( result.status == "FOUND" ) {
//The payment reached PawaPay. Check the status of it from the response.
} else if ( result.status == "NOT_FOUND" ) {
//The payment did not reach PawaPay. Safe to mark it as failed.
myOrder.setPaymentStatus(FAILED);
} else {
//Unable to determine the status. Leave the payment as pending.
//We will create a status recheck cycle later for such cases.
//In case of a system crash, we should also leave the payment in pending status to be handled in the status recheck cycle.
}
}
```
The important thing to notice here is that we only mark a payment as FAILED when there is a clear indication of its failure.
We use the [check payout status](/v2/api-reference/payouts/check-payout-status) endpoint when in doubt whether the payment was `ACCEPTED` by PawaPay.
Implementing the considerations listed above avoids almost all discrepancies of payment statuses between your system and PawaPay.
When using callbacks to receive the final statuses of payments, issues like network connectivity, system downtime, and configuration errors might cause the callback not to be received by your system.
To avoid keeping your customers waiting, we strongly recommend implementing a status recheck cycle.
This might look something like the following.
```json theme={null}
//Run the job every few minutes.
var pendingOrders = orders.getAllPendingForLongerThan15Minutes();
for ( order in pendingOrders ) {
var checkResult = PawaPay.checkPayoutStatus(order.getExternalPaymentId);
if ( checkResult.status == "FOUND" ) {
//Determine if the payment is in a final status and handle accordingly
handleOrderStatus(checkResult.data);
} else if (checkResult.status == "NOT_FOUND" ) {
//The payment has never reached PawaPay. Can be failed safely.
invoice.setPaymentStatus(FAILED);
} else {
//Something must have gone wrong. Leave for next cycle.
}
}
```
Having followed the rest of the guide, with this simple reconciliation cycle, you should not have any inconsistencies between your system and PawaPay.
Having these checks automated will take a load off your operations and support teams as well.
## Payments in reconciliation
When using PawaPay, you might find that a payment status is `IN_RECONCILIATION`.
This means that there was a problem determining the correct final status of a payment.
When using PawaPay **all payments are reconciled** by default and automatically - we validate all final statuses to ensure there are no discrepancies.
When encountering payments that are `IN_RECONCILIATION` you do not need to take any action.
The payment has already been sent to our automatic reconciliation engine and it's final status will be determined soon.
The reconciliation time varies by provider. Payments that turn out to be successful are reconciled faster.
## What to do next?
We've made everything easy to test in our sandbox environment before going live.
We have different phone numbers that you can use to test various failure scenarios on your sandbox account.
Make sure all the failure codes are handled.
To ensure your funds are safe even if your API token should leak, you can always implement signatures for financial calls to add another layer of security.
Have a look at what to consider to make sure everything goes well.
# Providers
Source: https://docs.pawapay.io/v2/docs/providers
Providers in our Merchant API refer to the specific Mobile Money Operators (MMOs) that are available through our platform.
They are a mandatory part of the `accountDetails` for both [Deposits](/v2/api-reference/deposits/initiate-deposit) and [Payouts](/v2/api-reference/payouts/initiate-payout).
This ensures the payment is routed to the correct provider.
You can always check which providers are available on your account from the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint.
You can use the [predict provider](/v2/api-reference/toolkit/predict-provider) endpoint to predict the provider for a phone number (MSISDN).
The number of decimal places that can be specified for the `amount` of the payment varies between different providers.
You can use the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint to dynamically fetch whether decimals are supported by the provider.
You can find the list of all available providers, their currency, authorisation type and supported decimal places below.
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| MTN | MTN\_MOMO\_BEN | BEN | XOF | PROVIDER\_AUTH | Not supported |
| Moov | MOOV\_BEN | BEN | XOF | PROVIDER\_AUTH | Not supported |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Moov | MOOV\_BFA | BFA | XOF | PROVIDER\_AUTH | Not supported |
| Orange | ORANGE\_BFA | BFA | XOF | PREAUTH | Not supported |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| MTN | MTN\_MOMO\_CMR | CMR | XAF | PROVIDER\_AUTH | Not supported |
| Orange | ORANGE\_CMR | CMR | XAF | PROVIDER\_AUTH | Not supported |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| MTN | MTN\_MOMO\_CIV | CIV | XOF | PROVIDER\_AUTH | Not supported |
| Orange | ORANGE\_CIV | CIV | XOF | PROVIDER\_AUTH | Not supported |
| Wave | WAVE\_CIV | CIV | XOF | REDIRECT\_AUTH | Not supported |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Vodacom | VODACOM\_MPESA\_COD | COD | CDF | PROVIDER\_AUTH | Not supported |
| | | | USD | PROVIDER\_AUTH | 2 places |
| Airtel | AIRTEL\_COD | COD | CDF | PROVIDER\_AUTH | 2 places |
| | | | USD | PROVIDER\_AUTH | 2 places |
| Orange | ORANGE\_COD | COD | CDF | PROVIDER\_AUTH | 2 places |
| | | | USD | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Safaricom MPesa | MPESA\_ETH | ETH | ETB | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Airtel | AIRTEL\_GAB | GAB | XAF | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| MTN | MTN\_MOMO\_GHA | GHA | GHS | PROVIDER\_AUTH | 2 places |
| AT | AIRTELTIGO\_GHA | GHA | GHS | PROVIDER\_AUTH | 2 places |
| Vodafone | VODAFONE\_GHA | GHA | GHS | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| MPesa | MPESA\_KEN | KEN | KES | PROVIDER\_AUTH | Deposits: not supported; payouts: 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| MPesa | MPESA\_LSO | LSO | LSL | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Airtel | AIRTEL\_MWI | MWI | MWK | PROVIDER\_AUTH | 2 places |
| TNM | TNM\_MWI | MWI | MWK | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Movitel | MOVITEL\_MOZ | MOZ | MZN | PROVIDER\_AUTH | Deposits: not supported; payouts: 2 places |
| Vodacom | VODACOM\_MOZ | MOZ | MZN | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Airtel | AIRTEL\_NGA | NGA | NGN | PROVIDER\_AUTH | Not supported |
| MTN | MTN\_MOMO\_NGA | NGA | NGN | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Airtel | AIRTEL\_COG | COG | XAF | PROVIDER\_AUTH | Not supported |
| MTN | MTN\_MOMO\_COG | COG | XAF | PROVIDER\_AUTH | Not supported |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Airtel | AIRTEL\_RWA | RWA | RWF | PROVIDER\_AUTH | Not supported |
| MTN | MTN\_MOMO\_RWA | RWA | RWF | PROVIDER\_AUTH | Not supported |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Free | FREE\_SEN | SEN | XOF | PROVIDER\_AUTH | Not supported |
| Orange | ORANGE\_SEN | SEN | XOF | PROVIDER\_AUTH | Not supported |
| Wave | WAVE\_SEN | SEN | XOF | REDIRECT\_AUTH | Not supported |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Orange | ORANGE\_SLE | SLE | SLE | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Airtel | AIRTEL\_TZA | TZA | TZS | PROVIDER\_AUTH | 2 places |
| Vodacom | VODACOM\_TZA | TZA | TZS | PROVIDER\_AUTH | Not supported |
| Tigo | TIGO\_TZA | TZA | TZS | PROVIDER\_AUTH | Not supported |
| Halotel | HALOTEL\_TZA | TZA | TZS | PROVIDER\_AUTH | Not supported |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Airtel | AIRTEL\_OAPI\_UGA | UGA | UGX | PROVIDER\_AUTH | Not supported |
| MTN | MTN\_MOMO\_UGA | UGA | UGX | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| Airtel | AIRTEL\_OAPI\_ZMB | ZMB | ZMW | PROVIDER\_AUTH | 2 places |
| MTN | MTN\_MOMO\_ZMB | ZMB | ZMW | PROVIDER\_AUTH | 2 places |
| Zamtel | ZAMTEL\_ZMB | ZMB | ZMW | PROVIDER\_AUTH | 2 places |
| MMO | Provider | Country | Currency | Authorisation | Decimals in amount |
| - | - | - | - | - | - |
| EcoCash | ECOCASH\_ZWE | ZWE | USD | PROVIDER\_AUTH | 2 places |
# Refunds
Source: https://docs.pawapay.io/v2/docs/refunds
Refunds allow you to return successfully collected funds back to the customer.
The funds will be moved from your wallet in PawaPay to the customer's mobile money wallet.
In this guide, we'll go through step by step on how to refund a deposit.
If you haven't already, check out the following information to set you up for success with this guide.
Understand some considerations to take into account when working with mobile money.
Sort out API tokens and callbacks.
## Initiating a refund
Let's start by hard-coding everything.
Let's send this payload to the [initiate refund](/v2/api-reference/refunds/initiate-refund) endpoint.
```json {5} theme={null}
POST https://api.sandbox.pawapay.io/v2/refunds
{
"refundId": "f02b543c-541c-4f21-bbea-20d2d56063d6",
"depositId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79"
}
```
We ask you to generate a UUIDv4 `refundId` to uniquely identify this refund.
This is so that you always have a reference to the refund you are initiating, even if you do not receive a response from us due to network errors.
This allows you to always **reconcile all payments** between your system and PawaPay.
You should store this refundId in your system **before** initiating the refund with PawaPay.
The `depositId` refers to the deposit you are looking to refund.
You will receive a response for the initiation.
```json {3} theme={null}
{
"payoutId": "f02b543c-541c-4f21-bbea-20d2d56063d6",
"status": "ACCEPTED",
"created": "2025-05-15T07:38:56Z"
}
```
The `status` shows whether this payout was `ACCEPTED` for processing or not.
We will go through failure modes later in the guide.
Refunds do not require any authorisation or other action from the customer to receive it.
The refund is usually processed within a few seconds.
The customer will get an SMS receipt informing them that they have received a refund.
You will receive a [callback](/v2/docs/what_to_know#callbacks) from us to your configured callback URL.
```json {3} theme={null}
{
"refundId": "64d4a574-5204-4700-bf75-acce936b8648",
"status": "COMPLETED",
"amount": "100.00",
"currency": "RWF",
"country": "RWA",
"recipient": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "260973456789",
"provider": "MTN_MOMO_RWA"
}
},
"customerMessage": "Google One",
"created": "2025-05-15T07:38:56Z"
}
```
The main thing to focus on here is the `status` which is `COMPLETED` indicating that the refund has been processed successfully.
If you have not configured callbacks, you can always poll the [check refund status](/v2/api-reference/refunds/check-refund-status) endpoint for the final status of the payment.
Congratulations! You have now made your very first refund with PawaPay.
We made a call to PawaPay to initiate the refund.
And we found out the final status of that refund.
The customer has been refunded the full amount of their deposit.
Next let's take a look at partial refunds.
## Partial refund
Sometimes only a part of the original payment should get refunded. Let's take a look at how to do that.
Let's send this payload to the [initiate a partial refund](/v2/api-reference/refunds/initiate-refund) endpoint.
```json {6,7} theme={null}
POST https://api.sandbox.pawapay.io/v2/refunds
{
"refundId": "f7232951-ab27-4175-bb63-6e15a9516df6",
"depositId": "afb57b93-7849-49aa-babb-4c3ccbfe3d79",
"amount": "30",
"currency": "RWF"
}
```
We are now specifying the `amount` and the `currency` of how much should be refunded.
Otherwise everything stays the same. The request should be `ACCEPTED` and you will receive a callback once it's processed.
You can initiate multiple partial refunds as long as the total amount does not exceed the amount of the original deposit.
If the total amount of all refunds exceeds the amount of the original deposit, the refund will be `REJECTED`.
```json {3, 5} theme={null}
{
"refundId": "bacec0ff-7e5b-4db7-bd2d-b49a9c6603e3",
"status": "REJECTED",
"rejectionReason": {
"rejectionCode": "AMOUNT_TOO_LARGE",
"rejectionMessage": "Amount should not be greater than 100"
}
}
```
If a full refund (no `amount` specified) is initiated after a successfully processed partial refund, the remaining amount will be refunded.
For example:
* Original deposit: 100 RWF
* Partial refund: 20 RWF
* Partial refund: 20 RWF
* Full refund (`amount` not specified on initiation): 100 - 20 - 20 = 60 RWF
We've now done partial refunds as well.
Let's take a look at other considerations to make sure everything works smoothly.
## Other considerations
For a single deposit, only one refund can be initiated at a time. If there is a refund that is `PROCESSING`, initiating a new refund will be rejected.
```json {3,5,6} theme={null}
{
"refundId": "4dca0890-7da2-4337-b540-b883f43b877c",
"status": "REJECTED",
"rejectionReason": {
"rejectionCode":"REFUND_IN_PROGRESS",
"rejectionMessage": "Another refund transaction is already in progress"
}
}
```
Once the full amount of the original deposit has been refunded, refunds for that deposit will be rejected.
```json {3,5,6} theme={null}
{
"refundId": "200e0f76-0a49-442e-8403-c58c45d22948",
"status": "REJECTED",
"rejectionReason": {
"rejectionCode":"DEPOSIT_ALREADY_REFUNDED",
"rejectionMessage": "Requested deposit has been already refunded"
}
}
```
Not all providers support decimals in amounts.
You can find which providers support them in the [providers](/v2/docs/providers) section.
To dynamically support rounding the amount to refund, this information is exposed in [active-configuration](/v2/api-reference/toolkit/active-configuration) for each provider.
```json {20, 37} theme={null}
GET https://api.sandbox.pawapay.io/v2/active-conf?country=RWA&operationType=REFUND
{
"companyName": "DEMO",
"countries": [
{
"country": "RWA",
...
"providers": [
{
"provider": "AIRTEL_RWA",
...
"currencies": [
{
"currency": "RWF",
...
"operationTypes": {
"REFUND": {
...
"decimalsInAmount": "NONE",
...
}
}
}
]
},
{
"provider": "MTN_MOMO_RWA",
...
"currencies": [
{
"currency": "RWF",
...
"operationTypes": {
"REFUND": {
...
"decimalsInAmount": "NONE",
....
}
}
}
]
}
]
}
],
...
}
```
The `decimalsInAmount` property shows whether the specific provider supports decimals in the amount when doing refunds.
The possible values for it are `TWO_PLACES` and `NONE`.
You can dynamically round the amount if decimals are not supported (`NONE`).
## Handling enqueued refunds and provider availability
Providers might have downtime in processing refunds.
Let's take a look at how to best handle cases when refund processing may be delayed or temporarily unavailable.
Providers may have downtime. We monitor providers performance and availability 24/7.
For your operational and support teams, we have a [status page](https://status.pawapay.io).
From there they can **subscribe** to get updates in email or slack for all the providers they are insterested in.
To avoid the customer getting failed or delayed payouts, we've also exposed this information over API from the both the [provider availability](/v2/api-reference/toolkit/availability) and [active configuration](/v2/api-reference/toolkit/active-configuration) endpoints.
This way you can be up front with the customer that their refund might take more time to be delivered.
Let's use the [provider availability](/v2/api-reference/toolkit/availability) endpoint here as it's more common for refund use cases.
We are filtering it to the specific country and operationType with the query parameters.
```json {10,16} theme={null}
GET https://api.sandbox.pawapay.io//v2/availability?country=BEN&operationType=REFUND
[
{
"country": "BEN",
"providers": [
{
"provider": "MOOV_BEN",
"operationTypes": {
"REFUND": "OPERATIONAL"
}
},
{
"provider": "MTN_MOMO_BEN",
"operationTypes": {
"REFUND": "DELAYED"
}
}
]
}
]
```
The values you might see are:
* `OPERATIONAL` - the provider is available and processing refunds normally.
* `DELAYED` - the provider is having downtime and all refund requests are being enqueued in PawaPay.
* `CLOSED` - the provider is having downtime and PawaPay is rejecting all refund requests.
Based on the above information you can inform the customer that their refund will be delayed (`DELAYED`) or is not possible at the moment and they can try again later (`CLOSED`).
You can validate the status of the provider and delay initiating refunds while the provider is unavailable (`CLOSED`).
We rarely close refunds processing.
Mostly we switch off processing, but enqueue your refund requests to be processed when the provider is operational again.
All refunds initiated when the status of the providers refunds is `DELAYED` will be `ACCEPTED` on initiation.
They will then be moved to the `ENQUEUED` status.
Our 24/7 payment operations team will be monitoring the provider for when their refunds service becomes operational again.
When that happens, the refunds will get processed as usual.
To find out whether a refund is enqueued, you can [check refund status](/v2/api-reference/refunds/check-refund-status).
```json {} theme={null}
GET https://api.sandbox.pawapay.io/v2/payouts/37b250e0-3075-42c8-92a9-cad55b3d86c8
{
"status": "FOUND",
"data": {
"refundId": "37b250e0-3075-42c8-92a9-cad55b3d86c8",
"status": "ENQUEUED",
"amount": "100.00",
"currency": "XOF",
"country": "BEN",
"recipient": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "22951345789",
"provider": "MTN_MOMO_BEN"
}
},
"customerMessage": "DEMO",
"created": "2025-05-28T06:28:29Z",
}
}
```
You do not need to take any action to leave the payout for processing once the provider is operational again.
## Handling failures on initiation
Having implemented the above suggestions, refund initiations should never be rejected.
Let's take a look at a couple of edge cases to make sure everything is handled.
The `UNKNOWN_ERROR` failureCode indicates that something unexpected has gone wrong when processing the payment.
There is no `status` in the response in this case.
It is **not safe** to assume the initiation has failed for this refund.
You should verify the status of the refund using the [check refund status](/v2/api-reference/refunds/check-refund-status) endpoint.
Only if the refund is `NOT_FOUND` should it be considered `FAILED`.
We will take a look later in the guide, how to ensure consistency of payment statuses between your system and PawaPay.
Initiating payouts should be handled now.
Next, let's take a look at payment failures that can happen during processing.
## Handling processing failures
As the PawaPay API is asynchronous, you will get a [refund callback](/v2/api-reference/refunds/refund-callback) with the final status of the refund.
If the `status` of the refund is `FAILED` you can find further information about the failure from `failureReason`.
It includes the `failureCode` and the `failureMessage` indicating what has gone wrong.
The `failureMessage` from PawaPay API is meant for you and your support and operations teams.
You are free to decide what message to show to the customer.
Find all the [failure codes](/v2/docs/failure_codes#transaction-failure-codes) and implement handling as you choose.
We have standardised the numerous different failure codes and scenarios with all the different providers.
The quality of the failure codes varies by provider.
The `UNSPECIFIED_FAILURE` code indicates that the provider indicated a failure with the payment, but did not provide any specifics on the reason of the failure.
In case there is a general failure, the `UNKNOWN_ERROR` failureCode would be returned.
We have now also taken care of failures that can happen during payment processing.
This way the customer knows what has happened and can take appropriate action to try again.
Now let's take a look at how to ensure consistency of statuses between you and PawaPay.
## Ensuring consistency
When working with financial APIs there are some considerations to take to ensure that you never think a payment has failed, when it is actually successful or vice versa.
It is essential to keep systems in sync on the statuses of payments.
Let's take a look at some considerations and pseudocode to ensure consistency.
All statuses should be checked defensively without assumptions.
```json theme={null}
if( status == "COMPLETED" ) {
myOrder.setRefundStatus(COMPLETED);
} else if ( status == "FAILED" ) {
myOrder.setRefundStatus(FAILED);
} else if ( status == "PROCESSING") {
waitForProcessingToComplete();
} else if( status == "ENQUEUED" ) {
determineIfShouldBeCancelled();
} else {
//It is unclear what might have failed. Escalate for further investigation.
myOrder.setRefundStatus(NEEDS_ATTENTION);
}
```
The key reason we require you to provide a `refundId` for each payment is to ensure that you can always ask us what is the status of a payment, even if you never get a response from us.
You should always store this `refundId` in your system **before** [initiating a refund](/v2/api-reference/refunds/initiate-refund).
```json theme={null}
var refundId = new UUIDv4();
//Let's store the refundId we will use to ensure we always have it available even if something dramatic happens
myOrder.setExternalRefundId(refundId).save();
myOrder.setRefundStatus(PENDING);
try {
var initiationResponse = PawaPay.initiateRefund(refundId, ...)
} catch (InterruptedException e) {
var checkResult = PawaPay.checkRefundStatus(payoutId);
if ( result.status == "FOUND" ) {
//The payment reached PawaPay. Check the status of it from the response.
} else if ( result.status == "NOT_FOUND" ) {
//The payment did not reach PawaPay. Safe to mark it as failed.
myOrder.setRefundStatus(FAILED);
} else {
//Unable to determine the status. Leave the payment as pending.
//We will create a status recheck cycle later for such cases.
//In case of a system crash, we should also leave the payment in pending status to be handled in the status recheck cycle.
}
}
```
The important thing to notice here is that we only mark a payment as FAILED when there is a clear indication of its failure.
We use the [check refund status](/v2/api-reference/refunds/check-refund-status) endpoint when in doubt whether the payment was `ACCEPTED` by PawaPay.
Implementing the considerations listed above avoids almost all discrepancies of payment statuses between your system and PawaPay.
When using callbacks to receive the final statuses of payments, issues like network connectivity, system downtime, and configuration errors might cause the callback not to be received by your system.
To avoid keeping your customers waiting, we strongly recommend implementing a status recheck cycle.
This might look something like the following.
```json theme={null}
//Run the job every few minutes.
var pendingRefunds = orders.getAllRefundsPendingForLongerThan15Minutes();
for ( refund in pendingRefunds ) {
var checkResult = PawaPay.checkRefundStatus(refund.getExternalRefundId);
if ( checkResult.status == "FOUND" ) {
//Determine if the payment is in a final status and handle accordingly
handleRefundStatus(checkResult.data);
} else if (checkResult.status == "NOT_FOUND" ) {
//The payment has never reached PawaPay. Can be failed safely.
invoice.setRefundStatus(FAILED);
} else {
//Something must have gone wrong. Leave for next cycle.
}
}
```
Having followed the rest of the guide, with this simple reconciliation cycle, you should not have any inconsistencies between your system and PawaPay.
Having these checks automated will take a load off your operations and support teams as well.
## Payments in reconciliation
When using PawaPay, you might find that a payment status is `IN_RECONCILIATION`.
This means that there was a problem determining the correct final status of a payment.
When using PawaPay **all payments are reconciled** by default and automatically - we validate all final statuses to ensure there are no discrepancies.
When encountering payments that are `IN_RECONCILIATION` you do not need to take any action.
The payment has already been sent to our automatic reconciliation engine and it's final status will be determined soon.
The reconciliation time varies by provider. Payments that turn out to be successful are reconciled faster.
## What to do next?
We've made everything easy to test in our sandbox environment before going live.
We have different phone numbers that you can use to test various failure scenarios on your sandbox account.
Make sure all the failure codes are handled.
To ensure your funds are safe even if your API token should leak, you can always implement signatures for financial calls to add another layer of security.
Have a look at what to consider to make sure everything goes well.
# Signatures in API
Source: https://docs.pawapay.io/v2/docs/signatures
## Signatures
The PawaPay API is secured by the API token as explained in [Authentication](/v2/docs/how_to_start#how-to-authenticate-calls-to-the-merchant-api).
To add a second layer of security, you can optionally sign your financial requests to us - deposit, payout and refund requests.
In this case, PawaPay will only accept financial requests that have been signed by you.
To utilize this additional capability, you should provide your public key in the PawaPay Dashboard and enable this feature.
Read how to do that from the [PawaPay Dashboard Docs](/dashboard/other/system_conf/api_tokens).
This ensures that even if your API token leaks, only you can initiate financial requests with PawaPay.
If configured, PawaPay will also send callbacks to your [callback URLs](/v2/docs/what_to_know#callbacks) with the final status of your payment.
Your network team can whitelist the PawaPay platform IP addresses for these callback URLs.
Additionally, you can also enable PawaPay to sign those callbacks.
You can then validate the signature that is included in the header of the callback to ensure that callbacks are in fact coming from PawaPay and have not been tampered with.
### Signatures in financial requests
Financial requests are requests sent to the PawaPay Merchant API to move funds.
These include [deposits](/v2/api-reference/deposits/initiate-deposit), [payouts](/v2/api-reference/payouts/initiate-payout), [bulk payouts](/v2/api-reference/payouts/initiate-bulk-payout) and [refunds](/v2/api-reference/refunds/initiate-refund).
The implementation of signatures in PawaPay is based on the standard described in [RFC-9421](https://datatracker.ietf.org/doc/rfc9421/).
When creating the financial request to send to the PawaPay Merchant API, you should create a Content-Digest, sign the request and add Signature and Signature-Input headers.
You can find sample node code for signing your requests from [Github](https://github.com/PawaPay/signatures-node-example).
#### Hash the request body
For generating the `Content-Digest` you can use either SHA-256 or SHA-512 algorithm.
The `Content-Digest` should be created from the request body.
Having the request body hashed and available as a header allows verification that the content of the request has not been tampered with.
```http theme={null}
Content-Digest: sha-512=:mXRb9GJnfR/lyXOVfa27Wg+QrRgX3DVhXpQwjxbWoG3BgX7ZHmXLpvQb4il2kxgLjWmj6oSdwDdn5rUAJVYnUw==:
{
"depositId": "d6df5c10-bd43-408c-b622-f10f9eaa568b",
"amount": "15",
"currency": "ZMW",
"payer": {
"type": "MMO",
"address": {
"provider": "MTN_MOMO_ZMB",
"phoneNumber": "260763456789"
}
}
}
```
You can read more about it [here](https://datatracker.ietf.org/doc/html/rfc9421#name-message-content).
#### Create the signature base
For creating the content that will be signed, you need to create a signature base.
This should include all details of the request that should be verifiable. We recommend including at least the following [Derived Components](https://datatracker.ietf.org/doc/html/rfc9421#name-derived-components).
* @method
* @authority
* @path
Also the following headers should be included into the request and the signature base.
* Signature-Date
* Content-Digest
* Content-Type
Let’s take the following example request to initiate a deposit.
```http theme={null}
Authorization: Bearer
Content-Type: application/json; charset=UTF-8
Accept-Encoding: gzip, x-gzip, deflate
Content-Digest: sha-512=:mXRb9GJnfR/lyXOVfa27Wg+QrRgX3DVhXpQwjxbWoG3BgX7ZHmXLpvQb4il2kxgLjWmj6oSdwDdn5rUAJVYnUw==:
Signature-Date: 2024-05-02T15:36:45.058799Z1714653405;expires=1714653465
Accept-Signature: rsa-pss-sha512,ecdsa-p256-sha256,rsa-v1_5-sha256,ecdsa-p384-sha384
Accept-Digest: sha-256,sha-512
{
"depositId": "d6df5c10-bd43-408c-b622-f10f9eaa568b",
"amount": "15",
"currency": "ZMW",
"payer": {
"type": "MMO",
"accountDetails": {
"provider": "MTN_MOMO_ZMB",
"phoneNumber": "260763456789"
}
}
}
```
The signature base for the above request would be the following.
```http theme={null}
"@method": POST
"@authority": localhost:8080
"@path": /deposits
"signature-date": 2024-05-02T15:36:45.058799Z
"content-digest": sha-512=:mXRb9GJnfR/lyXOVfa27Wg+QrRgX3DVhXpQwjxbWoG3BgX7ZHmXLpvQb4il2kxgLjWmj6oSdwDdn5rUAJVYnUw==:
"content-type": application/json; charset=UTF-8
"@signature-params": ("@method" "@authority" "@path" "signature-date" "content-digest" "content-type");alg="ecdsa-p256-sha256";keyid="CUSTOMER_TEST_KEY";created=1714653405;expires=1714653465
```
You can read more about creating the signature base [here](https://datatracker.ietf.org/doc/html/rfc9421#name-creating-the-signature-base).
#### Create the signature
You can use your private key now to sign the signature base. You can use one of the following algorithms:
* [RSASSA-PSS Using SHA-512](https://datatracker.ietf.org/doc/html/rfc9421#name-rsassa-pss-using-sha-512)
* [RSASSA-PKCS1-v1\_5 Using SHA-256](https://datatracker.ietf.org/doc/html/rfc9421#name-rsassa-pkcs1-v1_5-using-sha)
* [ECDSA Using Curve P-256 DSS and SHA-256](https://datatracker.ietf.org/doc/html/rfc9421#name-ecdsa-using-curve-p-256-dss)
* [ECDSA Using Curve P-384 DSS and SHA-384](https://datatracker.ietf.org/doc/html/rfc9421#name-ecdsa-using-curve-p-384-dss)
You can read more about creating the signature [here](https://datatracker.ietf.org/doc/html/rfc9421#name-creating-a-signature).
#### Include Signature and Signature-Input headers
Having generated the signature, you should include it into the `Signature` header of the request.
You also need to create the `Signature-Input` header which outlines the parameters and their order that were used to generate the `Signature` as well as metadata about the signature.
The metadata should include:
* The used algorithm (`alg`)
* The date the signature was created (`created`)
* The expiration date of the keypair (`expires`)
* The id of the key (`keyid`)
This allows PawaPay to validate the basis for the signature against your public key.
Read more about it [here](https://datatracker.ietf.org/doc/html/rfc9421#name-the-signature-input-http-fi).
The final request that can be sent to PawaPay would look as follows.
```http theme={null}
Authorization: Bearer
Content-Type: application/json; charset=UTF-8
Accept-Encoding: gzip, x-gzip, deflate
Content-Digest: sha-512=:mXRb9GJnfR/lyXOVfa27Wg+QrRgX3DVhXpQwjxbWoG3BgX7ZHmXLpvQb4il2kxgLjWmj6oSdwDdn5rUAJVYnUw==:
Signature-Date: 2024-05-02T15:36:45.058799Z
Signature: sig-pp=:MEQCIHoWKI71ADMmqwtwW48CHgfbDWdVItVMNlXTFJjoxmEDAiBTY30Le4wQd3RXqvmYubVwrxuP7Tz1SeZcnsNdHqjJDg==:
Signature-Input: sig-pp=("@method" "@authority" "@path" "signature-date" "content-digest" "content-type");alg="ecdsa-p256-sha256";keyid="CUSTOMER_TEST_KEY";created=1714653405;expires=1714653465
Accept-Signature: rsa-pss-sha512,ecdsa-p256-sha256,rsa-v1_5-sha256,ecdsa-p384-sha384
Accept-Digest: sha-256,sha-512
{
"depositId": "d6df5c10-bd43-408c-b622-f10f9eaa568b",
"amount": "15",
"currency": "ZMW",
"payer": {
"type": "MMO",
"accountDetails": {
"provider": "MTN_MOMO_ZMB",
"phoneNumber": "260763456789"
}
}
}
```
The PawaPay API would respond by accepting the payment for processing with the following response (headers irrelevant for signatures are omitted).
```http theme={null}
Content-Digest: sha-512=:NkvHr2fjqMoKW6nxA6V6jeQXhZyKVAcYdOv6Rmpa2cMn7yZmYDFrPzj/1LiAvOmJkCEdfsS5Bn9N/uZL8nCLZQ==:
Signature-Date: 2024-05-02T15:36:46.084331Z
Signature: sig-pp=:MEUCIFPakg6tQqN33NueVBPCKK4/GJ7BmHqux2yNQqWOEfmRAiEA43SOGd4JvlX2DWuh1oe0nP+/J8POSfr24SwXw2aRHRs=:
Signature-Input: sig-pp=("@status" "signature-date" "content-digest");alg="ecdsa-p256-sha256";keyid="HTTP_EC_P256_KEY:1";created=1714653406;expires=1714653466
{"depositId":"d6df5c10-bd43-408c-b622-f10f9eaa568b","status":"ACCEPTED","created":"2024-05-02T12:36:46Z"}
```
Do not forget to enable signed financial calls and upload your public key in the PawaPay Dashboard. Learn how to do that from the [PawaPay Dashboard Docs](/dashboard/other/system_conf/api_tokens#signed-requests).
#### Make the request
You can now send this request to PawaPay Merchant API to initiate a [deposit](/v2/api-reference/deposits/initiate-deposit), [payout](/v2/api-reference/payouts/initiate-payout), [bulk payout](/v2/api-reference/payouts/initiate-bulk-payout) and [refund](/v2/api-reference/refunds/initiate-refund).
### Signatures in callbacks
When receiving callbacks from PawaPay they will include the following headers.
* Signature
* Signature-Input
* Signature-Date
* Content-Type
* Content-Digest
You can verify that the request has not been tampered with and is coming from PawaPay.
Here is an example callback for a deposit.
```http theme={null}
Content-Type: application/json; charset=UTF-8
Accept-Encoding: gzip, x-gzip, deflate
Content-Digest: sha-512=:0ki7QBS/0MA424uwOq3k5HnJnL5SRkPjit12m0YMpd4JgWiMvm9+yNT3FunkpDaTSsKhTkliQwJlRw9bgsos9w==:
Signature-Date: 2024-05-02T16:45:51.131905Z
Signature: sig-pp=:MEQCIHFvGCUgyxmmowMufO4Yk20pBs3JHRax81si2QZVi9ByAiBPpg1WBhQjZ6fmi3a/gKcWiQ73Qm9Ol35On3c4K/flew==:
Signature-Input: sig-pp=("@method" "@authority" "@path" "signature-date" "content-digest" "content-type");alg="ecdsa-p256-sha256";keyid="CUSTOMER_TEST_KEY";created=1714657551;expires=1714657611
Accept-Signature: rsa-pss-sha512,ecdsa-p256-sha256,rsa-v1_5-sha256,ecdsa-p384-sha384
Accept-Digest: sha-256,sha-512
{
"depositId": "4985d482-454d-4ebc-abc9-ad525eef21b6",
"status": "COMPLETED",
"requestedAmount": "15",
"currency": "ZMW",
"country": "ZMB",
"payer": {
"type": "MMO",
"accountDetails": {
"provider": "MTN_MOMO_ZMB",
"phoneNumber": "260763456789"
}
},
"customerMessage": "Signed deposit",
"created": "2024-05-02T16:45:51.120601Z",
"amount": "15",
"providerTransactionId": "ABC123"
}
```
#### Validate content integrity
Create a hash of the request body using the algorithm specified in the `Content-Digest` header.
Comparing the generated value to the value in `Content-Digest` ensures the body of the request has not been tampered with.
#### Validate the signature
Based on the parameters in `Signature-Input`, generate the signature base for the request. You can read more about it [here](https://datatracker.ietf.org/doc/html/rfc9421#name-creating-the-signature-base).
Based on the previous example, the signature base would be the following.
```http theme={null}
"@method": POST
"@authority": localhost:8080
"@path": /callback
"signature-date": 2024-05-02T16:45:51.131905Z
"content-digest": sha-512=:0ki7QBS/0MA424uwOq3k5HnJnL5SRkPjit12m0YMpd4JgWiMvm9+yNT3FunkpDaTSsKhTkliQwJlRw9bgsos9w==:
"content-type": application/json; charset=UTF-8
"@signature-params": ("@method" "@authority" "@path" "signature-date" "content-digest" "content-type");alg="ecdsa-p256-sha256";keyid="CUSTOMER_TEST_KEY";created=1714657551;expires=1714657611
```
You can retrieve the public key to verify the signature from the [Public Keys](/v2/api-reference/toolkit/public-keys) endpoint.
Using the retrieved public key, the generated signature base and the signature, you can now verify that the the content (as specified by the `Signature-Input`) was in fact signed by PawaPay and therefore originates from PawaPay.
Do not forget to enable signed callbacks in the PawaPay Dashboard. Learn how to do that from the [PawaPay Dashboard Docs](/dashboard/other/system_conf/api_tokens#signed-callbacks).
# Sandbox test numbers
Source: https://docs.pawapay.io/v2/docs/test_numbers
## Overview
As part of using the PawaPay platform, you will have access to our sandbox environment.
Your sandbox account will have access to all the providers available on our platform.
This allows you to safely test your integration without using actual mobile money wallets and real funds.
In the sandbox, we provide special phone numbers (MSISDNs) that allow you to simulate both successful and unsuccessful transactions, helping you verify how your integration handles various payment outcomes.
## Sandbox test phone numbers (MSISDNs)
These phone numbers can only be used **in sandbox**, not in production.
The payment process is faster than in production as the customer does not explicitly authorise the payment.
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 22951345029 | FAILED | PAYER\_NOT\_FOUND |
| | 22951345039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 22951345069 | FAILED | UNSPECIFIED\_FAILURE |
| | 22951345129 | SUBMITTED | - |
| | 22951345789 | COMPLETED | - |
| Payout | 22951345089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 22951345119 | FAILED | UNSPECIFIED\_FAILURE |
| | 22951345129 | SUBMITTED | - |
| | 22951345789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 22995345679 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 22995345529 | FAILED | UNSPECIFIED\_FAILURE |
| | 22995345639 | SUBMITTED | - |
| | 22995345789 | COMPLETED | - |
| Payout | 22995345219 | FAILED | UNSPECIFIED\_FAILURE |
| | 22995345279 | SUBMITTED | - |
| | 22995345789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 22602345048 | INSUFFICIENT\_BALANCE | |
| | 22602345068 | UNSPECIFIED\_FAILURE | |
| | 22602345138 | SUBMITTED | - |
| | 22602345678 | COMPLETED | - |
| Payout | 22602345118 | UNSPECIFIED\_FAILURE | |
| | 22602345138 | SUBMITTED | - |
| | 22602345678 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 22607345148 | PAYMENT\_NOT\_APPROVED | |
| | 22607345128 | SUBMITTED | - |
| | 22607345678 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 237653456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 237653456029 | FAILED | PAYER\_NOT\_FOUND |
| | 237653456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 237653456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 237653456129 | SUBMITTED | - |
| | 237653456789 | COMPLETED | - |
| Payout | 237653456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 237653456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 237653456129 | SUBMITTED | - |
| | 237653456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 237693456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 237693456029 | FAILED | PAYER\_NOT\_FOUND |
| | 237693456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 237693456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 237693456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 237693456129 | SUBMITTED | - |
| | 237693456789 | COMPLETED | - |
| Payout | 237693456099 | FAILED | WALLET\_LIMIT\_REACHED |
| | 237693456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 237693456129 | SUBMITTED | - |
| | 237693456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 2250503456029 | FAILED | PAYER\_NOT\_FOUND |
| | 2250503456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 2250503456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 2250503456129 | SUBMITTED | - |
| | 2250503456789 | COMPLETED | - |
| Payout | 2250503456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 2250503456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 2250503456129 | SUBMITTED | - |
| | 2250503456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 2250734567030 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 2250734567060 | FAILED | UNSPECIFIED\_FAILURE |
| | 2250734567130 | SUBMITTED | - |
| | 2250734567890 | COMPLETED | - |
| Payout | 2250734567110 | FAILED | UNSPECIFIED\_FAILURE |
| | 2250734567130 | SUBMITTED | - |
| | 2250734567890 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 243813456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 243813456029 | FAILED | PAYER\_NOT\_FOUND |
| | 243813456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 243813456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 243813456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 243813456129 | SUBMITTED | - |
| | 243813456789 | COMPLETED | - |
| Payout | 243813456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 243813456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 243813456129 | SUBMITTED | - |
| | 243813456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 243973456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 243973456129 | SUBMITTED | - |
| | 243973456789 | COMPLETED | - |
| Payout | 243973456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 243973456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 243973456129 | SUBMITTED | - |
| | 243973456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 243893456029 | FAILED | PAYER\_NOT\_FOUND |
| | 243893456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 243893456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 243893456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 243893456129 | SUBMITTED | - |
| | 243893456789 | COMPLETED | - |
| Payout | 243893456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 243893456129 | SUBMITTED | - |
| | 243893456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 251700000010 | FAILED | PAYER\_LIMIT\_REACHED |
| | 251700000030 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 251700000040 | FAILED | INSUFFICIENT\_BALANCE |
| | 251700000050 | FAILED | TRANSACTION\_ALREADY\_IN\_PROCESS |
| | 251700000060 | FAILED | UNSPECIFIED\_FAILURE |
| | 251700000120 | SUBMITTED | - |
| | 251700000000 | COMPLETED | - |
| Payout | 251700000070 | FAILED | INSUFFICIENT\_BALANCE |
| | 251700000080 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 251700000090 | FAILED | WALLET\_LIMIT\_REACHED |
| | 251700000100 | FAILED | RECIPIENT\_LIMIT\_REACHED |
| | 251700000110 | FAILED | UNSPECIFIED\_FAILURE |
| | 251700000120 | SUBMITTED | - |
| | 251700000000 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 24174345048 | FAILED | INSUFFICIENT\_BALANCE |
| | 24174345068 | FAILED | UNSPECIFIED\_FAILURE |
| | 24174345128 | SUBMITTED | - |
| | 24174345678 | COMPLETED | - |
| Payout | 24174345088 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 24174345118 | FAILED | UNSPECIFIED\_FAILURE |
| | 24174345128 | SUBMITTED | - |
| | 24174345678 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 233593456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 233593456029 | FAILED | PAYER\_NOT\_FOUND |
| | 233593456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 233593456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 233593456129 | SUBMITTED | - |
| | 233593456789 | COMPLETED | - |
| Payout | 233593456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 233593456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 233593456129 | SUBMITTED | - |
| | 233593456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 233273456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 233273456129 | SUBMITTED | - |
| | 233273456789 | COMPLETED | - |
| Payout | 233273456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 233273456099 | FAILED | WALLET\_LIMIT\_REACHED |
| | 233273456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 233273456129 | SUBMITTED | - |
| | 233273456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 233503456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 233503456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 233503456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 233503456129 | SUBMITTED | - |
| | 233503456789 | COMPLETED | - |
| Payout | 233503456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 233503456099 | FAILED | WALLET\_LIMIT\_REACHED |
| | 233503456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 233503456129 | SUBMITTED | - |
| | 233503456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 254703456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 254703456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 254703456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 254703456059 | FAILED | TRANSACTION\_ALREADY\_IN\_PROCESS |
| | 254703456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 254703456129 | SUBMITTED | - |
| | 254703456789 | COMPLETED | - |
| Payout | 254703456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 254703456099 | FAILED | WALLET\_LIMIT\_REACHED |
| | 254703456109 | FAILED | RECIPIENT\_LIMIT\_REACHED |
| | 254703456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 254703456129 | SUBMITTED | - |
| | 254703456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 266100000010 | FAILED | PAYER\_LIMIT\_REACHED |
| | 266100000020 | FAILED | PAYER\_NOT\_FOUND |
| | 266100000030 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 266100000040 | FAILED | INSUFFICIENT\_BALANCE |
| | 266100000060 | FAILED | UNSPECIFIED\_FAILURE |
| | 266100000120 | SUBMITTED | - |
| | 266100000000 | COMPLETED | - |
| Payout | 266100000070 | FAILED | INSUFFICIENT\_BALANCE |
| | 266100000080 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 266100000090 | FAILED | WALLET\_LIMIT\_REACHED |
| | 266100000110 | FAILED | UNSPECIFIED\_FAILURE |
| | 266100000120 | SUBMITTED | - |
| | 266100000000 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 265993456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 265993456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 265993456129 | SUBMITTED | - |
| | 265993456789 | COMPLETED | - |
| Payout | 265993456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 265993456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 265993456129 | SUBMITTED | - |
| | 265993456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 265883456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 265883456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 265883456129 | SUBMITTED | - |
| | 265883456789 | COMPLETED | - |
| Payout | 265883456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 265883456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 265883456129 | SUBMITTED | - |
| | 265883456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 258100000010 | FAILED | UNSPECIFIED\_FAILURE |
| | 258100000000 | COMPLETED | - |
| Payout | 258100000110 | FAILED | UNSPECIFIED\_FAILURE |
| | 258100000120 | FAILED | PAYEE\_NOT\_ALLOWED\_TO\_RECEIVE |
| | 258100000130 | FAILED | INSUFFICIENT\_BALANCE |
| | 258100000130 | FAILED | PO\_MAX\_AMOUNT\_DAY |
| | 258100000150 | FAILED | PAYEE\_LIMIT\_REACHED |
| | 258100000160 | FAILED | UNSPECIFIED\_FAILURE |
| | 258100000000 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 2349034567069 | FAILED | UNSPECIFIED\_FAILURE |
| | 2349034567129 | SUBMITTED | - |
| | 2349034567899 | COMPLETED | - |
| Payout | 2349034567089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 2349034567119 | FAILED | UNSPECIFIED\_FAILURE |
| | 2349034567129 | SUBMITTED | - |
| | 2349034567899 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 2348134567019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 2348134567029 | FAILED | PAYER\_NOT\_FOUND |
| | 2348134567039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 2348134567069 | FAILED | UNSPECIFIED\_FAILURE |
| | 2348134567129 | SUBMITTED | - |
| | 2348134567899 | COMPLETED | - |
| Payout | 2348134567089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 2348134567099 | FAILED | WALLET\_LIMIT\_REACHED |
| | 2348134567119 | FAILED | UNSPECIFIED\_FAILURE |
| | 2348134567129 | SUBMITTED | - |
| | 2348134567899 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 242053456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 242053456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 242053456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 242053456129 | SUBMITTED | - |
| | 242053456789 | COMPLETED | - |
| Payout | 242053456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 242053456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 242053456129 | SUBMITTED | - |
| | 242053456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 242063456029 | FAILED | PAYER\_NOT\_FOUND |
| | 242063456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 242063456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 242063456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 242063456129 | SUBMITTED | - |
| | 242063456789 | COMPLETED | - |
| Payout | 242063456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 242063456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 242063456129 | SUBMITTED | - |
| | 242063456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 250733456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 250733456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 250733456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 250733456129 | SUBMITTED | - |
| | 250733456789 | COMPLETED | - |
| Payout | 250733456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 250733456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 250733456129 | SUBMITTED | - |
| | 250733456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 250783456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 250783456029 | FAILED | PAYER\_NOT\_FOUND |
| | 250783456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 250783456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 250783456129 | SUBMITTED | - |
| | 250783456789 | COMPLETED | - |
| Payout | 250783456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 250783456099 | FAILED | WALLET\_LIMIT\_REACHED |
| | 250783456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 250783456129 | SUBMITTED | - |
| | 250783456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 221763456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 221763456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 221763456129 | SUBMITTED | - |
| | 221763456789 | COMPLETED | - |
| Payout | 221763456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 221763456129 | SUBMITTED | - |
| | 221763456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 221773456029 | FAILED | PAYER\_NOT\_FOUND |
| | 221773456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 221773456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 221773456129 | SUBMITTED | - |
| | 221773456789 | COMPLETED | - |
| Payout | 221773456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 221773456139 | SUBMITTED | - |
| | 221773456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 23276123456 | COMPLETED | - |
| Payout | 23276123456 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 255683456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 255683456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 255683456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 255683456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 255683456129 | SUBMITTED | - |
| | 255683456789 | COMPLETED | - |
| Payout | 255683456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 255683456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 255683456129 | SUBMITTED | - |
| | 255683456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 255763456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 255763456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 255763456129 | SUBMITTED | - |
| | 255763456789 | COMPLETED | - |
| Payout | 255763456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 255763456129 | SUBMITTED | - |
| | 255763456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 255713456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 255713456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 255713456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 255713456129 | SUBMITTED | - |
| | 255713456789 | COMPLETED | - |
| Payout | 255713456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 255713456099 | FAILED | WALLET\_LIMIT\_REACHED |
| | 255713456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 255713456129 | SUBMITTED | - |
| | 255713456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 255623456029 | FAILED | PAYER\_NOT\_FOUND |
| | 255623456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 255623456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 255623456129 | SUBMITTED | - |
| | 255623456789 | COMPLETED | - |
| Payout | 255623456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 255623456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 255623456129 | SUBMITTED | - |
| | 255623456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 256753456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 256753456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 256753456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 256753456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 256753456129 | SUBMITTED | - |
| | 256753456789 | COMPLETED | - |
| Payout | 256753456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 256753456099 | FAILED | WALLET\_LIMIT\_REACHED |
| | 256753456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 256753456129 | SUBMITTED | - |
| | 256753456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 256783456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 256783456029 | FAILED | PAYER\_NOT\_FOUND |
| | 256783456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 256783456129 | SUBMITTED | - |
| | 256783456789 | COMPLETED | - |
| Payout | 256783456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 256783456099 | FAILED | WALLET\_LIMIT\_REACHED |
| | 256783456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 256783456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 260973456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 260973456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 260973456049 | FAILED | INSUFFICIENT\_BALANCE |
| | 260973456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 260973456129 | SUBMITTED | - |
| | 260973456789 | COMPLETED | - |
| Payout | 260973456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 260973456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 260973456129 | SUBMITTED | - |
| | 260973456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 260763456019 | FAILED | PAYER\_LIMIT\_REACHED |
| | 260763456029 | FAILED | PAYER\_NOT\_FOUND |
| | 260763456039 | FAILED | PAYMENT\_NOT\_APPROVED |
| | 260763456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 260763456129 | SUBMITTED | - |
| | 260763456789 | COMPLETED | - |
| Payout | 260763456079 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 260763456119 | FAILED | UNSPECIFIED\_FAILURE |
| | 260763456129 | SUBMITTED | - |
| | 260763456789 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 260953456704 | FAILED | INSUFFICIENT\_BALANCE |
| | 260953456712 | FAILED | UNSPECIFIED\_FAILURE |
| | 260953456789 | SUBMITTED | - |
| | 260953456700 | COMPLETED | - |
| Payout | 260953456712 | FAILED | UNSPECIFIED\_FAILURE |
| | 260953456789 | SUBMITTED | - |
| | 260953456700 | COMPLETED | - |
| Operation | MSISDN | Status | failureCode |
| - | - | - | - |
| Deposit | 263773456069 | FAILED | UNSPECIFIED\_FAILURE |
| | 263773456129 | SUBMITTED | - |
| | 263773456789 | COMPLETED | - |
| Payout | 263773456089 | FAILED | RECIPIENT\_NOT\_FOUND |
| | 263773456129 | SUBMITTED | - |
| | 263773456789 | COMPLETED | - |
# Upgrading from V1
Source: https://docs.pawapay.io/v2/docs/upgrade_from_v1
## What's new in V2 of the PawaPay API?
We have simplified the PawaPay API to make it easier to use.
We've also added clarity to allow providers with different payment authorisation flows to be implemented in a future-proof manner.
And we have restructured the payloads to consistently add features and capabilities to the API in the future.
Let's take a look one by one.
### Support for new payment authorisation flows
Some providers do not use the PIN prompt to authorise payments.
They either require the customer to preauthorise the payment by generating an OTP or use a redirection based authorisation.
Now both these mechanisms are supported in a way that allows you to implement once and all future providers with the same authorisation mechanism can be supported without a new implementation.
### Avoiding failures on initiation is now easier
We've added all the data you need to our [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint to make sure payments do not fail on initiation.
There is data there for handling decimals in amounts correctly, minimum and maximum transaction limits, provider availability information etc.
Also the [predict provider](/v2/api-reference/toolkit/predict-provider) endpoint helps you not only predict the provider for a phone number,
but also sanitises and validates the phone number to ensure it's valid for the country and provider.
### The best customer experience
The [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint also includes information about the specifics of how payments are authorised with the particluar provider.
This allows you to show the exact steps the customer needs to take and how to recover from cases where they don't enter the PIN prompt in time etc.
### Active configuration gets an overhaul
We beefed up the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint.
It is more logically structured now and supports filtering to get information on the exact countries and flows you need in your payment flow.
We've added a lot of static information like provider names and logos, currency names, country codes, flags and prefixes.
This way you are able to build a dynamic payment flow that does not need any changes when new providers are enabled on your account.
## How to upgrade from V1?
Let's first take a look at how to make everything work just as it was before.
This is not a large undertaking.
If you are still using `api.pawapay.cloud` as the baseUrl for the API, now it's time to change it to:
* `api.sandbox.pawapay.io` for your sandbox account
* `api.pawapay.io` for your live account
The V2 API is not any more available through the `.cloud` domain.
Update all your request paths to include the version number.
For example, deposits in V1 were at:
```json theme={null}
POST https://api.sandbox.pawapay.io/deposits
```
Now in V2 they are:
```json theme={null}
POST https://api.sandbox.pawapay.io/v2/deposits
```
When making calls to initiate [deposits](/v2/api-reference/deposits/initiate-deposit), [payouts](/v2/api-reference/payouts/initiate-payout) and [refunds](/v2/api-reference/refunds/initiate-refund) you should:
1. Rename all instances of `correspondent` to `provider`.
2. Move it inside the `accountDetails` object.
3. Change the `type` within `payer` or `recipient` from value `MSISDN` to `MMO`.
4. Change the `address` within `payer` or `recipient` to `accountDetails`.
5. Change the `value` within `address` to `phoneNumber`.
6. Remove the `customerTimestamp` from the request payload. This parameter has been removed.
7. Rename `statementDescription` to `customerMessage`.
8. Change metadata from `"fieldName": "orderId"` and `"fieldValue": "ORD-123456789"` format to `"orderId": "ORD-123456789"` format.
Let's take a look at a before and after example.
Before in V1:
```json {5,7,9,12,13,16-17,20-21} theme={null}
{
"depositId": "919b7674-b61d-4b69-ac58-d74d2b089192",
"amount": "15",
"currency": "ZMW",
"correspondent": "MTN_MOMO_ZMB",
"payer": {
"type": "MSISDN",
"address": {
"value": "260763456789"
}
},
"customerTimestamp": "2020-02-21T17:32:28Z",
"statementDescription": "Note of 4 to 22 chars",
"metadata": [
{
"fieldName": "orderId",
"fieldValue": "ORD-123456789"
},
{
"fieldName": "customerId",
"fieldValue": "customer@email.com",
"isPII": true
}
]
}
```
Now in V2:
```json {6-9,12,15,18-19} theme={null}
{
"depositId": "919b7674-b61d-4b69-ac58-d74d2b089192",
"amount": "15",
"currency": "ZMW",
"payer": {
"type": "MMO", //Was "MSISDN"
"accountDetails": { //Was address
"phoneNumber": "260763456789", //Was value
"provider": "MTN_MOMO_ZMB" //was correspondent
}
},
"customerMessage": "Note of 4 to 22 chars", //Was statementDescription
"metadata": [
{
"orderId": "ORD-123456789" //Was in annoying format
},
{
"customerId": "customer@email.com", //This one as well
"isPII": true
}
]
}
```
All financial requests should now be in the new format.
Let's take a look at responses, callbacks and status checks.
The only required change in responses is for [payouts](/v2/api-reference/payouts/initiate-payout).
We do not return the status of `ENQUEUED` any more on initiation.
The only statuses returned on initiation are:
* `ACCEPTED`
* `REJECTED`
* `DUPLICATE_IGNORED`
If you require to know whether the payout was enqueued, you can use the [check payout status](/v2/api-reference/payouts/check-payout-status) endpoint.
Also, the `rejectionReason` and `error` responses are now consolidated into `failureReason`.
```json {3} theme={null}
{
"status": "REJECTED",
"failureReason": {
"failureCode": "MISSING_PARAMETER",
"failureMessage": "Request does not include the required parameter 'provider'."
}
}
```
We will go over the changes in `failureReason` later in the guide.
Next let's look at callbacks and status checks.
We've changed the response payload of status check for [deposits](/v2/api-reference/deposits/check-deposit-status), [payouts](/v2/api-reference/payouts/check-payout-status) and [refunds](/v2/api-reference/refunds/check-refund-status) to determine the existence of a payment more clearly.
The response will now include the `status` of the request with possible values of `FOUND` and `NOT_FOUND`.
If `FOUND` the payment data will be available in the `data`.
```json {2,3} theme={null}
{
"status": "FOUND",
"data": {
"depositId": "919b7674-b61d-4b69-ac58-d74d2b089192",
"status": "COMPLETED",
"amount": "15.00",
"currency": "ZMW",
"country": "ZMB",
"payer": {
"type": "MMO",
"accountDetails": {
"phoneNumber": "260763456789",
"provider": "MTN_MOMO_ZMB"
}
},
"customerMessage": "Note of 4 to 22 chars",
"clientReferenceId": "",
"created": "2025-06-02T12:20:35Z",
"providerTransactionId": "09ae7c99-f351-4c87-a6d2-286337a70d21",
"metadata": {
"orderId": "ORD-123456789",
"customerId": "customer@email.com"
}
}
}
```
We have consolidated `rejectionReason`, `failureReason` and `error` responses to a `failureReason`.
You can now always find out what happened from a single place which is the `failureReason`.
You can find all the `failureCodes` in [our docs](/v2/docs/failure_codes). Make sure you have implemented handling for them.
The `requestedAmount` and `depositedAmount` are now just a single `amount`, showing the amount that was requested to be deposited.
If the payment `FAILED`, no funds have been deposited.
The `respondedByPayer`, `receivedByRecipient` and `suspiciousActivityReport` have been removed from all callbacks and status checks.
The `providerTransactionId` replaces the `correspondentIds` object.
It will contain the ID of the transaction as specified by the provider (if available from the provider).
Otherwise, all the changes in the request will be reflected in the callbacks and status checks as well, so make sure renamed or deprecated parameters are adjusted.
Before:
```json {4-5,15,18,19,23,} theme={null}
{
"depositId": "919b7674-b61d-4b69-ac58-d74d2b089192",
"status": "FAILED",
"requestedAmount": "15",
"depositedAmount": "0",
"currency": "ZMW",
"country": "ZMB",
"correspondent": "MTN_MOMO_ZMB",
"payer": {
"type": "MSISDN",
"address": {
"value": "260763456789"
}
},
"customerTimestamp": "2020-02-21T17:32:28Z",
"statementDescription": "Note of 4 to 22 chars",
"created": "2020-02-21T17:32:29Z",
"respondedByPayer": "2020-02-21T17:32:30Z",
"correspondentIds": {
"MTN_INIT": "ABC123",
"MTN_FINAL": "DEF456"
},
"suspiciousActivityReport": [
{
"activityType": "AMOUNT_DISCREPANCY",
"comment": "There is a discrepancy between requested and actual deposit amount."
}
],
"failureReason": {
"failureCode": "OTHER_ERROR",
"failureMessage": "Payers address is blocked"
},
"metadata": {
"orderId": "ORD-123456789",
"customerId": "customer@email.com"
}
}
```
After implementing the above changes, all the financial flows should work just like before.
Let's look at the other endpoints also.
We have unified the following endpoints by moving the respective payment ID from request body to query params:
* [Resend deposit callback](/v2/api-reference/deposits/resend-deposit-callback)
* [Resend payout callback](/v2/api-reference/payouts/resend-payout-callback)
* [Resend refund callback](/v2/api-reference/refunds/resend-refund-callback)
* [Cancel enqueued payout](/v2/api-reference/payouts/cancel-enqueued-payout)
For example resending deposit callback in V1:
```json theme={null}
POST https://api.sandbox.pawapay.io/deposits/resend-callback
{
"depositId": "919b7674-b61d-4b69-ac58-d74d2b089192"
}
```
Now in V2:
```json theme={null}
GET https://api.sandbox.pawapay.io/deposits/resend-callback/919b7674-b61d-4b69-ac58-d74d2b089192
//No body
```
Let's take a look at some of the other endpoints and how they have changed.
Most importantly, the [provider availability](/v2/api-reference/toolkit/availability) endpoint now only returns the countries and providers that have been configured on your PawaPay account.
This also means that you now need to authenticate calls to it.
The same API token can be used to authenticate as all other endpoints.
For a refresher, check out the [How to authenticate](/v2/docs/how_to_start#how-to-authenticate-calls-to-the-merchant-api) section of our docs.
We've also added filtering options for both countries and operation types, so that you can fetch provider availability information only for the flow you are interested in at the moment.
```json theme={null}
GET https://api.sandbox.pawapay.io/v2/availability?country=ZMB&operationType=DEPOSIT
[
{
"country": "ZMB",
"providers": [
{
"provider": "MTN_MOMO_ZMB",
"operationTypes": {
"DEPOSIT": "OPERATIONAL"
}
},
{
"provider": "AIRTEL_OAPI_ZMB",
"operationTypes": {
"DEPOSIT": "OPERATIONAL"
}
}
]
}
]
```
As you can see, `correspondent` has been renamed to `provider` everywhere in the API.
Next let's take a look at [predict provider](/v2/api-reference/toolkit/predict-provider).
Three changes have been done for this.
* The `correspondent` has been renamed to `provider`.
* The `operator` has been omitted in the response.
* The `msisdn` has been renamed to `phoneNumber`.
Next, let's take a look at [wallet balances](/v2/api-reference/finances/wallet-balances).
To unify filtering across the API, when filtering by country, you can now use the query param `country` to do that instead of a path parameter.
Checking wallet balances before:
```json theme={null}
GET https://api.sandbox.pawapay.io/v1/wallet-balances/ZMB
{
"balances": [
{
"country": "ZMB",
"balance": "21798.03",
"currency": "ZMW",
"mno": ""
}
]
}
```
Now in V2:
```json {9} theme={null}
GET https://api.sandbox.pawapay.io/v2/wallet-balances?country=ZMB
{
"balances": [
{
"country": "ZMB",
"balance": "42867.47",
"currency": "ZMW",
"provider": ""
}
]
}
```
As you can see, the `mno` has been renamed to `provider` as well.
Now let's look at [active configuration](/v2/api-reference/toolkit/active-configuration).
The `merchantId` has been removed from the response.
The `merchantName` has been renamed to `companyName`.
Of course the `correspondents` have been renamed to `providers` like in the rest of the API.
Within the list of `providers` you can now find the list of `currencies` they support.
Currently, the only country with multicurrency support is DRC.
In `currencies` you will now find a map of `operationTypes` which contain the minimum and maximum transaction limits.
The `minTransactionLimit` and `maxTransactionLimit` have been renamed to `minAmount` and `maxAmount` respectively.
The `ownerName` has been renamed to `nameDisplayedToCustomer`.
Getting your active configuration before:
```json {2,3,7,9,11,14-16} theme={null}
{
"merchantId": "DEMO", //Removed in V2
"merchantName": "DEMO",
"countries": [
{
"country": "BEN",
"correspondents": [
{
"correspondent": "MOOV_BEN",
"currency": "XOF",
"ownerName": "PAWAPAY",
"operationTypes": [
{
"operationType": "DEPOSIT",
"minTransactionLimit": "100",
"maxTransactionLimit": "2000000"
},
...
]
}
]
}
]
}
```
Now in V2:
```json {2,7,10,12,15,17,18} theme={null}
{
"companyName": "DEMO", //Was merchantName
"countries": [
{
"country": "BEN",
...
"providers": [ //Was correspondents
{
"provider": "MOOV_BEN",
"nameDisplayedToCustomer": "PAWAPAY", //Was ownerName
...
"currencies": [ //Currencies are now an array
{
"currency": "XOF",
"operationTypes": { //Now as a map
"DEPOSIT": {
"minAmount": "100", //Was minTransactionLimit
"maxAmount": "2000000", //Was maxTransactionLimit
...
},
...
}
}
]
}
]
}
]
}
```
Currently we went over the minimal changes needed when upgrading to V2.
Later we will review what has been added to the endpoint to help build a more dynamic and high quality payment flow.
Next let's take a look at the [payment page](/v2/api-reference/payment-page/deposit-via-payment-page).
The payment page also has some small changes needed to upgrade it to V2.
The `msisdn` has been renamed to `phoneNumber`.
The `statementDescription` has been renamed to `customerMessage`.
The `amount` is now contained in the `amountDetails` object together with the `currency`.
Before in V1:
```json {4,5,6} theme={null}
{
"depositId": "5fcea42c-bbe2-469f-9c59-179f6363d0d5",
"returnUrl": "https://merchant.com/returnUrl",
"statementDescription": "Note of 4 to 22 chars",
"amount": "15",
"msisdn": "260763456789"
}
```
Now in V2:
```json {4,5-7,9} theme={null}
{
"depositId": "5fcea42c-bbe2-469f-9c59-179f6363d0d5",
"returnUrl": "https://merchant.com/returnUrl",
"customerMessage": "Note of 4 to 22 chars",
"amountDetails": {
"amount": "15",
"currency": "ZMW"
},
"phoneNumber": "260763456789"
}
```
Failures of initiating the payment page have also been unified into `failureReason` like the rest of the API.
When checking for the final status of the payment, the changes listed earlier in the guide must be implemented to ensure everything works.
Now everything should be upgraded to V2 and working just like it was before.
Now let's delve into the benefits of doing so.
## How to get the best out of V2?
We've added some capabilities that allow for a more dynamic, lower maintenance integration.
We've also enabled a better customer experience for your customers.
All API first as we do, so you can build exactly what you want.
There are some providers like Orange in Burkina Faso and Wave in Senegal and Ivory Coast that do not use the common PIN prompt mechanism for authorising payments.
We've now made it easy to implement authorisation flows for providers with
* preauthentication
* redirection
You can implement each of those once and know that new providers with the same authorisation flows will work as well.
There's a step by step guide with example of how to do that in the [Deposits guide](/v2/docs/deposits#other-payment-authorisation-flows).
When initiating deposits with most providers, the PIN prompt will pop up automatically on the payers phone.
In case the PIN prompt times out before the customer is able to authorise it, some of the providers allow the PIN prompt to be revived.
Also, some providers require the customer to dial a USSD code and go through the USSD menu to authorise their payment by entering their PIN.
To be able to show the customer **exactly** what they should do after they've "pressed pay", we have included all that information into our [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint.
We have it all explained in our step-by-step [guide for deposits](/v2/docs/deposits.mdx#after-initiating-the-deposit).
Ensuring that the data in your [deposit](/v2/api-reference/deposits/initiate-deposit) and [payout](/v2/api-reference/payouts/initiate-payout) initiations is valid improves the success rates.
You can use the [predict provider](/v2/api-reference/toolkit/predict-provider) endpoint to both sanitise and format the phone number to make sure it's valid.
The endpoint also predicts the correct provider for the phone number.
This way you can preselect the provider reducing failures due to wrong provider selection and reducing the amount of clicks for your customers.
Also as part of the [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint,
you have not only the minimum and maximum transaction amounts for all operations, but also information on whether decimals are supported or not.
This is all covered in the step-by-step [guide for deposits](/v2/docs/deposits).
Of course we have the same available for [payouts](/v2/docs/payouts) and [refunds](/v2/docs/refunds) as well.
Providing information to customers on which providers are currently experiencing downtime and not available for payments allows for a much better customer experience.
We've both made it easier to get the specific information you need from [provider availability](/v2/api-reference/toolkit/availability) endpoint,
but it is also a part of [active configuration](/v2/api-reference/toolkit/active-configuration) now.
This way it's even easier to show availability information to your customers when you are setting up their payment experience.
You can read more how to do that from the step-by-step [guide for deposits](/v2/docs/deposits#avoiding-failures-on-initiation).
It should be as easy as possible to expand into new countries and add new providers as they become available.
We've added lots of information to [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint to make it as dynamic and easy as possible.
We have provider names and logos, country flags and prefixes.
All the transaction limits and decimals support in amounts.
Company name shown on the PIN prompt, currencies and their commonly used abbreviations etc.
You can fetch all that information when rendering the payment flow to your customer reducing the amount of effort required to add providers or expand into new markets.
We've put together some guides to take you through the implementation of different payment flows.
Step by step guide on how to get paid by your customers.
Step by step on how to pay your customers.
Sometimes you need to refund. Follow this guide to refund your customers.
Find out how to get paid, with an out-of-the-box payment experience.
Check the API reference to see all the endpoints, payloads, sample requests and responses.
# Welcome
Source: https://docs.pawapay.io/v2/docs/welcome
The PawaPay API enables you to pay and get paid with mobile money in Sub-Saharan Africa.
## Learn more...
Here you can get a better idea of what mobile money is, how it's similar to and different from card payments, and much more.
Find some useful information you should know about asynchronous financial APIs and mobile money peculiarities.
Read about where the PawaPay API is, how it's authenticated and how to set everything up for development and testing.
And some considerations when moving from your sandbox account to live payments.
## ...and then jump right into it
We've put together some guides to take you through the implementation of different payment flows.
Test and get a feel for the API using our Postman collection.
Step by step guide on how to get paid by your customers.
Step by step on how to pay your customers.
Sometimes you need to refund. Follow this guide to refund your customers.
Find out how to get paid, with an out-of-the-box payment experience.
Get paid with a hosted payment widget and track the whole payment intent through a single checkout.
Find here the API reference to see all the endpoints, payloads, sample requests and responses.
# What you should know
Source: https://docs.pawapay.io/v2/docs/what_to_know
The PawaPay Merchant API simplifies integrating mobile money into your payment flows.
It provides a single standardised API to connect to Mobile Money Operators (MMOs) providing mobile money services across Africa.
The following chapters introduce some important aspects of mobile money and working with financial APIs to help you build a high-quality integration that your customers will love and that is easy to operate.
## Callbacks
The PawaPay Merchant API is **asynchronous**. This is necessary to provide high performance and quality payment infrastructure that is able to process millions of payments every day.
When you call any financial endpoint ([payout](/v2/api-reference/payouts/initiate-payout), [deposit](/v2/api-reference/deposits/initiate-deposit), [refund](/v2/api-reference/refunds/initiate-refund)), the response will confirm whether the transaction is **accepted** or **rejected** for processing.
A transaction may be rejected due to reasons such as using the same ID more than once or the provider being unavailable at the moment.
If the response confirms that the payment has been **accepted** for processing, it will take some time for it to process (remember that for deposits, the user needs to enter their PIN on their phone).
There are two ways to **determine the final status** of a payment.
### Polling for status
First, you can use the corresponding `Check Status` endpoint for the transaction type to periodically **check the status of a payment**.
### Through callbacks
Second, you can configure callback URLs in the PawaPay Dashboard.
This will automatically **send a callback to your configured callback URL** as soon as a payment has reached its final status.
You will have to implement a callback handler that is able to receive calls from our platform and handle them accordingly.
### Setting up callbacks
Callback URLs are configured from the PawaPay Dashboard.
You can find instructions on how to do that from the [PawaPay Dashboard Docs](/dashboard/other/system_conf/callback_urls).
### Handling callbacks
When implementing your callback handler, please consider the following points.
* Your endpoint for receiving callbacks must be **idempotent**.
* Sending a callback more than once for the same payment should not cause a double credit or debit in your system.
* Your endpoint needs to allow us to `POST` the callback.
* Make sure you use an SSL certificate from a **trusted CA**.
* We will make up to **8 delivery attempts** over approximately **25 minutes**. See [Callback delivery attempts](#callback-delivery-attempts) below.
* We expect you to return `HTTP 200 OK` response to consider the callback **delivered**.
* If the callback delivery fails, you can trigger a resend of the callback within **90 days** of the payment being initiated. This can be done by:
* Calling the respective `Resend callback` endpoint
* Manually from the [PawaPay Dashboard](/dashboard/transactions/callback_resend).
* For payments older than 90 days, callbacks can no longer be resent. The status of the payment remains available through the `Check Status` endpoints.
* The endpoint should be **accessible to our platform**.
* Ensure that you have excluded the callback endpoint from your application's regular authentication system.
* If you are using IP whitelisting, whitelist the below IPs to ensure we can deliver callbacks to you.
### Callback delivery attempts
When a payment reaches its final status, we send the callback straight away.
If your endpoint does not return `HTTP 200 OK`, we try again on the following schedule.
| Attempt | Sent at | Wait before next attempt |
| - | - | - |
| 1 | 0:00 | 1 minute |
| 2 | 1:00 | 1 minute |
| 3 | 2:00 | 3 minutes |
| 4 | 5:00 | 5 minutes |
| 5 | 10:00 | 5 minutes |
| 6 | 15:00 | 5 minutes |
| 7 | 20:00 | 5 minutes |
| 8 | 25:00 | — |
* Times are measured from when the callback is first sent. They are approximate.
* Once your endpoint returns `HTTP 200 OK`, the callback is **delivered** and no further attempts are made.
* If attempt 8 (around **25 minutes**) also fails, we stop retrying. You can then:
* Resend the callback: [deposits](/v2/api-reference/deposits/resend-deposit-callback), [payouts](/v2/api-reference/payouts/resend-payout-callback), [refunds](/v2/api-reference/refunds/resend-refund-callback), [remittances](/v2/api-reference/remittances/resend-remittance-callback).
* Get the final status: [deposits](/v2/api-reference/deposits/check-deposit-status), [payouts](/v2/api-reference/payouts/check-payout-status), [refunds](/v2/api-reference/refunds/check-refund-status), [remittances](/v2/api-reference/remittances/check-remittance-status).
* Because of retries, your endpoint may receive the same callback more than once. This is why it must be **idempotent**.
### Timeouts and slow responses
* We wait up to **10 seconds** to establish a connection to your callback URL and up to **30 seconds** for a response.
If either is exceeded, the attempt counts as failed.
* Respond with `HTTP 200 OK` as soon as you have received the callback and process it asynchronously. Do not hold the connection open while you run your own business logic.
* If we detect slow responses or `HTTP 429 Too Many Requests` from your callback URL, we may skip some callback delivery attempts to avoid adding to the load on your system.
### IPs to whitelist
Callbacks from PawaPay will be coming from one of the IPs listed below.
| Environment | IP |
| - | - |
| Sandbox | 3.64.89.224/32 |
| Production | 18.192.208.15/32 |
| Production | 18.195.113.136/32 |
| Production | 3.72.212.107/32 |
| Production | 54.73.125.42/32 |
| Production | 54.155.38.214/32 |
| Production | 54.73.130.113/32 |
## MMO stability
As described in the [What's mobile money](/v2/docs/whats_mobile_money#what-does-a-payment-look-like) section, mobile money transaction chains are complex.
This means that the stability of payment services needs to be taken into account when implementing a high-quality payment flow.
PawaPay provides multiple solutions to make this easy to manage.
Our 24/7 payment operations team monitors all MMOs on our platform for instability and downtime. We stop accepting payments to those MMOs if the success rate is too low.
This information is accessible from our [provider availability](/v2/api-reference/toolkit/availability) endpoint.
You can verify before initiating the payment if the MMO is currently experiencing any problems and inform the user before they attempt the payment.
Knowing that your mobile money provider is currently unavailable is a better customer experience than having your payment fail.
Failed payments usually take longer to process, leaving the customer waiting for the final result.
To provide the best support for your customers, information about MMO availability is also published in the [PawaPay Dashboard](/dashboard/status) and [Status page](https://status.pawapay.io).
You can subscribe to updates via email, Slack and other channels to ensure your support team is always informed about any potential problems with the MMOs.
When an MMO is experiencing difficulties in successfully processing disbursements, our platform will enqueue them to be processed once the MMO is operational.
Read more about [enqueued payouts](/v2/docs/payouts#handling-enqueued-payouts-and-provider-availability).
Occasionally, MMOs experience degraded system performance, which can prevent their APIs from returning the final status of payments.
During these periods, our platform continues to accept and process payments.
We reconcile these transactions through alternative methods using our reconciliation engine.
This ensures that, when using PawaPay, all payments are eventually reconciled to their final status.
Please note that this process may take slightly longer than during normal operations.
## Transaction limits
Mobile money wallets have different limits for transactions. Most common and relevant are:
* Maximum amount of deposits/payouts in a day/week/month.
* Maximum number of deposits/payouts in a day/week/month.
* Maximum balance of a mobile money wallet.
All MMOs that you transact with through PawaPay have transaction limits.
These are the lowest and highest amounts that can be collected, disbursed or refunded with the specific MMO.
They are based on the limits of the most common wallet types of this MMO.
As the effective limits depend on the specific user's transaction history, transactions may still fail due to hitting limits.
> For example, an MMO has a 1,000 GHS maximum deposit amount.
> In reality the wallet has a limitation of 1,000 GHS total transaction value per day/week/month.
> If the user has already transacted 500 GHS during this period, a transaction of 700 GHS might still fail even though it is **accepted** by PawaPay.
You can easily find the transaction limits from our [active configuration](/v2/api-reference/toolkit/active-configuration) endpoint.
## Backwards compatibility
The PawaPay Merchant API is always backwards compatible, but should not be strictly verified against the schema as backwards compatible changes might be introduced.
# What's mobile money?
Source: https://docs.pawapay.io/v2/docs/whats_mobile_money
## What is mobile money?
Mobile money is a form of digital money, where instead of a bank account and a card, people get a mobile money wallet connected to their phone number.
When people sign up for a mobile money account with their Mobile Money Operator (MMO), they undergo a 'Know Your Customer' (KYC) process, similar to opening a bank account.
Instead of ATMs and bank branches, you can put money on your mobile money wallet through an extensive agent network. Instead of getting a bank account number/IBAN, your mobile money wallet is identified by your phone number.
Mobile money enables users to send and receive funds from other mobile money users, pay bills, shop online, and receive their salaries.
Mobile money wallets have transaction limits like many modern bank accounts and cards. Most important limits are:
* Number of payments done within a day, week, or month.
* Value of payments done within a day, week, or month.
* Maximum balance of a mobile money wallet.
Users can have those limits raised by going through extended KYC processes with the MMOs.
## What does a payment look like?
Mobile money payments are different from classic card payments. They are more similar to A2A or 3-D Secure enabled card payments. Specifically, the user has to explicitly authorise each payment.
With most providers, when a user attempts to pay from their mobile money wallet, they need to enter their mobile money PIN code to authorise the payment.
The PIN code is only known by the issuer (MMO) and the owner of the mobile money wallet.
Authorisation happens between those two parties and the merchant is not involved.
There are also some alternative authorisation methods.
Some providers require redirecting the customer to the provider's page or app to authorise the payment.
Even fewer require the payment to be preauthorised.
In this case the customer needs to generate an OTP with the provider and share it with you. You then need to pass it to PawaPay to initiate the payment.
The owner of the mobile money wallet always receives an SMS receipt whenever their balance changes.
This makes mobile money payments much more secure than classic card payments reducing fraud and chargeback risk to a minimum.
This has two important implications.
First, this expands the technical infrastructure involved in processing every single payment.
Since the user needs to enter their mobile money PIN code to authorise each payment, both the server infrastructure and the cell network infrastructure are involved in the payment chain.
The PIN code is usually requested from the user by sending a USSD message over the cell network and utilising the handset's SIM toolkit to handle user interaction.
This gives mobile money incredible accessibility as it works securely on everything from the simplest of push-button handsets all the way to flagship smartphones.
On the flip side, there's a lot involved from server infrastructure, internet connectivity, cell networks, USSD gateways, etc.
Second, the user is now part of the processing chain of each transaction.
A user attempting to initiate a payment on your website or app needs to take out their handset and enter their PIN in a limited amount of time.
Or they might not find their phone, accidentally close the PIN prompt, forget their PIN code, get a phone call, etc.
To conclude, mobile money is highly accessible and secure.
The challenges to ensure high success rates and a great customer experience are different from other payment methods.
With the PawaPay Merchant API, you have the means to make sure the customer has the best experience possible.
With the PawaPay Merchant API you can easily transact with your customers in any way you need:
* **You can collect money from them**: Funds will move from the customer's mobile money wallet into your account in PawaPay. You can do that by following our guide for [Deposits](/v2/docs/deposits) or the [Payment page](/v2/docs/payment_page).
* **You can disburse money to them**: Funds will move from your account in PawaPay to the customer's mobile money wallet. You can do that by following our guide for [Payouts](/v2/docs/payouts).
* **You can refund them**: Already collected funds will move back from your account in PawaPay to the customer's mobile money wallet. You can do that following our guide for [Refunds](/v2/docs/refunds).
# History
Source: https://docs.pawapay.io/dashboard/finances/history
For easy reconciliation and auditability, you can always export detailed financial statements of your wallets. Every single transaction affecting your balance is recorded on the statement.
On the statement history page, you can find all your exported wallet statements.
Only statements generated within the last 90 days are available for download.
### Glossary of data in statements
| Column | Description |
| - | - |
| Time | The date and time at which this statement export was generated. Time is in the UTC time zone. |
| Requester | The user who requested this statement export. |
| Wallet | The wallet for which this statement export was generated. |
| Statement time range | The time period that is included in this statement export. |
| Status | The current status of this statement export. One of:
**Completed**: The statement export is available to download.
**Generating**: The statement export is currently being generated and will be available soon.
**Failed**: The statement export failed to generate. Please try again. |
# Settlements
Source: https://docs.pawapay.io/dashboard/finances/settlements
Choose where, how often and how much we settle from your PawaPay wallets.
PawaPay settles the funds in your wallets to your bank accounts on a schedule you choose. You set this up once on the *Settlements* page under *Finances*, and you can change it at any time.
You need the **Settlements** or **Administrator** role to set up and view settlements. See [User roles](/dashboard/user_management/user_roles).
## Before you start
Have these details ready for each bank account you want to add:
* Account holder name
* Account number or IBAN
* Bank name and SWIFT/BIC code
* Branch name, branch address and, if it has one, branch sort code
* Country and currency of the account
You can settle to two kinds of bank account:
| Account | Currency | Wallets it can take |
| :- | :- | :- |
| Cross-border | EUR, USD or GBP | Wallets from any country, except countries that only allow local settlement |
| Local | The currency of a country where PawaPay processes your payments | Wallets from the same country and currency |
## Set up your settlements
Setup has three steps, then a summary where you can change any step before you confirm.
Add a bank account, then select the wallets that settle to it under *Your wallets*. Select *Add* to save the account.
You can link some or all of your wallets to a cross-border account. Link any other wallet to a local account in its own country. Once you have a cross-border account, *Country* and *Currency* only offer the countries and currencies of your unlinked wallets.
You can add one cross-border account. All your cross-border settlements are paid in its currency.
A local account in XOF, XAF or CDF can also take wallets in other currencies:
| Account currency | Also takes wallets in |
| :- | :- |
| XOF | XOF from other countries, XAF, CDF, USD |
| XAF | XAF from other countries, XOF, CDF, USD |
| CDF | USD |
Choose *Weekly*, *Bi-weekly* or *Monthly*. Settlements always start on a Monday.
| Frequency | When we settle |
| :- | :- |
| Weekly | Every Monday |
| Bi-weekly | Every second Monday |
| Monthly | Every fourth Monday |
A minimum wallet balance is the amount we always leave in a wallet. We never settle it. We only settle the balance above it. For example, if a wallet has a minimum wallet balance of 500,000 XOF and a balance of 1,000,000 XOF, 500,000 XOF can be settled. Each wallet can have its own minimum.
Select *I need to set minimum balances for my wallets* to enter them. If you leave it unticked, every minimum is 0 and we settle the full balance.
Check the summary. To change a section, select its edit icon. When everything is correct, select *Confirm your preferences*. Your first settlement starts on the next Monday.
## When we settle
Settlements start on Mondays, at the frequency you chose. When a settlement starts, we settle the part of each wallet's balance that is above its minimum wallet balance. Funds that arrive after that are included in your next settlement.
The *Settlements* tab shows the date of your upcoming settlement. How long the funds take to reach your bank depends on the country and the account.
## How much we settle
We never settle a wallet's minimum wallet balance. When a settlement starts, only the balance above it can be settled. For example, if a wallet has a minimum wallet balance of 500,000 XOF and a balance of 1,000,000 XOF, 500,000 XOF can be settled.
The total amount being settled must also reach PawaPay's minimum settlement amount:
* **Local account:** the total amount settled to the account must be worth at least 50 USD.
* **Cross-border account:** the total amount settled to the account must be worth at least 100 USD.
A local XOF, XAF or CDF account that takes more than one wallet, or a wallet in another currency, counts as a cross-border account here.
If a wallet doesn't qualify, we skip it for that settlement and its funds stay in the wallet. A wallet is skipped when:
* its balance is 0
* its balance is not above its minimum wallet balance
* the total amount being settled is below PawaPay's minimum settlement amount
* we have temporarily paused settlements in its country
## Track your settlements
The *Settlements* tab lists your previous settlements and shows the date of your upcoming one. Each settlement has one of these statuses:
| Status | What it means |
| :- | :- |
| In progress | We have taken the funds from your wallets and are sending them to your bank. |
| Completed | We have sent the funds. Cross-border settlements show the exchange rate, and you can download the proof of payment once it's attached. |
| Cancelled | We couldn't complete the settlement. All funds were returned to your wallets. |
| Skipped | None of your wallets qualified for this settlement. |
### In progress
An *In progress* settlement shows each wallet included, the amount taken from it and the bank account it goes to. Wallets that were skipped are listed too.
If you settle to more than one bank account, you see how much from each wallet goes to each account, with the bank name and country.
Exchange rates for cross-border settlements are shown once the settlement is complete. To see the rates we used over the past four weeks, select *Historical FX rates*.
To see why a wallet was skipped, select its *Skipped* status.
Our Treasury team may leave comments on a settlement. You'll find them in the top-right corner of the settlement.
### Completed
A completed cross-border settlement shows the amount settled, the exchange rate and the destination bank account. Download the proof of payment (PoP) with *PoP*.
A completed local settlement shows the amount settled, the destination bank account and the proof of payment.
If your bank charged a fee, it is deducted from the amount settled. If you settle to more than one bank account, the fee is shown in the *Bank fee* column.
If you settle to one bank account, the amount next to the date is the total amount settled. When your bank charged a fee, select *Amount Breakdown* under that amount to see the amount, the bank charges and the total amount settled.
### Cancelled
If we cancel a settlement, the funds go back to your wallets straight away. Our Treasury team may leave a comment explaining why.
### Skipped
A settlement is *Skipped* when none of your wallets qualified. See [How much we settle](#how-much-we-settle) for the reasons.
## Change your preferences
You can change your preferences at any time on the *Configuration* tab. Changes apply from the next settlement.
### Bank accounts
To update a bank account's details, or link more wallets to it, select its edit icon. You can't change the *Country* or *Currency* of an existing account, or unlink a wallet that already settles to an account.
### Frequency
To change how often we settle, select the edit icon next to *How often do you want to be settled?*, choose *Weekly*, *Bi-weekly* or *Monthly*, then select *Done*.
### Minimum wallet balances
To change how much we leave in a wallet, select the edit icon next to *Minimum wallet balances*, enter the new amount for each wallet you want to change, then select *Done*. We never settle a wallet's minimum wallet balance, only the balance above it. To settle the full balance of every wallet, untick *I need to set minimum balances for my wallets*.
### New countries
When you start processing payments in a new country, its wallet is not settled until you link it to a bank account. The *Configuration* tab shows which countries we can't settle yet. Select the link in the message to fix it.
You can link the new wallet to your cross-border account, or add a local account for it.
# Statements
Source: https://docs.pawapay.io/dashboard/finances/statements
You always have access to a full financial statement for each of your wallets to be able to audit and reconcile your balance on our platform.
## Search statements
To see every single transaction on your wallet, please start by choosing:
* The time period
* A wallet
Pressing "Apply filters" will display a table with the transactions on this wallet for the selected period. You can also limit the results to specific Mobile Money Operators (MMOs) or transaction types.
You can choose up to a **one-year period at a time**. For some accounts the limit is one month.
A maximum of 10,000 transactions are displayed.
Statement data for the current day may be up to 20 minutes behind the latest transactions.
By default, results are shown starting from the oldest transaction. You can change the order by pressing on the respective column in the header of the table.
## Export statements
After selecting the wallet and time period, press the "Export Statement" button to generate a CSV file with the results.
You will be asked to confirm this action and then taken to the [History](/dashboard/finances/history) page to download the resulting CSV file.
You can choose up to a **one-year period** at a time. For some accounts the limit is one month.
There is no limit to the number of transactions.
The export may take some time to generate. The [History](/dashboard/finances/history) page will refresh itself automatically to show the latest status.
## Data in statements
After exporting or viewing the statement data in our portal, the following data will be included:
| Column | Description |
| - | - |
| Creation Date | The date and time when this transaction was recorded on our platform. The time is in the UTC time zone. |
| MSISDN | The phone number of the mobile money wallet that was involved in the transaction. |
| Transaction ID | The ID of this transaction as specified by you through our API. You can read more about transaction IDs from [our API docs](/v1/api-reference/deposits/request-deposit). |
| Amount | The amount of the transaction. |
| Currency | The currency in which the transaction took place. |
| Country | The country in which this transaction took place. |
| MNO | The Mobile Money Operator (MMO) that fulfilled this transaction. |
| Type | The type of this transaction. See the list of all [transaction types](/dashboard/finances/statements#transaction-types) below. |
| Balance | Your balance after this transaction. |
| Reason | The reason for this transaction, for example, "Initial top-up" for your first top-up of this wallet. |
| Metadata | Metadata in JSON format you attached to the payments for providing additional context about the payment. |
Not all columns are populated for all transactions. For example, transactions that represent a settlement of your balance do not have an MSISDN and MMO specified.
## Transaction types
You may encounter the following transaction types in your statements:
| Transaction type | Description |
| - | - |
| Deposit | Money collected from a customer has been deposited on your wallet. |
| Payout | Money has been paid out to a customer from your wallet. |
| Failed payout refund | Money initially reserved from your wallet for a payout has been returned due to the payout failing. |
| Refund | Money initially collected to your wallet has been refunded to the customer. |
| Failed refund reversal | Money initially reserved from your wallet for a refund has been returned due to the refund failing. |
| Remittance | Money has been sent from your wallet for a remittance. |
| Failed remittance refund | Money initially reserved from your wallet for a remittance has been returned due to the remittance failing. |
| Deposit pass through fees | A fee charged by the Mobile Money Operator (MMO) for a deposit. |
| Payout pass through fees | A fee charged by the Mobile Money Operator (MMO) for a payout. |
| Refund pass-through fees | A fee charged by the Mobile Money Operator (MMO) for a refund. |
| Remittance pass-through fees | A fee charged by the Mobile Money Operator (MMO) for a remittance. |
| Deposit PawaPay fees | A fee charged by PawaPay for a deposit. |
| Payout PawaPay fees | A fee charged by PawaPay for a payout. |
| PawaPay refund fee | A fee charged by PawaPay for a refund. |
| PawaPay remittance fee | A fee charged by PawaPay for a remittance. |
| Top up | Funds sent by you have been added to your wallet. Read more about [Topping up your wallets](/dashboard/topping_up). |
| Top up - Wallet to Wallet transfer | Funds have been moved into this wallet from another of your PawaPay wallets. |
| Withdrawal | Funds withdrawn from your wallet to be settled to your bank. |
| Withdrawal - Wallet to Wallet transfer | Funds have been moved from this wallet to another of your PawaPay wallets. |
| Cancelled withdrawal reversal | A withdrawal was cancelled and the funds have been returned to your wallet. |
### Adjustments and compensations
We record these manually to correct your balance. The Reason column explains each one.
| Transaction type | Description |
| - | - |
| Adjustment - Collections received | Adds successful deposits that were not credited to your wallet. |
| Adjustment - Collections not credited | Deducts deposits that were credited to your wallet in error. |
| Adjustment - Payouts not debited | Deducts successful payouts that were not debited from your wallet. |
| Adjustment - Payouts debited in error | Deducts an extra debit made for a payout, for example when the MMO debited it twice. |
| Adjustment - Payout reversal by MMO | Adds back a payout that the MMO reversed. |
| Adjustment - MNO interest disbursement | Adds interest paid out by the MMO. |
| Adjustment - Pass-through fees - Rebate | Adds a rebate on MMO pass-through fees. |
| Adjustment - Pass-through fee reimbursement | Adds back MMO pass-through fees. |
| Adjustment - Excise tax | Deducts excise tax. |
| Adjustment - Withholding tax | Deducts withholding tax. |
| Adjustment - Bank charges | Deducts bank charges. |
| Adjustment - Other (+) | Adds an amount for a reason not covered by another type. |
| Adjustment - Other | Deducts an amount for a reason not covered by another type. |
| Compensation - Payouts debited in error | Adds back payouts that were debited from your wallet in error. |
| Compensation - PawaPay fees reimbursement | Adds back PawaPay fees. |
| Compensation - Other | Adds compensation for a reason not covered by another type. |
| Manual pass-through fee deduction | Deducts MMO pass-through fees. |
| Manual PawaPay fee deduction | Deducts PawaPay fees. |
# Wallets
Source: https://docs.pawapay.io/dashboard/finances/wallets
On the PawaPay platform, all of your mobile money is stored in wallets. In most cases, wallets are issued per country and hold all your available mobile money in that country.
On the Wallets page, you can see the balances of all your wallets and view a breakdown of the transactions that have affected each wallet.
## Balance overview
In the balance overview section, you can see each of your wallets and their:
* Country
* Current balance
* Currency
* Active providers (MMOs)
Click on a wallet to instantly view its breakdown in the Wallet breakdown panel.
In normal circumstances, wallets are issued per country.\
If a wallet shows **No active provider**, none of your payment flows are currently active for it.
## Wallet breakdown
When you select a wallet, its breakdown is displayed automatically.
Choose a quick filter to quickly view yesterday, last week, or last month. Or select a custom date range using the date picker.
The wallet breakdown is displayed from **midnight to midnight** in the UTC time zone. You can select a period of up to **one month** at a time.
The breakdown shows how your wallet balance has changed over the selected period.
Together with the opening and closing balance, you will see the total value of:
* Deposits
* Split Deposits
* Refunds
* Payouts
* Split Payouts
* Remittances
* Fees
* Withdrawals
* Top ups
* Adjustments
* Compensations
* Uncategorized
Money in is displayed as a positive value in green. Money out is displayed as a negative value in red, in brackets.
### Export wallet breakdown
You can export the breakdown as a CSV or PDF file at any time by selecting "Export statement".
# Global search
Source: https://docs.pawapay.io/dashboard/global_search
Any payment on our platform can be found quickly and easily using our global search which is always at the top left corner of the dashboard.
This functionality is available to users with the Transactions role.
Read more about [User roles](/dashboard/user_management/user_roles).
## How to search
Enter one of the following into the search bar:
* Phone number (MSISDN)
* MMO Transaction ID
* Transaction ID (deposit ID, payout ID, refund ID)
* Full Metadata value
* Metadata is searched based on full value of the metadata field. For example, searching for 12345, to find INV-12345, will not find results.
**Please note!**
Global search finds transactions from the last three months.
# PawaPay Dashboard
Source: https://docs.pawapay.io/dashboard/introduction
From the PawaPay Dashboard you can manage all your payments and operations in one single place. Learn more:
See how much funds you have across all the different markets you operate in.
Make sure the funds you have are as they are supposed to be.
To provide the best support to your customers, all payments in any market and with any MMO are available in one place along with their live status.
Global search finds any payment from the last three months quickly, regardless of where it was made.
Manage users with sophisticated access controls and high level of security and auditability.
Get everything set up for your integration quickly and easily.
# Audit logs
Source: https://docs.pawapay.io/dashboard/other/audit_log
Key actions taken in our dashboard are tracked and visible in the Audit Log.
You can find here any changes to user permissions, new users invited, who signed into the portal and when, etc.
This functionality is available to users with the Administrator role. Read more about [User roles](/dashboard/user_management/user_roles).
We strongly recommend using only personalised accounts on our platform - all accounts should be accessible to a single person.
This allows for compliant auditability of all actions taken on our dashboard.
## Searching and filtering
Please choose the time period you are interested in and press "Apply".
The following information will be available for each action:
| Column | Description |
| - | - |
| Time | The date and time when this action took place. |
| Action | The type of action that took place. Find below the glossary of all actions tracked. |
| Performed by | The user who took this action. |
| Description | Further details about the action. *For example, for the ROLE CHANGED action, it would include which role was assigned to the user.* |
| Affected user | The user who was affected by this action. *As an example, for whom the Role was changed.* |
You can always further filter down the results from the search bar or by pressing the filter icon in a column header.
As an example, you could search for the name of the user, which would filter the results to show all actions taken by or affecting this particular person during the selected time period.
## Glossary of actions
| Action | Description |
| - | - |
| User login | User has signed into the dashboard. |
| User invited | A new user has been invited into the dashboard. |
| User invite re-sent | A user has been resent the invitation email. |
| User registered | A user has activated their account in the dashboard. |
| User status change | User has been disabled or re-enabled to access the dashboard. |
| Password changed | A user has changed their password. |
| Role changed | A user's role has changed. Read more about [User roles](/dashboard/user_management/user_roles). |
| Country access changed | The countries that the user is able to see in the dashboard have changed. Read more about country-level access restrictions from [User roles](/dashboard/user_management/user_roles). |
| Mobile money API token generated | A new API token has been generated for your account. |
| Revoke API token | An API token has been revoked. |
| Batch disbursement | A new batch disbursement has been initiated. |
| Domain added | A new email domain has been added to the email domain whitelist. |
| Invite cancelled | A pending invitation has been deleted. |
| Enqueued payout cancellation | An enqueued payout has been cancelled. Read more about [cancelling enqueued payouts](/dashboard/transactions/cancel_enqueued). |
| Merchant admin invited | The first administrator of your account has been created at sign-up and sent a confirmation email. |
| Merchant admin confirmed | The first administrator of your account has confirmed their email address. |
# Batch disbursements
Source: https://docs.pawapay.io/dashboard/other/batch_disbursements
You can use our Batch disbursements page to initiate multiple disbursements at the same time directly from our dashboard.
This functionality is available to users with the Batch Disbursements role. Read more about [User roles](/dashboard/user_management/user_roles).
## Creating your batch disbursements file
You can download a template directly from the dashboard to create a new batch disbursement file.
The template contains the following columns to be filled out by you.
| Column | Description |
| - | - |
| Country | The country in which the recipient of the disbursement resides in. |
| Telco | The Mobile Money Operator (MMO) that the recipient's mobile money wallet is registered with. |
| Currency | The currency for this disbursement. |
| MSISDN | The phone number of the recipient of this disbursement. |
| Amount | The amount you wish to disburse. |
| Description | The description of this disbursement. |
The MSISDN must:
* not start with a + sign
* start with the country code
The description must:
* Be at least 4 characters
* Not be longer than 22 characters
* Contain only letters, numbers and spaces
* Not contain any special characters
You can find all the supported Country, Telco, and Currency combinations from [our API docs](/v2/docs/providers).
### Example batch disbursement file
A maximum of 1000 disbursements can be added to a single batch disbursement file.
## Uploading your batch disbursement file
After filling in your file, you can proceed to upload it into our system for validation. Your file will be validated for formatting errors and to ensure your account has configuration in place to disburse to the specified MMOs.
The results of validation will be shown directly in the dashboard.
| Result | Description |
| - | - |
| Ready | The transaction is ready to be disbursed. |
| Failed | Transaction is not valid. You can hover on the "Failed" result to see the failure reason. |
## Fixing validation failures
When encountering failures in your batch disbursement file, you can press the "Download Results" button to retrieve your batch disbursement file together with the failure reasons.
You can fix the errors in this file and attempt to upload again.
You can re-upload the file downloaded by pressing the "Download Results" button to attempt again.
You do not need to remove the failure reasons from this file before re-uploading.
## Initiating the batch disbursements
If every transaction is "Ready", you can press the "Schedule payment" button to initiate the disbursements.
# API tokens
Source: https://docs.pawapay.io/dashboard/other/system_conf/api_tokens
The PawaPay Merchant API can only be used with an authentication token. You can read more about how this token can be used to access our API from our [API docs](/using_the_api#authentication).
This functionality is available to users with the Technical Administration role. Read more about [User roles](/dashboard/user_management/user_roles).
## Generating an API token
Press the "Generate token" button to generate an API token. You can have at most 10 active API tokens at a time.
You can use the "Copy to clipboard" button to easily copy the API token.
**Store your token safely as PawaPay does not store your token after generating it for security reasons.**
**You need to configure your [Callback URLs](/using_the_api#callback-urls) to be able to generate API tokens.**
## Active API tokens
Your active API tokens are listed together with the information about who generated them, when they were generated, and the Token ID.
Note that the Token ID is not usable as the token itself but is only used for identifying a specific token when contacting our support team.
## Revoking tokens
If you need to revoke an API token, you can press the "Revoke" button which is found under the "Actions" column.
**Revoking an API token will immediately stop all payments that use the given token to authenticate API calls!**
## Signed requests
You can enable PawaPay to only accept signed requests for financial calls. You will need to provide us with the public key of the key pair you are signing your requests with.
### Accepted algorithms
We accept 4 kinds of encryption algorithms:
* [RSASSA-PSS Using SHA-512](https://datatracker.ietf.org/doc/html/rfc9421#name-rsassa-pss-using-sha-512)
* [RSASSA-PKCS1-v1\_5 Using SHA-256](https://datatracker.ietf.org/doc/html/rfc9421#name-rsassa-pkcs1-v1_5-using-sha)
* [ECDSA Using Curve P-256 DSS and SHA-256](https://datatracker.ietf.org/doc/html/rfc9421#name-ecdsa-using-curve-p-256-dss)
* [ECDSA Using Curve P-384 DSS and SHA-384](https://datatracker.ietf.org/doc/html/rfc9421#name-ecdsa-using-curve-p-384-dss)
### Adding a public key
You can add your public key by navigating to the API Security tab and pressing on “Add public key”.
You will then be presented a window where you can:
1. Name your public key
2. Enter the public key itself
3. Choose whether you want to immediately start accepting only signed requests (shown when you add your first key)
After successfully adding a key you will be able to view and remove it.
You can also switch this feature on and off by using the toggle. Switching the feature off **will not** remove any existing keys.
## Signed callbacks
Enable this feature to make PawaPay sign all callbacks.
You can then verify those signatures when receiving the callback to ensure they have not been tampered with and are coming from PawaPay.
Read more about signed callbacks in our [API docs](/using_the_api#signatures-in-callbacks).
# Callback URLs
Source: https://docs.pawapay.io/dashboard/other/system_conf/callback_urls
Our platform uses callbacks to notify you of the final statuses of payments. You can read more about how callbacks are used on our platform from [our API docs](/using_the_api#callback-urls).
This functionality is available to users with the Technical Administration role. Read more about [User roles](/dashboard/user_management/user_roles).
## Specifying the callback URLs
Please enter the URL at which you wish to receive notifications about the [final statuses](/dashboard/transactions/transaction_statuses) of payments. You can use the same URL for all types.
Ensure you enter the correct URL.
If you are using a firewall, ensure that you have whitelisted our IPs.
The IPs can be found in our [API docs](/using_the_api#callback-urls).
If you do not plan to use our API (e.g., you are only planning to use only our [Batch disbursements](/dashboard/other/batch_disbursements) or will be polling for final status, please indicate that by checking the below checkbox.
# How to log in
Source: https://docs.pawapay.io/dashboard/signing_in
The PawaPay Dashboard supports two methods of user authentication: traditional password-based authentication and Google-based authentication.
If you opt for Google-based authentication, your organization must already utilize [Google Workspace](https://workspace.google.com/), or a comparable service where Google manages email addresses. This feature facilitates a seamless login process, allowing users to access the Dashboard using their existing Google accounts.
We recommend forcing 2-step verification on your Google Workspace account for increased security.
## Logging into the dashboard
#### Authentication with username and password
Go to our [log in page](https://dashboard.pawapay.io/), enter your email address, and press "Continue with email".
Enter your password and press "Continue with email".
Our dashboard follows [OWASP ASVS Level 3](https://owasp.org/www-pdf-archive/OWASPApplicationSecurityVerificationStandard3.0.pdf) password policy.
If [two-factor authentication](/dashboard/user_management/security) is enabled for your company, you'll be prompted for a verification code (OTP) sent to your email, unless you've selected the "Remember me" checkbox, which remembers your device for 30 days (up to 3 devices). Enter the code and press "Continue" to be redirected to the PawaPay Dashboard.
#### Authentication with Google
Go to our [log in page](https://dashboard.pawapay.io/) and select "Continue with Google". There's no need to enter your email here.
If you've chosen Google as your authentication method, the Dashboard will present you an option to authenticate via signing in with your Google account.
When using multiple Google accounts (i.e., business and personal), make sure you choose the account that was used to invite you to our Dashboard.
### Resetting your password
Enter your email on our [log in page](https://dashboard.pawapay.io/) and press "Continue with email". Then select "Forgot your password?".
Enter your email and press "Reset my password".
You will receive an email with a link to reset your password. Press on the "Reset password" button.
You will be forwarded to the password reset page where you can enter your new password. When done, press on "Change my password".
You will then see a confirmation. Press "Login to the dashboard" to go to our [log in page](https://dashboard.pawapay.io/) and log in with your new password.
## Activating your account
When invited to our dashboard, you will receive an invitation email with an activation link. Activate your account within 30 days.
Pressing "Activate your account" will take you to the account activation page where you are asked to read and accept our Terms and Conditions and Privacy Policy, and choose your authentication method - Google or username and password - if both are available to you.
The password meter at the bottom of the "Password" input helps you choose a strong password to secure your account.
Pressing "Activate" will take you to our Dashboard [log in page](https://dashboard.pawapay.io/).
# Platform and MMO status
Source: https://docs.pawapay.io/dashboard/status
Any relevant information about incidents and maintenance that affect your merchant account will be displayed on the top right corner of the PawaPay Dashboard.
Pressing the button will expand your view and provide more details on the ongoing incidents and upcoming maintenance.
Pressing "Read more" will direct you to the incident page with more information about it.
The "Status Page" button will direct you to our [status page](https://status.pawapay.io/).
On the [status page](https://status.pawapay.io/) you can subscribe to receive updates about incidents through different channels like e-mail, Slack and more.
## Availability statuses of MMOs
| Status | Description |
| - | - |
| Operational | Service is operating as expected. |
| Degraded performance | Degraded performance for some transactions. |
| Partial outage | Degraded performance with intermittent unavailability. |
| Major outage | Service is unavailable and no transactions are accepted for processing. |
| Under maintenance | There is an active maintenance window. Degraded performance or intermittent unavailability might occur. |
Our support and payment operation teams work 24/7 and are aware of, and acting on, any statuses shown on our status page.
We provide regular updates on the progress of resolving all issues that arise.
## More about MMO availability
To provide the best experience for your customers, we suggest implementing our Correspondent Availability Status endpoints.
This allows you to inform your customers proactively about MMO downtime.
Read more from our [API docs](/v1/api-reference/toolkit/availability).
# Contacting our support
Source: https://docs.pawapay.io/dashboard/support
Our dedicated support team operates around the clock, 24/7, to assist you with any queries you may have regarding your transactions or the functionalities of our platform.
You can contact our Support team by signing into the PawaPay Dashboard and selecting the "Payment Support" link from the "Help" menu in the top-right of the Dashboard.
Note: "Payment Support" is only available on Production, not the Sandbox.
Then select the type of query you have.
#### Payments
From the *Payments* form, select an option from the *Category* dropdown — these include:
| Category | Description |
| - | - |
| Missing deposit enquiry | To enquire about deposits which do not reflect in the PawaPay system, but are deemed as successful by the MMO.
*For example, you have a MMO financial ID of a successful deposit transaction that you are unable to locate in the PawaPay Dashboard.* |
| Deposit status enquiry | To enquire about deposits which reflect in the PawaPay system, but you are seeking for further clarification. |
| Payout status enquiry | To enquire about payouts which reflect in the PawaPay system, but you are seeking for further clarification. |
| Other | In case you have any other questions about transactions on your PawaPay wallets. |
##### What to include in a *Payments* request?
To help us resolve your request quickly and effectively, please include as many relevant details as possible. This will allow us to identify, investigate, and address the issue more efficiently.
| Detail | Description |
| - | - |
| Amount | The amount in country specific currency. |
| MMO transaction ID | This is the ID that the customer would see for this transaction.
*Particularly important for reconciling payments that do not appear in our dashboard or are not in a final state. Read more about [Transaction statuses](/dashboard/transactions/transaction_statuses) .* |
| Phone number (MSISDN) | The phone number of the customer.
*For mobile money, this is the equivalent of a bank account or card number.* |
| Date and time | The date and time the problem occurred.
*Particularly important for reconciling payments that do not appear in our dashboard.* |
| Transaction ID | The ID of the deposit, payout or refund as seen in our dashboard. |
| Additional details & attachments | Any additional details about your request, plus any relevant attachments (optional). |
#### Other enquiries
From here you can enquire about:
| Category | Description |
| - | - |
| Financial statement enquiry | Use this if you have any questions about the data displayed on your financial statements. |
| General Finance & Treasury enquiry | In case you have any other questions related to Treasury or Finance. |
| Raise a complaint | Please use this form to submit a formal complaint regarding our service, product or an issue at hand that requires further investigation. We take all feedback seriously and aim to resolve issues promptly. |
| Other | In case you have any other general questions. |
# Topping up your wallets
Source: https://docs.pawapay.io/dashboard/topping_up
How to pre-fund your PawaPay wallet so you can make payouts.
To avoid any delays, please make sure that all funds for payouts/disbursements are sent from an account owned by your company. If you need to send funds through a contractual partner, please go through the [adding a Liquidity Partner](#adding-a-liquidity-partner) journey described below on this page.
In order to make payouts via our API, your wallet must have sufficient funds available. Where a payout request is made and the wallet has insufficient balance to complete the transaction, the transaction will fail.
Accordingly, if you intend to use payouts you will be required to pre-fund (top up) your PawaPay wallet.
In order to streamline the process you are encouraged to follow the instructions below whenever a top up is being made to your PawaPay wallet.
## Send your funds to the correct bank account
When sending funds to top up your wallet in a particular country, you are requested to send those funds to the bank account for the respective market which will be presented to you in the PawaPay Dashboard. Remember to always include your account specific ID to each payment you make. This will also be presented to you in the payment instruction we give.
To use Top ups in the Dashboard your merchant account must have either payouts or remittances enabled.
You must also have either the "Top ups" role, or be an "Administrator".
If you don’t have access, please contact your account administrator or the PawaPay team.
## Creating a top up
You can start a new top up at any time from the **Top ups** page in the Dashboard. Click **Create new top up** and the Dashboard will walk you through five short steps. If your account has both payouts and remittances enabled, an extra step asks what the funds are for. A top up from another PawaPay wallet skips the currency and payment steps.
Select one or more wallets that will receive the money. Each wallet is shown with its country and currency — for example, *Uganda UGX* or *Ghana GHS*. You can tick as many as you need, or use **Select all** to pick them all at once.
If you don't see a wallet you expect to be there, your PawaPay account may not be set up for that market yet.
Pick the method you will use to send us the money. See [Choosing a funding method](#choosing-a-funding-method) below for a plain-English explanation of each option.
Pick the currency you will send. The Dashboard only shows currencies that are valid for the wallets and funding method you chose in the previous steps. If something isn't listed, it isn't currently supported for your selection.
For each wallet you picked in step 1, type in how much you want credited. The Dashboard keeps a running **Total amount to top up** on the right so you can see the full amount you need to transfer.
This is the final step. The Dashboard will show you:
* The **bank account** you should send the funds to.
* Your **unique merchant code** — a short reference you must include with the transfer (see [Your unique merchant code](#your-unique-merchant-code) below).
* An **expected timeline**. We always try to process top ups as soon as possible but the timelines vary country by country.
Once you've made the transfer from your bank, come back to this screen and upload your Proof of Payment — a screenshot or PDF of the SWIFT confirmation or bank receipt. Then hit **Top up**.
## Choosing a funding method
Step 2 of the wizard asks how you want to fund your balance. You will see one or more of the following options — the ones available depend on your account setup:
Send funds from a bank account owned by your company via a standard bank/SWIFT transfer. This is the most common option. The Dashboard will show you the PawaPay bank account you should send the money to at step 5.
Move money you already hold in one PawaPay wallet into another PawaPay wallet. You'll be asked to pick which PawaPay wallet the money should come from.
A Liquidity Partner is a third party — for example, an aggregator or on-the-ground partner — that funds your wallet on your behalf. Before you can use one, they need to be approved by our Treasury team. See [Adding a Liquidity Partner](#adding-a-liquidity-partner) below.
## Your unique merchant code
Every top up comes with a **unique merchant code**, which is a short reference shown on the payment instructions screen.
You **must** include this code in the reference / description / narration field of your bank transfer. Without it, our Treasury team cannot match your payment to your account, which will delay your top up.
The Dashboard has a **Copy** button next to the code so you can paste it straight into your bank's transfer form. The same code needs to be reused across your top ups as it identifies your account, not the individual payment. Some bank accounts need their own reference instead. In that case the Dashboard shows that reference, so always use the code shown.
## Adding a Liquidity Partner
If someone other than your own company is going to send us the funds — for example, a local aggregator or a contractual partner — then they need to be onboarded as a **Liquidity Partner** first. This is a one-time setup per partner.
### How to add one
From the **Top ups** page, click **Add liquidity partner**, then **Add liquidity partner** again on the **Liquidity Partners** page (or use the **Add new Liquidity Partner** link inside step 2 of the top up wizard).
You'll be asked for:
* **Liquidity Partner name** — the legal name of the partner.
* **Your relationship with the partner** — a short description of how you work together.
* **Line of business** — what the partner does.
* **Country coverage** — the countries they're licensed / allowed to operate in.
Attach:
* **Licenses** — relevant financial or operating licenses the partner holds.
* **KYC documents** — incorporation documents, plus identification for shareholders with 5% or more stake and for directors.
You can upload multiple files in PDF, JPEG or PNG format, up to 5 MB each.
Click **Submit**. Your request goes to our Treasury team for review. You can track it on the **Liquidity Partners** page — the status will show as **In review** until it's been approved or rejected. You may be contacted by our team via email for further clarification.
Once a Liquidity Partner is **Approved**, they will appear as a selectable option under *Liquidity Partner* on step 2 of the top up wizard.
## After you submit a top up
Once you've uploaded your Proof of Payment, your top up lands on the **Top ups** page with a status you can track:
| Status | What it means |
| :- | :- |
| **Pending** | We've received your submission but haven't started processing it yet. |
| **In progress** | Our Treasury team is reviewing the payment and crediting your wallet. |
| **Completed** | The funds are in your wallet and ready to use for payouts. |
| **Failed** | Something went wrong with processing — our team will be in touch. |
| **Rejected** | The Proof of Payment couldn't be matched to a payment we've received. |
| **Cancelled** | You (or our team) cancelled the top up before it was processed. |
You can open any top up to see its details per wallet, add a comment for our Treasury team, or download the Proof of Payment you uploaded. While a top up is **Pending**, you can also cancel it.
Need to do another top up that looks just like a past one? Use **Repeat top up** on any completed top up. The Dashboard will pre-fill the wizard with the same wallets, funding method and currency so you can adjust the amounts and submit.
# Callback resend
Source: https://docs.pawapay.io/dashboard/transactions/callback_resend
When a payment is completed, we communicate the final status to your systems by sending a callback.
However, delivery of these callbacks may be unsuccessful due to network instabilities, downtime in the receiving system, or similar issues.
In such scenarios, you have the option to resend the callback through our dashboard.
Callbacks can only be resent for payments initiated within the last 90 days.
Older payments remain visible in the dashboard and their status can still be checked — only the callback resend is no longer available for them.
The option to resend a callback is only available when our initial attempt to deliver it has been unsuccessful. In the event of a problematic transaction, you can navigate to the respective payment details page. Here, you'll find the option to resend the callback.
To resend the callback, click on the 'Resend callback' button.
The process should conclude within a few seconds, and a confirmation message will appear upon its successful completion.
There is also an option to initiate this process via an API call.
Read more about this from our API documentation for [deposits](/v1/api-reference/deposits/resend-deposit-callback), [payouts](/v1/api-reference/payouts/resend-payout-callback) and [refunds](/v1/api-reference/refunds/resend-refund-callback).
# Cancel enqueued payout
Source: https://docs.pawapay.io/dashboard/transactions/cancel_enqueued
During certain incidents involving Mobile Money Operators (MMOs), PawaPay may temporarily suspend the processing of payouts.
We understand the importance of maintaining transaction flow.
Therefore, we continue to accept your payout requests during these periods.
These requests are assigned a status of **Enqueued**.
This status indicates that your payout request has been received and is in line for processing as soon as the network stabilizes.
However, you have the option to cancel these transactions by navigating to the respective payment details page and initiating a cancellation from there.
To cancel an Enqueued transaction, click on the "Cancel transaction" button.
The process should conclude within a few seconds, and a confirmation message will appear upon its successful completion.
There is also an option to initiate this process via an API call.
Read more regarding this functionality from our [API documentation](/v1/api-reference/payouts/cancel-enqueued-payout).
# Deposit details
Source: https://docs.pawapay.io/dashboard/transactions/deposit_details
You can see the details of a deposit by using either our [Global search](/dashboard/global_search) or [Deposits](/dashboard/transactions/deposits) search functionality and clicking on the specific payment.
You can find the glossary of the data found on the deposit details from the [Deposits](/dashboard/transactions/deposits) documentation.
All possible statuses of transactions are documented in [Transaction statuses](/dashboard/transactions/transaction_statuses).
## Successful deposit
On successful deposits, the Provider Transaction ID is available. This ID is also available to the customer on their SMS receipt or their transaction history. When contacted by a customer, their transaction can be found using this ID.
## Failed deposit
We constantly monitor and improve the failure reasons to be as precise and accurate as possible.\
Some Mobile Money Operators (MMO) do not provide specific failure reasons for failed transactions.
# Deposits
Source: https://docs.pawapay.io/dashboard/transactions/deposits
You can find all deposits made on our platform on the Deposits page.
This functionality is available to users with the Transactions role. Read more about [User roles](/dashboard/user_management/user_roles).
## Search for deposits
Please choose the time period you are interested in by clicking on the time period selector. You can also use the quick links next to the selector to quickly see transactions made in the last hour, today, yesterday, or the current week.
If you are interested in transactions of a specific Mobile Money Operator (MMO), you can filter by picking them from the Providers dropdown.
Pressing "Apply filters" will show you the results.
A maximum of 10,000 records will be shown. If your search results in more than that, the oldest 10,000 will be visible in your results.
## Glossary of results
| Column | Description |
| - | - |
| Creation date | The date and time at which this deposit was initiated. Time is in the UTC time zone. |
| Phone number | The phone number of the party being collected from. |
| Provider Transaction ID | The ID for this transaction specified by the MMO.
**Please note!** This ID is visible to your customers in their SMS receipts or Mobile Money applications transaction history. |
| Amount | The amount on the original deposit request. |
| Currency | The currency in which this transaction took place. |
| Flow subtype | The type of deposit. |
| Status | The current status of this transaction. Read more about statuses from [Transaction statuses](/dashboard/transactions/transaction_statuses). |
| Country | The country in which this transaction took place. |
| Provider | The Mobile Money Operator who carried out the transaction. |
| Reason | The reason for this payment specified by you as the `statementDescription` in our API. Read more about our deposits in our API from our [API docs](/v1/api-reference/deposits/request-deposit). |
# Payout details
Source: https://docs.pawapay.io/dashboard/transactions/payout_details
To see payout details, use either our [Global search](/dashboard/global_search) or [Payouts](/dashboard/transactions/payouts) search functionality, and click on the specific payment.
You can find the glossary of the data found on the payout details from the [Payouts](/dashboard/transactions/payouts) documentation.
All possible statuses of transactions are documented in [Transaction statuses](/dashboard/transactions/transaction_statuses).
## Successful payout
On successful payouts, the Provider Transaction ID is available. This ID is also available to the customer on their SMS receipt or their transaction history. When contacted by a customer, their transaction can be found using this ID.
## Failed payout
We constantly monitor and improve the failure reasons to be as precise and accurate as possible.\
Some Mobile Money Operators do not provide specific failure reasons for failed transactions.
### Proof of payment (PoP)
View and download your proof of payment / payout receipt on the transaction details page once your transaction is complete.
# Payouts
Source: https://docs.pawapay.io/dashboard/transactions/payouts
You can find all of your payouts made on our platform on the Payouts page. Both payouts and refunds are available to search.
This functionality is available to users with the Transactions role. Read more about [User roles](/dashboard/user_management/user_roles).
## Search for payouts
Please choose the time period you are interested in by clicking on the time period selector. You can also use the quick links next to the selector to quickly see transactions made in the last hour, today, yesterday, or the current week.
If you are interested in transactions of a specific Mobile Money Operator (MMO), you can filter by picking them from the Providers dropdown.
Pressing "Apply filters" will show you the results.
A maximum of 10,000 records will be shown. If your search results in more than that, the oldest 10,000 will be visible in your results.
## Glossary of results
| Column | Description |
| - | - |
| Creation date | The date and time at which this payout was initiated. Time is in UTC timezone. |
| Phone number | The phone number of the party being paid. |
| Financial Transaction ID | The ID for this transaction specified by the MMO.
**Please note!** This ID is visible to your customers in their SMS receipts or Mobile Money apps transaction history. |
| Amount | The amount paid out. |
| Currency | The currency in which this transaction took place. |
| Flow Subtype | The type of disbursement, for example a payout or a refund. |
| Status | The current status of this transaction. Read more about statuses from [Transaction statuses](/dashboard/transactions/transaction_statuses). |
| Country | The country in which this transaction took place. |
| Provider | The Mobile Money Operator who carried out the transaction. |
| Reason | The reason for this transaction specified by you as the `statementDescription` in our API. Read more about our payouts in our API from our [API docs](/v1/api-reference/payouts/request-payout). |
# Transaction statuses
Source: https://docs.pawapay.io/dashboard/transactions/transaction_statuses
All transactions on our platform can be in one of five possible statuses.
| Status | Description |
| - | - |
| `Completed` | Transaction has been **successfully** processed. This is a **final** status and will not change. |
| `Failed` | Transaction **failed** to process and no funds have been moved. This is a **final** status and will not change. |
| `In reconciliation` | There was a problem processing this transaction, and it has been moved to our reconciliation engine to determine its final status. The transaction will be moved to its final status as soon as it’s been reconciled. |
| `Processing` | The transaction is currently being processed. |
| `Enqueued` | The transaction has been **accepted** by us for processing but is currently **enqueued** and not being shared with the MMO. Transactions with this status can be manually cancelled on the transaction details page. |
Transaction statuses `Completed` and `Failed` are final statuses and will not change.
# Inviting users
Source: https://docs.pawapay.io/dashboard/user_management/inviting_users
Our dashboard uses an invite-based user management system. All users invited will get an email with instructions on how to activate their account.
This functionality is available to users with the User Management role. Read more about [User roles](/dashboard/user_management/user_roles).
## Inviting a new user
All users are invited to the dashboard based on their email address.
You can limit this user to only see transactions and wallets from specific countries by specifying them in the "Limited to" dropdown.
Choose the appropriate roles for the user based on what tasks they need to perform in the dashboard.
Read more about country limits and our [User roles](/dashboard/user_management/user_roles).
You can read more about the activation flow for the user from [Signing into the PawaPay Dashboard](/dashboard/signing_in).
You can only invite other users using roles that are active on your user.
# Managing users
Source: https://docs.pawapay.io/dashboard/user_management/managing_users
Users on the dashboard can be managed by pressing the tab "Manage users" on the User Management page.
This functionality is available to users with the User Management role. Read more about [User roles](/dashboard/user_management/user_roles).
You can navigate between active, pending, and disabled users by pressing on the respective tab.
## Active users
On the **Active** tab, you can see the currently active users on the dashboard.
Your roles might affect which users you are able to see and manage.
### User Information
The following information is available for each user:
| Column | Description |
| - | - |
| Name | The first and last name of the user. |
| E-mail | The email address of the user they were invited into the dashboard with. |
| Role | The roles the user has. |
| Geo Roles | The countries this user is limited to. |
| Actions | The actions that can be done for this user. |
If the user has more than three roles, you can see all of them by hovering on the "+N more" label.
### Changing users' country limitations
You can change the countries a user has access to by opening the "Geo Roles" dropdown and checking the countries they need.
Changes to country limitations take effect immediately.
### Changing users' roles
User roles can be changed by pressing the pencil icon in the user's "Actions" column.
You can then make the changes needed and press "Save changes."
### Disabling users
You can disable a user by pressing the icon in the user's "Actions" column.
You will be asked to confirm this action by pressing "Yes."
You can find the disabled user in the "Disabled" tab.
## Pending users
Users who have yet to activate their account from their invite email are found in the **Pending** tab.
You can change their role and country limitations just like for active users.
In case their invite has expired or is lost, you can resend the invitation email with a new activation link by pressing the "Resend invitation" button.
You will be asked to confirm this action by pressing "Yes."
You may also choose to delete the invitation by clicking on this icon under the Actions column:
## Disabled users
Users who have been disabled are found in the **Disabled** tab.
You can still change country limitations and roles for disabled users.
You can enable users by pressing on the icon in the user's "Actions" column.
You will be asked to confirm this action by pressing "Yes."
You can find the enabled user in the "Active" tab.
# Security
Source: https://docs.pawapay.io/dashboard/user_management/security
The 'Security' tab, located under the 'User Management' page, is visible only to users with both the User Management and Technical Administration [roles](/dashboard/user_management/user_roles). The Administrator role includes both.
## Two-factor authentication
You can enable two-factor authentication for users who do not use Google as a sign-in option. When enabled, users will receive a six-digit numerical code via email with each sign-in attempt.
## Google authentication
If you are using Google Workspace for your emails, you can require all users to authenticate with Google.
This means that any active user who currently signs in with a password will no longer be able to use their credentials and will be directed to authenticate with Google.
Additionally, all new users will only have the option to confirm their registration through Google authentication.
Any active user who currently signs in with a password will no longer be able to use their credentials and will be directed to authenticate with Google!
# User roles
Source: https://docs.pawapay.io/dashboard/user_management/user_roles
To ensure functionality and data is visible to only the people you need, we provide user roles and country-based limitations for users.
## Country based limitations
Users' access in our dashboard can be limited to specific countries.
For example, limiting a user's access to Ghana only as their tasks involve providing support for Ghana customers.
## User roles
Roles limit users to specific functionality in the dashboard. For example, the User Management role could be enabled only for the team leads of your various support teams. They are then able to invite new users independently. They are only able to invite users with all or some of the roles they have for their user.
| Role | Description |
| - | - |
| **Administrator** | Full access to all functionality in the dashboard.
Includes all other roles automatically. |
| **Batch disbursements** | Can use the [Batch disbursements](/dashboard/other/batch_disbursements) functionality of our platform.
Includes Finances and Transactions roles automatically. |
| **Finances** | Has access to [Finances](/dashboard/finances/wallets) to see the financial data in [Wallets](/dashboard/finances/wallets), [Statements](/dashboard/finances/statements), etc.
Includes Transactions role automatically. |
| **Transactions** | Has access to [Transactions](/dashboard/transactions/deposits) to see deposits, payouts, and refunds in the countries they are limited to. |
| **User Management** | Able to invite new and manage existing users with the same or fewer roles that they have.
**Please note!**
This role can only be combined with other roles. |
| **Technical Administration** | Able to see [Developers page](/dashboard/other/system_conf/callback_urls) to configure technical aspects of the platform. |
| **Settlements** | Access to Settlements to see any settlement related information and set preferences. Only available with Administrator role, or combined with Batch disbursements or Finances role. |
| **Top ups** | Can request, view and manage [Top ups](/dashboard/topping_up). Only available with Administrator role, or combined with Batch disbursements or Finances role. |
Dependent roles get assigned automatically. For example, adding the Finances role automatically adds the Transactions role to the user.