Subscriptions

Recurring billing with fixed and seat-based prices, trials, proration, discounts and bank-invoice collection.

A Subscription (sub_…) bills a Customer for one or more Prices on a schedule. Borga owns the cycle: at the end of each period it builds an invoice for what was used, books it in your accounting system, and collects it from the customer's saved card or by bank invoice. Failed charges are retried automatically.

Building blocks

ObjectPurpose
Product (prod_)What you sell, for example "Pro plan".
Price (price_)How much and how often: unit_amount, currency, recurring.interval. A product can have several prices.
Customer (cus_)Who pays. Needs a saved card for charge_automatically, or a kennitala for send_invoice.
Subscription (sub_)The customer's commitment to a set of prices.
Subscription item (si_)One price within the subscription, with a quantity (seats).

Create the catalogue once:

Node.jsts
const product = await borga.products.create({
  name: "Pro plan",
  description: "Everything in Starter plus priority support",
});

const monthly = await borga.prices.create({
  product: product.id,
  type: "recurring",
  currency: "ISK",
  unit_amount: 4990, // 4.990 kr. per seat per month
  recurring: { interval: "month", interval_count: 1 },
});

const yearly = await borga.prices.create({
  product: product.id,
  type: "recurring",
  currency: "ISK",
  unit_amount: 49900,
  recurring: { interval: "year" },
});

Every item in a subscription must share the same billing interval and currency. Quarterly and half-yearly plans use interval: "month" with interval_count 3 or 6.

Billing model

Borga bills in arrears: when a period ends, it issues an invoice for that period (licensed items times quantity, metered usage, prorations and one-off items) and collects it. The invoice appears as draft, is finalised in your accounting provider, then paid.

A trial period is free. The subscription is trialing until the trial ends, becomes active, and the first paid period is billed when it ends. Subscriptions started from a checkout session prepay their first period instead; see below.

Start a subscription

From a checkout session

The simplest path when the customer does not have a saved card yet. Pass subscription on a hosted session: the checkout collects and saves the card, charges the session amount as the first period upfront, and creates the subscription when the payment succeeds.

Node.jsts
// One hosted session collects the card, charges the first period upfront
// and starts the subscription. The card is saved automatically. Set
// `amount` to one period of the items: 3 seats at 4.990 kr.
const session = await borga.paymentSessions.create({
  amount: 14970,
  currency: "ISK",
  customer: "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
  subscription: {
    items: [{ price: "price_2Ab4Cd6Ef8Gh0Ij2Kl4Mn6Op", quantity: 3 }],
  },
  return_url: "https://yoursite.is/billing/welcome",
  cancel_url: "https://yoursite.is/pricing",
});

// Redirect to session.url. Listen for subscription.created.

The subscription binds to a Customer resolved from the payer's email. Pass customer or customer_email when you know it; leave both out and the checkout adds an email field to the card form and saves what the payer enters before the payment is submitted.

Set amount to one period of the items (quantity times unit amount, summed). Its invoice carries one line per price at the product's VAT rate, or one line per rate when amount differs from the prices' total (a promotion code, say), unless you send line_items or a tax_rate of your own. Because the first period is prepaid, the invoice at the end of that period carries no licensed lines, only metered usage, prorations and one-off items; every later period is billed in arrears as normal. The payment id is recorded on the subscription's metadata as initial_payment_id. You receive payment.succeeded, payment_method.attached, subscription.created and subscription.active.

From the API

When the customer already has a saved card (see Saved cards), create the subscription directly. The customer's default payment method is charged each period.

Node.jsts
// The customer already has a saved card (payment_method.attached fired).
const subscription = await borga.subscriptions.create({
  customer: "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
  items: [{ price: "price_2Ab4Cd6Ef8Gh0Ij2Kl4Mn6Op", quantity: 3 }],
  collection_method: "charge_automatically",
  trial_period_days: 14,
  metadata: { plan: "pro" },
});

console.log(subscription.status); // "trialing"
console.log(subscription.current_period_end);

Collect by bank invoice

Set collection_method to send_invoice and a days_until_due. Each period Borga issues a krafa through PayDay to the customer's kennitala instead of charging a card, and the invoice is marked paid when the bank confirms. Requires PayDay connected, ISK prices and a customer with a kennitala; see Bank invoices.

Node.jsts
// Bill by bank invoice: each period Borga issues a krafa through the
// merchant's accounting provider and the customer pays it in their bank.
const subscription = await borga.subscriptions.create({
  customer: "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz", // needs a kennitala
  items: [{ price: "price_2Ab4Cd6Ef8Gh0Ij2Kl4Mn6Op" }],
  collection_method: "send_invoice",
  days_until_due: 14,
});

