Merchant API reference

GatePay Merchant REST API

Create hosted payment links, accept configured wallet and gateway methods, and follow every payment state through webhooks or merchant-scoped status verification.

One API, two environments. The request mode and credential prefixes select sandbox or live processing. The merchant ID is always derived from the credentials and must never be sent in the request body.

Authentication

Every request is authenticated with a mode-matching client ID and client secret. Keep both credentials on your server and send JSON explicitly.

Environment-aware API integration
GatePay supports sandbox testing and live production processing through the same endpoints. Always complete payment, redirect, status, and webhook testing in sandbox before switching your server integration to live credentials.
Environment configuration
Sandbox mode

Use for: Development, integration testing, simulated provider outcomes, and webhook verification.

Request mode: sandbox

Credentials: ci_test_... and cs_test_...

Processing: Uses sandbox merchant and payment records. No real provider charge or wallet balance movement occurs.

Live mode

Use for: Production payment links, real gateway processing, and customer wallet payments.

Request mode: live

Credentials: ci_live_... and cs_live_...

Processing: Uses production merchant records and the payment gateways configured by the GatePay administrator.

01Choose the modesandbox uses the sandbox database. Live uses production services.
02Match the credentialsci_test_ / cs_test_ or ci_live_ / cs_live_.
03Send JSON headersUse JSON Accept on every request and JSON Content-Type on POST requests.

Required headers

HeaderValueRequiredDescription
Acceptapplication/jsonYesRequired on POST and GET.
Content-Typeapplication/jsonPOSTRequired for payment creation.
X-Client-Idci_test_...YesMerchant application client ID.
X-Client-Secretcs_test_...YesMerchant application client secret.
Security notice Never expose your Client Secret in client-side code. Store the Client ID and Client Secret securely on your server.

Quick start

A complete integration starts on your server and ends with webhook verification or a status lookup. The hosted checkout handles the customer-facing payment step.

01Generate credentialsCopy sandbox or live credentials from the approved merchant application.
02Create the linkPOST payment information with the selected currency. Do not send payment_methods.
03Redirect customerOpen the returned payment_url in the customer browser.
04Process returnRead trx_reference and your original reference from the merchant URL.
05Verify paymentUse the signed success webhook or poll by your merchant reference.
06Handle idempotentlyDeduplicate webhook deliveries and safely handle repeated callbacks.

Merchant setup and credentials

The REST API is available through an Express merchant application. GatePay creates credentials for the merchant mode, while every request remains scoped to the authenticated application and its owning merchant.

Merchant dashboard

One Express merchant, mode-specific credentials

Open Merchant, select the Express merchant, and use App Info / Merchant Credentials to copy the client ID and secret. Credentials are server-side secrets and must never be exposed in browser or mobile application code.

Merchant typeExpress
Required statusApproved
Owner statusActive
Credential sourceApp Info
Register or create an Express merchantFrontend merchant registration creates the Express merchant application and its initial mode-specific credential pair.
Complete approvalThe Express merchant must be Approved, and its owning GatePay user must be Active in both sandbox and live requests.
Copy credentials securelyUse the client ID and client secret from App Info / Merchant Credentials only from your backend server.
Rotate when necessaryCredential regeneration requires verification. After rotation, the previous client ID and secret stop authenticating immediately.

Environment credential matrix

ModeClient IDClient secretData and processing
sandboxci_test_...cs_test_...Merchant application and payment records use the sandbox database. Provider outcomes are simulated and no wallet balance moves.
liveci_live_...cs_live_...Merchant application and payment records use production data and configured live gateway processors.
Credential mode is enforced. The mode in the request body, both credential prefixes, and the database containing the merchant application must agree. Mixed or mode-incompatible credentials are rejected before payment data is stored.

Currency and gateway rules

GatePay derives payment methods from the selected currency. Do not send payment_methods in a create-payment request.

Requests containing payment_methods are rejected with HTTP 422. Existing unpaid links use current currency settings.

The response payment_methods array is a creation-time snapshot. Checkout rechecks availability before starting a payment.

Admin-managed availability. Checkout shows active supported methods enabled for Merchant payment in the selected currency.

