# Integrate: Hosted Checkout

An interactive bench for hosted-checkout integrations. You create a payment with `confirm: false` and `payment_link: true`; Peach hosts the payment page and you share a link. There is no checkout for you to build and no payment method data in your request.

## What this page covers
- A generated `POST /payments` body with `confirm: false`, `payment_link: true` and `session_expiry`. It opens on **All payment methods**, which sends no `allowed_payment_method_types` so the page offers everything the account has enabled; picking a method in the rail adds the narrowing
- A form over `payment_link_config` that writes straight into the request body
- The generated link with copy and open actions, plus a live preview of the page it opens
- The shopper paying in an embedded frame, with a new-tab fallback
- Final-state confirmation through the webhook feed and `GET /payments/{id}?force_sync=true`
- Refund steps for the methods that support them

## Customisation
`payment_link_config` controls the hosted page:

| Option | Effect |
|---|---|
| `theme` | Accent colour |
| `logo` | Logo shown on the page |
| `seller_name` | Merchant name displayed to the shopper |
| `branding_visibility` | Whether Peach branding shows |
| `sdk_layout` | `accordion`, `tabs` or `spaced_accordion` |
| `details_layout` | `layout1` or `layout2` |
| `display_sdk_only` | Hides the order summary, leaving just the payment form |
| `show_card_form_by_default` | Opens the card form on load |
| `enabled_saved_payment_method` | Offers the customer's saved cards |
| `hide_card_nickname_field` | Hides the nickname input on the card form |
| `payment_button_text` | Label on the pay button |
| `custom_message_for_card_terms` | Message under the card form |
| `transaction_details` | Key/value rows shown to the shopper |

## Recurring: storing a credential here, charging it later

The flows above are all **CIT** — customer-initiated, with the shopper present. Switch the panel to **Merchant (MIT)** and the sequence changes: the payment that stores the credential comes first, then an off-session charge with nobody there.

The setup payment is the same payment link with `setup_future_usage: "off_session"`, a `customer_id` and `mandate_data` added. The shopper agrees to the mandate on Peach's page, so you never handle the credential or the acceptance yourself:

```json
"payment_link": true,
"customer_id": "cust_123",
"setup_future_usage": "off_session",
"authentication_type": "three_ds",
"mandate_data": {
  "customer_acceptance": {
    "acceptance_type": "online",
    "online": { "ip_address": "127.0.0.1", "user_agent": "Mozilla/5.0" }
  },
  "mandate_type": { "multi_use": { "amount": 100000, "currency": "ZAR" } }
}
```

The off-session charge is identical in every integration style — how the shopper paid the first time changes nothing about charging them later:

```json
"amount": 6500,
"currency": "ZAR",
"confirm": true,
"off_session": true,
"customer_id": "cust_123",
"recurring_details": { "type": "payment_method_id", "data": "pm_..." }
```

`payment_method_id` is **not** a field on `POST /payments` — the stored credential is referenced through `recurring_details`. Only card-backed credentials can be stored this way, so the toggle is offered for cards and network tokens and not for the alternative methods.

## Key concepts
- The response carries the link as `payment_link.link`.
- `session_expiry` is in seconds and expires the client secret, which is what makes a shared link stop working.
- `payment_link_config` is read when the link is created and baked into the page. A link cannot be restyled after the fact — editing the appearance while a link is live prompts you to create a new one, which is a new payment and a new URL.
- The link's page collects the payment method, so your request sends no `payment_method_data`. Omit `allowed_payment_method_types` and the page offers every method the account has enabled for the currency; send it to restrict what the shopper can choose.
- Amounts are in minor units. Alternative payment methods are automatic capture only.
- Peach rate-limits status queries to 2 per minute per transaction; automatic polling here is 30 seconds apart.

## Related
- [Integrate: Server-to-Server](/integrate/api-only.md): send the payment method data yourself
- [Integrate: Embedded Checkout](/integrate/sdk.md): one embedded checkout in your own app
- [Payment links](/flows/payment-links.md): the guided walkthrough and the full config reference
- [API reference](/playground.md): every endpoint, with the OpenAPI reference beside it

---

Interactive version: https://playground.peachpayments.com/integrate/hosted-checkout
