Skip to main content

Rendering the Checkout Widget

Once you have a sessionToken from your backend, you can launch the Surge widget on your frontend using our lightweight JavaScript SDK.

1. Include the SDK

Add the following script before the end of your <body> tag:
<script src="https://consumer.gosurge.xyz/surge.js"></script>

2. Initialize and Open

Call init() and then openCheckout() with the token obtained from your server.
import { useCallback } from 'react';

// Assumes the SDK script is already loaded in your index.html:
// <script src="https://consumer.gosurge.xyz/surge.js"></script>

export default function BuyNowPayLaterButton({ product }) {
  const handleClick = useCallback(async () => {
    // Get a session token from YOUR backend (never call the Surge API directly from the browser)
    const res = await fetch('/api/surge-checkout', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        amount: product.priceKobo,
        title: product.name,
        orderReference: product.orderRef,
      }),
    });
    const { sessionToken } = await res.json();

    const surge = window.SurgeConnect.init();
    surge.openCheckout({
      sessionToken,

      onSuccess: ({ paymentPlanId }) => {
        // ✅ Plan confirmed — NOT yet charged.
        // Show a "Processing" screen; wait for the webhook to fulfill the order.
        console.log('Plan created:', paymentPlanId);
      },

      onCancel: () => {
        console.log('Customer closed the widget.');
      },

      onError: (error) => {
        console.error('Surge error:', error.message);
      },
    });
  }, [product]);

  return <button onClick={handleClick}>Buy Now, Pay Later</button>;
}
Important: onSuccess fires when the customer confirms the installment plan, not when their card is charged. The initial deposit collection happens asynchronously. Always use Webhooks as the trigger to release goods.

Checking Customer Eligibility

This endpoint is called from the Surge widget / consumer app context, not from your merchant server. It requires a customer JWT (the Surge account token for the shopper, not your merchant token). Most merchant storefronts do not call this directly — the widget handles eligibility internally. Use this only if you are building a custom checkout experience where the customer is already logged into Surge.
Once a customer is authenticated with Surge, you can check their eligibility to surface or hide the BNPL option before opening the widget.

Endpoint

GET /api/v1/checkout/sessions/{token}/eligibility
Authorization: Bearer <CUSTOMER_JWT>

Response — Eligible

{
  "ok": true,
  "data": {
    "eligible": true,
    "reason": null,
    "customer_score": 724,
    "customer_tier": "Surge Silver",
    "required_tier": "Surge Bronze"
  }
}

Response — Ineligible

{
  "ok": true,
  "data": {
    "eligible": false,
    "reason": "no_payment_method",
    "customer_score": 500,
    "customer_tier": "Surge Bronze",
    "required_tier": "Surge Bronze"
  }
}
Possible reason values:
ReasonMeaning
no_payment_methodCustomer has no linked card or bank account
delinquentCustomer has an active missed payment
tier_restrictedCustomer’s Surge Score tier is below the merchant’s minimum
insufficient_trust_tierCustomer has not completed identity verification

Session Constraints

When you fetch a checkout session (GET /api/v1/checkout/sessions/{token}), the response includes a constraints object that defines what plan options are available for this merchant:
{
  "ok": true,
  "data": {
    "merchant_id": "mer_998877",
    "amount": 120000,
    "currency": "NGN",
    "title": "Apple AirPods Pro",
    "customer_email": "customer@example.com",
    "status": "pending",
    "expires_at": "2026-05-01T13:00:00Z",
    "constraints": {
      "allowed_frequencies": ["monthly", "weekly"],
      "max_weekly_duration": 12,
      "max_monthly_duration": 6,
      "min_upfront_pct": 20,
      "allowed_schedule_types": ["weekly", "bi_weekly", "monthly"]
    }
  }
}
The Surge widget reads these constraints automatically and only presents plan options that fall within the merchant’s configured limits. You do not need to enforce these yourself.