Runtime decision order

01
Validate the currency

The currency code must exist, be Active, and have the Fiat type. Crypto currency records are not accepted as the payment currency.

02
Match the business currency

The payment currency must be the same currency assigned to the authenticated business. A different currency is rejected before a payment link is created.

03
Resolve currency payment methods

GatePay resolves all active payment methods with a supported REST API processor.

04
Apply Merchant payment activation

Checkout shows active supported methods enabled for Merchant payment in the selected currency.

05
Apply Wallet requirements

Mts follows the Wallet payment flow. Live payments require an eligible payer wallet, while sandbox payments use demo wallet outcomes.

06
Calculate Merchant payment fees

Gateway percentage and fixed charges come from the Payment_Received transaction type shown as Merchant payment, then the merchant group fee and fee-bearer rule are applied.

Configuration matrix

Method or ruleRequired configurationCheckout behavior
Mts, Stripe, Paypal, PayUmoney, CoinPayments, Payeer, Coinbase, Flutterwave, Mtn, Airtel, CybersourceCheckout shows active supported methods enabled for Merchant payment in the selected currency.Mts requires an active logged-in payer with an eligible wallet. MTN and Airtel are guest, asynchronous mobile-money checkouts. Airtel Zambia accepts ZMW only. Cybersource uses Secure Acceptance Hosted Checkout (same as deposit and invoice) and settles only after signature validation with decision=ACCEPT and reason_code=100. PayUmoney sandbox links require INR.
Mtn provider prerequisitesMTN uses the administrator-configured provider mode and the credentials required by that mode; a public HTTPS callback URL and the MTN reconciliation scheduler are also required.The REST payment remains pending after RequestToPay is accepted and becomes final through polling, callback verification, or scheduled reconciliation—even if the customer closes the tab.
Airtel provider prerequisitesAirtel must be activated for Merchant payment on ZMW. Live REST payments use the administrator-selected Airtel Collection environment and credentials, authenticated callback URL, and reconciliation scheduler.The customer enters a Zambian Airtel number and approves on the phone. An accepted collection request remains pending until Airtel confirms success; only then is the merchant wallet credited once.
Merchant receiver walletNo wallet check is performed when the payment link is created.After a successful live payment, GatePay creates the merchant currency wallet if it does not already exist, then credits it once.
Availability is currency-based. GatePay excludes unavailable methods and returns payment_methods_unavailable only when no eligible methods remain.
POST

Create payment link

Creates a hosted checkout link for an approved merchant, active fiat currency, and payment methods enabled for merchant payment.

https://gatepay.techvill.net/api/v1/payments
Credentials required Sandbox and live Active fiat currency
Payment method selection. Payment methods are determined by the selected currency. Do not send payment_methods; requests containing it are rejected with HTTP 422. External gateways must be active for merchant payment and the selected currency. Airtel requires ZMW and Collection credentials for live-mode payments. Cybersource additionally requires active Payment Received fees and limits plus Secure Acceptance Hosted Checkout credentials (profile_id, access_key, secret_key, mode).

Request body

FieldTypeRequiredDescription
modestringYessandbox or live.
amountstringYesPositive decimal with up to eight decimal places.
currencystringYesActive three-letter fiat currency code matching the currency assigned to the authenticated business.
referencestringYesYour unique reference, up to 100 characters.
success_urlURLYesReturn URL for success and pending outcomes.
webhook_urlURLYesPublic endpoint for payment lifecycle events.
cancel_urlURLYesReturn URL for failed and cancelled outcomes.
descriptionstringOptionalPayment description, up to 1000 characters.
metadataobjectOptionalUp to 20 scalar key-value pairs.
Merchant ID is prohibited. GatePay derives the merchant from the authenticated application. Live URLs must use HTTPS, and callback_url is not accepted; use success_url.
curl --request POST 'https://gatepay.techvill.net/api/v1/payments' \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --header 'X-Client-Id: ci_test_your_client_id' \
  --header 'X-Client-Secret: cs_test_your_client_secret' \
  --data '{
    "mode": "sandbox",
    "amount": "100.00",
    "currency": "USD",
    "reference": "ORDER-1001",
    "success_url": "https://merchant.example/payments/success",
    "webhook_url": "https://merchant.example/webhooks/payments",
    "cancel_url": "https://merchant.example/payments/cancel",
    "description": "Payment for order 1001",
    "metadata": {"order_id": "1001"}
  }'
