# Recurring payments and subscriptions

Set up recurring payments where you charge customers without them being present (Merchant-Initiated Transactions). Uses mandates to establish formal authorisation with defined limits, providing the strongest protection against disputes. Choose between charging an initial amount (subscriptions) or zero-auth (free trials).

## Use cases

- SaaS subscriptions
- Membership fees
- Streaming services
- Insurance premiums
- Recurring donations
- Installment payments
- Usage-based billing
- Free trial → paid conversion

## Steps

### 1. Create payment with customer and mandate — `POST /payments`

Create a payment with customer details and mandate_data to establish recurring authorisation.

Key parameters:
• customer.id (required) - Your unique customer identifier
• customer.email, customer.name, customer.phone (optional) - Additional details
• amount: Set your subscription price, or use 0 for free trials
• setup_future_usage: "off_session" - For merchant-initiated charges
• mandate_data: Documents customer consent with limits
  - customer_acceptance: Records how/when consent was given
  - mandate_type.multi_use: Maximum amount per charge

Why mandates are essential for recurring:
• Formal consent with documented terms
• Defined maximum amounts (dispute protection)
• Required in some regions (India, and so on)
• Card networks recognise as proper recurring auth

The customer is created if the ID doesn't exist, or matched to an existing customer.

Request body:

```json
{
  "amount": 6500,
  "currency": "ZAR",
  "confirm": false,
  "capture_method": "automatic",
  "authentication_type": "three_ds",
  "setup_future_usage": "off_session",
  "description": "Subscription setup with initial payment",
  "return_url": "https://example.com/flows/recurring-payments?status=complete",
  "is_iframe_redirection_enabled": true,
  "billing": {
    "address": {
      "line1": "123 Main Street",
      "line2": "Apartment 4B",
      "city": "Cape Town",
      "state": "Western Cape",
      "zip": "8001",
      "country": "ZA",
      "first_name": "John",
      "last_name": "Doe"
    },
    "phone": {
      "number": "821234567",
      "country_code": "+27"
    },
    "email": "john.doe@example.com"
  },
  "shipping": {
    "address": {
      "line1": "456 Delivery Road",
      "line2": "Unit 7",
      "city": "Cape Town",
      "state": "Western Cape",
      "zip": "8001",
      "country": "ZA",
      "first_name": "John",
      "last_name": "Doe"
    },
    "phone": {
      "number": "821234567",
      "country_code": "+27"
    },
    "email": "john.doe@example.com"
  },
  "customer": {
    "id": "cus_subscriber",
    "email": "subscriber@example.com",
    "name": "Test Subscriber",
    "phone": "+27123456789"
  },
  "mandate_data": {
    "customer_acceptance": {
      "acceptance_type": "online",
      "accepted_at": "2026-07-23T20:45:51.058Z",
      "online": {
        "ip_address": "127.0.0.1",
        "user_agent": "Mozilla/5.0"
      }
    },
    "mandate_type": {
      "multi_use": {
        "amount": 100000,
        "currency": "ZAR"
      }
    }
  }
}
```

### 2. Customer authorises mandate

Customer enters card details and completes 3DS authentication. This establishes the mandate for future charges.

After success, you'll receive:
• mandate_id: Use this for all future charges
• payment_method_id: The saved card details
• status: "succeeded" confirms mandate is active

Important: Save the mandate_id - you'll need it for every recurring charge.

### 3. Process recurring charge — `POST /payments`

Charge the customer using their mandate. No authentication required - the mandate authorises you to charge.

Key parameters:
• amount: The charge amount (must be ≤ mandate max)
• off_session: true - Customer is not present
• confirm: true - Process immediately
• mandate_id: The mandate authorising this charge
• recurring_details: { type: "mandate_id", data: "man_xxx" }

Handle failures gracefully:
• Retry with exponential backoff
• Notify customer if card declined
• Provide self-service card update

Request body:

```json
{
  "amount": 6500,
  "currency": "ZAR",
  "confirm": true,
  "off_session": true,
  "description": "Recurring subscription charge"
}
```

