Cost+Docs

C# / .NET

Official C#/.NET SDK for the Cost+ payment gateway

Official C#/.NET SDK for the Cost+ payment gateway. Simplifies the HPP (Hosted Payment Page) redirect flow, HMAC payload signing, and webhook verification.

Features

  • Zero dependencies — uses only built-in System.Text.Json and System.Security.Cryptography
  • Targets .NET 8.0 with C# 12 features (records, file-scoped namespaces, pattern matching)
  • Nullable reference types enabled throughout
  • HMAC-SHA256 signature generation and constant-time verification
  • Automatic snake_case/PascalCase mapping between the API and the SDK
  • Webhook parsing + API-based order verification
  • Fully async API surface

Requirements

Installation

dotnet add package NoPayn

Or as a local project reference:

dotnet add reference path/to/src/NoPayn/NoPayn.csproj

Quick Start

1. Initialise the Client

using NoPayn;
using NoPayn.Models;

var nopayn = new NoPaynClient(new NoPaynConfig(
    ApiKey: "your-api-key",
    MerchantId: "your-project"
));

2. Create a Payment and Redirect to the HPP

var result = await nopayn.GeneratePaymentUrlAsync(new CreateOrderParams
{
    Amount = 1295,              // €12.95 in cents
    Currency = "EUR",
    MerchantOrderId = "ORDER-001",
    Description = "Premium Widget",
    ReturnUrl = "https://shop.example.com/success",
    FailureUrl = "https://shop.example.com/failure",
    WebhookUrl = "https://shop.example.com/webhook",
    Locale = "en-GB",
    ExpirationPeriod = "PT30M",
});

// Redirect the customer
// result.OrderUrl   → HPP (customer picks payment method)
// result.PaymentUrl → direct link to the first transaction's payment method
// result.Signature  → HMAC-SHA256 for verification
// result.OrderId    → NoPayn order UUID

3. Handle the Webhook

app.MapPost("/webhook", async (HttpContext ctx) =>
{
    using var reader = new StreamReader(ctx.Request.Body);
    var rawBody = await reader.ReadToEndAsync();
    var verified = await nopayn.VerifyWebhookAsync(rawBody);

    Console.WriteLine(verified.Order.Status); // "completed", "cancelled", etc.
    Console.WriteLine(verified.IsFinal);      // true when the order won't change

    if (verified.Order.Status == "completed")
    {
        // Fulfil the order
    }

    return Results.Ok();
});

API Reference

new NoPaynClient(config, httpClient?)

ParameterTypeRequiredDefault
ApiKeystringYes
MerchantIdstringYes
BaseUrlstringNohttps://api.nopayn.co.uk

An optional HttpClient can be passed as the second constructor parameter for custom HTTP handling or testing.

client.CreateOrderAsync(params): Task<Order>

Creates an order via POST /v1/orders/.

ParameterTypeRequiredDescription
AmountintYesAmount in smallest currency unit (cents)
CurrencystringYesISO 4217 code (EUR, GBP, USD, NOK, SEK)
MerchantOrderIdstring?NoYour internal order reference
Descriptionstring?NoOrder description
ReturnUrlstring?NoRedirect after successful payment
FailureUrlstring?NoRedirect on cancel/expiry/error
WebhookUrlstring?NoAsync status-change notifications
Localestring?NoHPP language (en-GB, de-DE, nl-NL, etc.)
PaymentMethodsIReadOnlyList<string>?NoFilter HPP methods
ExpirationPeriodstring?NoISO 8601 duration (PT30M)

Available payment methods: credit-card, apple-pay, google-pay, vipps-mobilepay

client.GetOrderAsync(orderId): Task<Order>

Fetches order details via GET /v1/orders/{id}/.

client.CreateRefundAsync(orderId, amount, description?): Task<Refund>

Issues a full or partial refund via POST /v1/orders/{id}/refunds/.

client.GeneratePaymentUrlAsync(params): Task<PaymentUrlResult>

Convenience method that creates an order and returns:

public record PaymentUrlResult(
    string OrderId,        // NoPayn order UUID
    string OrderUrl,       // HPP URL
    string? PaymentUrl,    // Direct payment URL (first transaction)
    string Signature,      // HMAC-SHA256 of amount:currency:orderId
    Order Order            // Full order object
);

client.GenerateSignature(amount, currency, orderId): string

Generates an HMAC-SHA256 hex signature.

client.VerifySignature(amount, currency, orderId, signature): bool

Constant-time verification of an HMAC-SHA256 signature.

client.VerifyWebhookAsync(rawBody): Task<VerifiedWebhook>

Parses the webhook body, then calls GET /v1/orders/{id}/ to verify the actual status.

Standalone HMAC Utilities

using NoPayn;

var sig = NoPaynSignature.Generate("your-api-key", 1295, "EUR", "order-uuid");
var ok  = NoPaynSignature.Verify("your-api-key", 1295, "EUR", "order-uuid", sig);

Error Handling

using NoPayn.Exceptions;

try
{
    await nopayn.CreateOrderAsync(new CreateOrderParams { Amount = 100, Currency = "EUR" });
}
catch (ApiException ex)
{
    Console.Error.WriteLine(ex.StatusCode);  // 401, 400, etc.
    Console.Error.WriteLine(ex.ErrorBody);   // Raw API error response
}
catch (NoPaynException ex)
{
    Console.Error.WriteLine(ex.Message);     // Network or parsing error
}
ExceptionDescription
NoPaynExceptionBase exception (network, parsing)
ApiExceptionHTTP error from the API
WebhookExceptionInvalid webhook payload

Order Statuses

StatusFinal?Description
newNoOrder created
processingNoPayment in progress
completedYesPayment successful — deliver the goods
cancelledYesPayment cancelled by customer
expiredYesPayment link timed out
errorYesTechnical failure

Webhook Best Practices

  1. Always verify via the API — the webhook payload only contains the order ID, never the status. The SDK's VerifyWebhookAsync() does this automatically.
  2. Return HTTP 200 to acknowledge receipt. Any other code triggers up to 10 retries (2 minutes apart).
  3. Implement a backup poller — for orders older than 10 minutes that haven't reached a final status, poll GetOrderAsync() as a safety net.
  4. Be idempotent — you may receive the same webhook multiple times.

Test Cards

Use these cards in Cost+ test mode (sandbox website):

CardNumberNotes
Visa (success)4111 1111 1111 1111Any CVV
Mastercard (success)5544 3300 0003 7Any CVV
Visa (declined)4111 1111 1111 1105Do Not Honor
Visa (insufficient funds)4111 1111 1111 1151Insufficient Funds

Use any future expiry date and any 3-digit CVC.

Demo App

A Docker-based ASP.NET Core demo app is included in the GitHub repository for testing the full payment flow.

Support

Need help? Reach out to our support team at support@costplus.io.

On this page