use Illuminate\Support\Facades\Http;

$response = Http::acceptJson()
    ->withHeaders([
        'X-Client-Id' => 'ci_test_your_client_id',
        'X-Client-Secret' => 'cs_test_your_client_secret',
    ])
    ->post('https://gatepay.techvill.net/api/v1/payments', [
        'mode' => 'sandbox',
        'amount' => '100.00',
        'currency' => 'USD',
        'reference' => 'ORDER-1001',
        'success_url' => 'https://merchant.example/payments/success',
        'webhook_url' => 'https://merchant.example/webhooks/payments',
        'cancel_url' => 'https://merchant.example/payments/cancel',
        'description' => 'Payment for order 1001',
        'metadata' => ['order_id' => '1001'],
    ]);

201 Created

{
  "response": {
    "status": {
      "code": 201,
      "message": "Payment link created successfully."
    },
    "records": {
      "code": "payment_link_created",
      "transaction_id": "01KXQBBN9Y1CN3T3NSQHZ15W4V",
      "reference": "ORDER-1001",
      "mode": "sandbox",
      "status": "PENDING",
      "amount": "100.00000000",
      "currency": "USD",
      "payment_methods": ["Stripe", "Paypal"],
      "payment_url": "https://gatepay.techvill.net/pay/8fK2mQ7xN4vR9cT6pL3zWa",
      "expires_at": "2026-07-21T10:30:00+00:00",
      "metadata": {"order_id": "1001"}
    }
  }
}

Idempotency: Repeating the same reference with the identical payload returns the existing link with HTTP 200. Reusing it with changed data returns HTTP 409.

GET

Verify transaction status

Fetches the current public transaction status using the exact merchant reference supplied during link creation. A found transaction always returns HTTP 200.

https://gatepay.techvill.net/api/v1/payments/status?mode=sandbox&reference=ORDER-1001
Credentials required Merchant-scoped lookup No Content-Type required

Query parameters

ParameterRequiredDescription
modeYesMust match the credential prefixes and transaction environment.
referenceYesExact, case-sensitive merchant reference.
curl --get 'https://gatepay.techvill.net/api/v1/payments/status' \
  --data-urlencode 'mode=sandbox' \
  --data-urlencode 'reference=ORDER-1001' \
  --header 'Accept: application/json' \
  --header 'X-Client-Id: ci_test_your_client_id' \
  --header 'X-Client-Secret: cs_test_your_client_secret'
use Illuminate\Support\Facades\Http;

$response = Http::acceptJson()
    ->withHeaders([
        'X-Client-Id' => 'ci_test_your_client_id',
        'X-Client-Secret' => 'cs_test_your_client_secret',
    ])
    ->get('https://gatepay.techvill.net/api/v1/payments/status', [
        'mode' => 'sandbox',
        'reference' => 'ORDER-1001',
    ]);

200 OK

{
  "response": {
    "status": {
      "code": 200,
      "message": "Transaction status retrieved successfully."
    },
    "records": {
      "code": "transaction_status_retrieved",
      "trx_reference": "01KXQBBN9Y1CN3T3NSQHZ15W4V",
      "reference": "ORDER-1001",
      "status": "SUCCESS",
      "mode": "sandbox",
      "amount": "100.00000000",
      "currency": "USD",
      "payment_method": "Stripe"
    }
  }
}

Checkout and merchant returns

Redirect the customer to the returned payment_url. Checkout resolves currently available payment methods for the transaction currency and validates the selected method again before processing.

SUCCESSReturns to success_url.
PENDINGReturns to success_url while processing continues.
FAILEDReturns to cancel_url.
CANCELLEDReturns to cancel_url.
EXPIREDUnstarted link passed its stored expiry.
Return URL data. GatePay preserves existing merchant query parameters and appends only trx_reference and reference. Treat browser returns as informational and use webhooks or status verification for fulfillment.

