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

# Quickstart

> Integrate Surge Buy Now, Pay Later into your store in under 30 minutes. This guide takes you from zero to a working checkout.

## Step 1 — Get your API key

1. Sign up at [merchant.gosurge.xyz/register](https://merchant.gosurge.xyz/register).
2. After your account is approved, the Surge team enables API access for your account.
3. Log in to your merchant dashboard, go to **Settings → API Keys**, and click **Generate API Key**.
4. Copy the key immediately — it is shown once. Note your **Merchant ID** (`mer_...`) on the same page.

<Warning>
  Never hard-code your API key in source files. Use environment variables and never commit them to git.
</Warning>

***

## Step 2 — Create a checkout session (backend)

Your server calls Surge to create a short-lived checkout session tied to the order. Pass the session token to your frontend to open the widget.

<CodeGroup>
  ```javascript Node.js / Express theme={null}
  const express = require('express');
  const { v4: uuidv4 } = require('uuid');

  const app = express();

  // ⚠️ Register the webhook route BEFORE app.use(express.json())
  // so Express does not consume the raw body before signature verification.
  // The full handler is defined below — JavaScript hoists function declarations.
  app.post('/webhooks/surge', express.raw({ type: 'application/json' }), handleSurgeWebhook);

  // Global JSON middleware for all other routes
  app.use(express.json());

  // --- Checkout session endpoint ---
  // Called by your frontend when the customer clicks "Buy Now, Pay Later"
  app.post('/api/surge-checkout', async (req, res) => {
    const { amount, title, orderReference, customerEmail } = req.body;

    try {
      const idempotencyKey = uuidv4();

      const response = await fetch('https://api.gosurge.xyz/api/v1/checkout/sessions', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.SURGE_API_KEY}`,
          'Content-Type': 'application/json',
          'Idempotency-Key': idempotencyKey,
        },
        body: JSON.stringify({
          merchant_id: process.env.SURGE_MERCHANT_ID,
          amount,           // in kobo — e.g. 120000 = ₦1,200.00
          currency: 'NGN',
          title,
          order_reference: orderReference,
          customer_email: customerEmail,
        }),
      });

      if (!response.ok) {
        const error = await response.json();
        return res.status(response.status).json({ error: error.detail ?? 'Checkout failed' });
      }

      const { data } = await response.json();
      res.json({ sessionToken: data.session_token });

    } catch (err) {
      res.status(500).json({ error: 'Failed to create checkout session' });
    }
  });

  app.listen(3000);
  ```

  ```python Python / Flask theme={null}
  import os
  import uuid
  import requests
  from flask import Flask, request, jsonify

  app = Flask(__name__)

  # --- Checkout session endpoint ---
  @app.route('/api/surge-checkout', methods=['POST'])
  def surge_checkout():
      body = request.get_json()

      response = requests.post(
          'https://api.gosurge.xyz/api/v1/checkout/sessions',
          headers={
              'Authorization': f'Bearer {os.environ["SURGE_API_KEY"]}',
              'Content-Type': 'application/json',
              'Idempotency-Key': str(uuid.uuid4()),
          },
          json={
              'merchant_id': os.environ['SURGE_MERCHANT_ID'],
              'amount': body['amount'],   # in kobo
              'currency': 'NGN',
              'title': body['title'],
              'order_reference': body['order_reference'],
              'customer_email': body.get('customer_email'),
          }
      )

      data = response.json()
      if not response.ok:
          return jsonify({'error': data.get('detail')}), response.status_code

      return jsonify({'sessionToken': data['data']['session_token']})
  ```
</CodeGroup>

**Environment variables you need:**

```bash .env theme={null}
SURGE_API_KEY=surge_live_sk_...
SURGE_MERCHANT_ID=mer_...
SURGE_WEBHOOK_SECRET=whsec_...
```

<Note>
  **All amounts are in kobo** (Nigerian lowest denomination). ₦1,200.00 = `120000` kobo. Never pass naira values directly — divide by 100 to display to the customer.
</Note>

***

## Step 3 — Open the widget (frontend)

Once your server returns the `sessionToken`, open the Surge checkout modal.

<CodeGroup>
  ```jsx React theme={null}
  import { useCallback } from 'react';

  // Add this once to your index.html:
  // <script src="https://consumer.gosurge.xyz/surge.js"></script>

  export default function BuyNowPayLaterButton({ product }) {
    const handleSurgeCheckout = useCallback(async () => {
      // 1. Ask your backend to create a checkout session
      const res = await fetch('/api/surge-checkout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          amount: product.priceKobo,       // e.g. 120000 = ₦1,200
          title: product.name,
          orderReference: `ORD-${Date.now()}`,
          customerEmail: product.customerEmail,
        }),
      });

      const { sessionToken, error } = await res.json();
      if (error || !sessionToken) {
        alert('Could not start checkout. Please try again.');
        return;
      }

      // 2. Open the Surge widget
      const surge = window.SurgeConnect.init();
      surge.openCheckout({
        sessionToken,

        onSuccess: ({ paymentPlanId }) => {
          // Plan confirmed — NOT yet charged. Show a pending state.
          window.location.href = `/orders/pending?ref=${paymentPlanId}`;
        },

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

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

    return (
      <button onClick={handleSurgeCheckout}>
        Buy Now, Pay Later with Surge
      </button>
    );
  }
  ```

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

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

  // Safely load the Surge SDK and track when it's ready
  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 CheckoutButton({ 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]);

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

  ```html Vanilla JavaScript theme={null}
  <!-- Include the SDK -->
  <script src="https://consumer.gosurge.xyz/surge.js"></script>

  <button id="surge-btn">Buy Now, Pay Later</button>

  <script>
    document.getElementById('surge-btn').addEventListener('click', async () => {
      const res = await fetch('/api/surge-checkout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          amount: 120000,   // 120000 kobo = ₦1,200.00
          title: 'Apple AirPods Pro',
          orderReference: 'ORD-' + Date.now(),
        }),
      });

      const { sessionToken } = await res.json();

      const surge = SurgeConnect.init();
      surge.openCheckout({
        sessionToken,
        onSuccess: ({ paymentPlanId }) => {
          console.log('Plan confirmed:', paymentPlanId);
        },
        onCancel: () => console.log('Cancelled'),
        onError: (err) => console.error(err.message),
      });
    });
  </script>
  ```
</CodeGroup>

<Warning>
  `onSuccess` fires when the customer **confirms the plan**, not when their deposit is charged. Always use the `payment.success` webhook (Step 4) as your trigger to fulfill the order.
</Warning>

***

## Step 4 — Listen for webhooks

Surge sends a `payment.success` event to your configured webhook URL after the deposit is collected and the plan is activated.

### Configure your webhook URL

In your merchant dashboard go to **Settings → Webhooks** and enter your endpoint URL. Set a strong random string as your **Webhook Secret** and save it to your `SURGE_WEBHOOK_SECRET` env var.

### Handle the event

```javascript Node.js theme={null}
// ⚠️ This function must be declared BEFORE app.use(express.json()) is called,
// and the route is registered at the top of the file (see Step 2).
// JavaScript function declarations are hoisted, so this works even though
// it appears later in the file.