Change a subscription

Node.jsts
const id = "sub_9Qw1Er3Ty5Ui7Op9As1Df3Gh";

// Change seats; Borga prorates the difference on the next invoice.
const [item] = (await borga.subscriptions.retrieve(id)).items;
await borga.subscriptionItems.update(item.id, {
  quantity: 5,
  proration_behavior: "create_prorations",
});

// Add a one-off charge to the next invoice.
await borga.invoiceItems.create({
  customer: "cus_8Jk2Lm4Np6Qr8St0Uv2Wx4Yz",
  subscription: id,
  amount: 15000,
  description: "Onboarding workshop",
});

// Cancel when the period ends (reversible with uncancel).
await borga.subscriptions.cancel(id, { at_period_end: true });
await borga.subscriptions.uncancel(id);

// Pause and resume billing.
await borga.subscriptions.pause(id);
await borga.subscriptions.resume(id);
  • Seats. Update the item's quantity. Because the period-end invoice bills the quantity the item has then, an increase is billed for the whole period and needs no proration. A decrease charges the dropped seats for the part of the period already used: with proration_behavior: "create_prorations" (default) on the next invoice, with always_invoice right away, and with none not at all. Changes during a trial are free; in a first period prepaid at checkout, the rest of the period is prorated.
  • Add or remove prices. Create or delete subscription items. Adding works like an increase, removing like a decrease, with the same proration_behavior options.
  • One-off charges. Create an invoice item on the subscription; it lands on the next invoice as a line. A negative amount is a credit: it is not a line but goes into the customer's credit balance, applied to the next invoice after tax.
  • Credits. What a customer is owed for a prorated decrease or a paused stretch is not a line on the next invoice. The invoice keeps its full lines and VAT, the credit is applied to it after tax as credit_applied, and only amount_due is collected; whatever the invoice does not use waits for the one after. A bank claim is issued for the amount due.
  • Discounts. A coupon is a percentage or a fixed amount off the cycle invoice, for the next invoice, a number of months or for good; customers redeem one with a promotion code, or you apply it to the subscription by id. The subscription keeps a snapshot of the discount. The discount comes off the lines' total, is booked as a negative line per VAT rate so the provider sees it, and VAT is computed on what the customer pays. Every discount is a coupon, so a one-off reduction for one customer is a coupon with one redemption. Price a subscription with or without a code using quote.
  • Cancel. cancel with at_period_end: true sets cancel_at_period_end and bills a final invoice for the current period; uncancel reverses it. Without the flag the subscription ends now and a final invoice bills the usage recorded so far plus the licensed items for the part of the period that was used (the same rule as lowering a quantity mid-period).
  • Pause and resume. pause stops the licensed charge, not the clock: every period is still invoiced when it ends, with usage in full and the licensed items only for the time before the pause, so a fully paused month with no usage produces no invoice. resume credits the paused stretch of the current period against the next invoice's amount due.

Every change emits subscription.updated; see the event catalog.

Statuses

StatusMeaning
trialingIn a free trial. Becomes active when it ends.
activeBilling normally.
past_dueA charge failed and retries were exhausted, or a bank invoice went overdue. Billing continues; a later successful payment returns it to active.
unpaidReserved for future dunning policy; not set today.
pausedPaused by you. Usage is still recorded and invoiced at period end; the licensed charge stops.
canceledEnded. Final invoice issued if the cancellation was at period end.

Failed payments

When a card charge for an invoice fails, Borga emits invoice.payment_failed and retries after 1, 3, 5 and 7 days (cumulative days 1, 4, 9 and 16 after the first failure). The schedule and what happens when it runs out are set per merchant in the dashboard, under Settings → Subscription billing. Each attempt emits another invoice.payment_failed with the attempt number, and the last one carries final: true. When the schedule is exhausted the subscription moves to past_due and subscription.past_due fires, or, if you chose to cancel, it is canceled and subscription.canceled fires with data: { id, cancellation_reason: "payment_failed" }.

Use these events to prompt the customer for another card. Send them a customer portal link: they can pay the failed invoice there with a new card, which becomes their default for later charges. A card the customer saves anywhere else also replaces a default that charges are failing on, and the failed invoices are retried with it within a minute. So does setting a default yourself.

For send_invoice subscriptions, an invoice that passes its due date emits invoice.overdue and also moves the subscription to past_due.

Invoices for subscriptions

Each cycle emits invoice.created, then invoice.finalized once the accounting provider has accepted it, then invoice.paid or invoice.payment_failed. Invoice line items carry the price's accounting_code if set, so revenue lands on the right account. See Invoicing and the Invoices API.