Skip to main content

Checkout: buying a plan from a link

An app owner can sell any of its plans from a link, to someone who has no account yet. The link opens the suite portal's Buy page; the buyer proves their email, gives billing details and pays; the paid plan is then claimed into an organization, by signing in (or creating an account) at the link in the receipt email.

<portal>/buy?app=<product code>&plan=<plan code>&org=<app owner slug>

Each row of a product's Plans tab has a Copy payment link button. It needs the portal URL (SUITE_PORTAL_URL of the Licentra app) and the app owner's slug (shown on the organization page). The Buy page checks all three with GET /v1/public/checkout/quote; anything that does not match answers 404, so a link cannot be used to find out which vendors exist. Only the owner's active plans are sold (never a reseller's).

The flow​

StepCall (no sign-in unless noted)What it proves or does
1POST /v1/public/checkout/email-codesEmails a 6-digit code (10 minutes). 5 per address and 20 per client IP per hour
2POST /v1/public/checkout/email-codes/verifyA right code returns a verification token (30 minutes, single use). 5 wrong tries lock the code
3POST /v1/public/checkout/ordersSpends the token (the order's email is the verified one) and creates the order with billing name and address. Returns an order token and the product's public payment settings. A free plan is granted here and returns its claim token
4POST /v1/public/checkout/orders/{id}/payNeeds the order token and a payment token. Marks the order paid, mints the claim token and emails the claim link
5POST /v1/admin/me/claims (signed in)Issues the license to the organization the account acts for (found or created as a client organization)

Only SHA-256 hashes of codes and tokens are stored. The claim token is single use per organization: claiming again from the same organization returns the same license; another organization gets 409. It is valid for 90 days. The plan starts at the claim, not at the payment, and the claiming organization must not already have an active license for the product.

Free plans and repeat sign-ups​

A plan with no price (or a price of 0) skips payment but not the email check. It is granted once per person and product: the address is normalized (plus-tags dropped, Gmail dots ignored), and a unique index backs the rule up under races. A business email domain also gets one free plan per product; shared providers (gmail.com, outlook.com, …) are exempt from the domain rule only.

Payment​

Each product has its own payment settings (Plans tab, GET/PUT /v1/admin/products/{id}/payment-settings, app_owner_admin to edit): the Google Pay gateway, gateway merchant id, merchant id and name, and an environment. Public values only; no secret is stored. The buyer's browser opens Google Pay with these values, so the money goes to the app owner's own account.

  • TEST: Google's test environment and the dev payment provider. No money moves; the Buy page also shows a Simulate payment button so the flow works on a machine without Google Pay.
  • PRODUCTION: Pay answers 400 until a payment processor that verifies the Google Pay token is connected behind CheckoutService.Pay. Not built yet.

Email​

The code and the claim link are sent through the suite's mail service, like every other suite email. Licentra authenticates to it with the shared app's certificate and names its own send-from and Reply-To on each message: License__MailFrom and License__MailReplyTo (per-app configuration). The send is triggered by the buyer's own request on the public Buy page, since the buyer has no account. In dev mode without the mail service (Mail__ServiceUrl empty) the message is written to the log instead.

Configuration​

VariableWhereMeaning
Mail__ServiceUrlLicentra APIThe mail service's URL. Empty = checkout and invitation email off (dev mode logs them)
License__MailFromLicentra APIThe send-from for checkout and invitation email: empty = the mail service's default sender; otherwise the default or an address on Mail__AllowedSenders
License__MailReplyToLicentra APIReply-To on checkout and invitation email
License__PortalUrlLicentra APIPortal base URL used in the claim link
LICENTRA_BASE_URLPortal (runtime) / VITE_LICENTRA_BASE_URLWhere the Buy page calls
SUITE_PORTAL_URLLicentra appBuilds the payment link

Known gaps​

  • No payment processor behind PRODUCTION; no refunds, invoices, tax or currency conversion.
  • A work-only app (T-pilot) can be claimed by a personal account; the catalog's per-app audiences are not checked at claim time yet.