Webhooks

GatePay posts lifecycle events to the webhook_url stored on the payment link. Sandbox and live use the same contract, with delivery records stored in their originating environment.

Supported events

EventPublic statusBody signature
payment.status.createdPENDINGYes
payment.status.successSUCCESSYes
payment.status.failedFAILEDYes
payment.status.cancelledCANCELLEDYes
payment.status.expiredEXPIREDYes

Delivery headers

HeaderDescription
X-EventLifecycle event name.
X-Delivery-IdUnique ID used to deduplicate at-least-once deliveries.
X-TimestampUnix timestamp used to verify the event signature.
Content-Typeapplication/json

Successful payment event

{
  "id": "evt_01KX...",
  "event": "payment.status.success",
  "created_at": "2026-07-21T10:00:00+00:00",
  "data": {
    "trx_reference": "01KXQBBN9Y1CN3T3NSQHZ15W4V",
    "reference": "ORDER-1001",
    "status": "SUCCESS",
    "mode": "live",
    "amount": "100.00000000",
    "currency": "USD",
    "payment_method": "Stripe",
    "metadata": {"order_id": "1001"}
  },
  "signature": "hex_hmac_sha256"
}
Webhook signatures. Every event contains a signature field. It is HMAC-SHA256 of timestamp + "." + unsigned compact JSON body using the matching client secret. Reject timestamps older than five minutes.

Laravel signature verification

$timestamp = request()->header('X-Timestamp');
abort_unless(is_string($timestamp) && ctype_digit($timestamp), 401);

$now = time();
abort_if((int) $timestamp < $now - 300 || (int) $timestamp > $now + 300, 401);

$rawBody = request()->getContent();
try {
    $event = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
    abort_unless(is_array($event), 401);

    $receivedSignature = $event['signature'] ?? null;
    abort_unless(is_string($receivedSignature), 401);

    unset($event['signature']);

    $unsignedBody = json_encode(
        $event,
        JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR
    );
} catch (\JsonException) {
    abort(401);
}

$expectedSignature = hash_hmac(
    'sha256',
    $timestamp.'.'.$unsignedBody,
    config('services.merchant_api.client_secret')
);

abort_unless(hash_equals($expectedSignature, $receivedSignature), 401);

return response()->noContent();

Retry policy: Any 2xx response acknowledges delivery. GatePay retries connection failures, timeouts, 408, 429, and 5xx responses immediately, then after 1 minute, 5 minutes, 15 minutes, and 1 hour. Other 4xx responses and redirects are terminal.

Sandbox testing

Sandbox checkout never calls a real provider or changes a wallet balance. Select an enabled method and use one of the demo values below to produce a deterministic result.

Provider rules. Stripe and Flutterwave accept listed test cards. MTN and Airtel use local phone simulators and require no provider credentials or network calls in REST sandbox mode. Airtel still requires ZMW. Cybersource uses the generic success, failed, pending, and cancelled simulator in API sandbox mode; live-mode links use Secure Acceptance Hosted Checkout like deposit and invoice. PayUmoney sandbox payments require INR. Bank is not supported by the REST API.

Wallet

WLT-1000000001 SUCCESS
WLT-1000000002 FAILED
WLT-1000000003 PENDING
WLT-1000000004 CANCELLED
WLT-1000000005 INSUFFICIENT BALANCE

Stripe

Card Number MM/YY CVC
4242424242424242 11/28 123 SUCCESS
4000000000000002 11/28 123 FAILED
4000000000009995 11/28 123 PENDING
4000000000000069 11/28 123 CANCELLED

PayPal

success@paypal.test SUCCESS
failed@paypal.test FAILED
pending@paypal.test PENDING
cancelled@paypal.test CANCELLED

Flutterwave

Card Number MM/YY CVV
5531886652142950 11/28 123 SUCCESS
5531886652142951 11/28 123 FAILED
5531886652142952 11/28 123 PENDING
5531886652142953 11/28 123 CANCELLED

CoinPayments

