Skip to content

Integrate the ICEPAY Checkout API

Learn how to create payments, redirect customers to ICEPAY Checkout, handle payment status updates, issue refunds, and forward funds.

The ICEPAY Checkout API lets you create a payment on your server, send the customer to the hosted ICEPAY Checkout Page, and receive the final payment state back in your application.

This guide explains the complete integration flow first, then goes deeper into payment methods, statuses, webhooks, refunds, payment forwarding, and error handling.

At a high level, your application creates a payment and ICEPAY returns a checkout URL. Your customer completes the payment on ICEPAY, while your backend keeps the payment state synchronized through webhooks.

1. Create the payment

Your backend sends the order reference, amount, and optional customer details to ICEPAY.

2. Redirect the customer

Use the links.checkout URL returned by ICEPAY to redirect the customer to the payment page.

3. Receive status updates

Configure a webhookUrl so ICEPAY can notify your backend when the payment status changes.

4. Confirm the result

Use the webhook state as your source of truth. When the customer returns, you can also retrieve the payment by its ICEPAY payment key.

  1. Customer starts checkout

    The customer starts the checkout process in your application.

  2. Create the payment

    Your backend sends the payment details to ICEPAY:

    POST /api/payments

  3. Receive the checkout URL

    ICEPAY creates the payment and returns a payment object containing:

    links.checkout

  4. Redirect the customer

    Redirect the customer’s browser to links.checkout to continue on the payment page.

  5. Customer completes or stops the payment

    After checkout, ICEPAY communicates the result through two separate channels.

You need:

  • an ICEPAY Merchant ID;
  • the corresponding Merchant Secret;
  • an HTTPS endpoint in your application for payment webhooks;
  • an HTTPS page to which customers can return after checkout.

The API uses HTTP Basic Authentication.

Use:

  • username: your ICEPAY Merchant ID;
  • password: your Merchant Secret.
Terminal window
curl --user "$ICEPAY_MERCHANT_ID:$ICEPAY_MERCHANT_SECRET" \
https://checkout.icepay.com/api/payments/methods

Create a payment with:

POST /api/payments

Only reference and amount are required. In most real integrations you should also send a redirectUrl and webhookUrl.

  1. Create an order in your own system

    Generate your own order or payment reference first. For example:

    ORD-16307

    This lets you connect the ICEPAY payment back to the correct order in your application.

  2. Build the payment request

    {
    "reference": "ORD-16307",
    "description": "Ice cream dipped in sprinkles",
    "amount": {
    "value": 299,
    "currency": "eur"
    },
    "redirectUrl": "https://merchant.example.com/payment-complete",
    "webhookUrl": "https://merchant.example.com/payment-webhook",
    "customer": {
    "email": "johndoe@example.com"
    }
    }
  3. Send the request from your backend

    Send the JSON payload to POST /api/payments using HTTP Basic Authentication.

    Request examples are shown directly below this flow.

  4. Store the ICEPAY payment key

    A successful response contains a unique payment key such as:

    pi-01j1ps8zf4jgnk0c3dnd477sp1

    Store this key with your own order. You need it when retrieving, refunding, or forwarding the payment.

  5. Redirect the customer to ICEPAY

    Use the returned checkout link:

    payment.links.checkout

    Example:

    https://checkout.icepay.com/checkout/pi-01j1ps8zf4jgnk0c3dnd477sp1
Terminal window
curl --request POST \
--user "$ICEPAY_MERCHANT_ID:$ICEPAY_MERCHANT_SECRET" \
--header "Content-Type: application/json" \
--data '{
"reference": "ORD-16307",
"description": "Ice cream dipped in sprinkles",
"amount": {
"value": 299,
"currency": "eur"
},
"redirectUrl": "https://merchant.example.com/payment-complete",
"webhookUrl": "https://merchant.example.com/payment-webhook",
"customer": {
"email": "johndoe@example.com"
}
}' \
https://checkout.icepay.com/api/payments

Amounts use minor currency units and must be sent as integers.

For a currency with two decimal places:

Customer-facing amount API value
€0.01 1
€2.99 299
€10.00 1000
€149.95 14995
Field Required Description
reference Yes Your payment or order reference. Maximum 255 characters.
amount.value Yes Payment amount in minor currency units. Minimum 1.
amount.currency Yes Supported three-letter currency code, for example eur.
description No Description shown during checkout. If omitted, the reference may be used.
paymentMethod No Preselects a payment method instead of letting the customer choose on the Checkout Page.
redirectUrl No HTTPS URL to which the customer returns when the payment is completed or stopped.
webhookUrl No HTTPS URL to which ICEPAY sends payment status updates.
customer No Customer email, name, IP address, and address details.
expireAfter No Payment lifetime in minutes. Defaults to four hours.

Both redirectUrl and webhookUrl must use HTTPS when supplied.

Payments remain open for four hours by default.

Use expireAfter to change this:

