RevKeen Docs
Using RevKeen

Subscriptions

Manage recurring billing with trials, discounts, and lifecycle events

Subscriptions automate recurring billing by charging customers at regular intervals. RevKeen handles billing dates, invoicing, payment collection, and dunning automatically. This page covers the complete subscription lifecycle.

Subscription Lifecycle

┌─────────┐     ┌──────────┐     ┌────────┐
│ PENDING │────>│ TRIALING │────>│ ACTIVE │
└─────────┘     └──────────┘     └───┬────┘

                    ┌────────────────┼────────────────┐
                    │                │                │
                    v                v                v
              ┌──────────┐    ┌──────────┐    ┌──────────┐
              │ PAST_DUE │    │  PAUSED  │    │ CANCELED │
              └────┬─────┘    └──────────┘    └──────────┘

                   v
              ┌──────────┐
              │  UNPAID  │
              └────┬─────┘

                   v
              ┌──────────┐
              │ EXPIRED  │
              └──────────┘

Subscription Statuses

StatusDescriptionBilling
pendingSubscription created, awaiting first paymentNot billing
trialingIn free trial periodNot billing
activeActive and billing normallyBilling
past_duePayment failed, retryingAttempting
pausedTemporarily paused by customer or merchantNot billing
canceledSubscription endedNot billing
unpaidAll payment retries exhaustedStopped
expiredSubscription term ended without renewalStopped

Creating Subscriptions

Via Checkout

The most common way to create subscriptions is through checkout links:

  1. Create a checkout link with a recurring price
  2. Customer completes checkout
  3. Subscription is automatically created
  4. First invoice is generated and charged

Via API

const subscription = await client.subscriptions.create({
  customerId: 'cus_xxxxxxxx',
  productId: 'prod_monthly',

  // Optional: Start with a trial
  trialDays: 14,

  // Optional: Apply a discount
  discountId: 'disc_xxxxxxxx',

  // Optional: Custom billing anchor (day of month)
  billingCycleAnchor: 1, // Bill on the 1st
});

console.log(subscription.data.status); // 'trialing' or 'active'
subscription = client.subscriptions.create(
    customer_id="cus_xxxxxxxx",
    product_id="prod_monthly",

    trial_days=14,
    discount_id="disc_xxxxxxxx",
    billing_cycle_anchor=1
)

print(subscription.data.status)  # 'trialing' or 'active'

If the product has a trial period defined, it will be applied automatically unless you explicitly set trialDays: 0. The billing interval (e.g., "Billed monthly") is automatically shown on all invoices, receipts, and the checkout success page.

Subscription Items

A subscription can contain multiple items, similar to Stripe's model. This allows you to bundle multiple products in a single subscription.

// Subscription with multiple items
const subscription = await client.subscriptions.create({
  customerId: 'cus_xxxxxxxx',
  items: [
    {
      priceId: 'price_pro_monthly',  // Pro plan at $49/mo
      quantity: 1,
    },
    {
      priceId: 'price_extra_users',  // Extra users at $10/mo each
      quantity: 5,
    },
  ],
});

// Each item generates its own line on invoices

Trial Periods

Trials allow customers to try your product before paying:

  • Subscription status is trialing
  • Customer has full access during the trial
  • No payment is collected
  • At trial end, first invoice is created and charged
  • If payment fails, subscription enters past_due

Trial periods can be set on the Price or overridden when creating the subscription.

Billing Cycle

Each subscription has a billing cycle that determines when invoices are generated:

FieldDescription
intervalBilling frequency: day, week, month, quarter, half_year, year
interval_countNumber of intervals between charges (e.g., 2 with month = every 2 months)
current_period_startStart of current billing period
current_period_endEnd of current billing period
billing_cycle_anchorDay of month/year for billing (e.g., 1st of each month)

The billing interval is automatically shown on invoices, receipts, and the checkout success page so customers always know when their next charge will occur.

Changing Plans

