SilverLabs.SilverDotPay.Sdk 1.9.0

SilverDotPay SDK

Typed .NET client for the SilverDotPay payment gateway.

Install

dotnet add package SilverLabs.SilverDotPay.Sdk

The SilverLABS NuGet feed must be configured. In nuget.config:

<add key="silverlabs" value="https://nuget.silverlabs.uk/v3/index.json" />

Quick start

services.AddSilverDotPaySdk(opts =>
{
    opts.BaseAddress = "https://silverdotpay.silverlabs.uk";
    opts.Mode = SilverDotPayClientMode.ApiKey;
    opts.ApiKey = configuration["SilverDotPay:ApiKey"];
    opts.Timeout = TimeSpan.FromSeconds(30);
})
.AddPublicClient()      // currencies + exchange rates (the server still requires X-API-Key on these routes)
.AddMerchantClient()    // orders, own API keys, Stripe Connect
.AddAdminClient()       // applications, keys, webhook secrets, settings, Stripe Connect
.AddCardClient();       // card rail: charge-saved, setup, saved cards, reconcile (1.3.0+)

Then inject any of:

  • ISilverDotPayClient — narrow legacy surface (order create, get). Its escrow members call parked routes (404) and go in 2.0 (see "Towards 2.0").
  • ISilverDotPayPublicClient — currencies, exchange rates. Despite the name, the server requires an API key on these routes today; configure the client in ApiKey mode.
  • ISilverDotPayMerchantClient — orders, the application's own API keys, Stripe Connect. Balance and withdrawal members are [Obsolete]; the escrow members call parked routes (404) and go in 2.0.
  • ISilverDotPayAdminClient — applications (incl. WalletMode, ExtendedPublicKeys), API keys, webhook secret rotation and delivery log, settings, Stripe Connect. Balance members are [Obsolete]; the escrow members call parked routes (404) and go in 2.0.
  • ISilverDotPayCardClient — card rail: rail status, charge saved card, setup-mode capture, list/remove saved cards, reconcile.

Auth modes

Pick one per HttpClient:

  • SilverDotPayClientMode.ApiKey — sets X-API-Key header from opts.ApiKey. Used by service accounts and admin keys.
  • SilverDotPayClientMode.PassportBearer — forwards a Passport SSO token from a pluggable ITokenSource. Used in ASP.NET Core hosts where the inbound user's token is propagated downstream.

Inbound webhook validation

services.AddSingleton<ISilverDotPayWebhookValidator>(sp =>
    new SilverDotPayWebhookValidator(secret: cfg["SilverDotPay:WebhookSecret"], logger));

Validator parses X-SilverDotPay-Signature: t=<unix>,v1=<hex>, recomputes HMAC-SHA256("<unix>.<raw body>", secret), and rejects if the timestamp falls outside a 5-minute replay window. Fails closed when no secret is configured.

See docs/WEBHOOKS.md in the silverdotpay repo for the wire format.

Card rail (1.3.0)

  • Card checkout: CreateOrderAsync with Rail = SilverDotPayRail.Card, GBP Amount, BuyerIdentifier (tenant id), SuccessUrl, CancelUrl; redirect the buyer to CreateOrderResponse.PaymentUrl. The card is saved for renewals.
  • payment_confirmed carries payment_method.ref — store it and pass it as PaymentMethodRef to ISilverDotPayCardClient.ChargeSavedAsync for renewals (optionally with BuyerIdentifier as an ownership check). Use a new ExternalOrderId per attempt; reuse it only to retry the same attempt (never double-charges).
  • ChargeSavedResponse.Status is one of SilverDotPayChargeStatus. processing means the outcome is unknown: do not treat it as failed and do not start another attempt; reconcile via POST /api/orders/{id}/reconcile.
  • payment_failed carries failure.reason (SilverDotPayFailureReason) and, for authentication_required, failure.action_url — send the customer there to authenticate.
  • payment_method_saved (setup mode) has no order; key it on payment_method.buyer_identifier.
  • A 503 (SilverDotPayException.StatusCode == 503) or GetCardRailStatusAsync().IsConfigured == false means cards are not configured for this application: hide the card option.

1.5.0 (additive)

  • livemode on CreateOrderResponse, OrderStatusResponse and the webhook order: the Stripe livemode of the completing payment (false = test mode). Null until paid, for crypto orders, or from a SilverDotPay older than this SDK. Consumers must not provision from a false payment outside a designated test tenant. A null on a paid card order means unknown: hold it for a person rather than provisioning, and never read null as test mode.
  • ISilverDotPayCardClient.ReconcileOrderAsync(orderId): asks SilverDotPay to reconcile a card order with Stripe (missed-webhook recovery) and returns the order; 404/503 surface as SilverDotPayException.

