Checkout and portal
Two surfaces your customers use directly, and neither needs them to have a login with you.
A checkout link sells a plan: a public URL a buyer opens, fills in, and subscribes through. A portal link is for someone who already is a customer: they see their subscriptions, invoices and wallet, and can do a controlled set of things without emailing you.
Checkout links
A link points at one plan and carries a slug, which is what appears in the URL. Everything else configures what the buyer is asked for.
What to collect
collect_address, collect_phone and collect_vat_id decide the form. Ask for
the least you need: an address is required to calculate tax correctly
for a business, and a VAT ID is what unlocks reverse charge, but a phone number
usually just costs you conversions.
What to assume
currency is the currency the sale is made in, and trial_days adds a free
trial phase at the start of the resulting subscription. The
default_* fields (default_payment_terms_days, default_preferred_provider,
default_wallet_mode) are applied to the customer a checkout creates. A buyer
never sees any of these.
How the buyer pays
A checkout link takes payment through whichever account you connected under
Payments: Stripe, Mollie (your own API key) or Kontier Payments.
When more than one can sell in the link's currency, the buyer picks; the one
marked default comes first, and default_preferred_provider decides which the
new customer is charged through afterwards.
On Mollie and Kontier Payments the buyer authorises a small verification payment (EUR 0.01, or 0.02 when Bancontact is offered) by card, iDEAL or Bancontact. That leaves the mandate their subscription is charged by from then on; in a currency other than EUR only the card is offered. A Mollie account is offered only when its gateway has a webhook URL, because the paid checkout is reported by that webhook.
The verification payment is not refunded automatically
The EUR 0.01 (or 0.02) is a real payment that stays with you. Refund it from your Mollie dashboard if you do not want to keep it. The same applies when a customer saves a method in the portal.
What Mollie and Kontier Payments can save: a card, or a SEPA mandate created through an iDEAL or Bancontact first payment (Dutch and Belgian bank accounts). German direct debit without such a first payment, and PayPal, are not available through them yet; for those, use Stripe or GoCardless.
Your own system's customer
A checkout link creates the customer itself, from what the buyer types. It
carries no external_id, and the subscription records only the link that sold
it. If your own system must know which of its accounts bought, do not sell
through a checkout link: create the customer with your external_id
(POST /v1/customers), have them save a method through a
portal link, and bill them from your side.
Consent
consent_config controls the agreement the buyer is shown and must accept.
Whatever it records is attached to the resulting subscription, which is what you
will want when somebody later asks what exactly was agreed.
The customer portal
The portal is the same idea from the other end: an existing customer, a tokenised link, no account.
POST /v1/customers/{id}/portal-links mints the link. /v1/portal-settings
controls what the portal exposes, org-wide, in two groups.
What is visible
show_subscriptions, show_invoices, show_wallets, show_payment_methods,
show_billing_settings.
What is permitted
allow_invoice_pay, allow_wallet_topup, allow_add_payment_method,
allow_cancel_subscription.
The split matters. Showing invoices without allowing payment is a perfectly reasonable read-only portal. Allowing cancellation is the one to think about hardest, because it is the only setting here that loses you revenue without a conversation.
Saving a payment method
To have a customer save a card or mandate without a checkout, mint a portal link scoped to it:
Code
Redirect the customer to the url it returns. They land on the portal's
payment-method page, pass the provider's step (on Mollie and Kontier Payments,
the verification payment above) and come back to the portal, not to your
application: return_path accepts only the portal's own pages. Your system
learns the method is saved from the payment_method.attached
webhook, and allow_add_payment_method must be on.
These settings are organisation-wide
Portal settings are not per customer. Turning on allow_cancel_subscription
turns it on for everybody who has a portal link.
Both links are public
A checkout link and a portal link are both URLs that work without authentication. That is the point, and it has consequences:
- Treat the link as the credential. Anyone holding it has the access it grants. A portal link shows real billing data.
- Send them to the person, not to a channel. A link in a shared inbox is a link everyone in that inbox holds.
- They can be replaced. If one is exposed, mint a new link and the exposed one stops being useful.
The same reasoning applies to a quote's public token, which is why it has an explicit rotate endpoint.
Dashboard and API names
| In the dashboard | In the API |
|---|---|
| Checkout Links | /v1/checkout-links |
| Slug | slug, the public URL segment |
| Collect address / phone / VAT | collect_address, collect_phone, collect_vat_id |
| portal visibility toggles | show_* on /v1/portal-settings |
| portal permission toggles | allow_* on /v1/portal-settings |
| a customer's portal link | POST /customers/{id}/portal-links |
Where these go next
- Checkout creates a subscription on the link's plan and activates it once the payment method is saved, so the first invoice can be collected. It is an ordinary subscription afterwards: change, pause and cancel it like any other.
- The portal reduces support load, but only for what you permit; everything else still comes to you.
- Quotes are the third public surface, for deals that need a signature rather than a checkout.
Reference
| Topic | When you need it |
|---|---|
| Plans | What a checkout link sells |
| Quotes | The signature-based alternative to checkout |
| Wallets | What allow_wallet_topup exposes |
| Checkout links API | Every field on a link |