Merchant API Reference

Bytepay Merchant API

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.

Production ready
POST /api/v1/initiate-payment
GET /api/v1/verify-payment/{trxId}
POST /api/v1/initiate-payout
GET /api/v1/payout-methods
GET /api/v1/verify-payout/{trxId}
Pay-in Pay-out Sandbox Signed API
Latest Merchant Flow Gateways are now configured after API generation from the merchant API Config page. Checkout only shows gateways that match the merchant currency and the active receiver wallet.

Fast Hosted Checkout

Create a payment session, redirect the customer, then verify or receive IPN updates when the transaction completes.

Wallet Payouts

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.

Configurable Gateways

Merchants can choose eligible payment gateways from API Config without regenerating API credentials.

Authentication

Bytepay API uses API keys to authenticate requests. You can obtain your credentials from your merchant dashboard.

Environment-Aware API Integration

Bytepay API supports both sandbox (testing) and production environments. Always test in sandbox first before going live.

Environment Configuration
Sandbox Mode

Use for: Development, testing, integration

X-Environment: sandbox

Credentials: Use test_* prefixed API keys

Production Mode

Use for: Live payments, real money

X-Environment: production (default if omitted)

Credentials: Use production API keys (no prefix)

Gateway Configuration Payment gateway selection is dashboard-managed. After generating API credentials, open Merchant -> Config -> Payment Gateway Controls to choose eligible gateways without changing your integration keys.
Signed requests Every API call must include X-Timestamp and X-Signature unless merchant API signatures are disabled by admin configuration.
Replay protection The timestamp must be fresh. Default tolerance is 300 seconds, configurable by admin.
Rate limit Merchant API calls are rate limited per merchant and IP. Default limit is 120 requests per minute.

Required Credentials

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
Security Notice Never expose your Client Secret in client-side code. Store all credentials securely on your server.
Signature Payload

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=.

Request Signing Examples

$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);

Quick Start

Use this flow for a production-safe integration. Credentials identify the merchant, while currency and gateway availability are controlled from the merchant dashboard.

01

Generate API Keys

Open Dashboard -> Merchant -> Config and copy the Merchant Key, API Key, and Client Secret for sandbox or production.

02

Confirm Currencies

Make sure the merchant supports the checkout currency and the merchant owner has an active wallet for that currency.

03

Select Gateways

From API Config, enable the payment gateways that should appear on hosted checkout for each eligible currency.

04

Sign & Create Payment

POST to /api/v1/initiate-payment with X-Timestamp and X-Signature, redirect the customer to payment_url, then verify or listen for IPN.

05

Payout (optional)

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.

API Config Currency + Wallet Gateway Filter Hosted Checkout
Open Testing Console

Use sandbox credentials first, then switch to production when your checkout and IPN are verified.

Latest Merchant Update API Config controls checkout after keys are generated

Merchant Setup & API Config

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.

Merchant Dashboard

One integration, dashboard-controlled checkout

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.

Sandbox keys Production keys Wallet rails Gateway controls
Default mode Sandbox
Live access Requires approval
Gateway source API Config

Merchant Review Status

Pending merchants can test in sandbox. Production API access and live checkout are available only after admin approval.

Currency Rails

Each request currency_code must exist in the merchant-supported currency list. Unsupported currencies are rejected before checkout opens.

Receiver Wallet Match

The merchant owner must have an active wallet for the requested currency, otherwise the API returns a wallet availability error.

Payment Gateway Controls

Only active automatic gateways that match the requested currency and merchant selection can appear on hosted checkout.

API Commission Mode

Choose Customer Pays or Merchant Pays under Merchant → API → API Commission. This affects initiate-payment totals and production balance checks.

Saved Payout Accounts

Save beneficiary accounts under Withdraw → Saved accounts, then pass withdraw_account_id on initiate-payout.

