1. Quick start #
If you already have a checkout page with a payment button, you can be running in sandbox in under five minutes. The SDK auto-mounts a widget, handles the opt-in toggle, fee display and entry recording — you keep your existing payment gateway.
<script src="https://cdn.gbemidebe.ng/sdk/v1/gbemidebe-sdk.min.js"></script> <script> const gbemidebe = Gbemidebe.init({ partnerId: "PSP-123", apiKey: "gbemidebe_test_...", environment: "sandbox", checkout: { summaryContainer: "#order-summary", totalAmount: "#total-amount", payButton: "#pay-button" }, purchaseAmount: 500000, // 5,000 NGN in kobo country: "NG", currency: "NGN" }); </script>
Need a sandbox key? Email partnerships@gbemidebe.ng and we’ll issue one within a business day.
2. Installation #
Pick the channel that fits your build pipeline. The CDN is recommended for the fastest start; npm is recommended if you bundle JS through Webpack, Vite, esbuild or similar.
<script src="https://cdn.gbemidebe.ng/sdk/v1/gbemidebe-sdk.min.js" defer></script>
npm install @gbemidebe/web
yarn add @gbemidebe/web
pnpm add @gbemidebe/web
CDN versioning
Pin to a major version (/sdk/v1/) to receive patch and minor updates automatically. To pin to an exact version (e.g. for compliance audits), use the immutable path:
<script src="https://cdn.gbemidebe.ng/sdk/v1.2.0/gbemidebe-sdk.min.js"></script>
3. Initialization #
Call Gbemidebe.init() once per page load, after the DOM containing your checkout selectors is available.
import { init } from "@gbemidebe/web"; const gbemidebe = init({ partnerId: "PSP-123", apiKey: "gbemidebe_live_...", environment: "production", language: "auto", // "auto" | "en" | "fr" | "es" flowType: "non_psp_customer", // or "psp_customer" checkout: { summaryContainer: "#order-summary", totalAmount: "#total-amount", payButton: "#pay-button" }, purchaseAmount: 500000, country: "NG", currency: "NGN", onReady: () => console.log("Gbemidebe ready"), onOptIn: (data) => console.log("Opted in", data), onOptOut: () => console.log("Opted out"), onError: (err) => console.error(err) });
Parameters #
| Field | Required | Description |
|---|---|---|
partnerId | Yes | PSP identifier assigned by Gbemidebe during onboarding. |
apiKey | Yes | Public, domain-locked API key. Use the gbemidebe_test_ prefix in sandbox, gbemidebe_live_ in production. |
environment | Yes | "sandbox" or "production". |
country | Yes | ISO 3166-1 alpha-2. Drives cycle resolution (Global → Market → PSP-in-Market). |
currency | Yes | ISO 4217 currency code. |
purchaseAmount | Yes | Amount in minor units (kobo, cents, pence…). |
checkout | Yes | CSS selectors for your summary container, total element and pay button. |
flowType | No | "psp_customer" (logged-in PSP user) or "non_psp_customer" (guest, requires phone/email). Default: non_psp_customer. |
language | No | "auto" (default), "en", "fr" or "es". |
debug | No | Set to true to enable verbose logging. |
Environments #
Sandbox is fully isolated — no live money moves, draws are deterministic, and you can replay any pot. Production keys are issued after merchant KYC is approved.
| Environment | API host | CDN | Funds |
|---|---|---|---|
| Sandbox | api.sandbox.gbemidebe.ng | cdn.gbemidebe.ng/sdk/v1/ | Test only |
| Production | api.gbemidebe.ng | cdn.gbemidebe.ng/sdk/v1/ | Live |
4. Auto-integration mode #
The simplest integration. Provide selectors for your summary, total and pay button — the SDK does the rest. It injects the opt-in widget, updates the displayed total when the user opts in, intercepts the pay button click to record the entry, and dispatches a custom event once the entry is confirmed.
When to use: standard HTML / JS checkouts where you can expose the total amount and pay button via CSS selectors.
<div id="order-summary"> <div>Subtotal: ₦5,000</div> <div>Total: <span id="total-amount">₦5,000</span></div> </div> <button id="pay-button">Pay now</button> <script> const gbemidebe = Gbemidebe.init({ /* see above */ }); // Fired once the entry is recorded & payment can proceed document.addEventListener("gbemidebe:entry-recorded", (e) => { paymentGateway.charge({ amount: e.detail.totalAmount, // includes Gbemidebe fee metadata: { gbemidebe_entry_id: e.detail.entryId } }); }); </script>
5. SDK-first flow #
For full control over the payment lifecycle, drive the flow explicitly: record a pending entry, run your payment, then confirm or fail the entry based on the result. This is the recommended path for custom checkouts and multi-step flows.
const gbemidebe = Gbemidebe.init({ partnerId, apiKey, environment: "sandbox" }); await gbemidebe.mount("#gbemidebe-widget", { country: "NG", currency: "NGN", purchaseAmount: 500000 }); document.getElementById("pay-button").addEventListener("click", async () => { if (!gbemidebe.isOptedIn()) { return processPayment(originalAmount); } // 1. Record pending entry const entry = await gbemidebe.recordEntry(`TXN-` + Date.now()); if (!entry.success) return showError("Could not enter the draw"); try { // 2. Run your payment const result = await processPayment(totalAmount, { gbemidebe_entry_id: entry.entryId }); // 3a. Confirm on success await gbemidebe.confirmPayment("success", result.transactionId); } catch (err) { // 3b. Fail on error (refund-safe) await gbemidebe.confirmPayment("failed", null, err.message); } });
recordEntry(transactionId) #
Reserves a slot in the active pot before payment. Entries live for 30 minutes in pending state — if you don’t confirm by then, the entry is auto-expired and excluded from draws.
| Returns | Description |
|---|---|
success | Boolean. false indicates a session or validation issue — inspect error. |
entryId | Server-issued entry identifier. Forward to your PSP as transaction metadata so the entry can be matched on webhook receipt. |
status | Always "pending" on success. |
transactionId | Echoes the ID you provided. Idempotency key — calling twice with the same ID is safe. |
confirmPayment(status, txnId?, reason?) #
Transitions the pending entry to valid or failed. Idempotent — calling twice returns the same result, so it is safe to retry on network errors.
| Status | Outcome |
|---|---|
"success" | Entry becomes valid and joins the pot. Draw is scheduled per cycle config. |
"failed" | Entry is excluded from draws. No fee is captured. |
Always call confirmPayment() — even on failure. This keeps your pending-entry ledger clean and unblocks accurate redemption analytics.
6. Events #
Listen to lifecycle events to drive analytics, custom UI, or downstream services.
gbemidebe.on("ready", () => {}); gbemidebe.on("optin", (d) => analytics.track("gbemidebe_optin", d)); gbemidebe.on("optout", () => {}); gbemidebe.on("feeUpdated", (fee) => {}); gbemidebe.on("entry", (d) => console.log(d.entryId, d.status)); gbemidebe.on("error", (err) => console.error(err.code, err.message));
7. Theming #
The default widget inherits sensible neutrals that match most checkouts. Override via runtime tokens or CSS classes.
Runtime tokens
gbemidebe.setTheme({ colorPrimary: "#2b3a1e", colorAccent: "#d4ff4a", colorError: "#d32f2f", fontFamily: "Inter, sans-serif", borderRadius: "12px", toggleStyle: "switch" // "switch" | "checkbox" });
CSS classes
For deeper control, target the scoped classes the widget renders — they are part of the public contract.
| Class | Element |
|---|---|
.gbemidebe-widget | Root container |
.gbemidebe-toggle | Opt-in switch / checkbox |
.gbemidebe-fee-display | Configured fee amount |
.gbemidebe-contact-input | Phone / email fields (Scenario B) |
.gbemidebe-terms-link | Localized compliance text |
.gbemidebe-error | Inline validation messages |
8. Security model #
The SDK is the only client that talks to Gbemidebe. Your PSP backend never calls the Gbemidebe API directly — it only receives signed webhooks. This narrows the trust surface and removes signing keys from your application code.
What the SDK does NOT do
- Process payments or touch card data
- Persist PII in
localStorageor cookies - Communicate with your PSP servers
- Ship third-party trackers
Signatures & tokens
| Mechanism | Purpose |
|---|---|
| Session token | Domain-locked, rotating, ephemeral. Proves the request originated from a valid SDK session. |
| Fee signature | HMAC-SHA256 over the fee + currency + cycle. Prevents fee tampering in transit. |
| Metadata signature | HMAC-SHA256 over the full payload. Prevents fake opt-ins reaching the Gbemidebe webhook handler. |
| Timestamp | Signatures expire after 15 minutes. Call gbemidebe.refreshMetadata() if your checkout sits idle. |
| TLS 1.3 + cert pinning | SDK ↔ Gbemidebe Backend channel hardened against MITM. |
Webhook verification
Gbemidebe will POST a signed payload to your registered webhook URL when an entry wins. Verify the X-GBEMIDEBE-Signature header before acting on the payload.
$payload = $request->getContent(); $signature = $request->header("X-GBEMIDEBE-Signature"); $expected = hash_hmac("sha256", $payload, $webhookSecret); if (!hash_equals($expected, $signature)) { abort(401, "Invalid signature"); }
9. Mobile SDKs #
Same API surface, native UI. Both packages wrap the shared TypeScript core, so behaviour matches the web SDK exactly.
import { GbemidebeWidget, useGbemidebe } from "@gbemidebe/react-native"; function Checkout() { const { metadata, isOptedIn } = useGbemidebe({ partnerId: "PSP-123", apiKey: "gbemidebe_test_...", country: "NG", currency: "NGN", amount: 5000 }); return ( <View> <GbemidebeWidget /> <Button title="Pay" onPress={() => handlePayment(metadata)} /> </View> ); }
import 'package:gbemidebe_sdk/gbemidebe_sdk.dart'; class CheckoutPage extends StatefulWidget { @override Widget build(BuildContext context) { return Column(children: [ GbemidebeWidget( partnerId: "PSP-123", apiKey: "gbemidebe_test_...", country: "NG", currency: "NGN", amount: 5000, onMetadataReady: (m) => _metadata = m ), ElevatedButton(onPressed: processPayment, child: Text("Pay")) ]); } }
10. Error handling #
All errors surface through the onError callback and the "error" event. Codes are stable and safe to switch on.
| Code | Cause | Resolution |
|---|---|---|
SDK_INIT_FAILED | Initialization failed | Verify partnerId, apiKey and network access. |
INVALID_PROVIDER | Unknown partnerId | Confirm with Gbemidebe onboarding. |
INVALID_CURRENCY | Currency not supported in market | Check the country × currency matrix in the dashboard. |
INVALID_COUNTRY | Country not enabled for your account | Request market activation via partnerships. |
FEE_CONFIG_ERROR | No cycle configured | Set a Global / Market / PSP-in-Market cycle. |
VALIDATION_ERROR | Phone / email invalid | Display the inline error — SDK already does this. |
SESSION_EXPIRED | Signatures older than 15 minutes | Call gbemidebe.refreshMetadata() before retrying. |
ENTRY_EXPIRED | 30 minutes since recordEntry() | Call recordEntry() again with a fresh transaction ID. |
NETWORK_ERROR | Connection to Gbemidebe failed | Retry with exponential backoff; the widget shows a disabled state. |
11. Go-live checklist #
- Sandbox API key issued and stored in your secrets manager.
- SDK loaded via CDN or installed via npm; version pinned for production.
partnerId,countryandcurrencywired up correctly per market.- Opt-in widget renders inside checkout flow; total updates on toggle.
- Pay button calls
recordEntry()(SDK-first) or auto-integration is configured. confirmPayment()called for both success and failure paths.- Webhook endpoint registered, verified with
X-GBEMIDEBE-Signature. - Error handler displays user-facing messages for
SESSION_EXPIREDandNETWORK_ERROR. - Sandbox transaction tested end-to-end with a deterministic Mode 1 (First-to-Fill) draw.
- Production key issued after merchant KYC approval.
Ready to go live?
30-minute call. Sandbox keys the same day. Live in your product next week.