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.

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 immediately, and creates the subscription when the payment succeeds.

Node.jsts
// One hosted session collects the card, charges the first period and
// starts the subscription. The card is saved automatically.
const session = await borga.paymentSessions.create({
  amount: 4990,
  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 session amount is a separate, immediate charge recorded on the subscription's metadata as initial_payment_id. Because periods are billed when they end, use it for a setup fee or a small verification amount rather than the first period. 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. Requires bank invoices to be enabled; 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. With proration_behavior: "create_prorations" (default) the difference for the rest of the period is added to the next invoice; always_invoice bills it right away; none skips proration.
  • Add or remove prices. Create or delete subscription items with the same proration_behavior options.
  • One-off charges. Create an invoice item on the subscription; it lands on the next invoice. Negative amounts are credits.
  • Discounts. discount takes amount_off or percent_off. Today the only duration is once, applied to the next invoice.
  • 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 immediately.
  • Pause and resume. pause stops billing; resume picks up with the same anchor.

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. No invoices are created.
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). 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. Use these events to prompt the customer to update their card; a new saved card set as default is used by the next attempt.

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.