Getting Started
The Hanypay Merchant API lets you accept AKL payments from customers, send payouts, issue refunds, and query transaction history. All communication is over HTTPS and authenticated via API keys.
Quick start
- Create a business in the Hanypay Dashboard
- Go to Business → API Keys and create an API key
- Copy the key — it is shown only once
- Pass the key in the
Hanypay-Api-Keyheader on every request
Authentication
Include your API key in every request using the Hanypay-Api-Key header.
curl https://payment-ms.hanypay.co/api/v1/merchant/charges/ \
-H "Content-Type: application/json" \
-H "Hanypay-Api-Key: YOUR_API_KEY" \
-d '{"amount": "100.00"}'All responses follow a consistent shape:
{
"success": true,
"message": "...",
"data": { ... }
}{
"success": false,
"error": "error_code",
"message": "Human-readable error message"
}Collections
Initiate Payment
Start a payment collection. The customer receives a 6-digit authorization code via email which must be verified to complete the payment.
| Field | Type | Required | Description |
|---|---|---|---|
| wallet_number | string(16) | Required | Customer's wallet number |
| amount | decimal | Required | Amount in AKL (min 0.000001) |
| currency_code | string(3) | Optional | Currency code (only "AKL", default "AKL") |
| txn_reference | string(256) | Optional | Your unique reference for idempotency |
| metadata | object | Optional | Arbitrary key-value data returned in webhooks |
| callback_url | url | Optional | URL to receive webhook notifications |
| charge_bearer | string | Optional | "customer" (default) or "merchant". Determines who pays the transaction fee. |
charge_bearer to "merchant" to absorb the transaction fee yourself. The customer will only be charged the payment amount. Default is "customer", where the fee is added on top of the amount.curl -X POST https://payment-ms.hanypay.co/api/v1/merchant/payments/initiate/ \
-H "Content-Type: application/json" \
-H "Hanypay-Api-Key: YOUR_API_KEY" \
-d '{
"wallet_number": "1234567890123456",
"amount": "100.00",
"txn_reference": "ORDER-001",
"charge_bearer": "merchant"
}'{
"success": true,
"message": "Authorization code has been sent to customer.",
"data": {
"transaction_id": "d4f8a2b1-...",
"txn_reference": "ORDER-001",
"verification_required": true
}
}txn_reference that already exists, the existing transaction is returned instead of creating a duplicate. Use this for safe retries.Verify Payment
Submit the customer's authorization code to complete the payment. The result is synchronous — you get success or failure immediately.
| Field | Type | Required | Description |
|---|---|---|---|
| code | string | Required | 6-character authorization code from customer |
| txn_reference | string | Required | Transaction reference from initiate response |
curl -X POST https://payment-ms.hanypay.co/api/v1/merchant/payments/verify/ \
-H "Content-Type: application/json" \
-H "Hanypay-Api-Key: YOUR_API_KEY" \
-d '{
"code": "ABC123",
"txn_reference": "ORDER-001"
}'{
"success": true,
"message": "Payment completed successfully.",
"data": {
"transaction_id": "d4f8a2b1-...",
"status": "Completed",
"amount": "100.000000"
}
}Error codes: invalid_code, code_expired, invalid_reference, processing_failed
Payout / Disbursement
Payout
Send AKL from your business wallet to a recipient wallet.
| Field | Type | Required | Description |
|---|---|---|---|
| wallet_number | string(16) | Required | Recipient wallet number |
| amount | decimal | Required | Amount in AKL |
| description | string(255) | Optional | Payout description |
curl -X POST https://payment-ms.hanypay.co/api/v1/merchant/payouts/ \
-H "Content-Type: application/json" \
-H "Hanypay-Api-Key: YOUR_API_KEY" \
-d '{
"wallet_number": "5555666677778888",
"amount": "50.00",
"description": "Salary payout"
}'{
"success": true,
"message": "Payout completed successfully.",
"data": {
"transaction_id": "a1b2c3d4-...",
"reference": "REF-001",
"status": "Completed"
}
}Error codes: invalid_wallet, limit_exceeded, same_wallet, payout_failed
Transactions
List Transactions
Retrieve a paginated list of your business transactions.
| Field | Type | Required | Description |
|---|---|---|---|
| status | string | Optional | Filter by status (Pending, Completed, Failed, etc.) |
| transaction_type | string | Optional | Filter by type (Purchase, Transfer, Refund) |
| start_date | date | Optional | Filter from date (YYYY-MM-DD) |
| end_date | date | Optional | Filter to date (YYYY-MM-DD) |
| query | string | Optional | Search by reference or wallet number |
| page | integer | Optional | Page number (default 1) |
| page_size | integer | Optional | Results per page (default 15) |
curl "https://payment-ms.hanypay.co/api/v1/merchant/transactions/?status=Completed&page=1" \ -H "Hanypay-Api-Key: YOUR_API_KEY"
{
"count": 50,
"next": "...?page=2",
"previous": null,
"total_pages": 4,
"current_page": 1,
"results": [
{
"reference": "abc123",
"txn_reference": "ORDER-001",
"amount": "100.000000",
"status": "Completed",
...
}
]
}Get Transaction
Retrieve a single transaction by its internal reference or your txn_reference.
curl https://payment-ms.hanypay.co/api/v1/merchant/transactions/ORDER-001/ \ -H "Hanypay-Api-Key: YOUR_API_KEY"
{
"success": true,
"message": "Transaction retrieved.",
"data": {
"id": 123,
"reference": "abc123",
"txn_reference": "ORDER-001",
"amount": "100.000000",
"charges": "1.500000",
"currency_code": "AKL",
"transaction_type": "Purchase",
"status": "Completed",
"channel": "Wallet Transfer",
"description": "",
"metadata": {},
"created_at": "2026-01-15T10:30:00Z"
}
}Refund
Refund a completed purchase transaction. Supports full and partial refunds. Cumulative refunds are tracked — the total cannot exceed the original amount.
| Field | Type | Required | Description |
|---|---|---|---|
| transaction_reference | string | Required | Reference of the original transaction |
| amount | decimal | Optional | Partial refund amount (defaults to full amount) |
| description | string(255) | Optional | Reason for refund |
curl -X POST https://payment-ms.hanypay.co/api/v1/merchant/refunds/ \
-H "Content-Type: application/json" \
-H "Hanypay-Api-Key: YOUR_API_KEY" \
-d '{
"transaction_reference": "ORDER-001",
"amount": "25.00",
"description": "Customer requested partial refund"
}'{
"success": true,
"message": "Refund completed successfully.",
"data": {
"refund_reference": "REF-REFUND-001",
"amount": "25.0",
"status": "Completed"
}
}Utilities
Calculate Charges
Preview the transaction fee for a given amount before initiating a payment.
| Field | Type | Required | Description |
|---|---|---|---|
| amount | decimal | Required | Transaction amount |
curl -X POST https://payment-ms.hanypay.co/api/v1/merchant/charges/ \
-H "Content-Type: application/json" \
-H "Hanypay-Api-Key: YOUR_API_KEY" \
-d '{"amount": "100.00"}'{
"success": true,
"message": "Charges calculated.",
"data": {
"amount": "100.000000",
"charges": "1.500000",
"total": "101.500000"
}
}Exchange Rates
Convert between AKL and other currencies. One of the currencies must be AKL.
| Field | Type | Required | Description |
|---|---|---|---|
| base_currency_code | string(3) | Required | Source currency (e.g. "AKL") |
| target_currency_code | string(3) | Required | Target currency (e.g. "USD") |
| amount | decimal | Required | Amount to convert |
curl -X POST https://payment-ms.hanypay.co/api/v1/merchant/exchange-rates/ \
-H "Content-Type: application/json" \
-H "Hanypay-Api-Key: YOUR_API_KEY" \
-d '{
"base_currency_code": "AKL",
"target_currency_code": "USD",
"amount": "100.00"
}'{
"success": true,
"message": "Exchange rate retrieved.",
"data": {
"base_currency": "AKL",
"target_currency": "USD",
"rate": 0.052,
"original_amount": 100.0,
"converted_amount": 5.2
}
}Guides
Payment Flow
The collection flow involves two API calls with a customer action in between:
Webhooks
Webhook URLs are registered in the Dashboard under Business → API Keys → Webhook URLs. When a payment completes or fails, we send a POST request to each active URL.
Payload format
{
"event": "payment.completed",
"data": {
"reference": "abc123",
"txn_reference": "ORDER-001",
"amount": "100.000000",
"charges": "1.500000",
"currency_code": "AKL",
"status": "Completed",
"transaction_type": "Purchase",
"created_at": "2026-01-15T10:30:00Z"
}
}Events
| Event | Description |
|---|---|
| payment.completed | Customer payment succeeded |
| payment.failed | Customer payment failed |
| payment.updated | Payment status changed |
| payout.completed | Disbursement succeeded |
| payout.failed | Disbursement failed |
Signature verification
Each webhook includes an X-Hanypay-Signature header containing an HMAC-SHA256 hex digest of the JSON body signed with your webhook secret.
import hmac, hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode("utf-8"),
body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)Error Reference
| Code | Description |
|---|---|
| invalid_api_key | Missing or invalid API key |
| invalid_wallet | Wallet number not found |
| insufficient_balance | Not enough AKL balance |
| invalid_code | Authorization code not found |
| code_expired | Authorization code has expired |
| invalid_reference | Transaction reference not found |
| invalid_amount | Amount exceeds allowed limit |
| processing_failed | Payment processing failed |
| limit_exceeded | Transaction limit exceeded |
| same_wallet | Source and destination wallets are the same |
| payout_failed | Payout processing failed |
| refund_failed | Refund processing failed |
| conversion_failed | Currency conversion failed |
Transaction Statuses
| Status | Description |
|---|---|
| Pending | Transaction created, awaiting verification |
| Initiated | Transaction initiated with provider |
| Completed | Transaction completed successfully |
| Failed | Transaction failed |
| Cancelled | Transaction cancelled |
| FundsReserved | Funds reserved for transaction |

