Skip to content

Integrate with the PHP Checkout SDK

Learn how to integrate ICEPAY Checkout in a PHP application using the official Checkout SDK.

The ICEPAY Checkout SDK provides a PHP interface around the Checkout API. It handles HTTP communication, maps API responses to PHP models, provides consistent exception handling, and includes a postback handler for verifying payment status notifications.

This guide walks through a complete integration, from installing the SDK and creating a payment to handling postbacks, retrieving payments, issuing refunds, and forwarding funds.

The SDK runs in your backend application and communicates with the ICEPAY Checkout API for you.

  1. Create a checkout request

    Build the payment using the SDK’s request and amount models.

  2. Send the payment to ICEPAY

    Call createCheckout() on the CheckoutClient.

  3. Redirect the customer

    ICEPAY returns a checkout URL through the response model. Redirect the customer to that URL.

  4. Handle payment updates

    Use the SDK’s PostbackHandler to verify incoming ICEPAY postbacks and parse the updated payment.

  5. Retrieve or manage the payment when needed

    Use the same client to retrieve a payment, issue a refund, forward funds, or retrieve available payment methods.

The Checkout SDK requires:

  • PHP 8.1 or newer;
  • Composer;
  • compatible PSR HTTP client, message, and factory implementations;
  • your ICEPAY Merchant ID and Merchant Secret.

The SDK does not force one specific HTTP client implementation. This means it can work with different HTTP stacks depending on your application.

Install the SDK with Composer:

Terminal window
composer require icepay/checkout-sdk

The SDK requires implementations for the PSR HTTP interfaces it uses. If your project does not already provide compatible implementations, you can install the SDK together with Nyholm PSR-7 and Symfony HTTP Client:

Terminal window
composer require icepay/checkout-sdk nyholm/psr7 symfony/http-client

Keep your credentials outside your source code, for example as environment variables:

.env
ICEPAY_MERCHANT_ID=your_merchant_id
ICEPAY_MERCHANT_SECRET=your_merchant_secret

Create the CheckoutClient and configure the credentials:

<?php
require_once 'vendor/autoload.php';
use ICEPAY\Checkout\CheckoutClient;
$merchantId = getenv('ICEPAY_MERCHANT_ID');
$merchantSecret = getenv('ICEPAY_MERCHANT_SECRET');
if ($merchantId === false || $merchantSecret === false) {
throw new RuntimeException('Missing ICEPAY credentials.');
}
$checkoutClient = (new CheckoutClient())->withAuthorization(
merchantId: $merchantId,
merchantSecret: $merchantSecret,
);

The client uses these credentials when communicating with the ICEPAY Checkout API.

A payment starts with a Checkout request model.

<?php
use ICEPAY\Checkout\Models\Amount;
use ICEPAY\Checkout\Models\Request\Checkout as CheckoutRequest;
$checkoutRequest = new CheckoutRequest(
reference: 'ORD-16307',
description: 'Ice cream dipped in sprinkles',
amount: new Amount(
value: 299,
currency: Amount::CURRENCY_EUR,
),
redirectUrl: 'https://merchant.example.com/payment-complete',
webhookUrl: 'https://merchant.example.com/payment-webhook',
);
$response = $checkoutClient->createCheckout($checkoutRequest);

The response is mapped to an SDK response model, so you can access the returned values as PHP properties instead of manually decoding JSON.

For example:

$paymentKey = $response->key;
$checkoutUrl = $response->links->checkout;
$status = $response->status->toString();

After creating the payment, redirect the customer to the returned checkout URL:

header('Location: ' . $response->links->checkout, true, 303);
exit;

The customer can then complete the payment on the hosted ICEPAY Checkout Page.

The Amount model takes the amount as an integer.

For currencies with two decimal places:

Customer-facing amount SDK value
€0.01 1
€2.99 299
€10.00 1000
€149.95 14995

For example:

$amount = new Amount(
value: 299,
currency: Amount::CURRENCY_EUR,
);

The SDK’s CheckoutRequest model supports the following properties:

Property Type Purpose
reference string Your payment or order reference.
description string Description of the payment.
amount Amount Payment amount and currency.
redirectUrl string URL to which the customer returns after checkout.
webhookUrl string URL to which ICEPAY sends payment status updates.
paymentMethod PaymentMethod or string Optionally preselect a payment method.
metadata Metadata Additional metadata sent with the payment.
expireAfter ?int Optional payment expiration period.

You can supply these values in the constructor or use the helper methods provided by the request model.

For example:

