Checkout
The invoice page and the payment page that customers see, how they work without JavaScript, and how to brand them.
Turmite serves the pages that customers use to pay. Every page works with JavaScript off. All motion on the pages is CSS.
Pages
| Path | Page |
|---|---|
/i/{invoice_token} |
Invoice. Shows the amount and the coins, grouped by network |
/pay/{payment_token} |
Payment. Shows the amount, the network, the address, the memo, the QR code and the countdown |
/pay/{payment_token}/status |
Status. Loaded in a frame on the payment page |
/x/{payment_token} |
Pay with another coin: the coin list. See Pay with any coin |
/x/{swap_token} |
The swap: the exchange's deposit address and amount |
/x/{swap_token}/status |
Swap status. Loaded in a frame on the swap page |
The tokens are random. They are not the numeric payment_id, so a customer cannot guess the link of another payment.
How the customer pays
- Your shop creates an invoice with
POST /v1/invoiceand sends the customer toinvoice_url. - The customer selects a coin. Each coin is a form button. An invoice with a
pay_currencyskips this step. - Turmite creates the payment and redirects the customer to
/pay/{payment_token}. When the customer selects the same coin again, Turmite uses the open payment. - The customer sends the amount to the address. The QR code is inline SVG. Open in wallet opens a payment link in the customer's wallet app.
- The status frame shows the progress until the payment is final.
- When the payment is paid, the frame shows a "Return to {store}" button. When the payment ends while the page is open, the frame also shows Done. It loads the page again without the address and the QR code.
While the invoice is open and the payment is not final, the payment page has the link Choose another coin.
If you create the payment with POST /v1/payment, your shop shows the address and the amount on its own page. See Merchant API.
Without JavaScript
The pages use HTML forms and CSS only.
- The coin list is a form. Each coin button sends a
POST. Turmite answers with a303redirect to the payment page. - The status frame reloads itself with
<meta http-equiv="refresh">every 4 seconds. It stops when the payment isfinished,expired,failedorrefunded. - Each reload adds
?seen=with the state that the frame showed. The animation of a new state plays only once. - Looping animations take their position from the server clock, so a reload does not restart them.
- The countdown is a CSS animation. Its duration is the number of seconds until the payment window closes. Then it shows "Expired" and the page dims the address.
The customer does not need to reload the page.
Status on the page
| Title on the page | Payment status | The frame shows |
|---|---|---|
| Waiting for payment | waiting |
"Send {amount} {coin} to the address below." The countdown |
| Payment received | confirming |
The confirmations, for example 1 of 3, and an estimate of the time left |
| Partly paid | partially_paid |
The amount received and the rest. "Send {rest} {coin} more to the same address." The countdown |
| Payment confirmed | confirmed |
"Almost done." |
| Paid | finished |
A check mark that draws itself, in green. The "Return to {store}" button |
| Expired | expired |
A cross. "The payment window closed. Do not send funds to this address." |
| Payment failed | failed |
A cross, in red. "This payment cannot be completed. Contact {store}." |
| Refunded | refunded |
"{store} sent this payment back." |
The status frame shows a 7 x 7 grid with the Turmite mark. The confirmations fill the grid. The number of confirmations comes from the network settings. See Networks and nodes.
Green and red are the only status colours. Green means paid. Red means failed.
Reduced motion
The pages respect prefers-reduced-motion. When the customer's system asks for reduced motion, nothing moves. The countdown still counts down.
Return to the store
The "Return to {store}" button opens:
| Payment status | Target |
|---|---|
finished |
The success_url of the payment or the invoice. Else the project's Return URL |
expired, failed |
The cancel_url of the payment or the invoice. Else the project's Return URL |
refunded |
The project's Return URL |
Each payment keeps the project's Return URL of the time when the payment was created. Without a target, the page shows no button. When the invoice is still open, an expired payment also shows Start again.
When an invoice or a payment has a cancel_url, the page shows the link "Cancel and return to {store}".
The button is for the customer only. Mark the order as paid from a webhook. Do not mark it as paid when the customer opens the return page. See Webhooks.
Allowed return hosts
Set the hosts in Settings → Checkout → Allowed return hosts, one host per line, at most 50.
- When the list is empty, Turmite accepts a
success_urland acancel_urlon any host. - When the list has hosts, the merchant API answers
400withINVALID_REQUEST_PARAMSfor asuccess_urlorcancel_urlon another host. The Return URL must also use one of the hosts. - The host must match exactly. Add
www.shop.example.comandshop.example.comseparately if you use both.
All these URLs must use https:// on a mainnet install. A testnet install also accepts http://.
Branding
Each project has its own branding. Set it in Settings → Checkout. You need the project_admin role. Saving needs no two-factor code. The preview on the right shows the checkout with sample data and your saved settings.
| Setting | Effect |
|---|---|
| Image file or Or an image URL | The logo at the top of the page. PNG, JPEG or WebP, at most 2 MB. SVG is refused. Turmite scales the logo to fit 512 x 512 pixels and stores it as PNG |
| Theme | Auto, Light or Dark. Auto follows the customer's system |
| Accent colour | Used for buttons, progress and the countdown. Use black and white keeps the default |
| Message on the checkout | Short text on the page, for example delivery times. At most 280 characters |
| Support email, Support page | Links at the foot of the page |
| Return URL | Default target of the "Return to {store}" button |
| Allowed return hosts | See above |
The project name in Settings → General is the store name on the checkout. Without a logo, the page shows the first letter of the name.
Turmite downloads a logo URL once. The customer's browser loads the logo from Turmite, not from your site. The source image can have at most 8192 pixels on each side.
The time limits are in Settings → General:
| Setting | Default | Range | Effect |
|---|---|---|---|
| Payment window (minutes) | 60 | 5 to 10080 | Time to pay once a coin is chosen. The countdown uses it |
| Invoice window (minutes) | 1440 | 5 to 43200 | How long an invoice link stays open |
Checkout domains
A project can show its checkout on its own host name, for example pay.shop-two.com. On that host, Turmite serves only /pay/, /i/, /x/, /v1/, /assets/, /branding/, /healthz and /favicon.svg. The pages of other projects answer 404. See Projects and checkout domains.
Security
The checkout pages send this content security policy:
default-src 'none'; style-src 'self'; style-src-attr 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-src 'self'; form-action 'self'; frame-ancestors 'none'; base-uri 'none'
The status frames send the same policy without frame-src, and with frame-ancestors 'self'.
- The pages run no scripts.
frame-ancestors 'none'andX-Frame-Options: DENYstop other sites from showing the checkout in a frame. Link or redirect to the checkout. Do not put it in an iframe on your shop.- Forms only send to Turmite.
- The status frame comes from the same host as the payment page.
- The pages send
Cache-Control: no-store. - Each IP address can load 300 checkout pages per minute.