const crypto = require('crypto');

function handleSurgeWebhook(req, res) {
  const signature = req.headers['x-surge-signature'];
  const expected = crypto
    .createHmac('sha256', process.env.SURGE_WEBHOOK_SECRET)
    .update(req.body)  // req.body is a Buffer when using express.raw()
    .digest('hex');

  // timingSafeEqual throws if buffers differ in length, so check first
  const sigBuf = Buffer.from(signature ?? '');
  const expBuf = Buffer.from(expected);
  if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(expBuf, sigBuf)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body.toString());

  if (event.eventType === 'payment.success') {
    const { orderReference, resourceId } = event.data;
    // ✅ Safe to fulfill the order now
    fulfillOrder(orderReference, resourceId);
  }

  res.status(200).send('OK');
}
```

<Note>
  Surge signs outbound webhooks with **HMAC-SHA256** using your `webhook_secret`. The header is `X-Surge-Signature`. Return `200 OK` immediately and process fulfillment asynchronously to avoid retry storms.
</Note>

See the [Webhooks guide](/docs/webhooks) for all event types and retry behaviour.

***

## You're done

| Step   | What you built                                  |
| :----- | :---------------------------------------------- |
| Step 1 | Merchant account + API key                      |
| Step 2 | Server endpoint that creates a checkout session |
| Step 3 | Frontend button that opens the Surge widget     |
| Step 4 | Webhook handler to fulfill orders after payment |

**Next steps:**

* [Check customer eligibility](/docs/client-integration#checking-customer-eligibility) before showing the button
* [Create payment links](/docs/server-integration#payment-links) for WhatsApp / social selling
* [Handle payment methods](/docs/payment-methods) for returning customers