$checkoutRequest = new CheckoutRequest(
reference: 'ORD-16307',
description: 'Ice cream dipped in sprinkles',
amount: new Amount(299, Amount::CURRENCY_EUR),
);
$checkoutRequest
->withRedirectUrl('https://merchant.example.com/payment-complete')
->withWebhookUrl('https://merchant.example.com/payment-webhook')
->withCustomerEmail('johndoe@example.com');

For a customer email address, use:

$checkoutRequest->withCustomerEmail('johndoe@example.com');

You can also provide a customer array:

$checkoutRequest->withCustomer([
'email' => 'johndoe@example.com',
'firstName' => 'John',
'lastName' => 'Doe',
]);

The SDK stores this information in the request metadata before serializing the payment request.

If you do not set paymentMethod, redirect the customer to:

$response->links->checkout

The hosted Checkout Page can then present the available payment methods to the customer.

If the customer already selected a payment method in your own checkout, include it in the request.

You can pass the payment method as a string:

$checkoutRequest = new CheckoutRequest(
reference: 'ORD-16307',
description: 'Ice cream dipped in sprinkles',
amount: new Amount(299, Amount::CURRENCY_EUR),
paymentMethod: 'ideal',
);

Or use the SDK’s PaymentMethod model:

use ICEPAY\Checkout\Models\PaymentMethod;
$checkoutRequest = new CheckoutRequest(
reference: 'ORD-16307',
description: 'Ice cream dipped in sprinkles',
amount: new Amount(299, Amount::CURRENCY_EUR),
paymentMethod: new PaymentMethod(
type: PaymentMethod::TYPE_IDEAL,
),
);

When a direct payment-method URL is returned, it is available through:

$response->links->direct

Because the direct link is optional, check that it is available before using it:

if ($response->links->direct !== null) {
header('Location: ' . $response->links->direct, true, 303);
exit;
}

Use getPaymentMethods() to retrieve the payment methods available to the authenticated merchant:

$paymentMethods = $checkoutClient->getPaymentMethods();
foreach ($paymentMethods as $paymentMethod) {
echo $paymentMethod->id;
echo $paymentMethod->description;
}

Each item is returned as an SDK response model with properties such as:

$paymentMethod->id;
$paymentMethod->description;
$paymentMethod->logo;

After a payment status changes, ICEPAY sends a request to the webhookUrl supplied when you created the payment.

The SDK includes PostbackHandler, which:

  • verifies the ICEPAY-Signature;
  • uses your Merchant Secret for signature validation;
  • parses the JSON body;
  • returns the payment as an SDK Checkout response model.

This means you do not have to implement the HMAC verification yourself.

<?php
use ICEPAY\Checkout\Exceptions\InvalidSignature;
use ICEPAY\Checkout\PostbackHandler;
$merchantSecret = getenv('ICEPAY_MERCHANT_SECRET');
if ($merchantSecret === false) {
throw new RuntimeException('Missing ICEPAY Merchant Secret.');
}
$handler = new PostbackHandler(
merchantSecret: $merchantSecret,
);
$body = file_get_contents('php://input');
$signature = $_SERVER['HTTP_ICEPAY_SIGNATURE'] ?? '';
try {
$payment = $handler->handle($body, $signature);
} catch (InvalidSignature) {
http_response_code(400);
return;
}
$paymentKey = $payment->key;
$status = $payment->status->toString();
$financialStatus = $payment->financialStatus->toString();
// Update the payment in your application.

If you only need to check the authenticity of a postback:

if (!$handler->verify($body, $signature)) {
http_response_code(400);
return;
}

If your framework already gives you a PSR-7 MessageInterface, the handler can read the body and signature header directly:

use ICEPAY\Checkout\Exceptions\InvalidSignature;
use Psr\Http\Message\MessageInterface;
/** @var MessageInterface $request */
try {
$payment = $handler->handleRequest($request);
} catch (InvalidSignature) {
http_response_code(400);
return;
}

You can also use:

$handler->verifyRequest($request);

when you only want to verify the request.

Store the ICEPAY payment key returned when the payment is created:

$paymentKey = $response->key;

You can retrieve the current payment later with:

$payment = $checkoutClient->getCheckout($paymentKey);

The returned model exposes the payment state:

$payment->key;
$payment->reference;
$payment->amount;
$payment->paymentMethod;
$payment->links;
$payment->status->toString();
$payment->financialStatus->toString();

The SDK maps the payment status to the Status enum.

Possible values handled by the SDK are:

started
pending
completed
expired
cancelled
unknown

The separate financial status is represented by the FinancialStatus enum:

uncleared
cleared
unknown

Create a refund request using ICEPAY\Checkout\Models\Request\Refund.

