Fast Hosted Checkout
Create a payment session, redirect the customer, then verify or receive IPN updates when the transaction completes.
Accept hosted checkout payments (pay-in), send wallet payouts (pay-out), and manage commissions — with sandbox and production credentials, signed requests, and dashboard-controlled gateways.
Create a payment session, redirect the customer, then verify or receive IPN updates when the transaction completes.
Debit your merchant wallet and send NGN bank transfers via Korapay, PocketFi, or XudoPay. GET payout-methods, POST initiate-payout, then verify-payout or payout IPN. See the Payout Guide for fees, method codes, and error handling.
Merchants can choose eligible payment gateways from API Config without regenerating API credentials.
Bytepay API uses API keys to authenticate requests. You can obtain your credentials from your merchant dashboard.
Bytepay API supports both sandbox (testing) and production environments. Always test in sandbox first before going live.
Use for: Development, testing, integration
X-Environment: sandbox
Credentials: Use test_* prefixed API keys
Use for: Live payments, real money
X-Environment: production (default if omitted)
Credentials: Use production API keys (no prefix)
| Credential | Header | Description | Location |
|---|---|---|---|
Merchant ID |
X-Merchant-Key |
Your unique merchant identifier | Dashboard → Merchant → Config |
API Key |
X-API-Key |
API authentication key | Dashboard → Merchant → Config |
Request Timestamp |
X-Timestamp |
Unix timestamp in seconds used for replay protection | Generated by your server for each API request |
API Signature |
X-Signature |
HMAC-SHA256 signature for the request | Generated using your Client Secret |
Client Secret |
- | Used for API request signing and webhook signature verification | Dashboard → Merchant → CONFIG |
| Production API Key | X-API-Key |
Production API key (no prefix) | Merchant Dashboard > API Config > Production Mode |
| Production Merchant Key | X-Merchant-Key |
Production merchant identifier (no prefix) | Merchant Dashboard > API Config > Production Mode |
When API signatures are required, sign this exact payload with your Client Secret (test_api_secret for sandbox, api_secret for production):
timestamp.HTTP_METHOD.path_with_query.raw_body
For GET requests the raw body is an empty string. X-Signature may be sent as the hex digest or prefixed with sha256=.
$timestamp = (string) time();
$method = 'POST';
$path = '/api/v1/initiate-payout';
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$signature = hash_hmac('sha256', "{$timestamp}.{$method}.{$path}.{$body}", $apiSecret);
Use this flow for a production-safe integration. Credentials identify the merchant, while currency and gateway availability are controlled from the merchant dashboard.
Open Dashboard -> Merchant -> Config and copy the Merchant Key, API Key, and Client Secret for sandbox or production.
Make sure the merchant supports the checkout currency and the merchant owner has an active wallet for that currency.
From API Config, enable the payment gateways that should appear on hosted checkout for each eligible currency.
POST to /api/v1/initiate-payment with X-Timestamp and X-Signature, redirect the customer to payment_url, then verify or listen for IPN.
GET payout-methods to obtain withdraw_method_id (e.g. korapay_ngn_transfer), ensure wallet balance covers payout_amount + fee, POST initiate-payout with beneficiary or withdraw_account_id, then verify-payout or listen for payout IPN. See Payout Guide for full details.
Use sandbox credentials first, then switch to production when your checkout and IPN are verified.
The merchant API is no longer a single-currency, fixed-gateway flow. A merchant can now support multiple currency rails, keep separate sandbox and production credentials, and choose eligible gateways from the API Config page without regenerating API keys.
Your code sends amount, currency, redirects, IPN URL, and optional gateway keywords. The dashboard decides which currency rails, wallets, and payment gateways are actually available for that merchant.
Pending merchants can test in sandbox. Production API access and live checkout are available only after admin approval.
Each request currency_code must exist in the merchant-supported currency list. Unsupported currencies are rejected before checkout opens.
The merchant owner must have an active wallet for the requested currency, otherwise the API returns a wallet availability error.
Only active automatic gateways that match the requested currency and merchant selection can appear on hosted checkout.
Choose Customer Pays or Merchant Pays under Merchant → API → API Commission. This affects initiate-payment totals and production balance checks.
Use korapay_ngn_transfer, pocketfi_ngn_transfer, or xudopay_ngn_transfer from GET payout-methods for API settlements. See Korapay Integration, PocketFi Integration, and XudoPay Integration for pay-in, pay-out, and webhook setup.
Save beneficiary accounts under Withdraw → Saved accounts, then pass withdraw_account_id on initiate-payout.
X-Environment decides which merchant key, API key, and API secret are used.
Rejected or disabled merchants are blocked. Production also requires approved merchant status.
currency_code must match a merchant-supported rail such as USD, EUR, or BDT.
The merchant owner needs an active wallet for that same currency.
Merchant API Config selections restrict checkout to the chosen eligible gateways. If none are selected, the safe fallback is all active automatic gateways for that currency.
allow_payment_methods can narrow the available gateway list, but it cannot expose an unconfigured or wrong-currency gateway.
| Case | API Result | Fix |
|---|---|---|
| Merchant supports USD and EUR, but only USD wallet exists. | 422 Receiver wallet for this currency is not available. |
Create/activate the EUR merchant wallet before accepting EUR checkout. |
| Stripe USD is selected, but the request sends currency_code EUR. | Stripe USD will not appear on checkout. | Select an active EUR gateway or send USD. |
| Merchant has not selected any gateway yet. | Checkout falls back to all active automatic gateways that match the requested currency. | Use API Config to lock checkout to preferred gateways. |
allow_payment_methods=["paypal"] but PayPal is not configured for the merchant. |
PayPal remains hidden. | Enable PayPal from Payment Gateway Controls if its currency matches. |
| Pending merchant calls production API. | 403 Merchant Pending Approval |
Use sandbox until admin approval is complete. |
A gateway configured for USD cannot process EUR checkout unless a separate EUR gateway exists.
API Secret is used only to sign server-to-server API requests and verify IPN signatures.
The returned payment_url is time-limited. Create a new payment session for expired checkout links.
A merchant can verify only its own transaction and only in the matching sandbox or production environment.
Each merchant has an API commission percentage set by the platform administrator. You choose who pays that fee from Merchant → API → API Commission. Payout fees are separate and come from the withdrawal method configuration.
The customer pays payment_amount plus commission. You receive the full payment_amount in your wallet after a successful checkout.
customer_pays = payment_amount + commissionmerchant_receives = payment_amountThe customer pays only payment_amount. Commission is deducted from your wallet when the payment completes (production only).
customer_pays = payment_amountmerchant_receives = payment_amount| Field | Description |
|---|---|
info.base_amount |
Original payment_amount from your request. |
info.api_fee_percent |
Commission percentage configured for your merchant account. |
info.api_commission_fee / info.commission_fee |
Calculated commission amount. |
info.api_fee_bearer / info.commission_bearer |
customer or merchant |
info.customer_pays |
Total amount the customer must pay on hosted checkout. |
info.merchant_receives |
Net amount credited to your wallet when payment succeeds. |
{
"payment_url": "https://bytepay.online/payment/checkout?...",
"info": {
"ref_trx": "ORDER_1001",
"amount": 1000,
"base_amount": 1000,
"currency_code": "NGN",
"environment": "production",
"api_fee_bearer": "merchant",
"api_fee_percent": 5,
"api_commission_fee": 50,
"commission_fee": 50,
"commission_bearer": "merchant",
"customer_pays": 1000,
"merchant_receives": 1000
}
}
On successful payments, verify-payment and payment IPN payloads include a commission object:
"commission": {
"base_amount": 1000,
"commission_fee": 50,
"commission_bearer": "merchant",
"customer_paid": 1000,
"merchant_received": 1000,
"commission_trx_id": "TXN..."
}
Merchant API checkout is controlled by three layers: merchant-supported currency, active receiver wallet, and configured payment gateways. This keeps checkout options accurate after API credentials are generated.
A gateway appears on hosted checkout only when every rule below is true. If the merchant has selected gateways, those selected gateways become the allowed set for checkout.
| Layer | Who controls it | Effect |
|---|---|---|
currency_code |
API request | Must match a merchant-supported currency with an active receiver wallet. |
Payment Gateway Controls |
Merchant dashboard | Defines the gateway set available to hosted checkout for each eligible currency. |
allow_payment_methods |
API request | Optional name/code keyword filter applied after merchant gateway rules. It cannot expose a gateway the merchant did not configure. |
Bytepay uses Korapay as the primary NGN payout provider for merchant API settlements, merchant withdrawals, and wallet bank transfers. Pay-in is available via Korapay hosted checkout and optional virtual account deposits.
Configure Korapay under Admin → Integration Center → Korapay:
| Setting | Description |
|---|---|
public_key |
Korapay publishable key (pk_live_… / pk_test_…) |
secret_key |
Korapay secret key used for API calls and payout disbursements |
encryption_key |
Required for client-side encryption flows if enabled |
webhook_secret |
HMAC secret for verifying x-korapay-signature on platform webhooks |
sandbox |
Enable sandbox mode — must match your Korapay key environment |
base_url |
Default: https://api.korapay.com |
https://bytepay.online/webhooks/korapay
Register this URL in your Korapay merchant dashboard. Korapay sends charge.success for deposits and transfer.success / transfer.failed for payouts.
| Flow | Deposit method code | Behavior |
|---|---|---|
| Hosted checkout | korapay-ngn |
Customer redirected to Korapay checkout (card, bank transfer, pay with bank). Wallet credited on charge.success webhook or redirect verify. |
| Virtual account | korapay_ngn_va |
One-time VA issued per deposit. Requires BVN on user profile (or sandbox BVN). Wallet credited when transfer matches. |
Include currency_code NGN and optional allow_payment_methods containing Korapay.
Open payment_url from the response.
Receive payment IPN on your ipn_url. Poll GET /api/v1/verify-payment/{trx_id} if needed.
Payouts debit the merchant owner wallet immediately. Korapay disbursement runs asynchronously when status is processing; final status arrives via Korapay webhook and merchant IPN.
| Field | Required | Notes |
|---|---|---|
bank_code |
Yes | Korapay bank code (not Paystack code) |
account_number |
Yes | 10-digit NUBAN account number |
account_name |
No | Auto-resolved via Korapay account resolve before transfer |
{
"payout_amount": 5000,
"currency_code": "NGN",
"ref_trx": "SETTLE_20260608_001",
"ipn_url": "https://merchant.example/webhooks/bytepay-payout",
"withdraw_method_id": 18,
"description": "Vendor settlement",
"beneficiary": {
"bank_code": "044",
"account_number": "0123456789"
}
}
| Status | Meaning |
|---|---|
pending |
Wallet debited; Korapay disbursement submitted or processing |
completed |
Korapay confirmed success; funds delivered to beneficiary |
failed / canceled |
Transfer failed; wallet amount refunded automatically for API payouts |
error_code |
When |
|---|---|
gateway_not_configured |
Korapay or StroWallet credentials missing in admin |
invalid_beneficiary |
Invalid bank code or account could not be verified |
insufficient_balance |
Merchant wallet cannot cover payout + fee |
merchant_payout_disabled |
Withdraw feature or KYC rules block payouts |
gateway_payout_failed |
Korapay rejected disbursement after wallet debit (refunded) |
payout_failed |
Korapay disburse not enabled, IP not whitelisted, or other gateway error (refunded) |
provider_insufficient_balance |
Rare for Korapay — usually indicates platform Korapay balance issue; contact support |
| Purpose | Korapay endpoint |
|---|---|
| Initialize checkout (pay-in) | POST /merchant/api/v1/charges/initialize |
| Verify charge | GET /merchant/api/v1/charges/{reference} |
| Create virtual account | POST /merchant/api/v1/virtual-bank-account |
| Bank list | GET /merchant/api/v1/misc/banks |
| Resolve account | POST /merchant/api/v1/misc/banks/resolve |
| Disburse / payout | POST /merchant/api/v1/transactions/disburse |
| Verify payout | GET /merchant/api/v1/transactions/{reference} |
Merchant owner must pass platform KYC and have Withdraw Money enabled.
Collect pay-in via initiate-payment or manual funding.
Note korapay_ngn_transfer withdraw_method_id for NGN.
Send bank_code + account_number; platform resolves account name.
Listen for event payout on your ipn_url; verify X-Signature.
Bytepay supports PocketFi as a secondary NGN payment gateway alongside Korapay. Use PocketFi for hosted checkout collections, dynamic virtual accounts, and bank transfer payouts without affecting existing Korapay or StroWallet integrations.
Configure PocketFi under Admin → Integration Center → Payment Gateways → PocketFi:
| Setting | Description |
|---|---|
api_key |
PocketFi Bearer API token from Settings → API Keys |
secret_key |
Secret key used for webhook HMAC-SHA512 verification |
webhook_secret |
Optional override for webhook signature (falls back to secret_key) |
business_id |
Your PocketFi business ID (required for checkout and virtual accounts) |
default_va_bank |
Virtual account bank provider: kuda, palmpay, paga, 9psb, or saveheaven |
sandbox |
Use sandbox base URL (https://api.pocketfi.ng/api/test) |
pay_in_enabled |
Enable/disable PocketFi collections (checkout + virtual accounts) |
pay_out_enabled |
Enable/disable PocketFi bank transfer payouts |
https://bytepay.online/webhooks/pocketfi
Register this URL under PocketFi Dashboard → Settings → Webhooks. PocketFi signs payloads with HMAC-SHA512 in the HTTP_POCKETFI_SIGNATURE header.
| Flow | Deposit method code | Behavior |
|---|---|---|
| Hosted checkout | pocketfi-ngn |
Customer redirected to PocketFi checkout. Wallet credited on webhook or redirect verify. |
| Dynamic virtual account | pocketfi_ngn_va |
Amount-locked VA per deposit. Wallet credited when transfer is confirmed via webhook. |
Use withdraw_method_id from GET /api/v1/payout-methods where method_code is pocketfi_ngn_transfer.
| Field | Required | Notes |
|---|---|---|
bank_code |
Yes | PocketFi bank code from GET /api/v1/payout/bank-list |
account_number |
Yes | 10-digit NUBAN account number |
account_name |
Optional | Auto-resolved via PocketFi verify-bank when omitted |
POCKETFI_WEBHOOK_SECRET=
POCKETFI_LOG_CHANNEL=pocketfi
POCKETFI_VA_BANK=kuda
POCKETFI_SANDBOX_BVN=
POCKETFI_SANDBOX_NIN=
Schedule pending payout reconciliation: php artisan pocketfi:reconcile-pending-payouts
Bytepay supports XudoPay as an additional NGN payment gateway alongside Korapay, PocketFi, and StroWallet. Use XudoPay payment links for collections and the withdrawals API for bank transfer payouts without modifying existing gateway integrations.
| Setting | Description |
|---|---|
secret_key |
XudoPay secret key (Bearer token: xudo_sk_test_* or xudo_sk_live_*) |
api_key |
Optional alias for secret_key |
public_key |
Optional public key if required by your XudoPay account |
webhook_secret |
Webhook signing secret for X-XudoPay-Signature (HMAC-SHA256) |
sandbox |
Use test API keys when enabled |
pay_in_enabled |
Enable/disable XudoPay payment link collections |
pay_out_enabled |
Enable/disable XudoPay bank transfer payouts |
https://bytepay.online/webhooks/xudopay
Register this URL in XudoPay → Settings → API Settings → Webhooks. Verify the X-XudoPay-Signature header using HMAC-SHA256 over the raw JSON body.
| Flow | Deposit method code | Behavior |
|---|---|---|
| Payment link checkout | xudopay-ngn |
Bytepay creates a XudoPay payment link and redirects the customer to hosted checkout. Wallet is credited on webhook or callback verification. |
POST https://xudopay.com/api/v1/payment-links
Authorization: Bearer xudo_sk_live_...
{
"amount": 5000,
"currency": "NGN",
"title": "Wallet deposit",
"reference": "TXN123456",
"callback_url": "https://bytepay.online/status/callback?gateway=xudopay&trx=TXN123456"
}
Use withdraw_method_id from GET /api/v1/payout-methods where method_code is xudopay_ngn_transfer. Minimum payout_amount is ₦500. The XudoPay provider wallet must have sufficient balance to cover the transfer and withdrawal fee.
POST https://xudopay.com/api/v1/withdrawals
Authorization: Bearer xudo_sk_live_...
{
"currency": "NGN",
"amount": 5000,
"bank_code": "057",
"account_number": "0123456789",
"account_name": "John Doe"
}
GET https://xudopay.com/api/v1/transactions?reference=TXN123456
Authorization: Bearer xudo_sk_live_...
XUDOPAY_WEBHOOK_SECRET=
XUDOPAY_LOG_CHANNEL=xudopay
XUDOPAY_BASE_URL_LIVE=https://xudopay.com/api/v1
XUDOPAY_BASE_URL_SANDBOX=https://xudopay.com/api/v1
php artisan xudopay:reconcile-pending-deposits
php artisan xudopay:reconcile-pending-payouts
Create a hosted checkout session for a merchant-supported currency. The returned payment_url should be opened by the customer.
https://bytepay.online/api/v1/initiate-payment
| Header | Value | Required | Description |
|---|---|---|---|
Content-Type |
application/json |
Yes | Request content type |
Accept |
application/json |
Yes | Expected response type |
X-Environment |
sandbox | production |
Yes | Use sandbox for testing and production for live payments |
X-Merchant-Key |
{merchant_key} |
Yes | Merchant identifier from the API Config page |
X-API-Key |
{api_key} |
Yes | API key for the selected environment |
X-Timestamp |
{unix_timestamp} |
Yes | Current Unix timestamp in seconds. Requests outside the allowed tolerance are rejected. |
X-Signature |
sha256={hmac} |
Yes | HMAC-SHA256 of timestamp.METHOD.path_with_query.raw_body using the API Secret for the selected environment. |
| Parameter | Type | Required | Description |
|---|---|---|---|
payment_amount |
number | Yes | Payment amount. Minimum value is 1.00. |
currency_code |
string | Yes | 3-letter currency code. Must be enabled for the merchant and backed by an active merchant wallet. |
ref_trx |
string | Yes | Your unique order or transaction reference. Max 60 characters. |
success_redirect |
url | Yes | Customer is redirected here after a successful hosted checkout. |
cancel_redirect |
url | Yes | Customer is redirected here when checkout is canceled or cannot continue. |
ipn_url |
url | Yes | Server-to-server webhook URL for payment status updates. |
description |
string | No | Payment description visible in merchant transaction context. |
customer_name |
string | No | Customer name. Max 100 characters. |
customer_email |
No | Customer email address. Max 100 characters. | |
allow_payment_methods |
string | array | No | Optional keyword filter applied after merchant gateway rules. Example: "stripe,paypal" or ["stripe","paypal"]. |
| Field | Type | Meaning |
|---|---|---|
info.merchant_payment_methods_restricted |
boolean | true when the merchant has selected specific gateways in API Config. |
info.merchant_payment_method_ids |
array | Eligible deposit method IDs for the requested currency. |
info.merchant_payment_method_codes |
array | Eligible method codes that can appear on hosted checkout. |
| Field | Description |
|---|---|
info.base_amount |
Same as payment_amount from your request. |
info.api_fee_percent |
Admin-configured API commission percentage. |
info.commission_fee |
Calculated commission amount. |
info.commission_bearer |
customer or merchant — see API Commission |
info.customer_pays |
Total charged on hosted checkout. |
info.merchant_receives |
Net amount credited on successful payment. |
curl -X POST "https://bytepay.online/api/v1/initiate-payment" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "X-Environment: sandbox" \
-H "X-Merchant-Key: test_merchant_key" \
-H "X-API-Key: test_api_key" \
-H "X-Timestamp: 1778650000" \
-H "X-Signature: sha256=generated_hmac_signature" \
-d '{
"payment_amount": 250.00,
"currency_code": "USD",
"ref_trx": "ORDER_12345",
"description": "Premium Subscription",
"success_redirect": "https://merchant.example/payments/success",
"cancel_redirect": "https://merchant.example/payments/cancel",
"ipn_url": "https://merchant.example/webhooks/bytepay",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"allow_payment_methods": ["stripe", "paypal"]
}'
<?php
use Illuminate\Support\Facades\Http;
$payload = [
'payment_amount' => 250.00,
'currency_code' => 'USD',
'ref_trx' => 'ORDER_12345',
'description' => 'Premium Subscription',
'success_redirect' => route('payments.success'),
'cancel_redirect' => route('payments.cancel'),
'ipn_url' => route('webhooks.bytepay'),
'customer_name' => 'John Doe',
'customer_email' => 'john@example.com',
'allow_payment_methods' => ['stripe', 'paypal'],
];
$body = json_encode($payload, JSON_THROW_ON_ERROR);
$timestamp = (string) time();
$path = '/api/v1/initiate-payment';
$signature = hash_hmac(
'sha256',
$timestamp.'.POST.'.$path.'.'.$body,
config('services.bytepay.api_secret')
);
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'X-Environment' => 'sandbox',
'X-Merchant-Key' => config('services.bytepay.merchant_key'),
'X-API-Key' => config('services.bytepay.api_key'),
'X-Timestamp' => $timestamp,
'X-Signature' => 'sha256='.$signature,
])->withBody($body, 'application/json')
->post('https://bytepay.online/api/v1/initiate-payment')
->throw()
->json();
return redirect()->away($response['payment_url']);
const crypto = require('crypto');
const payload = {
payment_amount: 250.00,
currency_code: 'USD',
ref_trx: 'ORDER_12345',
description: 'Premium Subscription',
success_redirect: 'https://merchant.example/payments/success',
cancel_redirect: 'https://merchant.example/payments/cancel',
ipn_url: 'https://merchant.example/webhooks/bytepay',
customer_name: 'John Doe',
customer_email: 'john@example.com',
allow_payment_methods: ['stripe', 'paypal']
};
const body = JSON.stringify(payload);
const timestamp = Math.floor(Date.now() / 1000).toString();
const path = '/api/v1/initiate-payment';
const signature = crypto
.createHmac('sha256', process.env.DIGIKASH_API_SECRET)
.update(`${timestamp}.POST.${path}.${body}`)
.digest('hex');
const response = await fetch('https://bytepay.online/api/v1/initiate-payment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-Environment': 'sandbox',
'X-Merchant-Key': process.env.DIGIKASH_MERCHANT_KEY,
'X-API-Key': process.env.DIGIKASH_API_KEY,
'X-Timestamp': timestamp,
'X-Signature': `sha256=${signature}`
},
body
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Payment initiation failed');
}
window.location.href = data.payment_url;
{
"payment_url": "https://Bytepay.test/payment/checkout?token=encrypted&signature=signed",
"info": {
"ref_trx": "ORDER_12345",
"description": "Premium Subscription",
"ipn_url": "https://merchant.example/webhooks/bytepay",
"cancel_redirect": "https://merchant.example/payments/cancel",
"success_redirect": "https://merchant.example/payments/success",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"merchant_id": 1,
"merchant_name": "Demo Store",
"amount": 250,
"currency_code": "USD",
"environment": "sandbox",
"is_sandbox": true,
"allow_payment_methods": ["stripe", "paypal"],
"merchant_payment_methods_restricted": true,
"merchant_payment_method_ids": [7],
"merchant_payment_method_codes": ["stripe-usd"],
"base_amount": 250,
"api_fee_bearer": "customer",
"api_fee_percent": 5,
"api_commission_fee": 12.5,
"commission_fee": 12.5,
"commission_bearer": "customer",
"customer_pays": 262.5,
"merchant_receives": 250
}
}
The requested currency_code is not in the merchant-supported currency list.
The merchant owner does not have an active wallet for the requested currency.
When commission_bearer is merchant, your wallet must cover the commission at initiate-payment time (production).
Check X-Environment, X-Merchant-Key, and X-API-Key for the same environment.
Fetch the latest status for a hosted checkout transaction. Always verify server-side before fulfilling an order.
https://bytepay.online/api/v1/verify-payment/{trxId}
| Header | Value | Required | Description |
|---|---|---|---|
Accept |
application/json |
Yes | Expected response type |
X-Environment |
sandbox | production |
Yes | Must match the environment used to initiate the payment |
X-Merchant-Key |
{merchant_key} |
Yes | Merchant identifier from API Config |
X-API-Key |
{api_key} |
Yes | API key for the selected environment |
X-Timestamp |
{unix_timestamp} |
Yes | Use the same timestamp format as payment initiation. |
X-Signature |
sha256={hmac} |
Yes | Sign timestamp.GET./api/v1/verify-payment/{trxId}. using the matching API Secret. The raw body is empty for GET requests. |
| Parameter | Type | Required | Description |
|---|---|---|---|
trxId |
string | Yes | Bytepay transaction ID returned by hosted checkout or IPN. |
curl -X GET "https://bytepay.online/api/v1/verify-payment/TXNQ5V8K2L9N3XM1" \
-H "Accept: application/json" \
-H "X-Environment: sandbox" \
-H "X-Merchant-Key: test_merchant_key" \
-H "X-API-Key: test_api_key" \
-H "X-Timestamp: 1778650000" \
-H "X-Signature: sha256=generated_hmac_signature"
{
"status": "success",
"trx_id": "TXNQ5V8K2L9N3XM1",
"amount": 237.5,
"fee": 12.5,
"currency": "USD",
"net_amount": 237.5,
"commission": {
"base_amount": 250,
"commission_fee": 12.5,
"commission_bearer": "customer",
"customer_paid": 262.5,
"merchant_received": 250,
"commission_trx_id": null
},
"customer": {
"name": "John Doe",
"email": "john@example.com"
},
"description": "Premium Subscription",
"created_at": "2026-05-13T10:30:00.000000Z",
"updated_at": "2026-05-13T10:35:45.000000Z"
}
| Status | Description | Recommended Action |
|---|---|---|
pending |
Payment is still processing. | Wait for IPN or verify again later. |
success |
Payment completed successfully. | Fulfill the order after idempotency checks. |
failed |
Payment failed or was canceled. | Do not fulfill; show retry or cancel state. |
Fetch gateway branding, environment labels, and customer-facing checkout metadata for a merchant-authenticated integration.
https://bytepay.online/api/v1/site-info
curl -X GET "https://bytepay.online/api/v1/site-info" \
-H "Accept: application/json" \
-H "X-Environment: sandbox" \
-H "X-Merchant-Key: test_merchant_key" \
-H "X-API-Key: test_api_key" \
-H "X-Timestamp: 1778650000" \
-H "X-Signature: sha256=generated_hmac_signature"
{
"site_name": "Bytepay",
"site_logo": "https://bytepay.online/storage/images/2026/06/05/20260605_090923_chatgpt_image_jun_5_2026_04_03_22_am_nueh.png",
"site_url": "https://bytepay.online",
"gateway_name": "Bytepay Payment Gateway",
"gateway_description": "Secure payment powered by Bytepay",
"features": {
"ssl_secured": "SSL Secured",
"instant_processing": "Instant",
"global_support": "Global",
"mobile_ready": "Mobile Ready"
},
"environments": {
"production": "Production Mode - Live payment processing is active",
"sandbox": "Test Mode - This is a test transaction. No real money will be charged"
},
"api_version": "1.0",
"status": "active"
}
Use the payout API to send funds from your merchant owner wallet to a beneficiary bank account. Payouts debit your Bytepay wallet immediately; automatic gateways (Korapay, PocketFi, XudoPay) settle asynchronously and notify you via IPN or verify-payout.
Collect pay-in via initiate-payment or receive manual credits. Your wallet must cover payout_amount plus the withdrawal method fee (payable_amount).
List active auto and manual withdrawal methods. Note the id, min_withdraw, max_withdraw, and fields for your chosen rail.
Either save an account in Dashboard → Withdraw → Saved accounts (withdraw_account_id), or send inline beneficiary fields from the payout-methods response.
Sign the request with your API Secret. Wallet is debited immediately. Automatic payouts return status pending while the gateway processes.
Receive payout IPN on your ipn_url (event: payout), or poll GET /api/v1/verify-payout/{trx_id}.
If the gateway rejects a transfer after wallet debit, Bytepay refunds payable_amount automatically. Check error_code and the error message.
When Korapay, PocketFi, and XudoPay are configured, payout-methods returns them in this priority order. Only gateways with valid admin credentials appear in the API list.
| method_code | Name | type | min_withdraw | Notes |
|---|---|---|---|---|
korapay_ngn_transfer |
Korapay Bank Transfer (NGN) | auto |
₦100 | Primary NGN rail. Requires Korapay Disburse/Payout enabled and server IP whitelisted. See Korapay Integration. |
pocketfi_ngn_transfer |
PocketFi Bank Transfer (NGN) | auto |
₦100 | Secondary NGN rail. Requires PocketFi payouts enabled on the platform account. |
xudopay_ngn_transfer |
XudoPay Bank Transfer (NGN) | auto |
₦500 | Additional NGN rail. XudoPay enforces a ₦500 minimum. Provider wallet must have sufficient balance. |
Bytepay |
Bank Transfer (manual) | manual |
₦500 | Admin-processed manual withdrawal. Not instant settlement. |
| Field | Description |
|---|---|
payout_amount |
Net amount sent to the beneficiary (before platform withdrawal fee is added to the debit). |
fee |
Withdrawal method charge applied to the merchant owner wallet. |
payable_amount |
Total debited from your wallet: payout_amount + fee. Your balance must cover this at initiate-payout time. |
net_amount |
Amount delivered to the beneficiary bank account (equals payout_amount for standard bank transfers). |
Inline beneficiary objects accept canonical field names from payout-methods, plus these aliases:
| Alias | Canonical field |
|---|---|
bank | bank_code |
account | account_number |
name | account_name |
nameEnquiryReference | name_enquiry_reference |
Send funds from the merchant owner wallet to a beneficiary bank account or saved payout account. Debits wallet balance immediately; automatic gateways settle asynchronously.
https://bytepay.online/api/v1/initiate-payout
| Header | Required | Description |
|---|---|---|
Content-Type |
Yes | application/json |
Accept |
Yes | application/json |
X-Environment |
Yes | sandbox or production |
X-Merchant-Key |
Yes | Merchant identifier from API Config |
X-API-Key |
Yes | API key for the selected environment |
X-Timestamp |
Yes | Unix timestamp in seconds |
X-Signature |
Yes | HMAC-SHA256 of timestamp.POST./api/v1/initiate-payout.raw_body |
| Parameter | Type | Required | Description |
|---|---|---|---|
payout_amount |
number | Yes | Amount to send. Must be within min_withdraw and max_withdraw from payout-methods (e.g. ₦500 minimum for XudoPay). |
currency_code |
string | Yes | 3-letter ISO code (e.g. NGN). Must be enabled for the merchant with an active wallet. |
ref_trx |
string | Yes | Your unique payout reference. Max 60 characters. |
ipn_url |
url | Yes | Webhook URL for payout status updates (event: payout). |
withdraw_method_id |
integer | Yes | Active withdrawal method id from GET /api/v1/payout-methods. Match method_code such as korapay_ngn_transfer. |
withdraw_account_id |
integer | Conditional | Saved payout account on the merchant owner profile. Preferred for repeat payouts. Required if beneficiary is omitted. |
beneficiary |
object | Conditional | Key/value map matching fields from payout-methods. Required if withdraw_account_id is omitted. |
description |
string | No | Optional payout description. Max 255 characters. |
Create a saved account in Dashboard → Withdraw → Saved accounts, then send only withdraw_account_id. No bank details in each API call.
Send beneficiary fields required by the selected withdraw_method_id. Use GET payout-methods to see field names and api_auto_resolved flags.
{
"payout_amount": 5000,
"currency_code": "NGN",
"ref_trx": "PAYOUT_20260607_001",
"ipn_url": "https://merchant.example/webhooks/bytepay-payout",
"withdraw_method_id": 18,
"withdraw_account_id": 7,
"description": "Vendor settlement"
}
withdraw_method_id is an example — always use the id from GET payout-methods for korapay_ngn_transfer.
{
"payout_amount": 5000,
"currency_code": "NGN",
"ref_trx": "PAYOUT_98765",
"ipn_url": "https://merchant.example/webhooks/payout",
"withdraw_method_id": 18,
"description": "Vendor settlement",
"beneficiary": {
"bank_code": "044",
"account_number": "0123456789"
}
}
{
"payout_amount": 2000,
"currency_code": "NGN",
"ref_trx": "PAYOUT_PF_001",
"ipn_url": "https://merchant.example/webhooks/payout",
"withdraw_method_id": 15,
"description": "Affiliate commission",
"beneficiary": {
"bank_code": "100004",
"account_number": "8033106710"
}
}
{
"payout_amount": 500,
"currency_code": "NGN",
"ref_trx": "PAYOUT_XD_001",
"ipn_url": "https://merchant.example/webhooks/payout",
"withdraw_method_id": 17,
"description": "Supplier payment",
"beneficiary": {
"bank_code": "100004",
"account_number": "8033106710"
}
}
$payload = [
'payout_amount' => 5000,
'currency_code' => 'NGN',
'ref_trx' => 'PAYOUT_' . time(),
'ipn_url' => 'https://merchant.example/webhooks/payout',
'withdraw_method_id' => 18, // from GET payout-methods
'beneficiary' => [
'bank_code' => '044',
'account_number' => '0123456789',
],
];
$body = json_encode($payload, JSON_UNESCAPED_SLASHES);
$timestamp = (string) time();
$path = '/api/v1/initiate-payout';
$signature = hash_hmac('sha256', "{$timestamp}.POST.{$path}.{$body}", $apiSecret);
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'X-Environment' => 'production',
'X-Merchant-Key' => $merchantKey,
'X-API-Key' => $apiKey,
'X-Timestamp' => $timestamp,
'X-Signature' => $signature,
])->withBody($body, 'application/json')
->post('https://bytepay.online/api/v1/initiate-payout');
curl -X POST "https://bytepay.online/api/v1/initiate-payout" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "X-Environment: production" \
-H "X-Merchant-Key: {merchant_key}" \
-H "X-API-Key: {api_key}" \
-H "X-Timestamp: {unix_timestamp}" \
-H "X-Signature: {hmac_sha256_hex}" \
-d '{
"payout_amount": 5000,
"currency_code": "NGN",
"ref_trx": "PAYOUT_001",
"ipn_url": "https://merchant.example/webhooks/payout",
"withdraw_method_id": 18,
"beneficiary": {
"bank_code": "044",
"account_number": "0123456789"
}
}'
{
"trx_id": "TXNL8VXLVMTJS50",
"status": "pending",
"info": {
"merchant_id": 10,
"merchant_name": "Essential-NG",
"ref_trx": "PAYOUT_001",
"ipn_url": "https://merchant.example/webhooks/payout",
"environment": "production",
"is_sandbox": false,
"api_merchant_payout": true,
"withdraw_method_id": 18,
"withdraw_method": "Korapay Bank Transfer (NGN)",
"trx_id": "TXNL8VXLVMTJS50",
"payout_amount": 5000,
"currency_code": "NGN",
"fee": 75,
"net_amount": 5000,
"payable_amount": 5075,
"status": "pending",
"processing_type": "auto"
}
}
| status | Meaning | Merchant action |
|---|---|---|
pending |
Wallet debited; gateway disbursement submitted or processing | Wait for IPN or poll verify-payout |
completed |
Funds delivered to beneficiary | Mark settlement complete in your system |
failed / canceled |
Transfer failed; wallet refunded for API payouts | Review error message; retry with corrected details if needed |
error_code |
Meaning |
|---|---|
insufficient_balance |
Merchant Bytepay wallet cannot cover payout_amount + fee. |
provider_insufficient_balance |
Payment provider wallet (e.g. XudoPay) is too low; your Bytepay wallet was refunded. |
kyc_required |
Complete KYC or verify phone on the merchant owner account. |
merchant_payout_disabled |
Withdrawals disabled for this merchant or platform. |
payout_schedule_blocked |
Payouts not enabled for today per platform schedule. |
gateway_not_configured |
Selected payout gateway credentials missing in admin. |
invalid_beneficiary |
Invalid bank code or account could not be verified. |
gateway_payout_failed |
Gateway rejected the transfer; wallet refunded when debit already occurred. |
payout_failed |
Validation or processing error. See error message (includes gateway-specific guidance). |
{
"error": "Payout failed: Korapay has not enabled live payouts for this merchant account yet. Enable Disburse/Payout in Korapay, whitelist server IP, or contact Korapay support. The deducted amount has been refunded.",
"error_code": "payout_failed"
}
Poll payout status server-side. X-Environment must match the environment used to initiate the payout.
https://bytepay.online/api/v1/verify-payout/{trxId}
Same signed headers as other merchant API GET endpoints. Sign timestamp.GET./api/v1/verify-payout/{trxId}. with an empty raw body.
| Parameter | Description |
|---|---|
trxId |
Transaction ID returned from initiate-payout (trx_id or info.trx_id). |
curl -X GET "https://bytepay.online/api/v1/verify-payout/TXNL8VXLVMTJS50" \
-H "Accept: application/json" \
-H "X-Environment: production" \
-H "X-Merchant-Key: {merchant_key}" \
-H "X-API-Key: {api_key}" \
-H "X-Timestamp: {unix_timestamp}" \
-H "X-Signature: {hmac_sha256_hex}"
{
"status": "success",
"event": "payout",
"trx_id": "TXNL8VXLVMTJS50",
"ref_trx": "PAYOUT_001",
"amount": 5000,
"fee": 75,
"currency": "NGN",
"net_amount": 5000,
"description": "Withdraw via Korapay Bank Transfer (NGN)",
"created_at": "2026-06-07T16:26:22.000000Z",
"updated_at": "2026-06-07T16:26:26.000000Z"
}
{
"status": "pending",
"event": "payout",
"trx_id": "TXNL8VXLVMTJS50",
"ref_trx": "PAYOUT_001",
"message": "Payout is still pending."
}
{
"status": "failed",
"event": "payout",
"trx_id": "TXNL8VXLVMTJS50",
"ref_trx": "PAYOUT_001",
"message": "Payout failed or was canceled."
}
| status | Action |
|---|---|
success |
Payout completed — safe to mark as settled. |
pending |
Still processing — poll again or wait for IPN. |
failed |
Payout failed or was canceled. Wallet was refunded for automatic gateway failures. |
List active withdrawal methods and required beneficiary fields for a currency before calling initiate-payout.
https://bytepay.online/api/v1/payout-methods?currency=NGN
| Parameter | Required | Description |
|---|---|---|
currency |
Yes | 3-letter currency code enabled for the merchant (e.g. NGN). |
| Field | Description |
|---|---|
id |
Pass as withdraw_method_id on initiate-payout |
method_code |
Stable identifier (e.g. korapay_ngn_transfer). Prefer this for integration logic. |
type |
auto for instant gateway settlement; manual for admin-processed transfers |
min_withdraw / max_withdraw |
Allowed payout_amount range for this method |
fields |
Beneficiary schema. Fields with api_auto_resolved: true may be omitted. |
{
"currency_code": "NGN",
"methods": [
{
"id": 18,
"name": "Korapay Bank Transfer (NGN)",
"method_code": "korapay_ngn_transfer",
"type": "auto",
"currency": "NGN",
"min_withdraw": 100,
"max_withdraw": 5000000,
"fields": [
{
"name": "bank_code",
"type": "select",
"validation": "required",
"label": "Bank"
},
{
"name": "account_number",
"type": "text",
"validation": "required",
"label": "Account Number"
},
{
"name": "account_name",
"type": "text",
"validation": "nullable",
"label": "Account Holder Name",
"api_auto_resolved": true
}
]
},
{
"id": 15,
"name": "PocketFi Bank Transfer (NGN)",
"method_code": "pocketfi_ngn_transfer",
"type": "auto",
"currency": "NGN",
"min_withdraw": 100,
"max_withdraw": 5000000,
"fields": [
{
"name": "bank_code",
"type": "select",
"validation": "required",
"label": "Bank"
},
{
"name": "account_number",
"type": "text",
"validation": "required",
"label": "Account Number"
},
{
"name": "account_name",
"type": "text",
"validation": "nullable",
"label": "Account Holder Name",
"api_auto_resolved": true
}
]
},
{
"id": 17,
"name": "XudoPay Bank Transfer (NGN)",
"method_code": "xudopay_ngn_transfer",
"type": "auto",
"currency": "NGN",
"min_withdraw": 500,
"max_withdraw": 5000000,
"fields": [
{
"name": "bank_code",
"type": "select",
"validation": "required",
"label": "Bank"
},
{
"name": "account_number",
"type": "text",
"validation": "required",
"label": "Account Number"
},
{
"name": "account_name",
"type": "text",
"validation": "nullable",
"label": "Account Holder Name",
"api_auto_resolved": true
}
]
},
{
"id": 14,
"name": "Bank Transfer",
"method_code": "Bytepay",
"type": "manual",
"currency": "NGN",
"min_withdraw": 500,
"max_withdraw": 1000000,
"fields": [
{
"name": "Bank Name",
"type": "text",
"validation": "required"
},
{
"name": "Account Number",
"type": "text",
"validation": "required"
}
]
}
]
}
Method ids (18, 15, 17, 14) are examples from a typical installation. Always use live values from your payout-methods response. Only gateways with configured credentials are listed.
Bytepay sends real-time notifications to your specified IPN URL when payment status changes. This ensures you're immediately notified of payment completions, failures, and other status updates. Webhooks work identically in both sandbox and production environments.
Use the same webhook URL for both sandbox and production. Bytepay will include environment context in webhook payloads to help you differentiate between test and live transactions.
https://bytepay.online/webhooks/korapay.
Configure this in your Korapay dashboard. Merchant IPN URLs (ipn_url on initiate-payment / initiate-payout) are separate and use Bytepay HMAC signatures below.
https://bytepay.online/webhooks/pocketfi.
Configure this under PocketFi Dashboard → Settings → Webhooks. Verify HTTP_POCKETFI_SIGNATURE (HMAC-SHA512 of raw body). See
PocketFi Integration.
https://bytepay.online/webhooks/xudopay.
Configure this under XudoPay → Settings → API Settings → Webhooks. Verify X-XudoPay-Signature (HMAC-SHA256 of raw body). See
XudoPay Integration.
| Header | Description | Example |
|---|---|---|
Content-Type |
Always application/json |
application/json |
X-Signature |
Raw HMAC-SHA256 signature for verification | a8b9c2d1e5f3... |
X-Environment |
Webhook environment context | sandbox | production |
All webhook payloads include environment information to help you differentiate between sandbox and production transactions:
{
"event": "payment",
"data": {
"ref_trx": "ORDER_1234",
"trx_id": "TXNQ5V8K2L9N3XM1",
"description": "Order #1234",
"ipn_url": "https://merchant.com/webhooks/payment",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"merchant_name": "Demo Store",
"amount": 1000,
"base_amount": 1000,
"currency_code": "NGN",
"environment": "production",
"is_sandbox": false,
"commission": {
"base_amount": 1000,
"commission_fee": 50,
"commission_bearer": "merchant",
"customer_paid": 1000,
"merchant_received": 1000
}
},
"message": "Payment Completed",
"status": "completed",
"timestamp": 1705747245
}
{
"event": "payout",
"data": {
"ref_trx": "PAYOUT_001",
"trx_id": "TXNL8VXLVMTJS50",
"ipn_url": "https://merchant.com/webhooks/payout",
"environment": "production",
"is_sandbox": false,
"api_merchant_payout": true,
"payout_amount": 5000,
"currency_code": "NGN",
"withdraw_method_id": 18,
"withdraw_method": "Korapay Bank Transfer (NGN)"
},
"message": "Payout Completed",
"status": "completed",
"timestamp": 1705747300
}
Always verify webhook signatures to ensure authenticity and prevent unauthorized requests. Use your API secret (environment-specific) to verify signatures:
<?php
// Laravel Webhook Handler
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use App\Enums\EnvironmentMode;
class BytepayWebhookController extends Controller
{
public function handle(Request $request)
{
// Get webhook headers
$environment = $request->header('X-Environment', 'production');
$signature = $request->header('X-Signature');
// Get appropriate secret based on environment
$secret = $this->getSecretForEnvironment($environment);
// Verify signature
if (!$this->verifySignature($request->getContent(), $signature, $secret)) {
Log::warning('Bytepay webhook signature verification failed', [
'environment' => $environment
]);
return response()->json(['error' => 'Invalid signature'], 401);
}
$payload = $request->json()->all();
// Handle based on environment
if ($environment === EnvironmentMode::SANDBOX->value) {
return $this->handleSandboxWebhook($payload);
} else {
return $this->handleProductionWebhook($payload);
}
}
private function getSecretForEnvironment(string $environment): string
{
// Return test secret for sandbox, live secret for production
return $environment === 'sandbox'
? config('bytepay.test_api_secret')
: config('bytepay.api_secret');
}
private function verifySignature(string $payload, string $signature, string $secret): bool
{
$expectedSignature = hash_hmac('sha256', $payload, $secret);
$providedSignature = str_starts_with($signature, 'sha256=') ? substr($signature, 7) : $signature;
return hash_equals($expectedSignature, $providedSignature);
}
private function handleSandboxWebhook(array $payload): JsonResponse
{
Log::info('Processing sandbox webhook', $payload);
// Your sandbox-specific logic here
// Don't fulfill orders, don't send emails to real customers, etc.
return response()->json(['status' => 'sandbox_processed']);
}
private function handleProductionWebhook(array $payload): JsonResponse
{
Log::info('Processing production webhook', $payload);
// Your production logic here
// Fulfill orders, send confirmation emails, etc.
return response()->json(['status' => 'processed']);
}
}
const crypto = require('crypto');
const express = require('express');
const EnvironmentMode = {
SANDBOX: 'sandbox',
PRODUCTION: 'production'
};
// Webhook handler
app.post('/api/webhooks/bytepay', async (req, res) => {
const environment = req.headers['x-environment'] || 'production';
const signature = req.headers['x-signature'];
# Get appropriate secret based on environment
const secret = getSecretForEnvironment(environment);
# Verify signature
if (!verifySignature(JSON.stringify(req.body), signature, secret)) {
console.warn('Bytepay webhook signature verification failed', {
environment: environment
});
return res.status(401).json({ error: 'Invalid signature' });
}
const payload = req.body;
try {
# Handle based on environment
if (environment === EnvironmentMode.SANDBOX) {
await handleSandboxWebhook(payload);
} else {
await handleProductionWebhook(payload);
}
res.json({ status: 'processed' });
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: 'Processing failed' });
}
});
function getSecretForEnvironment(environment) {
# Return test secret for sandbox, live secret for production
return environment === 'sandbox'
? process.env.BYTEPAY_TEST_API_SECRET
: process.env.BYTEPAY_API_SECRET;
}
function verifySignature(payload, signature, secret) {
if (!signature) {
return false;
}
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const providedSignature = signature.startsWith('sha256=') ? signature.slice(7) : signature;
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(providedSignature)
);
}
async function handleSandboxWebhook(payload) {
console.log('Processing sandbox webhook:', payload);
# Your sandbox-specific logic here
# Don't fulfill orders, don't send emails to real customers, etc.
}
async function handleProductionWebhook(payload) {
console.log('Processing production webhook:', payload);
# Your production logic here
# Fulfill orders, send confirmation emails, etc.
}
import hmac
import hashlib
import json
import logging
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
logger = logging.getLogger(__name__)
ENVIRONMENT_MODE = {
'SANDBOX': 'sandbox',
'PRODUCTION': 'production'
}
@csrf_exempt
@require_http_methods(["POST"])
def bytepay_webhook(request):
environment = request.headers.get('X-Environment', 'production')
signature = request.headers.get('X-Signature', '')
# Get appropriate secret based on environment
secret = get_secret_for_environment(environment)
# Verify signature
if not verify_signature(request.body, signature, secret):
logger.warning('Bytepay webhook signature verification failed', extra={
'environment': environment
})
return JsonResponse({'error': 'Invalid signature'}, status=401)
try:
payload = json.loads(request.body)
# Handle based on environment
if environment == ENVIRONMENT_MODE['SANDBOX']:
handle_sandbox_webhook(payload)
else:
handle_production_webhook(payload)
return JsonResponse({'status': 'processed'})
except Exception as e:
logger.error(f'Webhook processing error:{str(e)}')
return JsonResponse({'error': 'Processing failed'}, status=500)
def get_secret_for_environment(environment):
from django.conf import settings
# Return test secret for sandbox, live secret for production
return (settings.BYTEPAY_TEST_API_SECRET
if environment == 'sandbox'
else settings.BYTEPAY_API_SECRET)
def verify_signature(payload, signature, secret):
if not signature:
return False
expected_signature = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
provided_signature = signature[7:] if signature.startswith('sha256=') else signature
return hmac.compare_digest(expected_signature, provided_signature)
def handle_sandbox_webhook(payload):
logger.info('Processing sandbox webhook', extra=payload)
# Your sandbox-specific logic here
# Don't fulfill orders, don't send emails to real customers, etc.
def handle_production_webhook(payload):
logger.info('Processing production webhook', extra=payload)
# Your production logic here
# Fulfill orders, send confirmation emails, etc.
Production-oriented integration examples for popular platforms and frameworks. Keep gateway selection in Merchant API Config, then sign each API request from your server.
// 1. List methods: GET /api/v1/payout-methods?currency=NGN
// Pick id where method_code = 'korapay_ngn_transfer' (primary NGN rail)
$methods = $bytepay->getPayoutMethods('NGN');
$korapay = collect($methods['methods'])->firstWhere('method_code', 'korapay_ngn_transfer');
// 2. Initiate payout (inline beneficiary or saved withdraw_account_id)
$payload = [
'payout_amount' => 5000,
'currency_code' => 'NGN',
'ref_trx' => 'PAYOUT_' . time(),
'ipn_url' => route('webhooks.payout'),
'withdraw_method_id' => $korapay['id'],
'beneficiary' => [
'bank_code' => '044',
'account_number' => '0123456789',
],
];
// Sign POST /api/v1/initiate-payout with sandbox or production api_secret
$result = $bytepay->initiatePayout($payload); // trx_id, status: pending
// 3. Verify: GET /api/v1/verify-payout/{trx_id} or handle payout IPN (event: payout)
<?php
// Laravel Integration Service
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Exception;
class BytepayService
{
private string $baseUrl;
private string $merchantKey;
private string $apiKey;
private string $environment;
public function __construct()
{
$this->baseUrl = config('bytepay.base_url');
$this->merchantKey = config('bytepay.merchant_key');
$this->apiKey = config('bytepay.api_key');
$this->environment = config('bytepay.environment'); // 'sandbox' or 'production'
}
public function initiatePayment(array $paymentData): array
{
try {
$response = Http::withHeaders([
'Content-Type' => 'application/json',
'X-Environment' => $this->environment,
'X-Merchant-Key' => $this->merchantKey,
'X-API-Key' => $this->apiKey,
])->post("{$this->baseUrl}/api/v1/initiate-payment", $paymentData);
if ($response->successful()) {
return $response->json();
}
throw new Exception('Bytepay API Error: Payment initiation failed');
} catch (Exception $e) {
throw new Exception('Bytepay API Error: ' . $e->getMessage());
}
}
public function verifyPayment(string $transactionId): array
{
try {
$response = Http::withHeaders([
'Accept' => 'application/json',
'X-Environment' => $this->environment,
'X-Merchant-Key' => $this->merchantKey,
'X-API-Key' => $this->apiKey,
])->get("{$this->baseUrl}/api/v1/verify-payment/{$transactionId}");
if ($response->successful()) {
return $response->json();
}
throw new Exception('Bytepay API Error: Payment verification failed');
} catch (Exception $e) {
throw new Exception('Bytepay API Error: ' . $e->getMessage());
}
}
}
// Configuration (config/bytepay.php)
return [
'base_url' => env('BYTEPAY_BASE_URL', 'https://bytepay.online'),
'environment' => env('BYTEPAY_ENVIRONMENT', 'sandbox'), // sandbox or production
'merchant_key' => env('BYTEPAY_MERCHANT_KEY'), // Use appropriate prefix
'api_key' => env('BYTEPAY_API_KEY'), // Use appropriate prefix
];
// Usage in Controller
class PaymentController extends Controller
{
public function initiatePayment(Request $request, BytepayService $bytepay)
{
$paymentData = [
'payment_amount' => $request->amount,
'currency_code' => 'USD',
'ref_trx' => 'ORDER_' . time(),
'description' => $request->description,
'success_redirect' => route('payment.success'),
'cancel_redirect' => route('payment.cancelled'),
'ipn_url' => route('webhooks.bytepay'),
'allow_payment_methods' => ['stripe', 'paypal'],
];
try {
$result = $bytepay->initiatePayment($paymentData);
return redirect($result['payment_url']);
} catch (Exception $e) {
return back()->withErrors(['error' => $e->getMessage()]);
}
}
}
// Node.js Integration Service
const axios = require('axios');
class BytepayService {
constructor() {
this.baseUrl = process.env.BYTEPAY_BASE_URL || 'https://bytepay.online';
this.environment = process.env.BYTEPAY_ENVIRONMENT || 'sandbox'; // sandbox or production
this.merchantKey = process.env.BYTEPAY_MERCHANT_KEY; // Use appropriate prefix
this.apiKey = process.env.BYTEPAY_API_KEY; // Use appropriate prefix
}
async initiatePayment(paymentData) {
try {
const response = await axios.post(`${this.baseUrl}/api/v1/initiate-payment`, paymentData, {
headers: {
'Content-Type': 'application/json',
'X-Environment': this.environment,
'X-Merchant-Key': this.merchantKey,
'X-API-Key': this.apiKey
}
});
return response.data;
} catch (error) {
throw new Error(`Bytepay API Error: ${error.message}`);
}
}
async verifyPayment(transactionId) {
try {
const response = await axios.get(`${this.baseUrl}/api/v1/verify-payment/${transactionId}`, {
headers: {
'Accept': 'application/json',
'X-Environment': this.environment,
'X-Merchant-Key': this.merchantKey,
'X-API-Key': this.apiKey
}
});
return response.data;
} catch (error) {
throw new Error(`Bytepay API Error: ${error.message}`);
}
}
}
// Express.js Route Example
const express = require('express');
const app = express();
const bytepay = new BytepayService();
app.post('/initiate-payment', async (req, res) => {
const paymentData = {
payment_amount: req.body.amount,
currency_code: 'USD',
ref_trx: `ORDER_${Date.now()}`,
description: req.body.description,
success_redirect: `${req.protocol}://${req.get('host')}/payment/success`,
cancel_redirect: `${req.protocol}://${req.get('host')}/payment/cancelled`,
ipn_url: `${req.protocol}://${req.get('host')}/webhooks/bytepay`,
allow_payment_methods: ['stripe', 'paypal'],
};
try {
const result = await bytepay.initiatePayment(paymentData);
res.redirect(result.payment_url);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
module.exports = BytepayService;
# Python/Django Integration Service
import os
import requests
from django.conf import settings
class BytepayService:
def __init__(self):
self.base_url = getattr(settings, 'BYTEPAY_BASE_URL', 'https://bytepay.online')
self.environment = getattr(settings, 'BYTEPAY_ENVIRONMENT', 'sandbox') # sandbox or production
self.merchant_key = getattr(settings, 'BYTEPAY_MERCHANT_KEY') # Use appropriate prefix
self.api_key = getattr(settings, 'BYTEPAY_API_KEY') # Use appropriate prefix
def initiate_payment(self, payment_data):
try:
headers = {
'Content-Type': 'application/json',
'X-Environment': self.environment,
'X-Merchant-Key': self.merchant_key,
'X-API-Key': self.api_key
}
response = requests.post(
f"{self.base_url}/api/v1/initiate-payment",
headers=headers,
json=payment_data,
timeout=30
)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
raise Exception(f'Bytepay API Error: {str(e)}')
def verify_payment(self, transaction_id):
try:
headers = {
'Accept': 'application/json',
'X-Environment': self.environment,
'X-Merchant-Key': self.merchant_key,
'X-API-Key': self.api_key
}
response = requests.get(
f"{self.base_url}/api/v1/verify-payment/{transaction_id}",
headers=headers,
timeout=30
)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
raise Exception(f'Bytepay API Error: {str(e)}')
# Django Settings Configuration
BYTEPAY_BASE_URL = 'https://bytepay.online'
BYTEPAY_ENVIRONMENT = 'sandbox' # Change to 'production' for live
BYTEPAY_MERCHANT_KEY = os.environ.get('BYTEPAY_MERCHANT_KEY') # Use appropriate prefix
BYTEPAY_API_KEY = os.environ.get('BYTEPAY_API_KEY') # Use appropriate prefix
# Django View Example
from django.shortcuts import redirect
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json
bytepay = BytepayService()
@csrf_exempt
def initiate_payment(request):
if request.method == 'POST':
data = json.loads(request.body)
payment_data = {
'payment_amount': data['amount'],
'currency_code': 'USD',
'ref_trx': f'ORDER_{int(time.time())}',
'description': data['description'],
'success_redirect': request.build_absolute_uri('/payment/success/'),
'cancel_redirect': request.build_absolute_uri('/payment/cancelled/'),
'ipn_url': request.build_absolute_uri('/webhooks/bytepay/'),
'allow_payment_methods': ['stripe', 'paypal'],
}
try:
result = bytepay.initiate_payment(payment_data)
return redirect(result['payment_url'])
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
# Environment Variables Setup
export BYTEPAY_ENVIRONMENT="sandbox" # or "production"
export BYTEPAY_MERCHANT_KEY="test_merchant_your_key" # or "merchant_your_key" for production
export BYTEPAY_API_KEY="test_your_api_key" # or "your_api_key" for production
# Initiate Payment
curl -X POST "https://bytepay.online/api/v1/initiate-payment" \
-H "Content-Type: application/json" \
-H "X-Environment: $BYTEPAY_ENVIRONMENT" \
-H "X-Merchant-Key: $BYTEPAY_MERCHANT_KEY" \
-H "X-API-Key: $BYTEPAY_API_KEY" \
-d '{
"payment_amount": 250.00,
"currency_code": "USD",
"ref_trx": "ORDER_12345",
"description": "Premium Subscription",
"success_redirect": "https://yoursite.com/payment/success",
"cancel_redirect": "https://yoursite.com/payment/cancelled",
"ipn_url": "https://yoursite.com/api/webhooks/bytepay",
"allow_payment_methods": ["stripe", "paypal"]
}'
# Verify Payment
curl -X GET "https://bytepay.online/api/v1/verify-payment/TXNQ5V8K2L9N3XM1" \
-H "Accept: application/json" \
-H "X-Environment: $BYTEPAY_ENVIRONMENT" \
-H "X-Merchant-Key: $BYTEPAY_MERCHANT_KEY" \
-H "X-API-Key: $BYTEPAY_API_KEY"
# Environment-specific credential examples:
# Sandbox: test_merchant_xxxxx, test_api_key_xxxxx
# Production: merchant_xxxxx, api_key_xxxxx
Advanced Bytepay payment gateway with modern WooCommerce Blocks support, dynamic branding, and enterprise-grade security features.
Production-ready payment gateway with WooCommerce Blocks (Gutenberg) support, dynamic branding, and secure webhook processing.
Enterprise-grade WooCommerce payment gateway with modern Blocks support and dynamic branding.
Enterprise-grade payment processing with modern architecture
Full Gutenberg checkout compatibility with React-based UI
Auto-fetch logo, colors, and branding from Bytepay API
HMAC-SHA256 signature verification for payment callbacks
Space-efficient, responsive design for all devices
Multi-header authentication with environment isolation
Seamless sandbox testing with production deployment
Get started with Bytepay WooCommerce integration in minutes
Download Bytepay WooCommerce Gateway v2.8.0 from the download section above. This includes all latest features and security updates.
Navigate to Plugins → Add New → Upload Plugin and select the downloaded ZIP file. The plugin will auto-extract and install.
Activate the plugin and go to WooCommerce → Settings → Payments → Bytepay. Enter your API credentials and webhook secret.
Enable Test Mode, process a sandbox transaction to verify Blocks checkout, webhook delivery, and order completion.
Disable test mode, ensure production API keys are configured, and start accepting real payments with full webhook processing.
Essential API settings for secure payment processing
https://bytepay.online
Your unique merchant credentials from Bytepay dashboard
RequiredHMAC-SHA256 signature verification for secure callbacks
RecommendedRequired headers for all Bytepay API requests:
X-Environment: sandbox|production
X-Merchant-Key: your_merchant_id
X-API-Key: your_api_key
Content-Type: application/json
Real-time payment status updates and order processing
https://yoursite.com/wc-api/bytepay_webhook
Configure this URL in your Bytepay merchant dashboard for automatic order updates.
Modern Gutenberg checkout with React-based payment UI
Compact, mobile-optimized payment interface that adapts to any screen size.
Automatically fetches and displays your Bytepay branding and logos.
Clear SSL and security badges to build customer trust during checkout.
Clear sandbox indicators for testing without affecting live transactions.
Ensure everything is configured correctly before going live
Common issues and solutions for Bytepay WooCommerce integration
Solutions:
Solutions:
Solutions:
Use this checklist before moving from test payments to live checkout. The same API routes work in both modes; only the environment header and credential set change.
Use test credentials and X-Environment: sandbox. Transactions are marked as sandbox and do not represent real money movement.
Use live credentials and X-Environment: production only after admin approval, wallet readiness, and gateway configuration are complete.
Test Bytepay API endpoints directly from this documentation. Use the demo credentials below for sandbox testing.
Use these demo credentials to test all payment methods in sandbox environment:
123456789
Wallet PIN:
123456
TESTVOUCHER
Auto Success
X-ENVIRONMENT: sandbox in your API requestssandbox for testing and production for live transactions. Only sandbox credentials use test_ prefix, production credentials have no prefix.
Base URL: https://bytepay.online
Environment Header: X-Environment: sandbox
Credentials: Use test_ prefixed keys
Purpose: Safe testing without real money
Base URL: https://bytepay.online
Environment Header: X-Environment: production
Credentials: No prefix for production keys
Purpose: Live transactions with real money
Bytepay API uses conventional HTTP response codes to indicate the success or failure of API requests.
| Code | Status | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Invalid or missing API credentials |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource not found |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server error occurred |
| Error Code | Description | Solution |
|---|---|---|
INVALID_CREDENTIALS |
Invalid API credentials provided | Check your Merchant ID and API Key |
INSUFFICIENT_FUNDS |
Customer has insufficient funds | Customer needs to add funds to their wallet |
PAYMENT_DECLINED |
Payment was declined by payment processor | Customer should try a different payment method |
INVALID_AMOUNT |
Payment amount is invalid | Check minimum and maximum amount limits |
INVALID_CURRENCY |
Unsupported currency code | Use a supported currency code (USD, EUR, etc.) |
DUPLICATE_REFERENCE |
Transaction reference already exists | Use a unique transaction reference |
EXPIRED_SESSION |
Payment session has expired | Create a new payment request |
MERCHANT_SUSPENDED |
Merchant account is suspended | Contact Bytepay support |
insufficient_balance |
Payout: merchant wallet too low for amount + fee | Fund the merchant wallet or reduce payout_amount |
kyc_required |
Payout: KYC or phone verification required | Complete verification on the merchant owner account |
gateway_payout_failed |
Payout gateway rejected the transfer | Check beneficiary details; wallet is refunded if debited |
merchant_payout_disabled |
Withdrawals disabled for merchant | Contact admin to enable Withdraw Money feature |
payout_schedule_blocked |
Payout: platform withdrawal schedule blocks today | Retry on an allowed day or contact support |
gateway_not_configured |
Payout: gateway credentials missing | Choose another method from payout-methods or contact support |
invalid_beneficiary |
Payout: bank code or account verification failed | Use correct bank_code for the gateway and a valid account number |
provider_insufficient_balance |
Payout: payment provider wallet too low (e.g. XudoPay) | Fund the provider account or use Korapay/PocketFi rail |
payout_failed |
Payout: validation or gateway error | Read error message; wallet refunded if debit occurred |
{
"error": "Insufficient wallet balance.",
"error_code": "insufficient_balance"
}
Need assistance with Bytepay API integration? Our technical team provides comprehensive support.