> This guide uses the **[Glomo Unified SDK](/platform/sdk/unified-sdk)** (Subscription Checkout journey) — see it for the full set of journeys and options. If you integrated earlier with the older `checkout-sdk`, it continues to work — switch the import to the Unified SDK when convenient.


# Set up recurring subscriptions

## Steps to integrate the Subscription on checkout

## Step 1 : Create a Subscription in the Server

Use your server and Glomo secret keys to create a subscription via the Create Subscription API.

### API Endpoint

```curl
POST https://api.glomopay.com/api/v1/subscriptions
```

### Request

```http
curl -i -X POST \
  https://api.glomopay.com/api/v1/subscriptions \
  -H 'Authorization: Bearer <YOUR_JWT_HERE>' \
  -H 'Content-Type: application/json' \
  -d '{
    "customer_id": "cust_5JU9yv0lGSUP",
    "product_name": "ShieldGuard Insurance",
    "product_description": "Flexible, monthly insurance for belongings, travel, and digital assets; easy to manage",
    "plan_name": "ShieldGuard Lite",
    "plan_description": "Simple, monthly insurance plan that covers your basic belongings and key digital assets",
    "amount": 1000,
    "currency": "USD",
    "interval_type": "month",
    "interval_count": 1,
    "billing_cycles": 12,
    "start_date": "2025-01-01",
    "expires_at": "2025-01-07",
    "notify_customer": true,
    "notes": {
      "key1": "value1",
      "key2": "value2"
    },
    "reference_number": "R0001",
    "starts_with_first_payment": true
  }'
```

### Example: Create an "as_presented" Subscription

For `as_presented` subscriptions, `interval_count`, `billing_cycles`, and `start_date` must not be provided. The billing schedule is determined by the merchant presenting charges as needed. A `max_amount` field is required to cap the maximum amount per payment.

```http
curl -i -X POST \
  https://api.glomopay.com/api/v1/subscriptions \
  -H 'Authorization: Bearer <YOUR_JWT_HERE>' \
  -H 'Content-Type: application/json' \
  -d '{
    "customer_id": "cust_5JU9yv0lGSUP",
    "product_name": "ShieldGuard Insurance",
    "product_description": "Flexible insurance for belongings easy to manage",
    "plan_name": "ShieldGuard Lite",
    "plan_description": "As presented billing plan",
    "amount": 1000,
    "max_amount": 3000,
    "currency": "USD",
    "interval_type": "as_presented",
    "expires_at": "2026-02-01",
    "notify_customer": true,
    "starts_with_first_payment": true
  }'
```

### Response

```http
{
    "id": "sub_6979fa805EFmv",
    "status": "created",
    "start_date": null,
    "end_date": null,
    "billing_cycles": null,
    "next_payment_date": null,
    "expires_at": "2026-02-01",
    "subscription_link_url": "https://your-dashboard-host.example/subscription-links/sub_6979fa805EFmv",
    "customer_id": "cust_5JU9yv0lGSUP",
    "cancelled_at": null,
    "reference_number": null,
    "notes": {},
    "plan_name": "ShieldGuard Lite",
    "plan_description": "As presented billing plan",
    "product_name": "ShieldGuard Insurance",
    "product_description": "Flexible insurance for belongings easy to manage",
    "amount": 1000,
    "currency": "USD",
    "interval_type": "as_presented",
    "interval_count": null,
    "max_amount": 3000
}
```

You will receive an “**id**” in the response. This **id** should be passed to the Checkout SDK.

## Step 2 : Add a Subscribe Button on checkout

Add a "**Subscribe**" button to your website and integrate the Glomo Checkout SDK as shown below:

```html
<button id="buy-button" class="buy-button">Buy Now</button>
  <script type="module">
    import { GlomoCheckoutApi } from 'https://unified-sdk.glomopay.com/index.js';

    const checkout = new GlomoCheckoutApi({
      subscriptionId: 'sub_679a16457aP6K', // Pass the id of the subscription received from the server in step 1
      publicKey: 'live_687b0151Bid24PAI', // Pass the public key received from the dashboard
      callbackUrl: 'https://yourwebsite.com/payment-result', // Optional: URL to redirect after payment completion
    });

    document.getElementById('buy-button').addEventListener('click', () => {
      checkout.open();
    });

    checkout.on('payment.success', function (response) {
      // Handle payment success
    });

  checkout.on('payment.failure', function (response) {
      // Handle payment failure
    });
  </script>
```

