OasisPay Docs

Build with OasisPay

Create checkout payments, issue virtual accounts, verify signed webhooks, and test safely before going live.

Quick Start

Use your secret key in the `Authorization` header. For every create request, send an `Idempotency-Key` so safe retries do not create duplicates.

cURL
curl https://api.oasispayhq.com/api/v1/payments \
  -H "Authorization: Bearer osk_test_xxx" \
  -H "Idempotency-Key: order_1001" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": { "amount": 5000, "currency": "NGN" },
    "customer": { "email": "[email protected]" },
    "paymentMethod": "hosted_checkout",
    "callbackUrl": "https://merchant.example.com/payment/callback"
  }'

Authentication

Authenticate server-side API calls with a secret key. Public keys identify your business in client-side contexts, but they must not authorize money movement.

Test secret keys

osk_test_xxx

Live secret keys

osk_live_xxx

Public test keys

opk_test_xxx

Public live keys

opk_live_xxx

Authorization: Bearer osk_test_xxx
Content-Type: application/json
Idempotency-Key: unique-operation-id

API Reference

Integration flow

Persistent / Virtual Account

Use this when you want to issue a bank account for transfer collections and reconcile credits by account or payment reference.

Create endpoint

POST /api/v1/virtual-accounts/reserved

Alternative endpoint

POST /api/v1/virtual-accounts/dynamic

Request
await fetch("https://api.oasispayhq.com/api/v1/virtual-accounts/reserved", {
  method: "POST",
  headers: {
    Authorization: "Bearer osk_test_xxx",
    "Content-Type": "application/json",
    "Idempotency-Key": "customer_account_1001"
  },
  body: JSON.stringify({
    customer: {
      email: "[email protected]",
      firstName: "Francis",
      lastName: "Samuel",
      phone: "08012345678"
    },
    preferredAccountName: "DAILYICTSOLUTIONS FRANCIS"
  })
});
Response
{
  "id": "va_xxx",
  "reference": "va_xxx",
  "type": "RESERVED",
  "status": "ACTIVE",
  "accountName": "DAILYICTSOLUTIONS FRANCIS",
  "accountNumber": "1234567890",
  "bankName": "Paga Bank",
  "providerReference": "paga_reference"
}

Important

  • Reserved accounts are persistent and reusable for a customer.
  • Dynamic accounts are amount-bound and expire after the configured payment window.
  • Use webhooks to confirm credits before fulfilling value.

Payout API Access

OasisPay uses one API base URL and one API key system. Payouts are permission-gated, so a merchant can use payments, virtual accounts, checkout, and webhooks without automatically being allowed to move money out through the API.

How to request access

Open the merchant dashboard, go to Developers, then API Keys, and submit a payout API use case. OasisPay reviews KYC, risk, intended use, beneficiaries, and limits before enabling access.

What stays separate

Dashboard withdrawals are not the same as Payout API access. Merchants can still use the normal withdrawal flow if their business is approved, live mode is enabled, and withdrawal checks pass.

Required controls
KYC approved
Payout API enabled by OasisPay
Developer Security IP whitelist configured
Live secret key
Scope: payouts:create
Request IP must match whitelist
Verified beneficiary
Sufficient balance
Daily and single-transfer limits
Idempotency-Key header
Access not enabled response
{
  "statusCode": 403,
  "message": "Payout API is not enabled for this business. Request access in the developer portal.",
  "requestId": "req_xxx"
}
IP whitelist response
{
  "statusCode": 403,
  "code": "PAYOUT_IP_WHITELIST_REQUIRED",
  "message": "Payout API requires IP whitelisting.",
  "requestId": "req_xxx"
}

{
  "statusCode": 403,
  "code": "PAYOUT_IP_NOT_ALLOWED",
  "message": "IP address not authorized for payout API.",
  "requestId": "req_xxx"
}

Subscriptions

Subscriptions let SaaS and recurring-revenue merchants create plans, enroll customers, generate invoices, and prepare renewals without using a separate API product. Actual payments still use OasisPay checkout, virtual accounts, and the normal collection fee rules.

Plans

Weekly, monthly, quarterly, or yearly pricing records that customers subscribe to.

Subscriptions

Customer enrollment records with current period, next billing date, and preferred payment method.

Invoices

Pending or paid billing records that can later be settled through checkout or virtual account payment flows.

Create a subscription plan
await fetch("https://api.oasispayhq.com/api/v1/subscriptions/plans", {
  method: "POST",
  headers: {
    Authorization: "Bearer osk_test_xxx",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    name: "Starter",
    amount: 5000,
    currency: "NGN",
    interval: "weekly",
    description: "Starter weekly SaaS plan"
  })
});
Create a customer subscription
await fetch("https://api.oasispayhq.com/api/v1/subscriptions", {
  method: "POST",
  headers: {
    Authorization: "Bearer osk_test_xxx",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    planId: "subplan_xxx",
    paymentMethod: "customer_choice",
    customer: {
      email: "[email protected]",
      firstName: "Francis",
      lastName: "Samuel",
      phone: "08012345678"
    }
  })
});

Payment And Virtual Account Examples