Runtime Decision Order

  1. 01
    Authenticate the environment

    X-Environment decides which merchant key, API key, and API secret are used.

  2. 02
    Check merchant availability

    Rejected or disabled merchants are blocked. Production also requires approved merchant status.

  3. 03
    Validate requested currency

    currency_code must match a merchant-supported rail such as USD, EUR, or BDT.

  4. 04
    Confirm receiver wallet

    The merchant owner needs an active wallet for that same currency.

  5. 05
    Resolve configured gateways

    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.

  6. 06
    Apply optional API filter

    allow_payment_methods can narrow the available gateway list, but it cannot expose an unconfigured or wrong-currency gateway.

Real Cases & Results

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.
No cross-currency gateways

A gateway configured for USD cannot process EUR checkout unless a separate EUR gateway exists.

Secrets stay server-side

API Secret is used only to sign server-to-server API requests and verify IPN signatures.

Signed checkout URL expires

The returned payment_url is time-limited. Create a new payment session for expired checkout links.

Verify is merchant scoped

A merchant can verify only its own transaction and only in the matching sandbox or production environment.

API Commission Who pays the platform fee on pay-in

API Commission (Pay-in)

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.

Customer Pays

The customer pays payment_amount plus commission. You receive the full payment_amount in your wallet after a successful checkout.

  • customer_pays = payment_amount + commission
  • merchant_receives = payment_amount

Merchant Pays

The customer pays only payment_amount. Commission is deducted from your wallet when the payment completes (production only).

  • customer_pays = payment_amount
  • merchant_receives = payment_amount
  • Your wallet must cover the commission fee at initiate-payment time in production.

initiate-payment Response Fields

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.

Example (Merchant Pays, 5% fee, ₦1,000 payment)

{
  "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
  }
}
Production balance check If api_fee_bearer is merchant and commission is greater than zero, initiate-payment returns 422 when your wallet cannot cover the commission fee.

verify-payment & IPN commission object

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..."
}

Currency & Gateway Rules

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.

Gateway Match Matrix

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.

Dashboard Source Merchant -> Config -> Payment Gateway Controls
Currency enabled for merchant The request currency_code must exist in the merchant-supported currency list.
Active merchant wallet exists The merchant owner must have an active wallet for the requested currency.
Gateway currency matches Only active automatic deposit methods for that exact currency are eligible.
Merchant gateway selection passes When gateways are selected in API Config, checkout is restricted to those selected methods.

How allow_payment_methods Works Now

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.
Safe Default If a merchant has not selected any gateways, checkout falls back to all active automatic gateways that match the requested currency. Once the merchant selects gateways, checkout uses only that configured list.

Korapay Integration (Pay-In & Pay-Out)

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.

Primary payout rail For NGN payouts, use withdraw_method_id from GET /api/v1/payout-methods where method_code is korapay_ngn_transfer. Korapay bank codes are required — load them from the same provider used by the platform bank list when Korapay is configured.

Admin configuration

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

Platform webhook URL (Korapay dashboard)

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.

Korapay Pay-In (Merchant collections)

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.

Pay-In API flow

  1. 01
    POST /api/v1/initiate-payment

    Include currency_code NGN and optional allow_payment_methods containing Korapay.

  2. 02
    Redirect customer

    Open payment_url from the response.

  3. 03
    Webhook + verify

    Receive payment IPN on your ipn_url. Poll GET /api/v1/verify-payment/{trx_id} if needed.

Korapay Pay-Out (Merchant settlements)

Payouts debit the merchant owner wallet immediately. Korapay disbursement runs asynchronously when status is processing; final status arrives via Korapay webhook and merchant IPN.

Required beneficiary fields (inline)

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

Example — Korapay NGN payout

{
  "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"
  }
}

Payout status lifecycle

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

Merchant API error codes (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
Korapay live payout setup Enable Disburse/Payout in your Korapay merchant dashboard and whitelist the Bytepay server IP. Payout method id comes from GET /api/v1/payout-methods (method_code: korapay_ngn_transfer).

Korapay API endpoints used by Bytepay

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}

