---
title: Set Up Subscriptions & Recurring Payments
slug: use-cases/setup-subscriptions-recurring
excerpt: >-
  Automate billing cycles for SaaS, memberships, and services using Pine Labs
  Online subscriptions.
sidebar_order: 4
hidden: false
metadata:
  title: Subscriptions & Recurring Payments — Automate Billing | Pine Labs
  description: >-
    Automate recurring billing with Pine Labs subscription APIs. Support fixed
    and flexible billing cycles, UPI mandates, SaaS subscriptions, membership
    renewals, EMI collection.
  keywords: >-
    subscriptions API, recurring payments, automated billing, UPI mandates, SaaS
    billing, membership subscriptions, recurring collections
  robots: index
---
Automate billing cycles for SaaS platforms, memberships, subscriptions, EMI collections, and recurring services using Pine Labs Online subscription APIs.

Pine Labs Online enables businesses to securely collect recurring payments using UPI mandates with support for fixed billing cycles, flexible recurring collections, automated payment authorization, and low-friction customer payment experiences.

---

## Subscription models supported

Pine Labs Online supports two subscription models based on billing frequency and payment flexibility.

<CardGrid>
<Card
title="Fixed Frequency Subscription"
icon="credit-card"
>

Collect a fixed amount at predefined intervals such as daily, weekly, monthly, quarterly, or yearly.

**Best for:**
- SaaS subscriptions
- Membership renewals
- EMI collections
- Insurance payments
- Education platforms

</Card>

<Card
title="Variable Frequency Subscription"
icon="refresh-cw"
>

Allow customers to make recurring payments flexibly while maintaining consistent pricing.

**Best for:**
- Wallet top-ups
- Usage-based billing
- Postpaid collections
- Flexible service renewals
- Customer-controlled payments

</Card>

</CardGrid>

---

## Mandate types supported

A mandate is a customer authorization that allows merchants to debit payments based on agreed billing terms.

<CardGrid>

<Card
title="One Time Mandate"
icon="credit-card"
>

Authorize a single payment transaction. Mandate expires after one debit.

**Use cases:**
- Security deposits
- One-time invoices
- Booking payments
- Service fees

</Card>

<Card
title="On Demand Mandate"
icon="zap"
>

Initiate debit transactions whenever required within the mandate validity period.

**Use cases:**
- Utility bills
- Credit repayments
- Usage-based billing
- Postpaid services

</Card>

<Card
title="Recurring Mandate"
icon="refresh-cw"
>

Automatically process recurring payments at fixed intervals for fixed or variable amounts.

**Use cases:**
- Monthly subscriptions
- Membership fees
- EMI payments
- Recurring service billing

</Card>

</CardGrid>

<Callout type="info" title="Integration modes">
Pine Labs Online supports both subscription models through **Redirect Checkout** for faster implementation and **Custom Checkout** for full control over the checkout UI.
</Callout>

---

## Subscription integration workflow

```mermaid
flowchart LR
    A[Generate Access Token] --> B[Create Plan]
    B --> C[Create Subscription]
    C --> D[Create Payment]
    D --> E[Customer Authorizes via UPI]
    E --> F[Verify Payment Signature]
    F --> G[Subscription Activated]

    style G fill:#16a34a,color:#fff,stroke:#15803d
```

---

## Before you begin