Create Checkout Payment
await fetch("https://api.oasispayhq.com/api/v1/payments", {
  method: "POST",
  headers: {
    Authorization: "Bearer osk_test_xxx",
    "Content-Type": "application/json",
    "Idempotency-Key": "checkout_1001"
  },
  body: JSON.stringify({
    amount: { amount: 5000, currency: "NGN" },
    customer: {
      email: "[email protected]",
      firstName: "Francis",
      lastName: "Samuel",
      phone: "08012345678"
    },
    paymentMethod: "hosted_checkout",
    callbackUrl: "https://merchant.example.com/payment/callback",
    metadata: { orderId: "ORD-1001" }
  })
});
Create Reserved Virtual Account
await fetch("https://api.oasispayhq.com/api/v1/virtual-accounts/reserved", {
  method: "POST",
  headers: {
    Authorization: "Bearer osk_test_xxx",
    "Content-Type": "application/json",
    "Idempotency-Key": "customer_account_1001"
  },
  body: JSON.stringify({
    customer: {
      email: "[email protected]",
      firstName: "Francis",
      lastName: "Samuel",
      phone: "08012345678"
    },
    preferredAccountName: "FRANCIS SAMUEL"
  })
});
Create Dynamic Virtual Account
await fetch("https://api.oasispayhq.com/api/v1/virtual-accounts/dynamic", {
  method: "POST",
  headers: {
    Authorization: "Bearer osk_test_xxx",
    "Content-Type": "application/json",
    "Idempotency-Key": "va_1001"
  },
  body: JSON.stringify({
    amount: 5000,
    currency: "NGN",
    customer: { email: "[email protected]" }
  })
});
Verify Payment
const response = await fetch("https://api.oasispayhq.com/api/v1/payments/osp_xxx", {
  headers: {
    Authorization: "Bearer osk_test_xxx"
  }
});

const payment = await response.json();

Webhooks

OasisPay sends `X-OasisPay-Signature`, `X-OasisPay-Timestamp`, and `X-OasisPay-Event`. Verify `HMAC_SHA256(timestamp + "." + rawBody, webhookSecret)`.

payment.successpayment.failedvirtual_account.credittransaction.createdsettlement.completedpayout.successfulpayout.failedsubscription.createdsubscription.renewedsubscription.expiredsubscription.payment.successsubscription.payment.failed
Node.js Verification
import crypto from "crypto";

function verifyWebhook(rawBody, headers, secret) {
  const timestamp = headers["x-oasispay-timestamp"];
  const signature = headers["x-oasispay-signature"];
  const expected = crypto
    .createHmac("sha256", secret)
    .update(timestamp + "." + rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Webhook Delivery Behind Cloudflare

If your webhook endpoint is behind Cloudflare and OasisPay shows HTTP 403 in Recent Attempts, your Cloudflare rules may be blocking OasisPay server-to-server delivery even though the payment succeeded.

OasisPay source IPs

167.233.33.130

2a01:4f8:1c18:4c3b::1

Recommended action

Skip WAF, custom rules, rate limiting, bot fight mode, and security checks only for the exact webhook hostname, path, method, and OasisPay IPs.

Best permanent option

Create a DNS-only webhook subdomain, such as `webhook.merchantdomain.com`, that points directly to your server instead of being proxied by Cloudflare. Then use a dedicated endpoint like `https://webhook.merchantdomain.com/your-oasispay-webhook-path`.

Cloudflare Custom Rule
(ip.src eq 167.233.33.130 or ip.src eq 2a01:4f8:1c18:4c3b::1)
and http.host eq "merchantdomain.com"
and http.request.uri.path eq "/your-oasispay-webhook-path"
and http.request.method eq "POST"

Verify delivery

Use webhook test or replay, confirm HTTP 200 in Recent Attempts, and check Cloudflare Security Events for OasisPay IPs.

Avoid unsafe bypasses

Do not disable Cloudflare globally, bypass every POST request, whitelist all IPs, or credit users from email receipts alone.

Errors

Every API error includes a `requestId`. Use it when checking API logs or contacting support.

{
  "statusCode": 401,
  "message": "Invalid API key.",
  "requestId": "req_xxx",
  "path": "/api/v1/payments",
  "timestamp": "2026-06-03T12:00:00.000Z"
}

Testing

Use osk_test_ keys for sandbox requests.

Use unique idempotency keys for POST retries.

Send test webhooks from the merchant webhook page.

Headers:
Authorization: Bearer osk_test_xxx
Idempotency-Key: your_unique_operation_id

SDKs And Language Examples

JavaScript
await client.payments.create({
  amount: { amount: 5000, currency: "NGN" },
  customer: { email: "[email protected]" },
  paymentMethod: "hosted_checkout"
});
PHP
$client->payments->create([
  "amount" => ["amount" => 5000, "currency" => "NGN"],
  "customer" => ["email" => "[email protected]"],
  "paymentMethod" => "hosted_checkout"
]);
Python
client.payments.create(
  amount={"amount": 5000, "currency": "NGN"},
  customer={"email": "[email protected]"},
  payment_method="hosted_checkout"
)
Laravel
OasisPay::payments()->create([
  "amount" => ["amount" => 5000, "currency" => "NGN"],
  "customer" => ["email" => "[email protected]"],
  "paymentMethod" => "hosted_checkout"
]);
OasisPay — Payments Infrastructure for Africa