<!-- ZenPays documentation · https://docs.zenpayz.com/docs/api-reference/subscriptions -->

# Subscriptions

Recurring billing — plans, stored payment methods, and the subscription lifecycle. Access via `zenpays.subscriptions`.

:::note JavaScript only
The Python SDK does not yet expose a `subscriptions` namespace. Call the REST endpoints directly from Python.
:::

## Plans

A plan defines *what* is charged and *how often*. Subscriptions attach a customer to a plan.

### createPlan

```typescript
const plan = await zenpays.subscriptions.createPlan({
  name: 'Pro Monthly',
  amount: 2999,
  currency: 'USD',
  interval: 'month',
  description: 'Pro tier, billed monthly',
  intervalCount: 1, // 3 + 'month' would be quarterly
  trialDays: 14,
})
```

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | `string` | Yes | Plan name shown to customers |
| `amount` | `number` | Yes | Amount charged each interval, in the currency's minor unit |
| `currency` | `string` | Yes | ISO 4217 currency code |
| `interval` | `'day' \| 'week' \| 'month' \| 'year'` | Yes | Billing interval |
| `description` | `string` | No | Longer plan description |
| `intervalCount` | `number` | No | Intervals between charges. Defaults to `1` |
| `trialDays` | `number` | No | Free trial length in days |
| `metadata` | `Record<string, unknown>` | No | Arbitrary key/value data |

Returns a [`SubscriptionPlan`](#subscriptionplan).

### listPlans

```typescript
const plans = await zenpays.subscriptions.listPlans()
```

Returns `SubscriptionPlan[]` for the authenticated merchant.

### archivePlan

Stops new subscriptions on the plan. Existing subscriptions continue to bill.

```typescript
const plan = await zenpays.subscriptions.archivePlan('plan_xxx')
```

### getPublicPlan

Unauthenticated plan details, safe to render on a hosted subscribe page.

```typescript
const plan = await zenpays.subscriptions.getPublicPlan('plan_xxx')
```

## Payment methods

To charge a subscription automatically you need a stored payment method. Collect one on the client with a setup token, then store the resulting provider token.

### createSetupToken

```typescript
const setupToken = await zenpays.subscriptions.createSetupToken({
  customerId: 'cust_xxx',
  customerEmail: 'customer@example.com',
  currency: 'USD',
})
```

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `customerId` | `string` | Yes | Customer the method will belong to |
| `customerEmail` | `string` | Yes | Customer email |
| `currency` | `string` | No | Currency the method will be charged in |
| `tspProvider` | `string` | No | Force a specific payment provider |

### storePaymentMethod

```typescript
const method = await zenpays.subscriptions.storePaymentMethod({
  customerId: 'cust_xxx',
  customerEmail: 'customer@example.com',
  tspToken: 'tok_from_provider',
  tspProvider: 'stripe',
  type: 'card',
  last4: '4242',
  brand: 'visa',
  expiryMonth: 12,
  expiryYear: 2028,
})
```

`tspToken` is the provider token representing the instrument the customer entered. Raw card details never reach ZenPays.

### listPaymentMethods

```typescript
const methods = await zenpays.subscriptions.listPaymentMethods('cust_xxx')
```

### revokePaymentMethod

```typescript
await zenpays.subscriptions.revokePaymentMethod('spm_xxx')
```

## Subscription lifecycle

### create

```typescript
const subscription = await zenpays.subscriptions.create({
  customerId: 'cust_xxx',
  customerEmail: 'customer@example.com',
  planId: 'plan_xxx',
  storedPaymentMethodId: 'spm_xxx',
})
```

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `customerId` | `string` | Yes | Customer to subscribe |
| `customerEmail` | `string` | Yes | Customer email |
| `planId` | `string` | Yes | Plan to subscribe them to |
| `storedPaymentMethodId` | `string` | No | Method to charge. Omit to require setup first |
| `tspProvider` | `string` | No | Force a specific payment provider |
| `metadata` | `Record<string, unknown>` | No | Arbitrary key/value data |

### list

```typescript
const all = await zenpays.subscriptions.list()
const active = await zenpays.subscriptions.list('active')
```

Optionally filtered by [`SubscriptionStatus`](#subscriptionstatus).

### get

```typescript
const subscription = await zenpays.subscriptions.get('sub_xxx')
```

### cancel

Cancels at the end of the current billing period by default.

```typescript
// At period end — the customer keeps access until currentPeriodEnd
await zenpays.subscriptions.cancel('sub_xxx')

// Immediately
await zenpays.subscriptions.cancel('sub_xxx', { immediately: true })
```

### pause

```typescript
const subscription = await zenpays.subscriptions.pause('sub_xxx')
```

### resume

```typescript
const subscription = await zenpays.subscriptions.resume('sub_xxx')
```

## Types

### SubscriptionStatus

`'trialing'` · `'active'` · `'past_due'` · `'paused'` · `'cancelled'`

### SubscriptionPlan

| Field | Type | Description |
|-------|------|-------------|
| `planId` | `string` | Plan identifier |
| `merchantId` | `string` | Owning merchant |
| `name` | `string` | Plan name |
| `description` | `string?` | Plan description |
| `amount` | `number` | Amount per interval |
| `currency` | `string` | ISO 4217 currency code |
| `interval` | `BillingInterval` | `'day' \| 'week' \| 'month' \| 'year'` |
| `intervalCount` | `number` | Intervals between charges |
| `trialDays` | `number` | Free trial length in days |
| `status` | `'active' \| 'archived'` | Plan status |
| `metadata` | `Record<string, unknown>?` | Arbitrary data |

### Subscription

| Field | Type | Description |
|-------|------|-------------|
| `subscriptionId` | `string` | Subscription identifier |
| `merchantId` | `string` | Owning merchant |
| `customerId` | `string` | Subscribed customer |
| `customerEmail` | `string` | Customer email |
| `planId` | `string` | Plan being billed |
| `status` | `SubscriptionStatus` | Current status |
| `collectionMethod` | `'auto_charge' \| 'send_invoice'` | How each period is collected |
| `storedPaymentMethodId` | `string?` | Method charged automatically |
| `tspProvider` | `string?` | Payment provider |
| `currentPeriodStart` | `string` | ISO 8601 timestamp |
| `currentPeriodEnd` | `string` | ISO 8601 timestamp |
| `nextBillingAt` | `string` | ISO 8601 timestamp of the next charge |
| `trialEnd` | `string?` | ISO 8601 timestamp the trial ends |
| `cancelAtPeriodEnd` | `boolean` | Whether cancellation is pending |
| `cancelledAt` | `string?` | ISO 8601 timestamp of cancellation |
| `pausedAt` | `string?` | ISO 8601 timestamp of pause |

### StoredPaymentMethod

| Field | Type | Description |
|-------|------|-------------|
| `storedPaymentMethodId` | `string` | Method identifier |
| `merchantId` | `string` | Owning merchant |
| `customerId` | `string` | Owning customer |
| `customerEmail` | `string` | Customer email |
| `tspProvider` | `string` | Payment provider |
| `type` | `string` | Instrument type, e.g. `card` |
| `brand` | `string?` | Card brand |
| `expiryMonth` | `number?` | Card expiry month |
| `expiryYear` | `number?` | Card expiry year |
| `isActive` | `boolean` | Whether the method can still be charged |
| `metadata` | `Record<string, unknown>?` | Arbitrary data |
