CubePay docs
Accept crypto payments on Cube Chain
CubePay is a hosted payment gateway: you create an invoice over HTTPS, send the buyer to the returned checkout page, and get a signed webhook when the payment confirms on-chain. Flat 1% — no chargebacks, no card networks.
Introduction
Everything runs against a single base URL. All request and response bodies are JSON.
Base URL
https://cubepay-backend.vercel.appThe flow: create an invoice server-side → redirect the buyer to checkout_url → the buyer pays on Cube Chain → you receive invoice.paid on your webhook → funds are swept to your payout address (invoice.completed).
Authentication
Authenticate every API call with your secret key in the Authorization header:
Authorization: Bearer ck_live_…Your key is shown once at signup and can be rotated from the dashboard (Settings → API keys). It is stored hashed on our side and can never be retrieved — keep it in an environment variable on your server and never ship it to a browser or mobile bundle. The dashboard itself signs in with email + password, which issues a 24-hour session token with the same permissions.
Currencies & settlement tokens
Invoices are priced in fiat (price_currency) and the pricing currency pins the on-chain token — there is nothing for the buyer to choose:
Passing a mismatched pay_token (e.g. USDT on an NGN invoice) returns 422. The quote rate is locked when the invoice is created and the exact token amount appears inpay_amount.
One account can also run the two currencies separately: the dashboard (Settings → Currency profiles) issues an NGN-scoped and a USD-scoped API key, each with its own webhook URL and settlement wallet. A scoped key defaults to its currency — price_currency can be omitted — and may not create invoices in the other one.
Create an invoice
One call creates the invoice, derives a unique deposit address, locks the quote and returnscheckout_url. Pick your stack:
curl -X POST https://cubepay-backend.vercel.app/api/v1/invoices \
-H "Authorization: Bearer ck_live_…" \
-H "Content-Type: application/json" \
-d '{
"price_amount": "25000",
"price_currency": "NGN",
"order_id": "order_1042",
"description": "Cube Phone case",
"success_url": "https://store.com/thank-you"
}'
# → { "id": "inv_…", "checkout_url": "https://cubepay-backend.vercel.app/checkout/inv_…", … }Optional fields: ttl_minutes (payment window, 5 min – 7 days, default 15),metadata (arbitrary JSON echoed back in webhooks), order_id,description, success_url and cancel_url.
Hosted checkout & drop-in SDK
checkout_url serves a mobile-friendly hosted page with the amount, a QR code and live payment status — nothing to build. To keep buyers on your site, load the drop-in SDK and open the checkout in a modal:
<script src="https://cubepay-backend.vercel.app/sdk/cubepay.js"></script>
<script>
CubePay.open({
checkoutUrl: invoice.checkout_url,
onPaid: (invoiceId) => { window.location = "/thank-you"; },
});
</script>The checkout's “paid” screen is buyer UX only — fulfil orders exclusively on the signedinvoice.paid webhook.
Payment links
A payment link is a reusable URL for a fixed price — share it in chats, invoices or social bios. Each visit to /l/{id} spawns a fresh invoice and redirects to its checkout, so one link serves unlimited buyers. Create and manage them in the dashboard (Payment links) or via the API:
curl -X POST https://cubepay-backend.vercel.app/api/v1/payment-links \
-H "Authorization: Bearer ck_live_…" \
-H "Content-Type: application/json" \
-d '{ "title": "Cube Phone case", "price_amount": "25000", "price_currency": "NGN" }'
# → { "id": "plk_…", "url": "https://cubepay-backend.vercel.app/l/plk_…", … }Webhooks
Set your endpoint in the dashboard (Settings → Integration). Every delivery is an HTTP POST with these headers; failed deliveries retry with backoff (30s → 6h, 8 attempts). Respond 2xx within 10 seconds and process each X-CubePay-Delivery id once.
X-CubePay-Event: invoice.paid
X-CubePay-Delivery: evt_… # idempotency key — process each once
X-CubePay-Timestamp: 1760000000
X-CubePay-Signature: hex(HMAC_SHA256(webhook_secret, "{timestamp}." + raw_body))invoice.pendingPayment seen on-chain, awaiting confirmationsinvoice.paidConfirmed in full — fulfil the order on this eventinvoice.underpaidConfirmed amount below the quote (outside tolerance)invoice.expiredTTL elapsed with no paymentinvoice.completedFunds swept to your payout addressVerify the signature with your webhook secret (Settings → API keys → Reveal) before trusting a delivery:
import crypto from "crypto";
function verifyCubePay(req, rawBody) {
const ts = req.headers["x-cubepay-timestamp"];
const sig = req.headers["x-cubepay-signature"];
const expected = crypto.createHmac("sha256", process.env.CUBEPAY_WEBHOOK_SECRET)
.update(`${ts}.`).update(rawBody).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}
// process each X-CubePay-Delivery id once; respond 2xx within 10sAPI reference
/api/v1/auth/loginEmail + password → 24h session token/api/v1/merchants/meYour merchant profile/api/v1/merchants/meUpdate webhook URL / payout address/api/v1/merchants/me/rotate-keyRotate API key (shown once)/api/v1/merchants/me/rotate-webhook-secretRotate webhook secret/api/v1/merchants/me/reveal-webhook-secretReveal the current webhook secret/api/v1/merchants/me/currency-profilesPer-currency keys, webhooks & settlement wallets/api/v1/merchants/me/currency-profiles/{ccy}/rotate-keyCreate/rotate the NGN- or USD-scoped key/api/v1/merchants/me/currency-profiles/{ccy}Set per-currency webhook URL / settlement wallet/api/v1/invoicesCreate invoice (price_amount, price_currency, ttl_minutes?, metadata?)/api/v1/invoices · /{id}List / fetch invoices/api/v1/invoices/{id}/settleSweep a paid invoice to your payout address/api/v1/payment-linksCreate a reusable payment link/api/v1/payment-linksList links with visit & paid stats/api/v1/payment-links/{id}Pause / activate a link/l/{id}Public: spawns a fresh invoice and redirects to checkout/checkout/{id}Public: hosted checkout pageErrors
Errors return a JSON body with a human-readable detail message:
{ "detail": "NGN invoices are payable only in XNGN" }401Missing, invalid or expired credentials404Resource not found (or owned by another merchant)409Conflict — e.g. settling an unpaid invoice422Validation failed — the detail message says which field5xxGateway problem — retry with backoffQuestions or stuck on an integration? Reach out from your dashboard — or open the Developers tab for keys and live examples.
