> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowlix.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Hosted Payment Page

> Let Flowlix handle card entry, 3D Secure, and PCI compliance

With the Hosted Payment Page (HPP), Flowlix renders the card form. Your
integration never sees card data, which keeps you out of PCI DSS scope for
cardholder data handling.

## How it works

```mermaid theme={null}
sequenceDiagram
    participant C as Customer
    participant M as Your server
    participant F as Flowlix

    C->>M: Proceeds to checkout on your site
    M->>F: POST /v1/payments/hpp
    F-->>M: 201 Payment (status REQUIRES_ACTION, next_action.reason, next_action.redirect_url)
    M->>C: Redirect to next_action.redirect_url for next_action.reason
    C->>F: Enters card details, completes 3D Secure if required
    F->>C: Redirect to your return_url
    C->>M: Lands on return_url
    M->>F: GET /v1/payments/{id} (or webhook payment.succeeded)
    F-->>M: Payment (status SUCCEEDED)
```

## 1. Create the payment

```bash theme={null}
curl -X POST https://api.flowlix.dev/v1/payments/hpp \
  -H "Authorization: Bearer api_test_sk_abc123def456" \
  -H "Idempotency-Key: 0d4f9a3e-7c1b-4f7a-9e2d-8b5c6a1f0e3d" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 2500,
    "currency": "EUR",
    "merchant_reference": 2345678901,
    "billing_details": {
      "email": "jenny@example.com",
      "first_name": "Jenny",
      "last_name": "Rosen",
      "address": {
        "line1": "Kurfuerstendamm 21",
        "city": "Berlin",
        "postal_code": "10719",
        "country": "DE"
      }
    },
    "description": "Order #5678",
    "return_url": "https://shop.example.com/checkout/complete"
  }'
```

Field notes:

* **`billing_details`** is required: at minimum the customer's `email` and
  `address` (`line1`, `city`, `postal_code`, `country`). The values you send
  prefill the hosted page. If the customer edits them there, the
  customer-entered values become authoritative for the payment.
* **`return_url`** is where the customer is sent after completing or abandoning
  the page.
* **`merchant_reference`** is an optional integer with exactly 10 digits
  (from `1000000000` to `9999999999`) that ties the payment to your order
  for reconciliation. Several payment attempts may share one `merchant_reference` (for example
  when a customer retries after a decline) — it does not deduplicate requests.
  Use the `Idempotency-Key` header for that; see
  [Idempotency](/guides/idempotency).

## 2. Redirect the customer

The response contains `next_action`:

```json theme={null}
{
  "id": "pay_q7Mk2Np8Vr4Xt6Yz9Ab3Cd5E",
  "status": "REQUIRES_ACTION",
  "integration_type": "HOSTED_PAYMENT_PAGE",
  "next_action": {
    "type": "redirect",
    "reason": "hosted_payment_page",
    "redirect_url": "https://pay.flowlix.dev/hpp/pay_q7Mk2Np8Vr4Xt6Yz9Ab3Cd5E"
  }
}
```

Redirect the customer's browser to `next_action.redirect_url` when
`next_action.reason` is `hosted_payment_page`, using an HTTP 303 redirect or a
client-side navigation. The page handles card entry, validation, and 3D Secure
authentication when the issuer requires it.

After the customer submits card details, the payment can stay
`REQUIRES_ACTION` with `next_action.reason: "three_d_secure"`. Keep polling
`GET /v1/payments/{id}` while the customer is waiting. If Flowlix returns a
different `next_action.redirect_url`, redirect the customer to that latest URL.
If the URL is unchanged for the same browser session, avoid redirecting in a
loop and keep showing a pending confirmation state.

## 3. The customer returns

After the customer pays — or cancels, or the page expires — Flowlix redirects
them to your `return_url`.

<Warning>
  Never fulfil an order based on the customer reaching `return_url`. Query
  parameters appended to the return URL are UX hints only. The payment status
  from `GET /v1/payments/{id}` or a [webhook](/guides/webhooks) event is the
  single source of truth.
</Warning>

Recommended handling on your return page:

1. Look up the payment ID you stored when creating the payment (keyed by your
   order/session, not by URL parameters).
2. Call `GET /v1/payments/{id}`.
3. Render the result:
   * `SUCCEEDED` — show the confirmation page and fulfil the order.
   * `REQUIRES_ACTION` — the hosted page completion has not been reconciled
     yet, or a follow-up 3D Secure browser action is required. Redirect to a
     new `next_action.redirect_url` when it changes; otherwise show a "payment
     confirmation pending" page and keep polling (or wait for the webhook). See
     [Payment lifecycle](/guides/payment-lifecycle) for polling guidance.
   * `FAILED` — show the failure message and offer to retry with a new
     payment. The `failure_code` tells you why; see
     [Operation failure codes](/guides/operation-failures).
   * `EXPIRED` or `CANCELED` — the customer did not complete the page; offer
     to start a new payment.

## Page lifetime

A hosted payment page is single-use and tied to one payment attempt. If the
customer does not complete it before it expires, the payment transitions to
`EXPIRED` and the link stops working — create a new payment for another
attempt.

## Customization

The hosted page automatically adapts to the customer's device and locale.
Branding options (logo, accent color) are managed in the Merchant Portal.