BTC SUCCESS
ETH FAILED
USDT PENDING
LTC CANCELLED

PayUmoney

payu-success SUCCESS
payu-failed FAILED
payu-pending PENDING
payu-cancelled CANCELLED

Payeer

P-SBX-100001 SUCCESS
P-SBX-100002 FAILED
P-SBX-100003 PENDING
P-SBX-100004 CANCELLED

Coinbase

cb-success SUCCESS
cb-failed FAILED
cb-pending PENDING
cb-cancelled CANCELLED

MTN

46733123450 FAILED
46733123451 PENDING
46733123452 EXPIRED
46733123453 SUCCESS

Airtel

260970000050 FAILED
260970000051 PENDING
260970000052 EXPIRED
260970000053 SUCCESS

Cybersource

success SUCCESS
failed FAILED
pending PENDING
cancelled CANCELLED

Sandbox & Go Live Checklist

Use this checklist before moving from simulated payments to live checkout. The same REST endpoints work in both environments; the request mode and the complete client credential pair must change together.

Sandbox readiness

Validate the complete integration without calling a real provider or moving wallet balances.

  • Send mode=sandbox with matching ci_test_... and cs_test_... credentials.
  • Test successful, failed, pending, cancelled, insufficient-balance, and expired outcomes where supported.
  • Confirm checkout returns, reference-based status polling, webhook signatures, deduplication, and retry handling.
  • Use only documented simulator values; sandbox never calls production gateway processors.

Live readiness

Enable real payment processing only after merchant access and production configuration are complete.

  • Send mode=live with matching ci_live_... and cs_live_... credentials.
  • The Express merchant must be Approved and its owning user must be Active.
  • Use an Active fiat currency and only methods enabled by the administrator for Merchant payment.
  • Configure production gateway credentials and public HTTPS success, webhook, and cancel URLs.
Production launch checks

Required before live traffic

Credentials and modeUse the live client ID and secret together with mode=live. Never mix test and live prefixes.
Merchant accessVerify the Express merchant is Approved and the merchant owner remains Active.
Currency and methodsCheckout shows active supported methods enabled for Merchant payment in the selected currency.
Gateway readinessConfigure production credentials and merchant-payment fees for every external gateway offered at checkout.
Wallet flowMts payers need login, an eligible wallet, and enough balance. A missing merchant receiver wallet is created after successful settlement.
Secure merchant URLsUse public HTTPS success, webhook, and cancel URLs, and safely handle the two returned reference parameters.
Webhook receiverVerify every event HMAC with the matching client secret, reject stale timestamps, deduplicate delivery IDs, and return 2xx quickly.
Verification and operationsPoll status by the exact merchant reference, run webhook queue and scheduler or cron processing, and monitor failed delivery attempts.
Switch the complete environment together. Do not change only mode or one credential. Change mode, client ID, and client secret as one set, keep merchant_id out of the request, and complete a low-value live payment before opening general traffic.

Errors and limits

Every JSON response uses the existing GatePay response envelope. The HTTP status represents request handling; transaction outcomes returned from status verification still use HTTP 200.

HTTPMachine code examplesMeaning
401invalid_credentialsMissing or invalid credential pair.
403credential_mode_mismatchCredential mode mismatch or unavailable merchant access.
404transaction_not_foundNo transaction for this app and exact reference.
406not_acceptableAccept header does not allow JSON.
409reference_conflictReference exists with a changed payload.
415unsupported_media_typePOST request body is not JSON.
422validation_error, business_currency_mismatchInvalid payload, a payment currency different from the business currency, or unavailable payment configuration.
429rate_limit_exceededConfigured request limit was exceeded.
503sandbox_unavailableRequested sandbox service is unavailable.

Error response format

{
  "response": {
    "status": {
      "code": 422,
      "message": "The request payload is invalid."
    },
    "records": {
      "code": "validation_error",
      "errors": {
        "currency": ["The selected currency is invalid."]
      }
    }
  }
}
Admin-managed policies. The default rate limit is 60 requests per minute. New links use the current 15, 30, 45, or 60 minute expiry preference, while existing links retain their stored expiry.