Finalization & payment

How invoices finalize and get paid.

View as MarkdownInstall skills

Finalization is the moment an invoice stops being editable and becomes a binding statement: VINR computes the amount due, locks the line items, and attempts collection against the customer's default payment method. Understanding the draft → finalized → paid transition is the key to predicting when money moves and which events fire.

The invoice lifecycleAsk

Every invoice moves through a fixed set of statuses. Transitions are one-way — once an invoice leaves draft, its line items are frozen.

StatusMeaningEditable?
draftAccumulating line items for the period.Yes
openFinalized and awaiting payment.No
paidCollected in full.No
uncollectibleAbandoned after dunning exhausted retries.No
voidCancelled before payment; reverses the statement.No

What finalization doesAsk

When a draft finalizes — automatically about an hour after creation, or immediately when you call the API — VINR performs four steps in order:

Apply discounts

Any coupons attached to the customer or subscription reduce the relevant line items.

Compute tax

VINR resolves the customer's tax location and adds tax line items. After this point the rates are fixed even if your configuration later changes.

Lock and number

Line items become immutable, the invoice receives its sequential number, and the status flips to open.

Attempt collection

If collection_method is charge_automatically, VINR creates a payment (pay_...) against the default method. For send_invoice, it instead surfaces a hosted payment page and waits.

Auto-advancing subscriptions finalize for you. You only call finalize explicitly when you created a standalone invoice and want to collect it now rather than wait for the grace window.

Finalize and collect a draftAsk

The SDK exposes finalization and payment as discrete calls so you can inspect the locked amount before charging.

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

// 1. Finalize a draft invoice — discounts + tax are applied and the total locks.
const invoice = await vinr.invoices.finalize('inv_8Qd2c1', {
  autoAdvance: false, // we'll trigger payment ourselves
});

console.log(invoice.status);       // "open"
console.log(invoice.amountDue);    // 2440  → EUR 24.40 (incl. tax)

// 2. Collect against the customer's default method.
const paid = await vinr.invoices.pay('inv_8Qd2c1');

if (paid.status === 'paid') {
  console.log('Settled via', paid.payment); // "pay_..."
} else {
  console.log('Collection failed; dunning will retry');
}

Prefer raw REST? The same two-step flow:

curl -X POST https://api.vinr.com/v1/invoices/inv_8Qd2c1/finalize \
  -H "X-Api-Key: $VINR_SECRET_KEY"

curl -X POST https://api.vinr.com/v1/invoices/inv_8Qd2c1/pay \
  -H "X-Api-Key: $VINR_SECRET_KEY"

Reacting to the resultAsk

Collection is asynchronous from your application's perspective — always drive fulfilment from webhooks, not the API response, because retries and bank latency can change the outcome minutes later.

EventWhen it firesWhat to do
invoice.finalizedStatus reaches open.Optionally notify the customer.
invoice.paidPayment succeeds.Grant or extend access.
invoice.payment_failedA collection attempt declines.Nothing — dunning will retry.
invoice.marked_uncollectibleAll retries exhausted.Revoke access, escalate to finance.
const event = vinr.webhooks.verify(payload, req.headers['x-vinr-signature']);

if (event.type === 'invoice.paid') {
  const invoice = event.data;        // "inv_..."
  await grantAccess(invoice.customer); // "cust_..."
}

When the first attempt failsAsk

A decline does not void the invoice. The invoice stays open and enters dunning: VINR retries on a configurable schedule, using updated card details if the customer fixes them via the hosted page. Only after the schedule is exhausted does the invoice become uncollectible.

Test the failure path in sandbox with card 4000 0000 0000 0002 (declined) before going live. A 4000 0000 0000 3220 card forces a 3DS challenge so you can verify your SCA handling on recurring charges.

Edge casesAsk

Next stepsAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page