## Response

Payment endpoints return the payment object. Key fields to read:

- `payment_id` — the payment identifier.
- `status` — current payment status (see the state transitions below).
- `client_secret` — pass to the SDK to complete the payment client-side.
- `next_action` — present when a redirect or 3-D Secure challenge is required.

## Key parameters

- mandate_data establishes formal recurring authorisation
- Customer authenticates once, you charge many times
- Mandate defines max amount per charge
- Use mandate_id + recurring_details for all future charges

## Why use mandates?

Mandates are formal agreements that authorise you to charge a customer's card repeatedly without requiring their presence for each transaction.

### Benefits

- **Dispute protection** — Mandates document explicit customer consent with timestamps, IP addresses, and agreed terms. This evidence is crucial when defending against 'I didn't authorise this' chargebacks.
- **Defined limits** — Set maximum charge amounts upfront. Card networks see charges within mandate limits as pre-authorised, reducing declines and fraud flags.
- **Higher approval rates** — Card networks recognise mandate-backed transactions as legitimate recurring charges, leading to fewer soft declines and better authorisation rates.
- **Network recognition** — Visa, Mastercard, and other networks have specific transaction codes for mandate-based recurring. This signals legitimacy to issuers.
- **Future-proof** — As regulations tighten globally (RBI in India, PSD2 in EU), mandates position you for compliance. African markets are following similar trends.

### Regulatory landscape

- **South Africa** — While SA doesn't mandate recurring payment rules like India's RBI, PASA (Payments Association of SA) and card schemes enforce strong consent requirements. POPIA requires clear consent for storing and processing payment credentials. Mandates provide the documented consent trail needed for compliance.
- **Nigeria** — CBN (Central Bank of Nigeria) requires explicit customer authorisation for recurring debits. Mandate documentation helps meet these requirements and reduces disputes in a market with high chargeback rates.
- **Kenya and East Africa** — CBK oversight and mobile money integration mean recurring payments need clear authorisation trails. Mandates work alongside M-Pesa and card payments for subscription businesses.
- **Card Network Rules** — Visa and Mastercard enforce recurring payment rules across all African markets. Their Credential-on-File (COF) and recurring payment indicators require proper mandate setup to avoid scheme fines and forced refunds.

### Without mandates

Without mandates, you rely on 'credential-on-file' which has weaker dispute protection. In African markets with higher dispute rates, this can lead to significant revenue loss and potential scheme penalties.

### Best practices

- Set mandate max amount 20-30% above your highest expected charge
- Store mandate_id securely - you need it for every future charge
- Display clear terms in customer's language before authorisation
- Send SMS/email receipts for each charge (required in some African markets)
- Provide easy cancellation - builds trust and reduces disputes
- Keep proof of customer acceptance for at least 18 months (dispute window)

## Flow diagram

```mermaid
flowchart LR
    subgraph Setup["Initial Setup (Customer Present)"]
    A[Create Payment + Customer + Mandate] --> B[Mount SDK]
    B --> C[Customer Authorises + 3DS]
    C --> D[Mandate Active]
    end
    subgraph Recurring["Recurring Charges (Customer Not Present)"]
    D --> E[Process Charge]
    E --> F{Success?}
    F -->|Yes| G[Complete]
    F -->|No| H[Retry/Notify]
    H --> E
    end
```

## Payment state transitions

```mermaid
stateDiagram-v2
    state "Mandate Setup" as setup {
        [*] --> requires_payment_method
        requires_payment_method --> requires_customer_action: Enter Card
        requires_customer_action --> succeeded: 3DS Complete
    }
    state "Recurring Charges" as recurring {
        [*] --> processing: mandate_id + amount
        processing --> succeeded: Charged
        processing --> failed: Declined
        failed --> processing: Retry
    }
    setup --> recurring: mandate_id saved

    note right of setup: Customer present - One-time authentication
    note right of recurring: Customer not present - No auth required
```

---

Interactive version: https://playground.peachpayments.com/flows/recurring-payments
