Skip to main content

Hosted Payment Page (HPP)

The Hosted Payment Page lets you accept payments without handling card details directly. You redirect the customer to a secure, Flowlix-hosted page where they enter their card information. After payment, the customer is sent back to your site.

Why use HPP?

  • No PCI scope — Flowlix handles all card data on our servers.
  • Less development effort — No need to build a card input form.
  • Consistent UX — A professional, mobile-optimized payment page.
  • Security — Card details never touch your servers.

How it works

  1. The customer clicks “Pay” on your site.
  2. Your server creates an HPP session via POST /v1/payments/hpp.
  3. You redirect the customer to the hpp_url from the response.
  4. The customer enters their card details on the Flowlix payment page.
  5. After payment, the customer is redirected to your redirect_url with query parameters.
  6. Your server fetches the payment to verify the status.

Step 1: Create an HPP session

curl -X POST https://api.flowlix.eu/v1/payments/hpp \
  -H "Authorization: Bearer fl_test_sk_abc123def456" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: hpp-order-5678" \
  -d '{
    "amount": 2500,
    "currency": "eur",
    "customer": {
      "email": "alex@example.com",
      "first_name": "Alex",
      "last_name": "Johnson",
      "country": "DE",
      "phone": "+491709876543",
      "address": "Friedrichstrasse 100",
      "city": "Berlin",
      "zip": "10117"
    },
    "redirect_url": "https://shop.example.com/checkout/complete"
  }'

Request parameters

ParameterRequiredDescription
amountYesAmount in minor units (e.g., 2500 = EUR 25.00).
currencyYesThree-letter ISO 4217 code (e.g., eur).
customer.emailYesCustomer email.
customer.first_nameYesCustomer first name.
customer.last_nameYesCustomer last name.
customer.countryYesCountry code (ISO 3166-1 alpha-2).
customer.phoneYesPhone in international format.
customer.addressYesStreet address.
customer.cityYesCity.
customer.zipYesPostal code.
redirect_urlYesWhere to send the customer after payment.

Response

{
  "id": "pay_1N4gLy3fAvLZmn3D1YkNs9qW",
  "object": "payment",
  "amount": 2500,
  "currency": "eur",
  "status": "pending",
  "description": null,
  "card": null,
  "customer": {
    "email": "alex@example.com",
    "name": "Alex Johnson"
  },
  "metadata": {},
  "decline_code": null,
  "decline_message": null,
  "redirect_url": "https://pay.flowlix.eu/hpp/pay_1N4gLy3fAvLZmn3D1YkNs9qW",
  "refunded_at": null,
  "succeeded_at": null,
  "failed_at": null,
  "created": 1719792000,
  "livemode": false
}
Note that the payment starts with "status": "pending" and "card": null because the customer has not entered their card details yet.

Step 2: Redirect the customer

Redirect the customer to the hpp_url:
# Flask example
from flask import redirect

@app.route("/checkout/pay")
def pay():
    # ... create HPP session ...
    return redirect(payment["hpp_url"])
// Express example
app.get("/checkout/pay", async (req, res) => {
  // ... create HPP session ...
  res.redirect(payment.hpp_url);
});

Step 3: Handle the redirect back

After the customer completes (or abandons) the payment, Flowlix redirects them to your redirect_url with query parameters:
https://shop.example.com/checkout/complete?payment_id=pay_1N4gLy3fAvLZmn3D1YkNs9qW&status=succeeded
Query parameterDescription
payment_idThe payment ID.
statusThe payment status (succeeded, failed, or pending).
Always verify the payment status server-side. Do not trust the query parameters alone — a malicious user could modify them. Fetch the payment from the API to confirm the actual status.

Step 4: Verify the payment

Fetch the payment from your server to confirm the status:
curl https://api.flowlix.eu/v1/payments/pay_1N4gLy3fAvLZmn3D1YkNs9qW \
  -H "Authorization: Bearer fl_test_sk_abc123def456"
{
  "id": "pay_1N4gLy3fAvLZmn3D1YkNs9qW",
  "object": "payment",
  "amount": 2500,
  "currency": "eur",
  "status": "succeeded",
  "card": {
    "brand": "visa",
    "last4": "4242",
    "exp_month": 12,
    "exp_year": 2027,
    "country": "US"
  },
  "customer": {
    "email": "alex@example.com",
    "name": "Alex Johnson"
  },
  ...
}

Full integration example

import requests
import uuid
from flask import Flask, redirect, request, jsonify

app = Flask(__name__)
FLOWLIX_API_KEY = "fl_test_sk_abc123def456"

@app.route("/checkout/pay", methods=["POST"])
def create_checkout():
    """Create HPP session and redirect customer."""
    order = get_current_order()  # Your order logic

    response = requests.post(
        "https://api.flowlix.eu/v1/payments/hpp",
        headers={
            "Authorization": f"Bearer {FLOWLIX_API_KEY}",
            "Content-Type": "application/json",
            "Idempotency-Key": f"hpp-{order.id}",
        },
        json={
            "amount": order.total_in_cents,
            "currency": "eur",
            "customer": {
                "email": order.customer_email,
                "first_name": order.customer_first_name,
                "last_name": order.customer_last_name,
                "country": order.customer_country,
                "phone": order.customer_phone,
                "address": order.customer_address,
                "city": order.customer_city,
                "zip": order.customer_zip,
            },
            "redirect_url": f"https://shop.example.com/checkout/{order.id}/complete",
        },
    )

    payment = response.json()
    order.payment_id = payment["id"]
    order.save()

    return redirect(payment["hpp_url"])


@app.route("/checkout/<order_id>/complete")
def checkout_complete(order_id):
    """Handle redirect back from Flowlix HPP."""
    order = get_order(order_id)
    payment_id = request.args.get("payment_id")

    # Always verify server-side
    response = requests.get(
        f"https://api.flowlix.eu/v1/payments/{payment_id}",
        headers={"Authorization": f"Bearer {FLOWLIX_API_KEY}"},
    )
    payment = response.json()

    if payment["status"] == "succeeded":
        order.mark_paid()
        return render_template("success.html", order=order)
    else:
        return render_template("failed.html", order=order)

Best practices

  • Always verify payment status server-side after the redirect. Never rely on query parameters alone.
  • Store the payment_id in your database when you create the HPP session, so you can reconcile later.
  • Use the Idempotency-Key tied to your order ID to prevent creating duplicate payment sessions.
  • Handle all statuses in your redirect handler: succeeded, failed, and pending.
  • Set a reasonable redirect_url that your customer recognizes and trusts.