{
"expireAfter": 1440
}

1440 means 24 hours.

The maximum value is 44640 minutes, which is 31 days.

If you omit paymentMethod, ICEPAY lets the customer select an available payment method on the hosted Checkout Page.

{
"reference": "ORD-16307",
"amount": {
"value": 299,
"currency": "eur"
}
}

For a simple checkout integration, this is usually the least complex approach because your application does not need to build its own payment-method selector.

If your own checkout already lets customers choose a payment method, send its identifier when creating the payment:

{
"reference": "ORD-16307",
"amount": {
"value": 299,
"currency": "eur"
},
"paymentMethod": {
"type": "ideal"
}
}

The OpenAPI document defines these payment method identifiers:

ideal
bancontact
onlineueberweisen
card
paypal
eps
paybybank

However, the methods actually available depend on the merchant configuration.

Retrieve the methods available to your merchant

Section titled “Retrieve the methods available to your merchant”

Use:

GET /api/payments/methods

Example response:

[
{
"id": "ideal",
"description": "iDEAL | Wero"
},
{
"id": "paypal",
"description": "PayPal"
},
{
"id": "card",
"description": "Card"
},
{
"id": "bancontact",
"description": "Bancontact"
}
]

A created or retrieved payment contains both the operational payment state and information that helps your application continue the checkout flow.

A shortened example:

{
"key": "pi-01j1ps8zf4jgnk0c3dnd477sp1",
"status": "started",
"financialStatus": "uncleared",
"amount": {
"value": 299,
"currency": "eur"
},
"paymentMethod": null,
"reference": "ORD-16307",
"isTest": true,
"refunds": [],
"createdAt": "2024-07-01T09:16:06.500000Z",
"expiresAt": "2024-07-01T13:16:06.433802Z",
"updatedAt": "2024-07-01T09:16:06.500000Z",
"links": {
"checkout": "https://checkout.icepay.com/checkout/pi-01j1ps8zf4jgnk0c3dnd477sp1",
"documentation": "https://docs.icepay.com"
}
}

The payment contains two status fields with different meanings.

Field Possible values Meaning
status started, pending, completed, expired, cancelled The current lifecycle state of the payment.
financialStatus uncleared, cleared Whether the payment funds have been received.

Do not treat these two properties as interchangeable.

The payment has been created and checkout can begin.

The payment is still being processed and does not yet have a final state.

The payment has completed.

The payment expired before it reached a successful final state.

The payment was cancelled.

A webhook is the server-to-server notification ICEPAY sends to your webhookUrl when the payment status changes.

This is important because the customer returning to your website is a browser event. A webhook is a backend event and does not depend on the customer keeping the browser open or successfully returning to your site.

  1. Expose an HTTPS endpoint

    For example:

    https://merchant.example.com/payment-webhook
  2. Read the raw JSON request body

    ICEPAY signs the raw JSON body, so signature verification should use the body exactly as it was received.

  3. Read the ICEPAY-Signature request header

    The signature is a Base64-encoded SHA-256 HMAC generated with your Merchant Secret.

  4. Calculate the expected signature

    Conceptually:

    Base64(
    HMAC-SHA256(
    raw request body,
    Merchant Secret
    )
    )
  5. Compare the received and expected signatures

    Use a timing-safe comparison where your language or framework provides one.

  6. Process the payment state

    The webhook payload has the same payment-shaped structure used elsewhere in the API.

    Use the payment key to locate the payment in your database, then update the stored status.

<?php
$rawBody = file_get_contents('php://input');
$receivedSignature = $_SERVER['HTTP_ICEPAY_SIGNATURE'] ?? '';
$expectedSignature = base64_encode(
hash_hmac(
'sha256',
$rawBody,
getenv('ICEPAY_MERCHANT_SECRET'),
true,
),
);
if (!hash_equals($expectedSignature, $receivedSignature)) {
http_response_code(401);
exit;
}
$payment = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
// Update your payment using $payment['key'] and $payment['status'].

The OpenAPI document defines the webhook payload and its signature, but does not define a delivery or retry policy.

For that reason, design your handler so receiving the same payment state more than once is safe.

For example:

1. Find payment by ICEPAY payment key.
2. Verify the webhook signature.
3. Compare the received state with your stored state.
4. Update the payment inside a database transaction.
5. Trigger fulfilment only when your own fulfilment condition changes from false to true.

This prevents duplicate business actions such as sending the same order twice.

When creating a payment you can supply:

{
"redirectUrl": "https://merchant.example.com/payment-complete"
}

ICEPAY redirects the customer there after the payment is completed or stopped.

The return page is useful for the customer experience, but it should not replace webhook processing.

A robust return-page flow is:

  1. Look up the order in your own application.
  2. Retrieve the current ICEPAY payment if you need a fresh state.
  3. Show the appropriate result to the customer.
  4. Keep webhook handling responsible for ongoing backend status updates.

