<!-- canonical: https://docs.costplus.io/docs/guides/hosted-payment-page -->

> Accept payments using Cost+'s hosted payment page
The Hosted Payment Page (HPP) is Cost+'s PCI DSS compliant payment form. It allows you to accept payments without handling sensitive card data on your own servers. You create an order via the API, redirect the customer to the hosted page, and they return to your site after payment.

## How It Works

1. Your server creates an order by calling **POST /v1/orders/**.
2. The API returns a URL pointing to the hosted payment page.
3. You redirect the customer to the payment page.
4. The customer completes payment on the Cost+ hosted page.
5. The customer is redirected back to your `return_url` (or `failure_url` for failed payments).
6. Cost+ sends a webhook notification to your `webhook_url` with the order status.

> [!NOTE]
> The hosted payment page is fully PCI DSS compliant. You never need to handle raw card numbers or sensitive payment data on your servers.

## Creating an Order

There are two approaches to using the HPP:

### Approach 1: Show All Payment Methods (Simplest)

Create an order **without** specifying `transactions`. The response includes an `order_url` — the customer is redirected there and sees all payment methods enabled for your account:

```json title="Request"
{
  "currency": "EUR",
  "amount": 1295,
  "merchant_order_id": "my-order-id-1",
  "description": "My amazing order",
  "return_url": "https://www.example.com",
  "webhook_url": "https://www.example.com/webhook"
}
```

```json title="Response"
{
  "id": "43114fde-da30-4115-8004-b7f808f9b25c",
  "status": "new",
  "currency": "EUR",
  "amount": 1295,
  "order_url": "https://api.costplus.online/pay/43114fde.../select-payment-method/",
  "return_url": "https://www.example.com",
  "webhook_url": "https://www.example.com/webhook"
}
```

Redirect the customer to the `order_url`. On the hosted page, all enabled payment methods are shown.

### Approach 2: Pre-select Payment Methods

Create an order **with** a `transactions` array to control which payment methods appear and in what order. Each transaction includes a `payment_method`, and the response returns a `payment_url` inside the transaction object:

```json title="Request"
{
  "currency": "EUR",
  "amount": 1295,
  "merchant_order_id": "my-order-id-1",
  "description": "My amazing order",
  "return_url": "https://www.example.com",
  "webhook_url": "https://www.example.com/webhook",
  "transactions": [
    { "payment_method": "credit-card" }
  ]
}
```

```json title="Response"
{
  "id": "4851e31c-4137-4e91-95ef-1df945ee76a2",
  "status": "new",
  "currency": "EUR",
  "amount": 1295,
  "transactions": [
    {
      "id": "d291f03f-a406-428a-967a-4895a46e03fd",
      "payment_method": "credit-card",
      "status": "new",
      "payment_url": "https://api.costplus.online/pay/4851e31c.../select-payment-method/credit-card/d291f03f.../"
    }
  ]
}
```

Redirect the customer to the `payment_url` from the transaction.

> [!TIP]
> If you provide only a single entry in the `transactions` array, the customer is taken directly to that payment method without seeing a selection screen. The `flags` array contains `"is-test"` when using a sandbox API key.

## Request Fields

| Field | Required | Description |
| --- | --- | --- |
| `currency` | Yes | ISO 4217 currency code (e.g., `EUR`, `GBP`, `SEK`) |
| `amount` | Yes | Amount in cents. For example, 12.95 EUR is represented as `1295` |
| `merchant_order_id` | No | Your own reference ID for the order |
| `return_url` | No | URL to redirect the customer after payment (default for all statuses) |
| `failure_url` | No | URL to redirect the customer on `cancelled`, `expired`, or `error` status (see Return URLs below) |
| `locale` | No | Language for the payment page. Supported: `en-GB`, `de-DE`, `nl-NL`, `nl-BE`, `fr-BE`, `sv-SE`, `no-NO`, `da-DK` |
| `description` | No | Description of the order, shown to the customer |
| `payment_methods` | No | Filter to a single payment method (e.g. `["credit-card"]`). Omit to show all enabled methods. For multiple specific methods, use the `transactions` array instead |
| `webhook_url` | No | URL to receive status change notifications |
| `expiration_period` | No | ISO 8601 duration for order expiry. Default is `PT30M` (30 minutes) |

> [!WARNING]
> The `amount` field is always in the smallest currency unit (cents). Passing `1295` means 12.95 in the given currency. Passing `1295.00` or `12.95` will result in an error or an incorrect charge.

## Multiple Payment Methods

There are two ways to control which payment methods appear on the hosted page:

**Option A — `payment_methods` (single filter).** Pass a single-element array to restrict the order to one payment method. Omit the field entirely to show all methods enabled for your account.

**Option B — `transactions` array (recommended for multiple methods).** Add one entry per payment method. Each transaction gets its own `payment_url`, and the methods appear in array order on the hosted page:

```json
"transactions": [
  { "payment_method": "credit-card" },
  { "payment_method": "apple-pay" }
]
```

> [!NOTE]
> The `payment_methods` field on orders accepts at most one value. To offer multiple specific methods, always use the `transactions` array. If you need a reusable link with multiple payment methods, consider [Payment Links](/docs/guides/payment-links) instead, which support a true `payment_methods` array.

## Return URLs

After payment, the customer is redirected based on the order status and which URLs you provided:

- **When both `return_url` and `failure_url` are set:**
  - `cancelled`, `expired`, or `error` → customer is redirected to `failure_url`
  - All other statuses → customer is redirected to `return_url`

- **When only `return_url` is set:**
  - All statuses → customer is redirected to `return_url`

> [!TIP]
> Use `failure_url` to show a retry or support page for failed payments, while `return_url` shows an order confirmation. If you only need one destination, `return_url` alone is sufficient.

## Cancel Button Behavior

The hosted payment page includes a cancel button. When the customer clicks it, they are redirected to `failure_url` (if provided) or `return_url`. The order status will transition to `cancelled`. Always verify the order status via the API or webhooks rather than relying on the redirect alone.

## Related Endpoints

- [Create Order](/api-reference/orders/createOrder) — create a payment order and receive the `payment_url`
- [Get Order](/api-reference/orders/getOrder) — check the order status after payment