Reconciliation & retries

  • Pending Korapay payouts are finalized by webhooks (transfer.success / transfer.failed).
  • Platform cron: php artisan korapay:reconcile-pending-payouts reconciles stuck pending transactions.
  • Use GET /api/v1/verify-payout/{trx_id} to poll payout status from your integration.

Merchant onboarding checklist

  1. 01
    Complete KYC

    Merchant owner must pass platform KYC and have Withdraw Money enabled.

  2. 02
    Fund NGN wallet

    Collect pay-in via initiate-payment or manual funding.

  3. 03
    GET payout-methods

    Note korapay_ngn_transfer withdraw_method_id for NGN.

  4. 04
    POST initiate-payout

    Send bank_code + account_number; platform resolves account name.

  5. 05
    Handle IPN

    Listen for event payout on your ipn_url; verify X-Signature.

PocketFi Integration (Pay-In & Pay-Out)

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.

Secondary gateway Enable PocketFi in Admin → Payment Gateways. Korapay remains the primary NGN rail when both are configured. Merchants select the withdraw method explicitly via withdraw_method_id.

Admin configuration

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

Platform webhook URL (PocketFi dashboard)

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.

PocketFi Pay-In

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.

PocketFi Pay-Out

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

Environment variables

POCKETFI_WEBHOOK_SECRET=
POCKETFI_LOG_CHANNEL=pocketfi
POCKETFI_VA_BANK=kuda
POCKETFI_SANDBOX_BVN=
POCKETFI_SANDBOX_NIN=

Reconciliation

Schedule pending payout reconciliation: php artisan pocketfi:reconcile-pending-payouts

XudoPay Integration (Pay-In & Pay-Out)

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.

Additional gateway Enable XudoPay in Admin → Payment Gateways. Existing gateways remain unchanged. Merchants choose the withdraw method via withdraw_method_id from GET /api/v1/payout-methods.

Admin configuration

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

Platform webhook URL

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.

XudoPay Pay-In

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.

Underlying XudoPay API

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"
}

XudoPay Pay-Out

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"
}

Transaction verification

GET https://xudopay.com/api/v1/transactions?reference=TXN123456
Authorization: Bearer xudo_sk_live_...

Environment variables

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

Reconciliation

php artisan xudopay:reconcile-pending-deposits

php artisan xudopay:reconcile-pending-payouts

POST

Initiate Payment

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
API key required HMAC signature required Sandbox + production Currency wallet checked
Gateway Selection Hosted checkout first applies merchant currency and wallet rules, then the gateways selected on Merchant API Config. allow_payment_methods is only an optional extra filter.

Request Headers

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.

Request Parameters

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 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"].

Gateway Availability Response Fields

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.

Commission Response Fields

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.

Code Examples

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;

Success Response

200 OK Payment session created
{
  "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
  }
}

Common Error Responses

422 Currency is not enabled for this merchant.

The requested currency_code is not in the merchant-supported currency list.

422 Receiver wallet for this currency is not available.

The merchant owner does not have an active wallet for the requested currency.

422 Insufficient merchant balance to cover the API commission fee.

When commission_bearer is merchant, your wallet must cover the commission at initiate-payment time (production).

401 Invalid API credentials.

Check X-Environment, X-Merchant-Key, and X-API-Key for the same environment.

GET

Verify Payment

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}
Use after redirect or IPN Merchant scoped

Request Headers

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.

Path Parameter

Parameter Type Required Description
trxId string Yes Bytepay transaction ID returned by hosted checkout or IPN.

cURL Example

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"

Success Response

200 OK Completed payment
{
  "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"
}

Payment Status Values

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.
Idempotency Use your ref_trx and the returned trx_id to make fulfillment idempotent. A webhook and a verify request may arrive close together.
GET

Site Info

Fetch gateway branding, environment labels, and customer-facing checkout metadata for a merchant-authenticated integration.

https://bytepay.online/api/v1/site-info
Merchant auth required Checkout branding Environment labels

cURL Example

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"

Success Response

200 OK Gateway metadata
{
  "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"
}