Retrieve the latest payment representation with:

GET /api/payments/{key}

Example:

Terminal window
curl --user "$ICEPAY_MERCHANT_ID:$ICEPAY_MERCHANT_SECRET" \
https://checkout.icepay.com/api/payments/pi-01j1ps8zf4jgnk0c3dnd477sp1

This returns the Payment object.

Use this endpoint when:

  • a customer returns to your application;
  • your backend needs to inspect the current payment state;
  • you need the latest payment representation before another operation.

For ongoing payment status updates, use webhooks.

You can include customer data when creating the payment:

{
"customer": {
"email": "johndoe@example.com",
"firstName": "Jane",
"lastName": "Doe",
"ip": "127.0.0.1",
"address": {
"streetName": "Orlyplein",
"houseNumber": "77",
"postalCode": "1043 DS",
"city": "Amsterdam",
"province": "Noord-Holland",
"country": "nl"
}
}
}

The address country uses an ISO 3166 alpha-2 country code such as nl.

Customer information is returned under the payment’s meta.customer object.

Create a refund with:

POST /api/payments/{key}/refund

The request requires:

  • your refund reference;
  • the refund amount.

The currency is inherited from the original payment and does not need to be supplied.

Example:

{
"reference": "RFD-00069",
"description": "Received chocolate dip instead of sprinkles",
"amount": {
"value": 299
}
}
Terminal window
curl --request POST \
--user "$ICEPAY_MERCHANT_ID:$ICEPAY_MERCHANT_SECRET" \
--header "Content-Type: application/json" \
--data '{
"reference": "RFD-00069",
"description": "Received chocolate dip instead of sprinkles",
"amount": {
"value": 299
}
}' \
https://checkout.icepay.com/api/payments/pi-01j1ps8zf4jgnk0c3dnd477sp1/refund

A refund has its own key and can have a status of:

pending
completed

The payment representation also contains a refunds array with refund summaries.

ICEPAY also defines a payment-forwarding operation:

POST /api/payments/{key}/forward

This forwards part of a payment to another ICEPAY merchant.

Before you use it:

  • payment forwarding must be enabled for the account;
  • the original payment must have financialStatus: "cleared".

Example request:

{
"reference": "FWD-00042",
"description": "Forwarding to merchant 1001",
"recipient": {
"id": "1001"
},
"amount": {
"value": 299
}
}

The destination ICEPAY merchant ID is supplied as recipient.id.

A forwarding request has its own key and can have a status of:

pending
completed

The payment representation can contain a forwards array with forwarding summaries.

The API defines the following HTTP error responses:

Status Meaning
400 The request is invalid.
401 Authentication failed.
404 The requested resource was not found.
422 The request could not be processed.
500 An unexpected server error occurred.

Error responses use a flexible structure that can contain:

{
"message": "Invalid request.",
"errors": {}
}

Because the exact error fields may vary by endpoint, your integration should not depend on one rigid error payload beyond the documented common fields.

A practical handler can branch primarily on the HTTP status:

if (response.ok) {
return response.json();
}
const error = await response.json().catch(() => null);
switch (response.status) {
case 400:
// Invalid request.
break;
case 401:
// Check Merchant ID and Merchant Secret.
break;
case 404:
// Check the payment key.
break;
case 422:
// Request was understood but could not be processed.
break;
default:
// Unexpected or server-side failure.
break;
}

The API does not prescribe how your application stores payments, but keeping your own order data and the ICEPAY payment state together makes the integration easier to maintain.

Field Example Purpose
id 4821 Internal payment record ID.
order_id 16307 Links the payment to your order.
provider icepay Identifies the payment provider.
provider_payment_key pi-... The unique payment key returned by ICEPAY.
reference ORD-16307 Your payment or order reference.
amount_value 299 Payment amount in minor currency units.
currency eur Payment currency.
status started Current ICEPAY payment status.
financial_status uncleared Whether the payment funds have been received.
payment_method null Payment method used for the payment, when known.
expires_at 2024-07-02T.. When the ICEPAY payment expires.
provider_created_at 2024-07-01T.. When ICEPAY created the payment.
provider_updated_at 2024-07-01T.. When ICEPAY last updated the payment.
updated_at 2024-07-01T.. Your application’s local update timestamp.

Before relying on the integration in a live checkout, verify that you:

  • create payments only from your backend;
  • keep the Merchant Secret outside source control and client-side code;
  • store the returned ICEPAY payment key;
  • send monetary values in minor currency units;
  • use HTTPS for redirectUrl and webhookUrl;
  • verify the ICEPAY-Signature before trusting webhook data;
  • process webhooks idempotently;
  • keep status and financialStatus as separate concepts;
  • handle 400, 401, 404, 422, and 500 responses;
  • use the payment retrieval endpoint for return-page or explicit state checks;
  • avoid making fulfilment depend only on the customer’s browser redirect.