Integrate with the .NET Checkout SDK
Learn how to integrate ICEPAY payments in a .NET application using the official .NET Checkout SDK.
The ICEPAY .NET Checkout SDK provides a .NET interface around the ICEPAY Checkout API.
It handles authentication, serialises request models, sends requests to the ICEPAY Checkout API, and deserialises API responses into .NET models.
This guide walks through a complete integration, including:
- installing and configuring the client;
- creating a checkout;
- redirecting the customer;
- retrieving available payment methods;
- handling webhook signatures;
- issuing refunds
- forwarding payment funds.
How the SDK fits into your integration
Section titled “How the SDK fits into your integration”The .NET Checkout SDK runs in your server-side .NET application and communicates with the ICEPAY Checkout API.
-
Create a checkout request
Build a checkout using the SDK request models.
-
Configure the client
Configure the client with your Merchant ID and Merchant Secret.
-
Send the checkout to ICEPAY
Call
CreateCheckout()to create the payment. -
Redirect the customer
ICEPAY returns a hosted checkout URL. Redirect the customer to that URL.
-
Handle payment updates
ICEPAY sends payment updates to your webhook URL. Verify the webhook signature before processing the payment.
-
Manage the payment when needed
Use the client to issue refunds or forward payment funds.
Requirements
Section titled “Requirements”The .NET Checkout SDK requires:
- .NET 8 or newer;
- an ICEPAY Merchant ID;
- an ICEPAY Merchant Secret;
- an HTTPS URL for your redirect endpoint
- an HTTPS URL for your webhook endpoint.
The current SDK project targets .NET 8:
<TargetFramework>net8.0</TargetFramework>Install the SDK
Section titled “Install the SDK”The SDK can be added to your solution as a project reference.
From your application directory, add a reference to the SDK project:
dotnet add reference ../Checkout-DotNet-SDK/ICEPAY_CheckoutSDK/ICEPAY_CheckoutSDK.csprojThen import the SDK namespace in your application:
using ICEPAY.CheckoutSDK;using ICEPAY.CheckoutSDK.Models;using ICEPAY.CheckoutSDK.Models.Request;Configure your credentials
Section titled “Configure your credentials”Keep your Merchant ID and Merchant Secret outside your source code.
For local development, the example application uses .NET user secrets:
dotnet user-secrets initdotnet user-secrets set "ICEPAY:MERCHANT_ID" "your-merchant-id"dotnet user-secrets set "ICEPAY:MERCHANT_SECRET" "your-merchant-secret"In an ASP.NET Core application, you can also use environment variables:
ICEPAY__MERCHANT_ID=your-merchant-idICEPAY__MERCHANT_SECRET=your-merchant-secretRead the credentials through IConfiguration and configure the client:
using ICEPAY.CheckoutSDK;
var merchantId = configuration["ICEPAY:MERCHANT_ID"] ?? throw new InvalidOperationException( "Missing ICEPAY Merchant ID.");
var merchantSecret = configuration["ICEPAY:MERCHANT_SECRET"] ?? throw new InvalidOperationException( "Missing ICEPAY Merchant Secret.");
if (string.IsNullOrWhiteSpace(merchantId) || string.IsNullOrWhiteSpace(merchantSecret)){ throw new InvalidOperationException("Missing ICEPAY credentials.");}
var checkoutClient = new CheckoutClient() .WithAuthorization(merchantId, merchantSecret);WithAuthorization() configures HTTP Basic Authentication using your Merchant ID and Merchant Secret.
Create your first payment
Section titled “Create your first payment”A payment starts with a Checkout request model.
The amount is represented in minor currency units. For example, €2.99 is represented as 299.
using ICEPAY.CheckoutSDK.Models;using ICEPAY.CheckoutSDK.Models.Request;
var checkoutRequest = new Checkout() .WithReference("ORD-16307") .WithDescription("Ice cream dipped in sprinkles") .WithAmount(new Amount(299, Amount.CurrencyEur)) .WithRedirectUrl( "https://merchant.example.com/payment-complete") .WithWebhookUrl( "https://merchant.example.com/payment-webhook");
var payment = await checkoutClient.CreateCheckout(checkoutRequest);The response contains the payment key and links returned by ICEPAY:
if (payment is null){ throw new InvalidOperationException( "ICEPAY did not return a payment.");}
var paymentKey = payment.Key;var checkoutUrl = payment.Links.Checkout;var paymentStatus = payment.Status;Store the returned payment key with your own order or payment record. You need it when issuing a refund or forwarding payment funds.
Understand the amount
Section titled “Understand the amount”The Amount model accepts an integer value and a currency.
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:
var amount = new Amount( 299, Amount.CurrencyEur);The SDK provides constants for supported currencies:
Amount.CurrencyEurAmount.CurrencyGbpAmount.CurrencyUsdAmount.CurrencyPlnAmount.CurrencySekAmount.CurrencyNokAmount.CurrencyDkkAmount.CurrencyCzkRedirect the customer
Section titled “Redirect the customer”After creating the payment, redirect the customer to the hosted ICEPAY Checkout Page.
For example, in an ASP.NET Core minimal API:
app.MapPost("/checkout", async ( CheckoutClient checkoutClient) =>{ var checkoutRequest = new Checkout() .WithReference("ORD-16307") .WithDescription("Ice cream dipped in sprinkles") .WithAmount(new Amount(299, Amount.CurrencyEur)) .WithRedirectUrl( "https://merchant.example.com/payment-complete") .WithWebhookUrl( "https://merchant.example.com/payment-webhook");
var payment = await checkoutClient.CreateCheckout(checkoutRequest);
if (payment?.Links?.Checkout is null) { return Results.Problem( "ICEPAY did not return a checkout URL."); }
return Results.Redirect(payment.Links.Checkout);});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.
The payment response may also contain a direct payment-method URL:
if (payment?.Links?.Direct is not null){ var directPaymentUrl = payment.Links.Direct;}Use the direct link only when your application deliberately preselects a supported payment method.
Configure the checkout request
Section titled “Configure the checkout request”The Checkout request 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 where the customer returns after checkout. |
WebhookUrl |
string |
URL where ICEPAY sends payment updates. |
PaymentMethod |
PaymentMethod |
Optional payment method selection. |
Meta |
Metadata |
Additional metadata sent with the payment. |
ExpireAfter |
int? |
Optional payment expiry period. |
You can set values using object initialisers:
var checkoutRequest = new Checkout{ Reference = "ORD-16307", Description = "Ice cream dipped in sprinkles", Amount = new Amount(299, Amount.CurrencyEur), RedirectUrl = "https://merchant.example.com/payment-complete", WebhookUrl = "https://merchant.example.com/payment-webhook", ExpireAfter = 3600};Or use the fluent helper methods:
var checkoutRequest = new Checkout() .WithReference("ORD-16307") .WithDescription("Ice cream dipped in sprinkles") .WithAmount(new Amount(299, Amount.CurrencyEur)) .WithRedirectUrl("https://merchant.example.com/payment-complete") .WithWebhookUrl("https://merchant.example.com/payment-webhook") .WithExpireAfter(3600);Add customer information
Section titled “Add customer information”Add a customer email address with WithCustomerEmail():
checkoutRequest.WithCustomerEmail( "customer@example.com");The customer information is stored in the request metadata:
var checkoutRequest = new Checkout() .WithReference("ORD-16307") .WithDescription("Example order") .WithAmount(new Amount(299, Amount.CurrencyEur)) .WithCustomerEmail("customer@example.com");For more customer fields, use a JsonObject:
using System.Text.Json.Nodes;
var customer = new JsonObject{ ["email"] = "customer@example.com", ["firstName"] = "John", ["lastName"] = "Doe"};
checkoutRequest.WithCustomer(customer);Let the customer choose a payment method
Section titled “Let the customer choose a payment method”If you do not set a payment method, redirect the customer to the hosted checkout URL:
var checkoutUrl = payment.Links.Checkout;The 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:
checkoutRequest.WithPaymentMethod("ideal");You can also set the request property directly:
checkoutRequest.PaymentMethod = new PaymentMethod{ Type = "ideal"};The payment-method type must be supported and enabled for the merchant account.
Add integration information
Section titled “Add integration information”For platform integrations or reusable modules, include integration information in the payment metadata:
checkoutRequest.WithIntegrationInformation( type: "custom", version: "1.0.0", developer: "Example Developer");This can be useful when the same ICEPAY Account receives payments from different integration types or versions.
Retrieve available payment methods
Section titled “Retrieve available payment methods”Use GetPaymentMethods() to retrieve the payment methods available to the authenticated merchant:
var paymentMethods = await checkoutClient.GetPaymentMethods();
foreach (var paymentMethod in paymentMethods ?? []){ Console.WriteLine(paymentMethod.Id); Console.WriteLine(paymentMethod.Description); Console.WriteLine(paymentMethod.Logo);}Each payment method can contain:
Id;DescriptionLogo.
Handle payment updates
Section titled “Handle payment updates”After a payment status changes, ICEPAY sends a request to the WebhookUrl supplied when the payment was created.
A webhook is a server-to-server notification. It does not depend on the customer keeping the browser open or successfully returning to your website.
The current .NET Checkout SDK does not provide a built-in webhook handler. Your webhook endpoint must:
- read the raw request body;
- read the
ICEPAY-Signatureheader; - calculate the expected HMAC signature;
- compare the signatures using a timing-safe comparison
- deserialise and process the payment only after verification succeeds.
Verify a webhook signature
Section titled “Verify a webhook signature”The signature is a Base64-encoded HMAC-SHA256 value generated from the raw request body and your Merchant Secret.
The following helper verifies a signature:
using System.Security.Cryptography;using System.Text;
static bool VerifyWebhookSignature( string rawBody, string receivedSignature, string merchantSecret){ if (string.IsNullOrWhiteSpace(merchantSecret)) { throw new InvalidOperationException("Missing ICEPAY Merchant Secret."); }
if (string.IsNullOrWhiteSpace(receivedSignature)) { return false; }
byte[] expectedSignature;
using (var hmac = new HMACSHA256( Encoding.UTF8.GetBytes(merchantSecret))) { expectedSignature = hmac.ComputeHash( Encoding.UTF8.GetBytes(rawBody)); }
byte[] receivedSignatureBytes;
try { receivedSignatureBytes = Convert.FromBase64String(receivedSignature); } catch (FormatException) { return false; }
return CryptographicOperations.FixedTimeEquals( expectedSignature, receivedSignatureBytes);}Use the helper in an ASP.NET Core endpoint. Replace the processing comments with the validation and durable update logic described below before returning a successful response:
using System.Text.Json;using ICEPAY.CheckoutSDK.Models;
app.MapPost("/payment-webhook", async ( HttpRequest request, IConfiguration configuration) =>{ var merchantSecret = configuration["ICEPAY:MERCHANT_SECRET"] ?? throw new InvalidOperationException( "Missing ICEPAY Merchant Secret.");
if (string.IsNullOrWhiteSpace(merchantSecret)) { throw new InvalidOperationException("Missing ICEPAY Merchant Secret."); }
using var reader = new StreamReader(request.Body); var rawBody = await reader.ReadToEndAsync();
var receivedSignature = request.Headers["ICEPAY-Signature"].ToString();
var signatureIsValid = VerifyWebhookSignature( rawBody, receivedSignature, merchantSecret);
if (!signatureIsValid) { return Results.Unauthorized(); }
Payment? payment;
try { payment = JsonSerializer.Deserialize<Payment>( rawBody, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); } catch (JsonException) { return Results.BadRequest(); }
if (payment is null) { return Results.BadRequest(); }
// 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.
return Results.Ok();});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
- 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.
Payment status
Section titled “Payment status”The SDK maps payment status values to the Status enum:
public enum Status{ Started, Completed, Pending, Expired, Cancelled}For example:
switch (payment.Status){ case Status.Started: // The payment has started. break;
case Status.Pending: // The payment is awaiting completion. break;
case Status.Completed: // The payment was completed. break;
case Status.Expired: // The payment expired. break;
case Status.Cancelled: // The payment was cancelled. break;}Store the ICEPAY payment status separately from your own order status. Your application may need additional business rules before an order is fulfilled.
Retrieve a payment
Section titled “Retrieve a payment”Store the payment key returned when the payment is created:
var paymentKey = payment.Key;The current .NET Checkout SDK exposes methods for creating payments, retrieving payment methods, issuing refunds, and forwarding funds. It does not currently expose a public method for retrieving an existing payment by key.
If you need to retrieve a payment after creation, use the Checkout API integration approach described in the Checkout API integration guide, or extend the client with a payment-retrieval method.
Refund a payment
Section titled “Refund a payment”Create a refund request using ICEPAY.CheckoutSDK.Models.Request.Refund.
The refund amount uses minor currency units:
using ICEPAY.CheckoutSDK.Models;using ICEPAY.CheckoutSDK.Models.Request;
var refundRequest = new Refund( reference: "RFD-00069", amount: new Amount( 299, Amount.CurrencyEur ), description: "Customer refund");
var refund = await checkoutClient.Refund( refundRequest, paymentKey);You can also build the request with the fluent methods:
var refundRequest = new Refund( "RFD-00069", new Amount(299, Amount.CurrencyEur));
refundRequest .WithDescription("Customer refund");
var refund = await checkoutClient.Refund( refundRequest, paymentKey);The paymentKey must identify the payment being refunded.
Forward payment funds
Section titled “Forward payment funds”Use the Forward request model to forward payment funds to another recipient.
using ICEPAY.CheckoutSDK.Models;using ICEPAY.CheckoutSDK.Models.Request;
var forwardRequest = new Forward() .WithReference("FWD-00042") .WithDescription("Forwarding payment funds") .WithAmount(new Amount(299, Amount.CurrencyEur)) .WithRecipient("1001");
var forwarded = await checkoutClient.Forward( forwardRequest, paymentKey);You can also provide a Recipient object:
var recipient = new Recipient{ Id = "1001"};
var forwardRequest = new Forward() .WithReference("FWD-00042") .WithDescription("Forwarding payment funds") .WithAmount(new Amount(299, Amount.CurrencyEur)) .WithRecipient(recipient);The recipient ID must identify a valid ICEPAY recipient.
Handle API errors
Section titled “Handle API errors”The current client deserialises the response returned by ICEPAY but does not expose a dedicated SDK exception hierarchy or automatically call EnsureSuccessStatusCode().
For production integrations, check whether the returned model is null and consider wrapping calls with your own error-handling logic:
try{ var payment = await checkoutClient.CreateCheckout( checkoutRequest);
if (payment is null) { throw new InvalidOperationException( "ICEPAY returned an empty response."); }}catch (HttpRequestException exception){ // Handle network failures, DNS errors, and timeouts. Console.Error.WriteLine(exception.Message);}catch (TaskCanceledException exception){ // Handle request cancellation or timeout. Console.Error.WriteLine(exception.Message);}For detailed HTTP status handling and API error payloads, use a custom HttpClient or extend the SDK’s request handling.
Use a custom HTTP client
Section titled “Use a custom HTTP client”The CheckoutClient accepts an optional HttpMessageInvoker. This allows you to provide a configured HttpClient:
using System.Net.Http;
var httpClient = new HttpClient{ Timeout = TimeSpan.FromSeconds(30)};
var checkoutClient = new CheckoutClient(httpClient) .WithAuthorization( merchantId, merchantSecret );You can use this to configure:
- request timeouts;
- proxy settings;
- custom handlers;
- logging
- test HTTP handlers.
For ASP.NET Core applications, register the client with dependency injection:
builder.Services.AddHttpClient("icepay");
builder.Services.AddSingleton(serviceProvider =>{ var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
var configuration = serviceProvider.GetRequiredService<IConfiguration>();
var httpClient = httpClientFactory.CreateClient("icepay");
var merchantId = configuration["ICEPAY:MERCHANT_ID"] ?? throw new InvalidOperationException( "Missing ICEPAY Merchant ID.");
var merchantSecret = configuration["ICEPAY:MERCHANT_SECRET"] ?? throw new InvalidOperationException( "Missing ICEPAY Merchant Secret.");
return new CheckoutClient(httpClient) .WithAuthorization(merchantId, merchantSecret);});Available client operations
Section titled “Available client operations”The main CheckoutClient operations are:
| Method | Purpose |
|---|---|
CreateCheckout(checkout) |
Create a payment. |
GetPaymentMethods() |
Retrieve available payment methods. |
Refund(refund, checkoutId) |
Refund a payment. |
Forward(forward, checkoutId) |
Forward payment funds. |
WithAuthorization(merchantId, merchantSecret) |
Configure merchant authentication. |
These methods correspond to the main Checkout API operations supported by the current .NET client.
Recommended integration flow
Section titled “Recommended integration flow”For a normal .NET integration:
-
Configure the client
Configure
CheckoutClientwith your Merchant ID and Merchant Secret. -
Create the payment
Build a
Checkoutrequest with your reference, amount, redirect URL, and webhook URL. -
Store the payment key
Save
payment.Keywith your own order or payment record. -
Redirect the customer
Send the customer to
payment.Links.Checkout. -
Process webhook notifications
Read the raw request body and verify the
ICEPAY-Signatureheader. -
Update your payment record
Deserialise the verified webhook payload and update your stored payment status.
-
Use additional operations when needed
Issue a refund or forward payment funds through the same
CheckoutClient.
Have a question?
Section titled “Have a question?”Contact the ICEPAY team with any questions about your SDK integration.

