Upgrades & downgrades

Move customers between plans fairly.

View as MarkdownInstall skills

Change a subscriber's plan immediately or at period end, with proration that credits unused time on the old price and charges for the new one. A plan change is just an update to the subscription's price — VINR computes the money math so an upgrade can charge mid-cycle and a downgrade can defer or refund.

What a plan change isAsk

A subscription points at a price. To move a customer, you update that price (and optionally quantity). Two decisions shape the outcome:

DecisionFieldEffect
When the new price takes effectproration_behaviorNow, at period end, or never.
Whether to bill immediatelyinvoice_nowIssue a proration invoice now vs. roll onto the next cycle.

The subscription and customer are unchanged objects throughout — you keep the same sub_ id, history, and billing cycle anchor unless you explicitly reset it.

How proration worksAsk

When a change takes effect mid-cycle, VINR splits the current period at the moment of change and writes two proration line items onto the next invoice (or an immediate one):

Credit unused time

VINR refunds the remaining time on the old price as a negative line item — e.g. half a month left on a €20 plan credits €10.00 (-1000).

Charge the new rate

It then charges the new price for that same remaining time — half a month of a €50 plan is €25.00 (2500).

Net it out

The next invoice nets the two. The example above adds €15.00 (1500) for the upgrade; a downgrade nets negative and becomes credit balance applied to future invoices.

Proration is line-item math on an invoice, not a separate refund. To return money to the card instead of crediting future invoices, issue a credit note with a cash refund.

Proration behaviorsAsk

Prop

Type

Upgrade immediatelyAsk

Move the customer up a tier now and bill the difference the same day:

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

const subscription = await vinr.subscriptions.update('sub_8Kd2', {
  price: 'price_pro_monthly',     // the new, higher price
  proration_behavior: 'always_invoice', // settle now, don't wait for next cycle
});
// -> invoice with two proration lines is created and collected immediately

Downgrade at period endAsk

Most teams defer downgrades so the customer keeps paid features they already bought through the end of the period. Schedule the change with no immediate proration:

const subscription = await vinr.subscriptions.update('sub_8Kd2', {
  price: 'price_starter_monthly', // the new, lower price
  proration_behavior: 'none',     // no credit/charge now
  effective: 'period_end',        // new price applies on next cycle
});
// access stays on "Pro" until period_end; next invoice bills the lower rate

A downgrade can reduce included quotas (seats, usage allowances). If current usage exceeds the new plan's limit, deferring to period end avoids stranding the customer mid-cycle. Validate quotas before applying immediately.

Preview before you commitAsk

Show the customer the exact net amount before charging by previewing the upcoming invoice with the proposed price:

const preview = await vinr.invoices.preview({
  subscription: 'sub_8Kd2',
  price: 'price_pro_monthly',
  proration_behavior: 'create_prorations',
});

console.log(preview.amount_due); // e.g. 1500 -> "€15.00 due today"

Resulting eventsAsk

A plan change emits a predictable sequence. Drive fulfilment and access changes from these rather than from the API response:

EventWhen it fires
subscription.updatedThe price (or quantity) on the subscription changed.
invoice.createdA proration invoice was generated (always_invoice).
invoice.paidThe proration charge was collected — grant the new entitlements here.
invoice.payment_failedThe upgrade charge failed; flows into dunning.
// In your webhook handler
const event = vinr.webhooks.verify(payload, signature); // x-vinr-signature
if (event.type === 'subscription.updated') {
  const sub = event.data;
  await syncEntitlements(sub.customer, sub.price); // grant/revoke features
}

Edge casesAsk

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page