loading
Warning:
A sheet stays open until you complete itShow resolves while the sheet is still on screen showing "processing". You then process PaymentResponse.Details on your server and call Complete with the outcome - that is what dismisses it. Forget the second call and the user is left staring at a spinner.
Note:
Preconditions Secure context, a user gesture, and a top-level page (or an iframe carrying allow="payment"). The demo below uses the basic-card method identifier, which most engines have retired - expect IsSupported to be true and the sheet to refuse to open anyway. A real integration names its processor's method identifier, e.g. https://google.com/pay.

Support check

IsSupported

Returns true when the runtime exposes window.PaymentRequest. During prerender/SSR the check returns false rather than throwing, so defer it to OnAfterRenderAsync.

C#
@inject Bit.Butil.PaymentRequest paymentRequest

var supported = await paymentRequest.IsSupported();
Live sample
support check output
Results will appear here when you interact with the samples.

Can the browser pay at all?

CanMakePayment

Asks whether any of the given payment methods could be used, without showing UI and without needing a gesture. It answers about the methods, not about the user having a usable card behind them - and browsers rate-limit it, so call it once per page rather than per render.

C#
var methods = new[]
{
    new PaymentMethod { SupportedMethods = "https://google.com/pay" }
};

var details = new PaymentDetails
{
    Total = new PaymentItem
    {
        Label = "Total",
        Amount = new PaymentCurrencyAmount { Currency = "USD", Value = "19.99" }
    }
};

var canPay = await paymentRequest.CanMakePayment(methods, details);
Live sample
Payment method identifier
can-make-payment output
Results will appear here when you interact with the samples.

Show the sheet

Show / Complete / Abort

Show opens the sheet from a click and resolves with what the user authorized, or null if they dismissed it. The response carries the processor payload as raw JSON in Details - send that to your server. Complete dismisses the sheet with the result; Abort closes it while the user is still deciding.

C#
var response = await paymentRequest.Show(methods, details, new PaymentOptions
{
    RequestPayerName = true,
    RequestPayerEmail = true
});

if (response is not null)
{
    // Post response.Details to the server, then close the sheet with the answer.
    var ok = await ProcessOnServer(response.Details);

    await paymentRequest.Complete(response.Id,
        ok ? PaymentCompleteResult.Success : PaymentCompleteResult.Fail);
}
Live sample
Total
payment sheet output
Results will appear here when you interact with the samples.

Payment handler

PaymentHandler.IsSupported / GetUserHint / SetUserHint / EnableDelegations

The other side of the boundary: what an installed app registers so other sites can pay through it. The handling itself happens in the service worker's canmakepayment and paymentrequest events; what a page controls is the account hint shown next to your app, and which fields your handler collects itself. Needs an active service worker registration.

@inject Bit.Butil.PaymentHandler paymentHandler

@code {
    // What a page controls: the account shown next to this app in the payment sheet, and which
    // fields the handler collects itself rather than letting the browser ask for them.
    private async Task Configure()
    {
        await paymentHandler.SetUserHint("[email protected]");

        var hint = await paymentHandler.GetUserHint();

        var enabled = await paymentHandler.EnableDelegations(
            new[] { "shippingAddress", "payerEmail" });
    }
}
Live sample
User hint
payment handler output
Results will appear here when you interact with the samples.

API reference

Member
Signature
Description
PaymentRequest.IsSupported
ValueTask<bool> IsSupported()
True when the runtime exposes window.PaymentRequest. Returns default (false) during prerender/SSR instead of throwing.
PaymentRequest.CanMakePayment
ValueTask<bool> CanMakePayment(PaymentMethod[] methods, PaymentDetails details)
Whether the browser could pay with any of these methods. No UI, no gesture needed, rate-limited by the browser.
PaymentRequest.Show
ValueTask<PaymentResponse?> Show(PaymentMethod[] methods, PaymentDetails details, PaymentOptions? options = null)
Opens the payment sheet from a user gesture. Null when the user dismissed it or the browser refused. Leaves the sheet open for Complete.
PaymentRequest.Complete
ValueTask Complete(string responseId, PaymentCompleteResult result = PaymentCompleteResult.Success)
Dismisses the sheet with the outcome of processing the payment. responseId is PaymentResponse.Id.
PaymentRequest.Abort
ValueTask<bool> Abort()
Closes the sheet this instance opened before the user authorized anything. False when nothing is in flight.
PaymentMethod
string SupportedMethods, object? Data
A payment method identifier (usually an https URL) plus that method's own configuration object.
PaymentDetails
string? Id, PaymentItem Total, PaymentItem[]? DisplayItems, PaymentShippingOption[]? ShippingOptions, PaymentDetailsModifier[]? Modifiers
What is being charged. Display items are not summed or checked against the total by the browser.
PaymentItem
string Label, PaymentCurrencyAmount Amount, bool Pending
One line in the sheet - the total itself, or a display item beneath it.
PaymentCurrencyAmount
string Currency, string Value
An ISO 4217 currency code and the amount as a decimal string - a string because floating point cannot hold 19.99 exactly.
PaymentShippingOption
string Id, string Label, PaymentCurrencyAmount Amount, bool Selected
One shipping choice, offered when PaymentOptions.RequestShipping is set.
PaymentDetailsModifier
string SupportedMethods, PaymentItem? Total, PaymentItem[]? AdditionalDisplayItems, object? Data
A per-method adjustment to the totals - a card-network surcharge, a wallet discount.
PaymentOptions
bool RequestPayerName, bool RequestPayerEmail, bool RequestPayerPhone, bool RequestShipping, string? ShippingType
The contact details the sheet collects. Each flag is another field the user has to confirm.
PaymentResponse
string Id, string RequestId, string MethodName, JsonElement Details, string? PayerName, string? PayerEmail, string? PayerPhone, string? ShippingOption, PaymentAddress? ShippingAddress
What the user authorized. Details is the processor payload as raw JSON - verify it server-side.
PaymentAddress
string[] AddressLine, string? Country, string? City, string? Region, string? PostalCode, string? DependentLocality, string? SortingCode, string? Organization, string? Recipient, string? Phone
The shipping address, when one was requested. Every field is optional - which are filled depends on the country.
PaymentCompleteResult
enum { Success, Fail, Unknown }
What Complete tells the user about the payment.
PaymentHandler.IsSupported
ValueTask<bool> IsSupported()
True when the runtime exposes ServiceWorkerRegistration.paymentManager. Chromium only.
PaymentHandler.GetUserHint
ValueTask<string> GetUserHint()
The account hint shown beneath your app's name in another site's payment sheet. Empty when none is set.
PaymentHandler.SetUserHint
ValueTask SetUserHint(string userHint)
Sets that hint. Set it after sign-in, clear it on sign-out - other sites' users see it.
PaymentHandler.EnableDelegations
ValueTask<bool> EnableDelegations(string[] delegations)
Declares the fields your handler collects itself (shippingAddress, payerName, payerEmail, payerPhone). All or nothing: a name the engine does not know rejects the whole call, so the answer is true or false.
An unhandled error has occurred. Reload 🗙