1.6.0 (additive)

  • Stripe Connect calls: ISilverDotPayMerchantClient and ISilverDotPayAdminClient (leading applicationId) gain StartStripeConnectAsync, CompleteStripeConnectAsync(StripeConnectCompleteRequest), GetStripeConnectStatusAsync and DisconnectStripeAsync, returning StripeConnectStartDto / StripeConnectStatusDto.
  • ApplicationDto += StripeConnectedAccountId, StripeConnectLivemode, StripeConnectedAtUtc, StripeChargesOnPlatform.
  • UpdateApplicationRequest += WalletMode, ExtendedPublicKeys.
  • Fix: RotateWebhookSecretAsync now POSTs to .../webhook-secret/rotate (the previous path did not match the API action).
  • Obsolete (still functional, removal scheduled for SDK 2.0): GetMyBalancesAsync, RequestWithdrawalAsync, ListWithdrawalsAsync, GetGlobalBalancesAsync, GetApplicationBalancesAsync. Custodial surfaces are parked.

1.7.0 (additive)

  • CreateApplicationRequest.WalletMode is now int?. Omitted means WatchOnly, the Silver Pay Stage 6 default: a non-custodial, card-only application needs no extended public keys. Set 1 (WatchOnly) explicitly if you prefer; 0 (PlatformCustody) is first-party only.
  • Behaviour change on the server (not the SDK): the custodial surfaces (balances, escrow) answer 404 while Platform:CustodialFeaturesEnabled is off, and custody-mode crypto orders are refused with 503 for non-first-party applications. The [Obsolete] custodial members remain for source compatibility.
  • Server-side since Silver Pay Stage 7 (no SDK change): a WatchOnly application takes BTC/LTC/ETH with its own account-level extended public keys, set through UpdateApplicationRequest.ExtendedPublicKeys (JSON {"BTC":"zpub…","LTC":"zpub…","ETH":"xpub…"}; Litecoin native segwit shares the zpub prefix, Mtub is wrapped segwit; {} clears). Invalid keys → 400 Invalid extended public keys: …; a crypto order without a usable key for the coin → 503 naming the problem. The application response now carries watchOnlyFirstAddresses (not yet on ApplicationDto; planned for the next SDK minor together with ExtendedPublicKeys). The BTCPay webhook route is gone.

1.8.0 (additive)

  • ApplicationDto += ExtendedPublicKeys (the stored JSON map) and WatchOnlyFirstAddresses (Silver Pay Stage 7: the 0/0 address per stored key for a WatchOnly application, so a console can show the merchant what to compare with their wallet). Both null when absent; older servers simply leave them null.

1.9.0 (additive)

  • CreateOrderResponse and OrderStatusResponse += DerivationPath: for an order of a WatchOnly application, the merchant key's derivation path xo:{fingerprint}/0/{index} so a console or wallet integration can track the address index. Null for card orders, platform-custody orders and older servers.

Towards 2.0

The custodial gateway is parked on the server (Platform:CustodialFeaturesEnabled=false, Silver Pay Stage 6). Its balance and withdrawal SDK members are [Obsolete]; the escrow members are not marked but call routes that answer 404. SDK 2.0 removes all of them. Plan for 2.0 by not calling:

  • Merchant client: GetMyBalancesAsync, RequestWithdrawalAsync, ListWithdrawalsAsync, ReleaseEscrowAsync, RefundEscrowAsync.
  • Admin client: GetGlobalBalancesAsync, GetApplicationBalancesAsync, AdminReleaseEscrowAsync, AdminRefundEscrowAsync.
  • Legacy ISilverDotPayClient: ReleaseEscrowAsync, RefundEscrowAsync.
  • Webhook models for escrow_*, payment_detected, overpayment_detected and underpayment_detected: no live rail sends those events (the hdwallet rail sends payment_seen, payment_confirming, payment_confirmed, payment_underpaid with a settlement block; the card rail sends payment_confirmed, payment_failed, payment_method_saved). order_expired is sent by both rails and stays.

Everything else (orders with Rail, Livemode, card client, Stripe Connect, applications with WalletMode/ExtendedPublicKeys/WatchOnlyFirstAddresses, keys, webhook secrets, settings, the webhook validator) carries into 2.0 unchanged. Wire format and routes are documented in docs/API-INTEGRATION.md and docs/WEBHOOKS.md of the silverdotpay repo.

Versioning

SemVer. Major versions track breaking API contract changes on the silverdotpay backend.

No packages depend on SilverLabs.SilverDotPay.Sdk.

Version Downloads Last updated
1.9.0 0 11/10/2026
1.8.0 28 11/09/2026
1.7.0 1 11/09/2026
1.6.0 19 11/06/2026
1.5.0 94 11/06/2026
1.4.0 691 10/06/2026
1.3.0 6 10/06/2026
1.2.0 2,537 04/28/2026
1.1.0 47 04/28/2026
1.0.0 21 04/28/2026