Skip to Content
SellubDevelopersCommerce API

Commerce API

The Commerce API plan lets partners embed Sellub payments and checkout in their own apps and websites. Today that means:

  • External payments — a REST API for one-off payments, hosted invoices and recurring subscriptions (Paystack plans), authenticated with a publishable key.
  • Webhooks — signed events for orders, payments, fulfilment, subscriptions and stock.
  • Sandbox — a separate test channel and pk_test_ keys that run on Paystack test mode.
  • The standard Shop API for catalog and cart — see API reference.

The easiest way in is @duabalabs/sellub-client.

Keys

Create keys in the dashboard: Studio → Integrate. Every key belongs to one shop (identified by its public account id, acct_…) and is shown once — only a hash is stored.

PrefixUseToday
pk_live_ / pk_test_Browser-safe; pinned to your allowed originsAuthenticates the external-payments API
sk_live_ / sk_test_Server-side onlyCan be created; no endpoint accepts it yet

A pk_test_ key routes payments to Paystack test mode, so it can never move real money. Send the publishable key in the X-Sellub-Publishable-Key header, with your accountId in the request:

curl -X POST https://api.sellub.com/external-payments/initialize \ -H "Content-Type: application/json" \ -H "X-Sellub-Publishable-Key: pk_live_…" \ -H "Origin: https://your-site.com" \ -d '{"accountId":"acct_…","email":"buyer@example.com","amount":5000}'

Requests must come from an origin on your allow-list (set on the Integrate page; https://*.example.com wildcards allowed), and any callback URL must be on your callback allow-list and use HTTPS. Each key is rate-limited to 10 requests a minute per IP and 100 per account.

External payments API

Base URL https://api.sellub.com/external-payments.

MethodPathDoes
POST/initializeStart a one-off payment; returns a Paystack authorization URL
GET/verify/:referenceCheck a payment’s status
POST/subscriptions/plans · GET /subscriptions/plansCreate / list your subscription plans
POST/subscriptions/startStart a subscription for a customer
GET/subscriptions/:code · /subscriptions/by-reference/:referenceLook up a subscription
POST/subscriptions/cancelCancel (needs the customer’s email token)
POST/invoices · GET /invoices/:codeCreate / read a hosted invoice

Money goes straight to your Paystack subaccount; Sellub’s fee is taken automatically by Paystack at your plan’s rate.

Plans

Commerce API access comes with the COMMERCE_API plan — Developer (GHS 500/mo, 1.5%), Growth (GHS 1,500/mo, 1%) or Enterprise (negotiated). The plan is billed monthly from your Sellub wallet:

active → grace_period → downgraded → cancelled
  • active — paid; everything works.
  • grace_period — a renewal failed; everything still works for up to 7 days while you top up.
  • downgraded — the grace period ran out; you’re back on the free Marketplace plan.
  • cancelled — cancelled by you or by Sellub.

Webhook signing

Register an endpoint on the Integrate page and choose its events. You get a signing secret (whsec_…) for it, which you can rotate there.

Headers

Sellub-Event: order.placed Sellub-Event-Id: <event id> Sellub-Signature: t=1714050000,v1=4f4d…

Signed payload

${eventId}.${timestamp}.${rawJsonBody}

timestamp is the t= value from Sellub-Signature, and v1 is the lowercase hex of HMAC_SHA256(secret, signedPayload).

Verification (Node)

import crypto from "node:crypto"; function verify(req, secret) { const parts = Object.fromEntries( req.headers["sellub-signature"].split(",").map((p) => p.split("=")), ); const id = req.headers["sellub-event-id"]; // 5-minute tolerance to defeat replay attacks if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) throw new Error("stale"); const expected = crypto .createHmac("sha256", secret) .update(`${id}.${parts.t}.${req.rawBody}`) .digest("hex"); if (!crypto.timingSafeEqual(Buffer.from(parts.v1 ?? ""), Buffer.from(expected))) { throw new Error("bad signature"); } }

Or let @duabalabs/sellub-webhooks (0.4.0 or later) do it, typed event included: verifyAndParse({ secret, payload: rawBody, headers: req.headers }).

Delivery semantics

  • At-least-once — dedupe on Sellub-Event-Id.
  • Timeout — 10 seconds per attempt.
  • Retries — up to 6 attempts: 30s, 2m, 10m, 1h, 6h, 24h, then the delivery is marked failed.
  • Auto-disable — an endpoint whose last 5 deliveries all failed is switched off.
  • Delivery log — every attempt is listed per endpoint on the Integrate page.

Events

order.placed, order.state_changed, order.refunded, payment.succeeded, payment.failed, fulfillment.shipped, fulfillment.delivered, subscription.activated, subscription.renewed, subscription.cancelled, inventory.updated, inventory.low_stock, inventory.out_of_stock, stock.movement. Payload types are in @duabalabs/sellub-types.

Last updated on