> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gosurge.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Client Integration

> Learn how to render the Surge checkout widget on your frontend using our JavaScript SDK.

## 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:

```html theme={null}
<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.

<CodeGroup>
  ```jsx React theme={null}
  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>;
  }
  ```

  ```tsx Next.js (App Router) theme={null}
  'use client';

  import { useEffect, useRef, useCallback } from 'react';

  // Dynamically load the SDK and track when it is ready (avoids SSR issues
  // and prevents calling window.SurgeConnect before the script has loaded)
  function useSurgeSDK() {
    const ready = useRef(false);

    useEffect(() => {
      if (typeof window === 'undefined') return;
      if ((window as any).SurgeConnect) { ready.current = true; return; }

      const script = document.createElement('script');
      script.src = 'https://consumer.gosurge.xyz/surge.js';
      script.async = true;
      script.onload = () => { ready.current = true; };
      document.body.appendChild(script);
    }, []);

    return ready;
  }

  export default function BuyNowPayLaterButton({ priceKobo, title, orderRef }: {
    priceKobo: number;
    title: string;
    orderRef: string;
  }) {
    const sdkReady = useSurgeSDK();

    const handleClick = useCallback(async () => {
      if (!sdkReady.current) {
        alert('Checkout is still loading — please try again in a moment.');
        return;
      }

      const res = await fetch('/api/surge-checkout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ amount: priceKobo, title, orderReference: orderRef }),
      });
      const { sessionToken } = await res.json();

      const surge = (window as any).SurgeConnect.init();
      surge.openCheckout({
        sessionToken,
        onSuccess: ({ paymentPlanId }: { paymentPlanId: string }) => {
          window.location.href = `/orders/pending?plan=${paymentPlanId}`;
        },
        onCancel: () => {},
        onError: (err: { message: string }) => console.error(err.message),
      });
    }, [priceKobo, title, orderRef, sdkReady]);

    return <button onClick={handleClick}>Pay in installments</button>;
  }
  ```

  ```javascript Vanilla JS theme={null}
  // Initialize the SDK
  const surge = SurgeConnect.init();

  // Open the checkout modal
  surge.openCheckout({
    sessionToken: "fcs_7a2b9d1e...", // The token from your backend

    onSuccess: ({ paymentPlanId }) => {
      // ✅ Plan confirmed — show a pending/processing state.
      console.log("Plan created:", paymentPlanId);
    },

    onCancel: () => {
      console.log("User closed the checkout widget.");
    },

    onError: (error) => {
      console.error("Surge Error:", error.message);
    }
  });
  ```
</CodeGroup>

<Warning>
  **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.
</Warning>

***

## Checking Customer Eligibility

<Note>
  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.
</Note>

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

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

### Response — Ineligible

```json theme={null}
{
  "ok": true,
  "data": {
    "eligible": false,
    "reason": "no_payment_method",
    "customer_score": 500,
    "customer_tier": "Surge Bronze",
    "required_tier": "Surge Bronze"
  }
}
```

**Possible `reason` values:**

| Reason                    | Meaning                                                     |
| :------------------------ | :---------------------------------------------------------- |
| `no_payment_method`       | Customer has no linked card or bank account                 |
| `delinquent`              | Customer has an active missed payment                       |
| `tier_restricted`         | Customer's Surge Score tier is below the merchant's minimum |
| `insufficient_trust_tier` | Customer 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:

```json theme={null}
{
  "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.