Merchant Payout Guide

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.

Recommended integration flow Always call GET payout-methods first to obtain the current withdraw_method_id and required beneficiary fields for your currency. Method IDs are assigned per installation — use method_code (e.g. korapay_ngn_transfer) to identify the rail, not a hardcoded integer.

End-to-end payout flow

  1. 01
    Fund your wallet

    Collect pay-in via initiate-payment or receive manual credits. Your wallet must cover payout_amount plus the withdrawal method fee (payable_amount).

  2. 02
    GET /api/v1/payout-methods?currency=NGN

    List active auto and manual withdrawal methods. Note the id, min_withdraw, max_withdraw, and fields for your chosen rail.

  3. 03
    Prepare beneficiary

    Either save an account in Dashboard → Withdraw → Saved accounts (withdraw_account_id), or send inline beneficiary fields from the payout-methods response.

  4. 04
    POST /api/v1/initiate-payout

    Sign the request with your API Secret. Wallet is debited immediately. Automatic payouts return status pending while the gateway processes.

  5. 05
    Poll or listen

    Receive payout IPN on your ipn_url (event: payout), or poll GET /api/v1/verify-payout/{trx_id}.

  6. 06
    Handle failures

    If the gateway rejects a transfer after wallet debit, Bytepay refunds payable_amount automatically. Check error_code and the error message.

NGN automatic payout methods

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.

Wallet, fees, and response amounts

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).

Beneficiary field aliases

Inline beneficiary objects accept canonical field names from payout-methods, plus these aliases:

Alias Canonical field
bankbank_code
accountaccount_number
nameaccount_name
nameEnquiryReferencename_enquiry_reference

Prerequisites checklist

  • Merchant approved for production (sandbox works with test credentials while pending).
  • Merchant owner has Withdraw Money feature enabled.
  • KYC and phone verification completed when required by platform rules.
  • Active wallet for the payout currency_code.
  • Wallet balance ≥ payable_amount (payout_amount + fee).
  • Payout amount within method min_withdraw and max_withdraw from payout-methods.
  • Platform withdrawal schedule allows payouts today.
  • Valid bank_code for the selected gateway (Korapay codes for Korapay, etc.).
Idempotency Use a unique ref_trx per payout attempt. Store trx_id from the response for verify-payout and IPN correlation. Do not reuse ref_trx for different payout amounts or beneficiaries.
POST

Initiate Payout

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
Signed API auth sandbox | production Debits merchant wallet + fee
Pay-in vs pay-out initiate-payment collects money into the merchant wallet. initiate-payout sends money out of the merchant wallet to a beneficiary.

Request Headers

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

Request Parameters

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.

Beneficiary options

Option A — Saved account (recommended)

Create a saved account in Dashboard → Withdraw → Saved accounts, then send only withdraw_account_id. No bank details in each API call.

Option B — Inline beneficiary

Send beneficiary fields required by the selected withdraw_method_id. Use GET payout-methods to see field names and api_auto_resolved flags.

Auto-resolved beneficiary fields For Korapay, PocketFi, and XudoPay NGN bank transfers, send only bank_code and account_number — account_name is verified automatically before disbursement. Fields marked api_auto_resolved: true in payout-methods can be omitted from beneficiary.

Example — Saved account

{
  "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.

Example — Korapay NGN (inline beneficiary, recommended)

{
  "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"
  }
}

Example — PocketFi NGN (inline beneficiary)

{
  "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"
  }
}

Example — XudoPay NGN (inline beneficiary, min ₦500)

{
  "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"
  }
}

Request signing (PHP)

$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 Example

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"
    }
  }'

Success Response

200 OK Payout accepted
{
  "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"
  }
}

Payout status lifecycle

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

Payout Error Codes (422 responses)

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).

Example error response

{
  "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"
}
GET

Verify Payout

Poll payout status server-side. X-Environment must match the environment used to initiate the payout.

https://bytepay.online/api/v1/verify-payout/{trxId}

Request Headers