use ICEPAY\Checkout\Models\Request\Refund as RefundRequest;
$refundRequest = new RefundRequest(
reference: 'RFD-00069',
amount: 299,
description: 'Received chocolate dip instead of sprinkles',
);
$refund = $checkoutClient->refund(
refund: $refundRequest,
checkoutId: $paymentKey,
);

The refund amount can be supplied as either an integer or an Amount object.

When an integer is supplied, the SDK serializes it as the request’s amount.value:

$refundRequest = new RefundRequest(
reference: 'RFD-00069',
amount: 299,
description: null,
);

The SDK exposes payment forwarding through the Forward request model.

use ICEPAY\Checkout\Models\Request\Forward as ForwardRequest;
$forwardRequest = new ForwardRequest(
reference: 'FWD-00042',
amount: 299,
recipient: '1001',
description: 'Forwarding to merchant 1001',
);
$forward = $checkoutClient->forward(
forward: $forwardRequest,
checkoutId: $paymentKey,
);

The SDK accepts either a recipient ID string or a Recipient model.

When a string is supplied:

recipient: '1001'

the SDK serializes it as:

{
"recipient": {
"id": "1001"
}
}

All CheckoutClient operations use the same base exception:

ICEPAY\Checkout\Exceptions\ApiException

This includes:

  • API errors returned by ICEPAY;
  • typed API exceptions;
  • transport failures such as timeouts, DNS errors, or refused connections.

A general error handler can therefore catch ApiException:

use ICEPAY\Checkout\Exceptions\ApiException;
try {
$response = $checkoutClient->createCheckout($checkoutRequest);
} catch (ApiException $exception) {
$message = $exception->getMessage();
$httpStatus = $exception->getCode();
$type = $exception->type;
$errors = $exception->errors;
// Log or handle the failure.
}

For transport failures, the status code can be 0.

The SDK also provides more specific exception classes for particular API problems. You can catch those when your application needs different behavior for a specific error, while keeping ApiException as the general fallback.

For example:

use ICEPAY\Checkout\Exceptions\ApiException;
use ICEPAY\Checkout\Exceptions\Payment\NotFound;
try {
$payment = $checkoutClient->getCheckout($paymentKey);
} catch (NotFound $exception) {
// Handle a payment that does not exist.
} catch (ApiException $exception) {
// Handle any other ICEPAY or connection error.
}

For platform integrations or reusable modules, the checkout request can include integration information:

$checkoutRequest->withIntegrationInformation(
type: 'custom',
version: '1.0.0',
developer: 'Example Developer',
);

The SDK stores this information in the payment metadata.

This can be useful when the same ICEPAY account receives payments from different integration types or versions.

For most integrations, automatic PSR implementation discovery is enough.

Advanced integrations can provide their own PSR-18 client by constructing the SDK’s HttpClient:

use ICEPAY\Checkout\CheckoutClient;
use ICEPAY\Checkout\HttpClient;
use Psr\Http\Client\ClientInterface;
/** @var ClientInterface $psrHttpClient */
$httpClient = new HttpClient(
client: $psrHttpClient,
);
$checkoutClient = (new CheckoutClient($httpClient))->withAuthorization(
merchantId: $merchantId,
merchantSecret: $merchantSecret,
);

This is useful when your application or framework already has its own HTTP transport layer.

The main CheckoutClient operations are:

SDK method Purpose
createCheckout($checkoutRequest) Create a payment.
getCheckout($paymentKey) Retrieve a payment.
getPaymentMethods() Retrieve available payment methods.
refund($refundRequest, $paymentKey) Refund a payment.
forward($forwardRequest, $paymentKey) Forward payment funds.

These methods correspond to the Checkout API operations while keeping request creation, response mapping, and exception handling inside the SDK.

For a normal PHP checkout integration:

  1. Install and configure the SDK

    Configure the CheckoutClient with your Merchant ID and Merchant Secret.

  2. Create the payment

    Build a CheckoutRequest with your reference, amount, redirect URL, and webhook URL.

  3. Store the payment key

    Save $response->key together with your own order or payment record.

  4. Redirect the customer

    Send the customer to $response->links->checkout, or use the direct link when you deliberately preselect a supported payment method.

  5. Process postbacks

    Use PostbackHandler::handle() to verify and parse status updates.

  6. Update your own payment record

    Store the returned status and financialStatus separately.

  7. Use additional operations when needed

    Retrieve the payment, refund it, forward funds, or retrieve available payment methods through the same CheckoutClient.

The SDK handles the HTTP requests and response mapping for you, but the underlying Checkout API is still useful when you need to understand the exact endpoint behavior or payload structure.