Create a subscription

Model products and prices, start a subscription, and handle the first invoice.

View as MarkdownInstall skills

This guide creates a recurring subscription end-to-end: model a product and price, attach a customer with a saved payment method, start the subscription, and react to the first invoice. Runnable against the sandbox.

OverviewAsk

A subscription ties a customer to one or more prices and generates an invoice each cycle, collecting payment automatically. You'll fulfil access on invoice events.

This guide assumes the customer already has a saved payment method. If not, run Save & reuse cards first — Billing reuses the same stored method.

Model products & pricesAsk

Create the product once, then a recurring price for it. Prices are immutable — to change pricing, create a new price.

import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });

const product = await vinr.products.create({ name: 'Pro plan' });

const price = await vinr.prices.create({
  product: product.id,
  amount: 2000,                       // €20.00
  currency: 'EUR',
  recurring: { interval: 'month' },   // 'day' | 'week' | 'month' | 'year'
});

Create the subscriptionAsk

Attach the customer, the price, and an optional trial. VINR immediately creates the first invoice (or schedules it after the trial).

const subscription = await vinr.subscriptions.create({
  customer: 'cust_abc123',
  items: [{ price: price.id, quantity: 1 }],
  trialPeriodDays: 14,                // omit to bill immediately
  metadata: { plan: 'pro' },
});

// subscription.id     → "sub_..."
// subscription.status → "trialing" (or "active" with no trial)

Handle the first invoiceAsk

Fulfil access from webhooks, not the API response — collection is asynchronous. The key events:

EventDo this
invoice.paidGrant or extend access for the period.
invoice.payment_failedLet dunning retry; warn the customer.
subscription.updatedSync plan/quantity changes.
subscription.deletedRevoke access.
export async function POST(req: Request) {
  const event = vinr.webhooks.verify(
    await req.text(),
    req.headers.get('x-vinr-signature'),
  );

  switch (event.type) {
    case 'invoice.paid':
      await grantAccess(event.data.customer, event.data.period_end);
      break;
    case 'subscription.deleted':
      await revokeAccess(event.data.customer);
      break;
  }
  return new Response('OK', { status: 200 });
}

Manage the lifecycleAsk

  • Upgrade/downgrade — change the item's price; VINR prorates automatically. See Upgrades & downgrades.
  • Cancelvinr.subscriptions.cancel(id) immediately, or cancelAtPeriodEnd: true to let it run out. See Cancellations.
  • Pause — suspend collection without losing the relationship. See Pausing & resuming.

Go liveAsk

Let customers self-serve

Enable the customer portal so customers update cards and plans without you building UI.

Configure dunning

Set retry schedules and emails in Dunning & recovery before real renewals happen.

Turn on tax

Enable automatic tax so every invoice collects the right amount.

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page