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 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-idAPI 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
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"
})
});{
"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.
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{
"statusCode": 403,
"message": "Payout API is not enabled for this business. Request access in the developer portal.",
"requestId": "req_xxx"
}{
"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.
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"
})
});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
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" }
})
});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"
})
});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]" }
})
});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)`.
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`.
(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_idSDKs And Language Examples
await client.payments.create({
amount: { amount: 5000, currency: "NGN" },
customer: { email: "[email protected]" },
paymentMethod: "hosted_checkout"
});$client->payments->create([
"amount" => ["amount" => 5000, "currency" => "NGN"],
"customer" => ["email" => "[email protected]"],
"paymentMethod" => "hosted_checkout"
]);client.payments.create(
amount={"amount": 5000, "currency": "NGN"},
customer={"email": "[email protected]"},
payment_method="hosted_checkout"
)OasisPay::payments()->create([
"amount" => ["amount" => 5000, "currency" => "NGN"],
"customer" => ["email" => "[email protected]"],
"paymentMethod" => "hosted_checkout"
]);