1. **Create a Pine Labs Online merchant account** — [Sign up](https://dashboardv2.pluralonline.com/signup) and enable API access.
2. **Generate API credentials** — Copy your Client ID and Client Secret from the [Dashboard → Settings](doc:dashboard-settings).
3. **Set up a backend server** — Required for secure API calls and webhook handling.
4. **Enable subscriptions** — Contact your account manager to enable subscriptions on your merchant account.

<EnvTable rows={[{ env: "UAT (Testing)", url: "https://pluraluat.v2.pinepg.in", badge: "sandbox" }, { env: "Production", url: "https://api.pluralpay.in" }]} />

---

## Step 1 — Generate access token

Pine Labs Online payment APIs use bearer tokens to authenticate. Generate an `access_token` from your backend.

<CodeTabs>
<Tab label="cURL - UAT">
```bash
curl --request POST \
--url https://pluraluat.v2.pinepg.in/api/auth/v1/token \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--data '{
  "client_id": "<your-client-id>",
  "client_secret": "<your-client-secret>",
  "grant_type": "client_credentials"
}'
```
</Tab>
<Tab label="cURL - PROD">
```bash
curl --request POST \
--url https://api.pluralpay.in/api/auth/v1/token \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--data '{
  "client_id": "<your-client-id>",
  "client_secret": "<your-client-secret>",
  "grant_type": "client_credentials"
}'
```
</Tab>
<Tab label="Response">
```json
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": 3600
}
```
</Tab>
</CodeTabs>

<Callout type="info" title="Note">
Use the `access_token` received to authenticate all subsequent Pine Labs Online API requests.
</Callout>

---

## Step 2 — Create a plan

Define your subscription plan with pricing, billing interval, and mandate limits.

<CodeTabs>
<Tab label="cURL - UAT">
```bash
curl --request POST \
--url https://pluraluat.v2.pinepg.in/api/v1/public/plans \
--header 'Authorization: Bearer <access_token>' \
--header 'Content-Type: application/json' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--data '{
  "plan_name": "Monthly Plan",
  "plan_description": "Monthly subscription plan",
  "frequency": "Month",
  "amount": {
    "value": 100,
    "currency": "INR"
  },
  "max_limit_amount": {
    "value": 210,
    "currency": "INR"
  },
  "initial_debit_amount": {
    "value": 110,
    "currency": "INR"
  },
  "trial_period_in_days": 0,
  "end_date": "2026-10-21T12:02:28Z",
  "merchant_metadata": {
    "key1": "DD"
  },
  "merchant_plan_reference": "<your-plan-reference>",
  "auto_debit_ot": false
}'
```
</Tab>
<Tab label="cURL - PROD">
```bash
curl --request POST \
--url https://api.pluralpay.in/api/v1/public/plans \
--header 'Authorization: Bearer <access_token>' \
--header 'Content-Type: application/json' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--data '{
  "plan_name": "Monthly Plan",
  "plan_description": "Monthly subscription plan",
  "frequency": "Month",
  "amount": {
    "value": 100,
    "currency": "INR"
  },
  "max_limit_amount": {
    "value": 210,
    "currency": "INR"
  },
  "initial_debit_amount": {
    "value": 110,
    "currency": "INR"
  },
  "trial_period_in_days": 0,
  "end_date": "2026-10-21T12:02:28Z",
  "merchant_metadata": {
    "key1": "DD"
  },
  "merchant_plan_reference": "<your-plan-reference>",
  "auto_debit_ot": false
}'
```
</Tab>
<Tab label="Response">
```json
{
  "plan_id": "v1-pla-250612061639-aa-666Zge",
  "status": "ACTIVE",
  "plan_name": "Monthly Plan",
  "plan_description": "Monthly subscription plan",
  "frequency": "Month",
  "amount": {
    "value": 100,
    "currency": "INR"
  },
  "max_limit_amount": {
    "value": 210,
    "currency": "INR"
  },
  "trial_period_in_days": 0,
  "start_date": "2025-06-12T06:16:39.927Z",
  "end_date": "2026-10-21T12:02:28Z",
  "merchant_metadata": {
    "key1": "DD"
  },
  "merchant_plan_reference": "b41770ac-cd3a-48d3-9e93-7dc9246b4751",
  "created_at": "2025-06-12T06:16:39.933Z",
  "modified_at": "2025-06-12T06:16:39.933Z",
  "initial_debit_amount": {
    "value": 110,
    "currency": "INR"
  },
  "auto_debit_ot": false
}
```
</Tab>
</CodeTabs>

---

## Step 3 — Create a subscription

Use the `plan_id` from the Create Plan response to create a subscription for a customer.

<CodeTabs>
<Tab label="cURL - UAT">
```bash
curl --request POST \
--url https://pluraluat.v2.pinepg.in/api/v1/public/subscriptions \
--header 'Authorization: Bearer <access_token>' \
--header 'Content-Type: application/json' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--data '{
  "merchant_subscription_reference": "<your-subscription-reference>",
  "plan_id": "<plan_id>",
  "enable_notification": true,
  "start_date": "2025-06-12T06:22:21Z",
  "end_date": "2025-06-21T17:32:28Z",
  "customer_id": "<customer_id>",
  "allowed_payment_methods": ["UPI"],
  "integration_mode": "REDIRECT",
  "merchant_metadata": {
    "key1": "DD",
    "key2": "XOF"
  },
  "is_tpv_enabled": false,
  "callback_url": "https://your-domain.com/success",
  "failure_callback_url": "https://your-domain.com/failure"
}'
```
</Tab>
<Tab label="cURL - PROD">
```bash
curl --request POST \
--url https://api.pluralpay.in/api/v1/public/subscriptions \
--header 'Authorization: Bearer <access_token>' \
--header 'Content-Type: application/json' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--data '{
  "merchant_subscription_reference": "<your-subscription-reference>",
  "plan_id": "<plan_id>",
  "enable_notification": true,
  "start_date": "2025-06-12T06:22:21Z",
  "end_date": "2025-06-21T17:32:28Z",
  "customer_id": "<customer_id>",
  "allowed_payment_methods": ["UPI"],
  "integration_mode": "REDIRECT",
  "merchant_metadata": {
    "key1": "DD",
    "key2": "XOF"
  },
  "is_tpv_enabled": false,
  "callback_url": "https://your-domain.com/success",
  "failure_callback_url": "https://your-domain.com/failure"
}'
```
</Tab>
<Tab label="Response">
```json
{
  "callback_url": "https://your-domain.com/success",
  "failure_callback_url": "https://your-domain.com/failure",
  "redirect_url": "https://api.pluralonline.com/api/v3/checkout-bff/redirect/checkout?token=...",
  "order_id": "v1-250612062122-aa-UZyPbu",
  "subscription_id": "v1-sub-250612062122-aa-3HLXL5",
  "merchant_subscription_reference": "f5d77ef9-fa11-4f58-a9f4-543e4a7022c5",
  "plan_details": {
    "plan_id": "v1-pla-250612061639-aa-666Zge",
    "status": "ACTIVE",
    "plan_name": "Monthly Plan",
    "frequency": "Month",
    "amount": {
      "value": 100,
      "currency": "INR"
    },
    "max_limit_amount": {
      "value": 210,
      "currency": "INR"
    }
  },
  "status": "CREATED",
  "integration_mode": "REDIRECT",
  "order_amount": {
    "value": 210,
    "currency": "INR"
  }
}
```
</Tab>
</CodeTabs>

<Callout type="info" title="Redirect URL">
The response includes a `redirect_url` — redirect the customer to this URL to complete their first payment and authorize recurring charges.
</Callout>

---

## Step 4 — Create payment

Use the `order_id` from the Create Subscription response to create a payment.

<CodeTabs>
<Tab label="cURL - UAT">
```bash
curl --request POST \
--url https://pluraluat.v2.pinepg.in/api/pay/v1/orders/{order_id}/payments \
--header 'Authorization: Bearer <access_token>' \
--header 'Content-Type: application/json' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--data '{
  "payments": [
    {
      "payment_method": "UPI",
      "merchant_payment_reference": "<your-payment-reference>",
      "payment_amount": {
        "value": 100,
        "currency": "INR"
      },
      "payment_option": {
        "upi_details": {
          "txn_mode": "INTENT"
        }
      },
      "mandate_info": {
        "request_type": "CREATE_MANDATE"
      }
    }
  ]
}'
```
</Tab>
<Tab label="cURL - PROD">
```bash
curl --request POST \
--url https://api.pluralpay.in/api/pay/v1/orders/{order_id}/payments \
--header 'Authorization: Bearer <access_token>' \
--header 'Content-Type: application/json' \
--header 'Request-ID: c17ce30f-f88e-4f81-ada1-c3b4909ed235' \
--header 'Request-Timestamp: 2024-07-09T07:57:08.022Z' \
--data '{
  "payments": [
    {
      "payment_method": "UPI",
      "merchant_payment_reference": "<your-payment-reference>",
      "payment_amount": {
        "value": 100,
        "currency": "INR"
      },
      "payment_option": {
        "upi_details": {
          "txn_mode": "INTENT"
        }
      },
      "mandate_info": {
        "request_type": "CREATE_MANDATE"
      }
    }
  ]
}'
```
</Tab>
<Tab label="Response">
```json
{
  "data": {
    "order_id": "v1-250408102515-aa-x2F8Qw",
    "status": "PENDING",
    "challenge_url": "upi://mandate?pa=PinelabsUat1@icici&pn=PinelabsUat&tr=EZM2025032516184000196272&am=1000.00&cu=INR&orgid=400011&mc=5732&purpose=01&tn=remark&validitystart=25032025&validityend=21062025&amrule=EXACT&Recur=ONETIME&Rev=Y&Share=Y&Block=Y&txnType=CREATE&mode=13",
    "payments": [
      {
        "id": "v1-250408102515-aa-x2F8Qw-up-c",
        "status": "PENDING",
        "payment_method": "UPI",
        "payment_option": {
          "upi_data": {
            "txn_mode": "INTENT"
          }
        },
        "mandate_info": {
          "request_type": "CREATE_MANDATE"
        }
      }
    ]
  }
}
```
</Tab>
</CodeTabs>

<Callout type="warning" title="Important">

- The `payment_amount.value` must match the `max_limit_amount.value` from the Create Plan API.
- Currently, subscriptions are supported only through **UPI payments** (UPI Intent or UPI Collect flow).
- The `challenge_url` in the response is applicable only for UPI Intent flow. For UPI Collect, the customer receives a notification in their UPI app.

</Callout>

---

## Step 5 — Handle payment callback

After the customer completes or fails payment, Pine Labs returns the result to your callback URL.

<CodeTabs>
<Tab label="Success Callback">
```json
{
  "order_id": "v1-4405071524-aa-qlAtAf",
  "status": "AUTHORIZED",
  "signature": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
}
```
</Tab>
<Tab label="Failure Callback">
```json
{
  "order_id": "v1-4405071524-aa-qlAtAf",
  "status": "AUTHORIZED",
  "error_code": "USER_AUTHENTICATION_REQUIRED",
  "error_message": "Consumer Authentication Required",
  "signature": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
}
```
</Tab>
</CodeTabs>

### Verify payment signature

Verify the authenticity of the payment details returned to your callback URL using SHA256.

**Parameters used for signature verification:**
- `order_id` — Unique order identifier
- `payment_status` — Payment status
- `error_code` — Short error code (if applicable)
- `error_message` — Error message (if applicable)
- `secret_key` — Provided during onboarding

If the signature generated on your server matches the Pine Labs signature returned in the callback URL, it confirms that the payment details are authentic.

<Callout type="warning" title="Mandatory">
Signature verification is a mandatory step. Always verify the authenticity of callback details before updating your system.
</Callout>

---

## Step 6 — Go live

1. **Verify your merchant account** — Complete KYC verification in the Dashboard.
2. **Switch base URL** — Replace `pluraluat.v2.pinepg.in` with `api.pluralpay.in`.
3. **Update credentials** — Use production Client ID and Client Secret.
4. **Configure production webhooks** — Point webhook endpoints to your live server.
5. **Test on production** — Make a small real payment and refund it to verify the flow.
6. **Enable signature verification** — Mandatory for all payment callbacks.
7. **Enable TLS 1.2+** — Required for all API communication.

---

## Related guides

<CardGrid>

<Card
title="Webhooks"
icon="code"
href="/developer-tools/webhooks"
>

Configure real-time subscription event notifications.

</Card>

<Card
title="Build with AI Agents"
icon="bot"
href="/build-with-ai-agents"
>

Automate subscription management with AI workflows.

</Card>

<Card
title="Developer Tools"
icon="terminal"
href="/developer-tools"
>

Explore Postman collections, test cards, and debugging utilities.

</Card>

</CardGrid>
