Integrate with the .NET API Client
Learn how to integrate ICEPAY payments in a .NET application using the official .NET API Client.
The ICEPAY .NET API Client provides a .NET interface around the ICEPAY Checkout API.
It handles authentication, serializes request models, sends requests to the ICEPAY Checkout API, and deserializes 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; and
- forwarding payment funds.
How the client fits into your integration
Section titled “How the client fits into your integration”The API client 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 API Client requires:
- .NET 8 or newer;
- an ICEPAY Merchant ID;
- an ICEPAY Merchant Secret;
- an HTTPS URL for your redirect endpoint; and
- an HTTPS URL for your webhook endpoint.
The current SDK project targets .NET 8:
<TargetFramework>net8.0</TargetFramework>Install the client
Section titled “Install the client”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.");
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 completes the payment on the hosted ICEPAY Checkout 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 expiration period. |
You can set values using object initializers:
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;Description; andLogo.
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 API Client does not provide a built-in postback 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; and
- deserialize 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(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:
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.");
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(); }
var payment = JsonSerializer.Deserialize<Payment>( rawBody, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (payment is null) { return Results.BadRequest(); }
// Locate your payment using payment.Key. // Update your stored payment status. // Keep webhook processing idempotent.
return Results.Ok();});Make webhook handling idempotent
Section titled “Make webhook handling idempotent”Your webhook endpoint may receive the same notification more than once.
Use the payment key and status, or another unique event identifier if available, to prevent duplicate processing:
if (await paymentRepository.HasAlreadyProcessedAsync( payment.Key, payment.Status)){ return Results.Ok();}
await paymentRepository.UpdateAsync( payment.Key, payment.Status);
await paymentRepository.MarkAsProcessedAsync( payment.Key, payment.Status);
return Results.Ok();Your application should return a successful response after a valid notification has been accepted.
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 API Client 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 direct API integration approach described in the API Explorer, 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 deserializes 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; and
- 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
Deserialize 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.
Explore the API
Section titled “Explore the API”The .NET API Client handles authentication, request serialization, and response deserialization for the operations it supports.
The underlying Checkout API is still useful when you need to understand exact endpoint behavior, request fields, response structures, or operations that are not currently exposed by the client.