Same signed headers as other merchant API GET endpoints. Sign timestamp.GET./api/v1/verify-payout/{trxId}. with an empty raw body.

Path Parameter

Parameter Description
trxId Transaction ID returned from initiate-payout (trx_id or info.trx_id).

cURL Example

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}"

Success Response (completed)

{
  "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"
}

Pending response

{
  "status": "pending",
  "event": "payout",
  "trx_id": "TXNL8VXLVMTJS50",
  "ref_trx": "PAYOUT_001",
  "message": "Payout is still pending."
}

Failed response

{
  "status": "failed",
  "event": "payout",
  "trx_id": "TXNL8VXLVMTJS50",
  "ref_trx": "PAYOUT_001",
  "message": "Payout failed or was canceled."
}

Status Values

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.
GET

Payout Methods

List active withdrawal methods and required beneficiary fields for a currency before calling initiate-payout.

https://bytepay.online/api/v1/payout-methods?currency=NGN

Query Parameters

Parameter Required Description
currency Yes 3-letter currency code enabled for the merchant (e.g. NGN).

Response fields

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.

Success Response (NGN example)

{
  "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.

Webhooks (IPN)

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.

Payout webhooks Payouts initiated via POST /api/v1/initiate-payout use the ipn_url from that request. Payloads include event: "payout" (payments use event: "payment").
Environment-Aware Webhooks

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.

Korapay provider webhooks Bytepay receives Korapay charge.success and transfer.success / transfer.failed at 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.
PocketFi provider webhooks Bytepay receives PocketFi payment notifications at https://bytepay.online/webhooks/pocketfi. Configure this under PocketFi Dashboard → Settings → Webhooks. Verify HTTP_POCKETFI_SIGNATURE (HMAC-SHA512 of raw body). See PocketFi Integration.
XudoPay provider webhooks Bytepay receives XudoPay notifications at 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.
Reliable Delivery Bytepay retries timeout webhook deliveries up to 3 attempts. Your endpoint should return a 2xx response as soon as the payload is safely queued or processed.

Webhook Headers

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

Webhook Payload

All webhook payloads include environment information to help you differentiate between sandbox and production transactions:

Environment context: The data.environment field is sandbox or production. trx_id values are platform transaction IDs (e.g. TXN...).

Payment IPN

{
  "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
}

Payout IPN

{
  "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
}

Signature Verification

Always verify webhook signatures to ensure authenticity and prevent unauthorized requests. Use your API secret (environment-specific) to verify signatures:

IPN signing secret: Verify X-Signature with HMAC-SHA256 of the raw JSON body. Use test_api_secret for sandbox IPN and api_secret for production IPN — the same secrets used to sign outbound API requests.
<?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.

Environment-Specific Best Practices

Sandbox Webhooks
  • Use for testing webhook integration
  • No real money transactions
  • Don't fulfill actual orders
  • Don't send emails to real customers
  • Use test webhook secret for verification
Production Webhooks
  • Process real customer orders
  • Send confirmation emails
  • Update inventory systems
  • Trigger fulfillment processes
  • Use production webhook secret for verification

Integration Examples

Production-oriented integration examples for popular platforms and frameworks. Keep gateway selection in Merchant API Config, then sign each API request from your server.

Environment Configuration: Replace {environment} with sandbox or production, and use corresponding credentials - test_ prefix for sandbox, no prefix for production in your configuration files.
Latest Checkout Rules The requested currency must match the merchant setup and active wallet. Gateway visibility is controlled from Merchant API Config; allow_payment_methods only narrows the already eligible gateway list.
Signature Required All examples must send X-Timestamp and X-Signature using the API Secret for the selected environment. See Authentication → Request Signing Examples.

Payout integration snippet

// 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

WooCommerce Integration

Advanced Bytepay payment gateway with modern WooCommerce Blocks support, dynamic branding, and enterprise-grade security features.

Advanced WooCommerce Integration

Production-ready payment gateway with WooCommerce Blocks (Gutenberg) support, dynamic branding, and secure webhook processing.

3 Minutes Setup
99.9% Uptime
24/7 Webhook Support
Latest Plugin Download

Enterprise-grade WooCommerce payment gateway with modern Blocks support and dynamic branding.

Plugin Size: 45.2 KB
Version: 2.8.0
Last Updated: Jun 17, 2026
System Requirements
  • WordPress 5.8+
  • WooCommerce 6.0+
  • PHP 8.1+ (8.2+ Recommended)
  • SSL Certificate (Required)
  • Bytepay Merchant Account
  • WooCommerce Blocks Support
Production Ready

Advanced Features

Enterprise-grade payment processing with modern architecture

WooCommerce Blocks

Full Gutenberg checkout compatibility with React-based UI

Dynamic Branding

Auto-fetch logo, colors, and branding from Bytepay API

Secure Webhooks

HMAC-SHA256 signature verification for payment callbacks

Compact Mobile UI

Space-efficient, responsive design for all devices

Advanced Security

Multi-header authentication with environment isolation

Test/Live Mode

Seamless sandbox testing with production deployment

Quick Installation Guide

Get started with Bytepay WooCommerce integration in minutes

1
Download Latest Plugin

Download Bytepay WooCommerce Gateway v2.8.0 from the download section above. This includes all latest features and security updates.

2
Install via WordPress Admin

Navigate to Plugins → Add New → Upload Plugin and select the downloaded ZIP file. The plugin will auto-extract and install.

3
Activate & Configure

Activate the plugin and go to WooCommerce → Settings → Payments → Bytepay. Enter your API credentials and webhook secret.

4
Test Integration

Enable Test Mode, process a sandbox transaction to verify Blocks checkout, webhook delivery, and order completion.

5
Go Live

Disable test mode, ensure production API keys are configured, and start accepting real payments with full webhook processing.

API Configuration

Essential API settings for secure payment processing

API Base URL

https://bytepay.online

Auto-configured
Merchant ID & API Key

Your unique merchant credentials from Bytepay dashboard

Required
Webhook Secret

HMAC-SHA256 signature verification for secure callbacks

Recommended
Authentication Headers

Required 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

Webhook Configuration

Real-time payment status updates and order processing

Webhook Endpoint
https://yoursite.com/wc-api/bytepay_webhook

Configure this URL in your Bytepay merchant dashboard for automatic order updates.

Supported Events
  • Payment Success
  • Payment Failed
  • Payment Cancelled
  • Payment Pending
  • Refund Processed
  • Payment Timeout

WooCommerce Blocks Integration

Modern Gutenberg checkout with React-based payment UI

Responsive Design

Compact, mobile-optimized payment interface that adapts to any screen size.

Dynamic Branding

Automatically fetches and displays your Bytepay branding and logos.

Security Indicators

Clear SSL and security badges to build customer trust during checkout.

Test Mode Support

Clear sandbox indicators for testing without affecting live transactions.

Production Deployment Checklist

Ensure everything is configured correctly before going live

Technical Requirements
API Configuration
Final Verification

Troubleshooting

Common issues and solutions for Bytepay WooCommerce integration

Payment method not showing in checkout

Solutions:

  • Verify plugin is activated and enabled in WooCommerce → Settings → Payments
  • Clear browser cache and WooCommerce cache
  • Check if API credentials are correctly configured
  • Ensure SSL certificate is properly installed
401 Unauthorized API errors

Solutions:

  • Verify Merchant ID and API Key are correct
  • Ensure environment (sandbox/production) matches your credentials
  • Check that all required headers are being sent
  • Contact Bytepay support to verify account status
Orders not updating after payment

Solutions:

  • Verify webhook URL is configured in Bytepay dashboard
  • Check webhook secret key matches plugin configuration
  • Review WordPress error logs for webhook processing errors
  • Test webhook delivery using Bytepay dashboard tools
Release Readiness Sandbox first, production after approval

Sandbox & Go Live Checklist

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.

Sandbox

Use test credentials and X-Environment: sandbox. Transactions are marked as sandbox and do not represent real money movement.

  • Generate or copy test Merchant ID, API Key, and API Secret.
  • Send signed API requests with the sandbox secret.
  • Test success, cancel, pending, failed, webhook, and verify flows.

Production

Use live credentials and X-Environment: production only after admin approval, wallet readiness, and gateway configuration are complete.

  • Merchant status must be approved.
  • Every live currency must have an active merchant wallet.
  • Payment Gateway Controls should contain only gateways you want customers to see.
Production Launch Checks Required before live traffic
Review Config
Approved merchant Production API returns 403 until admin approval is complete.
Server-side signing Each request includes X-Timestamp and X-Signature generated with the matching API secret.
Wallet coverage Each merchant currency has an active receiver wallet.
Gateway coverage Selected gateways match the currencies customers will use.
Redirect URLs success_redirect and cancel_redirect are HTTPS and handle repeated visits safely.
IPN verification Webhook signature is verified before fulfillment or wallet-side order updates.
Payout readiness Withdraw feature enabled, GET payout-methods returns korapay_ngn_transfer (or alternate rail), wallet covers payout_amount + fee, KYC complete, gateway provider configured (Korapay Disburse enabled, XudoPay min ₦500).
Commission mode If merchant pays commission, wallet must cover fees at initiate-payment (production).
Important Never switch only the X-Environment header. Always switch the Merchant ID, API Key, and API Secret together so the environment and credential set match.

Interactive API Testing

Test Bytepay API endpoints directly from this documentation. Use the demo credentials below for sandbox testing.

Demo Payment Information SANDBOX MODE

Use these demo credentials to test all payment methods in sandbox environment:

Demo Wallet
Wallet ID: 123456789 Wallet PIN: 123456
Auto-approved in sandbox
Demo Voucher
Voucher Code: TESTVOUCHER
Instant redemption
Gateway Payment
Behavior: Auto Success
No external redirection
Testing Guidelines
  • Environment Header: Always include X-ENVIRONMENT: sandbox in your API requests
  • Demo Credentials: Use the provided demo wallet/voucher codes for testing payment flows
  • Sandbox Behavior: All payments auto-complete successfully without real money processing
  • Gateway Controls: Hosted checkout respects the gateways configured from Merchant API Config for the selected currency.
  • Transaction Status: Sandbox transactions are marked with "SANDBOX_TRANSACTION" in remarks
  • IPN Notifications: Webhook notifications work normally in sandbox mode
  • Payouts: Use payout-methods to list withdrawal methods, then initiate-payout. IPN payloads use event: payout.
Environment Setup: Use sandbox for testing and production for live transactions. Only sandbox credentials use test_ prefix, production credentials have no prefix.
API Testing Console
Authentication Headers
Sandbox: test_*, Production: no prefix
Sandbox: test_*, Production: no prefix
Used locally to generate X-Signature. Prefer sandbox secrets in the browser console.
Request Parameters
Currency code must be uppercase (e.g. USD, EUR, BDT). You must use the currency that matches your merchant shop setup.
Limit checkout methods by name. Accepts CSV string or JSON array. Case-insensitive.
Sandbox Environment

Base URL: https://bytepay.online

Environment Header: X-Environment: sandbox

Credentials: Use test_ prefixed keys

Purpose: Safe testing without real money

Production Environment

Base URL: https://bytepay.online

Environment Header: X-Environment: production

Credentials: No prefix for production keys

Purpose: Live transactions with real money

Error Codes

Bytepay API uses conventional HTTP response codes to indicate the success or failure of API requests.

HTTP Status Codes

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

API Error Codes

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 Response Format

{
  "error": "Insufficient wallet balance.",
  "error_code": "insufficient_balance"
}
Error Handling Always check HTTP status codes and the error / error_code fields. Validation errors may return an errors object with field messages.

Support

Technical Support

Need assistance with Bytepay API integration? Our technical team provides comprehensive support.

Support Hours: 24/7 for critical issues