Skip to main content

Subscriptions

Charge a saved card on a schedule: plans, trials, coupons, proration, dunning and a hosted customer portal, all handled by Orchestrapay. This page explains every option; the Subscriptions API page has the endpoint reference.

How it works

Plan (pricing template) Subscription (customer instance)
amount / interval / trial price snapshot (grandfathered)
dunning / entitlements ──► card on file (payment method)
billing cycle + invoices
  1. You create a plan: the price, billing cadence, optional trial, dunning policy and entitlements.
  2. A customer subscribes with a card. The first cycle is paid on-session; every renewal is charged automatically off-session (a merchant-initiated transaction on the saved card).
  3. Each billing attempt writes an invoice (paid or failed, with the full subtotal / discount / tax / credit breakdown), sends the customer a receipt, and emits webhooks.

There are three ways to start a subscription:

  • Hosted subscribe page (zero integration): create a checkout session and share the URL. The customer picks up the plan, enters their card and the subscription auto-creates.
  • API / SDK: collect a card via the payment form with save_on_file, then orchestrapay.subscriptions.create({ plan_uuid, payment_method }).
  • Dashboard: create plans and subscriptions manually under Subscriptions.

Pricing options

  • Amount and currency — integer minor units (cents) with a lowercase 3-letter currency. A $5.00 monthly plan is amount: 500, currency: "usd".
  • Intervaldaily, weekly, monthly or yearly, times interval_count (e.g. interval: "weekly", interval_count: 2 bills every two weeks).
  • Grandfathering — the subscription snapshots the plan's price at creation. Editing or archiving a plan never changes what existing subscribers pay; only an explicit plan change does.

Pricing models

Every plan has a pricing_model that decides how amount and quantity combine into the cycle price:

ModelCycle priceUse case
flat (default)amountone price, quantity ignored
per_unitamount × quantityper-seat pricing (the hosted page shows a seat picker)
tieredtier ladder over quantityvolume discounts
meteredtier/unit price over reported usagepay-as-you-go

Tiered plans define a tiers ladder ([{ up_to: 10, unit_amount: 500 }, { up_to: null, unit_amount: 300 }]) with tiers_mode:

  • graduated — each tier bills its own units (first 10 at $5, the rest at $3)
  • volume — the tier the total quantity lands in prices ALL units

Metered billing: report usage as it happens (subscriptions.recordUsage); at each renewal the billed quantity is the period total (increments summed, a set overrides). Metered plans collect nothing at subscribe time — the card is saved with a no-charge setup.

Quantities are set at subscribe (quantity) and changed later with the same proration rules as plan changes.

Multi-currency: give a plan currency_options ({ "eur": 450 }) and the hosted subscribe page offers a currency picker; the chosen amount snapshots onto the subscription.

