Integrate with the PHP Checkout SDK
Learn how to integrate ICEPAY payments in a PHP application using the official PHP Checkout SDK.
The PHP Checkout SDK provides a PHP interface around the ICEPAY Checkout API. It handles HTTP communication, maps API responses to PHP models, provides consistent exception handling, and includes a webhook handler for verifying payment status notifications.
This guide walks through a complete integration, from installing the SDK and creating a payment to handling webhooks, retrieving payments, issuing refunds, and forwarding funds.
How the SDK fits into your integration
Section titled “How the SDK fits into your integration”The SDK runs in your backend application and communicates with the ICEPAY Checkout API for you.
-
Create a checkout request
Build the payment using the SDK’s request and amount models.
-
Send the payment to ICEPAY
Call
createCheckout()on theCheckoutClient. -
Redirect the customer
ICEPAY returns a checkout URL through the response model. Redirect the customer to that URL.
-
Handle payment updates
Use the SDK’s
PostbackHandlerto verify incoming ICEPAY webhooks and parse the updated payment. -
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.
Requirements
Section titled “Requirements”The PHP 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
Section titled “Install the SDK”Install the SDK with Composer:
composer require icepay/checkout-sdkThe 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:
composer require icepay/checkout-sdk nyholm/psr7 symfony/http-clientConfigure your credentials
Section titled “Configure your credentials”Keep your credentials outside your source code, for example as environment variables:
ICEPAY_MERCHANT_ID=your_merchant_idICEPAY_MERCHANT_SECRET=your_merchant_secretCreate 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 ( !is_string($merchantId) || $merchantId === '' || !is_string($merchantSecret) || $merchantSecret === '') { 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.
Create your first payment
Section titled “Create your first payment”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();Redirect the customer
Section titled “Redirect the customer”After creating the payment, redirect the customer to the returned checkout URL:
header('Location: ' . $response->links->checkout, true, 303);exit;The customer selects a method on the ICEPAY Checkout Page, then completes the payment on the ICEPAY Payment Page or the payment provider’s page.
Understanding the amount
Section titled “Understanding the amount”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,);Configure the checkout request
Section titled “Configure the checkout request”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 expiry period in minutes. |
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), expireAfter: 60,);
$checkoutRequest ->withRedirectUrl('https://merchant.example.com/payment-complete') ->withWebhookUrl('https://merchant.example.com/payment-webhook') ->withCustomerEmail('johndoe@example.com');expireAfter: 60 keeps the payment open for 60 minutes (1 hour). If omitted, the API default is 240 minutes (4 hours). The maximum is 44,640 minutes (31 days).
Add customer information
Section titled “Add customer information”For a customer email address, use:
$checkoutRequest->withCustomerEmail('johndoe@example.com');You can also provide customer information as an array:
$checkoutRequest->withCustomer([ 'email' => 'johndoe@example.com', 'firstName' => 'John', 'lastName' => 'Doe',]);The SDK stores this information in the request metadata before serialising the payment request.
Let the customer choose a payment method
Section titled “Let the customer choose a payment method”If you do not set paymentMethod, redirect the customer to:
$response->links->checkoutThe hosted Checkout Page can then present the available payment methods to the customer.
Preselect a payment method
Section titled “Preselect a payment method”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->directBecause 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;}Retrieve available payment methods
Section titled “Retrieve available payment methods”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;Handle payment webhooks
Section titled “Handle payment webhooks”After a payment status changes, ICEPAY sends a request to the webhookUrl supplied when you created the payment.
The PHP SDK uses the name postback for a webhook. Its class name remains PostbackHandler; both terms refer to the same payment status notification.
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
Checkoutresponse model.
This means you do not have to implement the HMAC verification yourself.
Basic PHP example
Section titled “Basic PHP example”<?php
use ICEPAY\Checkout\Exceptions\InvalidSignature;use ICEPAY\Checkout\PostbackHandler;
$merchantSecret = getenv('ICEPAY_MERCHANT_SECRET');
if (!is_string($merchantSecret) || $merchantSecret === '') { throw new RuntimeException('Missing ICEPAY Merchant Secret.');}
$handler = new PostbackHandler( merchantSecret: $merchantSecret,);
$body = file_get_contents('php://input');$signature = $_SERVER['HTTP_ICEPAY_SIGNATURE'] ?? '';
if ($body === false) { http_response_code(400); return;}
try { $payment = $handler->handle($body, $signature);} catch (InvalidSignature) { http_response_code(400); return;}
$paymentKey = $payment->key;$status = $payment->status->toString();$financialStatus = $payment->financialStatus->toString();
// Find the order using $payment->key.// Check that the payment matches the stored order details.// Ignore duplicate or older updates.// Update the payment and queue any additional work safely.Validate the payment before updating your order
Section titled “Validate the payment before updating your order”After verifying the webhook signature, make sure the payment belongs to the order you expect.
Use the payment key that you stored when the payment was created. Before updating or fulfilling the order, check important values such as:
- the merchant;
- the processing mode (Test mode or Live mode);
- the amount; and
- the currency.
Reject unknown payment keys or values that do not match your stored payment information.
Handle duplicate webhook updates
Section titled “Handle duplicate webhook updates”Your endpoint may receive the same payment update more than once.
Make sure processing the same update again does not repeat actions such as:
- fulfilling an order;
- sending a confirmation email;
- updating inventory; or
- performing another action that should only happen once.
Store the payment state and completed actions in your database. When updating an order, use a database transaction or equivalent protection so two webhook requests arriving at the same time cannot perform the same action twice.
If additional work is processed asynchronously, make sure those background jobs are also safe to run more than once.
Handle delayed webhook updates
Section titled “Handle delayed webhook updates”Webhook updates may arrive late or in a different order than expected.
Do not allow an older update to overwrite a newer payment state. If you cannot reliably determine which update is newer, retrieve the current payment through the ICEPAY Checkout API before updating your order.
Refunds and forwarded payments can change independently from the main payment status, so track them separately.
Return the response
Section titled “Return the response”Return HTTP 200 only after the verified update has been processed or safely stored for later processing.
If storing or processing the update fails, return an unsuccessful response so the webhook can be retried.
A duplicate update that was already processed successfully can return HTTP 200.
See Process webhooks for more information about validating and processing webhook updates.
Verify without parsing
Section titled “Verify without parsing”If you only need to check the authenticity of a webhook:
if (!$handler->verify($body, $signature)) { http_response_code(400); return;}Using a PSR-7 request
Section titled “Using a PSR-7 request”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.
Retrieve a payment
Section titled “Retrieve a payment”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();Payment statuses
Section titled “Payment statuses”The SDK maps the payment status to the Status enum.
Possible values handled by the SDK are:
startedpendingcompletedexpiredcancelledunknownThe separate financial status is represented by the FinancialStatus enum:
unclearedclearedunknownRefund a payment
Section titled “Refund a payment”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 serialises it as the request’s amount.value:
$refundRequest = new RefundRequest( reference: 'RFD-00069', amount: 299, description: null,);Forward payment funds
Section titled “Forward payment funds”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 serialises it as:
{ "recipient": { "id": "1001" }}Handle SDK errors
Section titled “Handle SDK errors”All CheckoutClient operations use the same base exception:
ICEPAY\Checkout\Exceptions\ApiExceptionThis 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 behaviour 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.}Add integration information
Section titled “Add integration information”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.
Use a custom HTTP client
Section titled “Use a custom HTTP client”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.
Available client operations
Section titled “Available client operations”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.
Recommended integration flow
Section titled “Recommended integration flow”For a normal PHP checkout integration:
-
Install and configure the SDK
Configure the
CheckoutClientwith your Merchant ID and Merchant Secret. -
Create the payment
Build a
CheckoutRequestwith your reference, amount, redirect URL, and webhook URL. -
Store the payment key
Save
$response->keytogether with your own order or payment record. -
Redirect the customer
Send the customer to
$response->links->checkout, or use the direct link when you deliberately preselect a supported payment method. -
Process webhooks
Use
PostbackHandler::handle()to verify and parse status updates. -
Update your own payment record
Store the returned
statusandfinancialStatusseparately. -
Use additional operations when needed
Retrieve the payment, refund it, forward funds, or retrieve available payment methods through the same
CheckoutClient.
Have a question?
Section titled “Have a question?”Contact the ICEPAY team with any questions about your SDK integration.