// Upgrade or downgrade immediately
const subscription = await client.subscriptions.update('sub_xxxxxxxx', {
  productId: 'prod_enterprise',
  proration: 'create_prorations', // Prorate the difference
});

// Proration options:
// - 'create_prorations': Credit for unused time, charge for new plan
// - 'none': No proration, new price starts next billing cycle
// - 'always_invoice': Create and finalize proration invoice immediately

Proration Preview

Preview proration calculations before changing plans to show customers what they will be charged.

// Get proration preview (newPlanId must be a UUID)
const preview = await client.subscriptions.prorationPreview('sub_xxxxxxxx', {
  newPlanId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
});

console.log({
  credit: preview.data.creditAmountMinor / 100,  // Credit for unused time
  debit: preview.data.debitAmountMinor / 100,    // Charge for new plan
  net: preview.data.netAmountMinor / 100,        // Net charge/credit
  daysRemaining: preview.data.daysRemaining,
});

Cancelling Subscriptions

// Cancel at end of billing period (recommended)
await client.subscriptions.cancel('sub_xxxxxxxx', {
  cancelAtPeriodEnd: true,
});

// Cancel immediately (stop access now)
await client.subscriptions.cancel('sub_xxxxxxxx', {
  cancelAtPeriodEnd: false,
});

Warning: Immediate cancellation stops access right away. Use cancelAtPeriodEnd: true to let customers use their remaining time.

Pausing Subscriptions

// Pause immediately
await client.subscriptions.pause('sub_xxxxxxxx', {
  pauseAt: 'now',
  resumeAt: '2026-03-01T00:00:00Z', // Optional: Auto-resume date
});

// Pause at end of current billing period (recommended)
await client.subscriptions.pause('sub_xxxxxxxx', {
  pauseAt: 'period_end',
});

// Resume manually
await client.subscriptions.resume('sub_xxxxxxxx');

Using pauseAt: 'period_end' lets customers use their remaining paid time before pausing.

Billing Anchor Day

Set a specific day of the month for billing. The first invoice will be prorated to align with the anchor.

// Set billing anchor to the 15th of each month
await client.subscriptions.setBillingAnchor('sub_xxxxxxxx', {
  day: 15, // 1-31
});

// For days 29-31, falls back to last day of short months

Smart Payment Retry

RevKeen automatically retries failed payments using smart logic that distinguishes between temporary and permanent failures.

Failure TypeExamplesRetry Behavior
Soft DeclineInsufficient funds, expired cardRetry on days 1, 3, 7
Hard DeclineLost/stolen card, fraudNo retry, request new card

Order Creation on Renewal

When a subscription renews and the invoice is paid, RevKeen checks whether the subscription contains products with a fulfillment_type of physical or digital. If so, an Order is automatically created to track fulfillment for that billing period.

This means a subscription for a monthly physical product (e.g., a supplement box) will create a new order every month when the renewal invoice is paid. Products with fulfillment_type: none (e.g., SaaS access) do not create orders.

Dunning (Payment Recovery)

When a subscription payment fails, RevKeen automatically enters the dunning process:

  1. Subscription status changes to past_due
  2. Payment is retried according to your retry schedule
  3. Customer receives reminder emails
  4. If all retries fail, subscription becomes unpaid or canceled

Configure dunning behavior at Settings > Dunning including retry intervals, email templates, and end behavior. See the Dunning page for details.

Subscription Events

EventWhen Triggered
subscription.createdSubscription is created
subscription.activatedTrial ends or first payment succeeds
subscription.renewedBilling period renews successfully
subscription.updatedPlan changed or details updated
subscription.pausedSubscription is paused
subscription.resumedPaused subscription resumes
subscription.cancelledSubscription is cancelled
subscription.expiredSubscription term ended without renewal
subscription.past_duePayment failed, entering retry period
subscription.unpaidAll payment retries exhausted
subscription.trial_will_endTrial period ending soon (3 days before)

On this page