Products group the price variants of one offering (Stripe's Product → Price): create with orchestrapay.products.create, then pass product_uuid when creating plans.

Add-ons (multiple line items)

Attach extra plans to a live subscription as add-on lines billed on the same cycle: subscriptions.addItem(sub, { plan, quantity }). Attaching charges the prorated remainder of the period; removing credits the unused time to the customer balance. Invoices carry the full per-line breakdown in lines.

Scheduled changes

Queue future-dated plan/quantity changes with subscriptions.schedule(sub, { effective_at, plan, quantity }) — they land at the first billing run at or after the date, with no proration.

Trials

Three trial shapes, configured on the plan:

ConfigurationBehavior
trial_days: 1414 days free, then full price. Card saved upfront with no charge (a setup, not a payment).
trial_days: 14, trial_amount: 10014 days at a $1.00 intro price, then full price.
trial_cycles: 3, trial_amount: 100First 3 billing cycles charged at $1.00, then full price.

During a trial the subscription is trialing and gets full entitlements.

Coupons and promotion codes

A coupon is the discount (percent_off or amount_off) with a duration:

  • once — first invoice only
  • repeating + duration_in_months — the next N invoices
  • forever — every invoice

A promotion code is the customer-facing string (e.g. WELCOME20) that redeems a coupon, with optional expiry and redemption caps. Customers enter it on the hosted subscribe page; the discount is snapshotted onto the subscription and burns down cycle by cycle.

Taxes

Attach a manual tax rate (percentage, inclusive or exclusive, a display label like "VAT") when creating a subscription. Exclusive tax is added on top of each invoice; inclusive tax is informational. Tax appears as its own line on invoices and receipts.

Upgrades, downgrades and proration

Changing plans mid-cycle follows Stripe's proration model. The billing date never moves; the remaining time is settled fairly:

  • Upgrade (proration_behavior: "create_prorations", the default) — the unused time on the old price becomes a credit, the remaining time on the new price is charged, and the difference is collected now. Access changes immediately.
  • Downgrade — the difference becomes a customer balance credit, automatically consumed by future invoices (an invoice fully covered by credit charges the card nothing).
  • proration_behavior: "none" — swap the price with no settlement.
  • Period-end switch (pending_plan_uuid) — no proration; the new plan starts at the next renewal.

Preview any change first with the upcoming invoice endpoint: it returns the exact charge-now amount or balance credit.

Failed payments and dunning

When a renewal charge fails the subscription goes past_due and the plan's dunning schedule takes over (default [1, 3, 7]: retry after 1, 3 and 7 days):

  1. The customer gets a payment-failed email with a link to the hosted invoice page, where they can pay immediately or enter a new card (no login needed — the link itself authorizes it).
  2. Each scheduled retry charges the card again automatically. Any successful payment (retry, hosted page, or card update) resets the subscription to active.
  3. During the plan's grace_period_days (default 7) entitlements stay on; past it they turn off while the subscription remains past_due.
  4. When every retry is exhausted the subscription is canceled and the customer is notified.

Bank authentication (SCA / 3DS) on renewals

When an issuer demands authentication for an off-session charge, the invoice parks as requires_action (with the bank's requires_action_url), the customer gets a "confirm your payment" email, and the hosted invoice page walks them through confirming with their bank and completing the payment. The retry ladder keeps running as a fallback.

Cancellation, pause and reactivation

  • Cancel at period end (default) — access continues until the paid period ends. Until then the customer can reactivate with one click.
  • Cancel immediately — access ends now.
  • Pause / resume — billing stops while paused (entitlements off); resuming bills immediately and starts a fresh period.
  • A cancellation reason can be captured for retention analytics, and the portal offers pause-instead as a retention path.

Entitlements

Plans carry a features map (boolean flags or numeric limits, e.g. {"api_access": true, "seats": 5}); subscriptions can override per customer (feature_overrides, for enterprise deals). Check them at request time:

GET /subscriptions/entitlements/check?customer_id=<cus_ or your external_id>&feature=api_access

Status drives access automatically: active and trialing allow, past_due allows during grace only, paused and canceled deny.

Customer portal

Every tenant gets a hosted, brand-styled portal at /portal/<tenant> where customers sign in with an email code (no password) and can:

  • see their subscriptions, next billing date and exact upcoming charge
  • switch plans with a proration preview before confirming
  • update their card (saved with no charge)
  • pause, resume, reactivate or cancel
  • browse their full billing history

Credit notes and refunds

Issue post-invoice adjustments with orchestrapay.creditNotes.create({ invoice, amount, reason }) — the amount is credited to the customer balance and consumed by future invoices (a fully covered renewal charges the card nothing). Card refunds go through the Refunds API against the invoice's transaction_intent.

Invoice PDFs

Every invoice renders as a PDF: merchants via GET /v202607/invoices/:id/pdf, customers via the download link on the hosted invoice page.

orchestrapay.billingPortal.createSession({ customer }) mints a 15-minute login-free portal URL — drop it behind a "Manage subscription" button in your app so customers skip the email code.

Receipts and emails

Customers automatically receive branded emails for: sign-in codes, payment receipts (with the full amount breakdown), failed payments (with the pay-now link) and final cancellation.

Webhooks

Pass webhook_urls when creating a subscription to receive Stripe-style envelope events (id / type / created_at / data):

EventWhen
subscription.createdSubscription created
subscription.updatedPlan change, pause/resume, reactivate, scheduled cancel
subscription.canceledImmediate cancel, period-end cancel executing, or dunning exhausted
invoice.paidA billing attempt succeeded
invoice.payment_failedA billing attempt failed (dunning is running)

SDK

import Orchestrapay from '@orchestrapay/sdk';
const orchestrapay = new Orchestrapay('orch_sk_live_...');

const plan = await orchestrapay.plans.create({
name: 'Pro Monthly', amount: 500, currency: 'usd', interval: 'monthly',
});

// Zero-integration: share a hosted subscribe link
const session = await orchestrapay.checkoutSessions.create({ plan: plan.id });
// session.url -> https://pay.orchestrapay.com/subscribe/<tenant>/<plan>

// Or create directly on a saved card
const sub = await orchestrapay.subscriptions.create({
plan_uuid: plan.id,
payment_method: 'pm_...', // from a save-on-file payment
});

// Prorated upgrade with preview
const preview = await orchestrapay.subscriptions.upcoming(sub.id, { plan: 'plan_...' });
await orchestrapay.subscriptions.update(sub.id, {
plan_uuid: 'plan_...', proration_behavior: 'create_prorations',
});

See the Subscriptions API reference for every endpoint and field.