### Notes:

- The code snippet will create a **checkout instance** and open the **checkout form** when the user clicks on the subscribe button.
- The **subscription** created via your server in **Step 1** should be passed to the checkout instance.
- Handle the payment success and failure events, in the handlers function you will get **payment object** or **error object** based on the payment status. Collect these and send them to your server and retry accordingly in case payment fails.
- If a **callbackUrl** is provided, users will be redirected to that URL after payment completion. In this case, the **success** and **failure** event handlers will not be triggered.


### About the Callback URL

When you provide a callbackUrl, the user will be automatically redirected to that URL after payment completion with the following parameters:

### For successful payments:

```curl
https://yourwebsite.com/payment-result?orderId=sub_679a16457aP6K&paymentId=payt_679a16457aP6K&status=success&signature=701eab675aca32c14cd1e5e041842d11ee9d04e91a3aaa73c629664a336b2952
```

### For failed payments:

```curl
https://yourwebsite.com/payment-result?orderId=sub_679a16457aP6K&paymentId=payt_679a16457aP6K&status=failed&errorCode=INVALID_CARD&errorMessage=Payment+failed+due+to+the+card+being+invalid%3B+please+use+a+different+card.
```

For a subscription, `orderId` holds your subscription ID. A failed redirect carries no `signature`, so confirm the payment on your server.

This allows server-side handling of payment results in addition to the client-side event handlers.

## Step 3: Verify checkout response and signature on the server

The `payment.success` handler receives `orderId`, `paymentId`, `status` and `signature`, with camelCase keys. For a subscription, `orderId` holds your subscription ID. Verify the signature on your server to confirm the payment is authentic. Sign `orderId|paymentId|status`, using the subscription ID you created in step 1. The signature is the hex HMAC-SHA256 of that string: compute it with each of your active secret keys, and accept the response if any one of them matches. Your active secret keys are every secret key on your account that has not expired. The signature may be made with any one of them, and it can differ between payments, so don't assume it is your newest key. Use a constant-time comparison. The `payment.failure` event is not signed.

When you revoke a key, or it expires, remove it from your list. A stale key left in the list opens no security hole, but keep the list matching the keys in your dashboard.

### Sample code to verify the signature

The sample loads your active secret keys from a comma-separated `GLOMO_SECRET_KEYS` setting in your config. It trims each key, drops empty entries, and stops with an error if no key is left. An empty key must never count as a match: anyone can compute the signature for an empty key, so it would let a forged response through.

```php
<?php
function load_secret_keys($value) {
    $keys = array_values(array_filter(array_map('trim', explode(',', (string) $value))));
    if (count($keys) === 0) {
        throw new RuntimeException('GLOMO_SECRET_KEYS is not set');
    }
    return $keys;
}

function verify_signature($order_id, $payment_id, $status, $signature, array $secret_keys) {
    $data = $order_id . "|" . $payment_id . "|" . $status;
    foreach ($secret_keys as $secret) {
        if ((string) $secret === '') {
            continue; // an empty key never matches
        }
        if (hash_equals(hash_hmac('sha256', $data, $secret), (string) $signature)) {
            return true;
        }
    }
    return false;
}

// Example Usage
$order_id = "sub_679a16457aP6K"; // your subscription ID from step 1
$payment_id = "payt_679a16457aP6K";
$status = "success";
$signature = "5f76c1b85e5214a124c163e757e83f9cb17d388fad5e657b3779d449c7ae89da"; // the signature you received
$secret_keys = load_secret_keys(getenv('GLOMO_SECRET_KEYS')); // every active secret key, e.g. "your_secret_key_1,your_secret_key_2"

var_dump(verify_signature($order_id, $payment_id, $status, $signature, $secret_keys));
?>
```

## Step 4: Verify payment status

Payment's status can be tracked in the following ways

- You can track the payments linked to your **sub_id** via **dashboard**.
- You can also track the payment status via polling the **[Payment APIs](/api-reference/openapi/payment/getpaymentbyid)**.
- You can also track the payment status via **[Webhooks](/platform/webhooks)** which will notify you about the payment status.