Payment Request API is a W3C browser API that lets a merchant page ask the browser to run the payment step, showing a native sheet instead of a checkout form. It is exposed as the PaymentRequest constructor in secure contexts. The browser sits between the payee, the payer and the payment method, and returns the result to the page as a PaymentResponse. The spec is a W3C Candidate Recommendation Draft dated 22 June 2026, and the API does not move money itself.
You build a request from three arguments: methodData, details and an optional options object. Each methodData entry has a supportedMethods identifier and optional data. The identifier is either a standardized one or a URL controlled by the payment method provider. Duplicate identifiers throw a RangeError.
The details object holds the id, the displayItems and a required total. Every price is a PaymentCurrencyAmount, a pair of currency and value. The value must be a string that matches ^-?[0-9]+(\.[0-9]+)?$, and the total can never be negative. The spec does not rewrite "55" into "55.00", so format values yourself.
The options object asks for extra data with requestPayerName, requestPayerEmail, requestPayerPhone and requestShipping.
Then the flow is:
Only one payment sheet can be shown per browser tab. A show() while the document is not visible rejects with AbortError, and an empty list of matching payment handlers rejects with NotSupportedError. Cross-origin iframes need allow="payment".
const MONEY = /^-?[0-9]+(\.[0-9]+)?$/;
function checkTotal(amount) {
if (!/^[A-Za-z]{3}$/.test(amount.currency)) throw new RangeError("invalid currency " + amount.currency);
if (!MONEY.test(amount.value)) throw new TypeError("invalid amount " + JSON.stringify(amount.value));
if (amount.value.startsWith("-")) throw new TypeError("total cannot be negative");
return amount;
}
for (const a of [{ currency: "OMR", value: "1.234" }, { currency: "USD", value: "55" }, { currency: "USD", value: "$5.00" }, { currency: "USD", value: "1,000.00" }, { currency: "USD", value: "-5.00" }, { currency: "US", value: "5.00" }]) {
try { checkTotal(a); console.log("ok ", JSON.stringify(a)); }
catch (e) { console.log(e.name + ":", e.message); }
}
console.log("sum 55.00 + 5.00 =", (55.00 + 5.00).toFixed(2), "| 0.1 + 0.2 =", 0.1 + 0.2);
Output:
ok {"currency":"OMR","value":"1.234"}
ok {"currency":"USD","value":"55"}
TypeError: invalid amount "$5.00"
TypeError: invalid amount "1,000.00"
TypeError: total cannot be negative
RangeError: invalid currency US
sum 55.00 + 5.00 = 60.00 | 0.1 + 0.2 = 0.30000000000000004
The currency check above is a simplification of the spec rule, which calls IsWellFormedCurrencyCode.
MDN compatibility data lists Chrome 60, Edge 15 and Safari 11.1 as the first versions with PaymentRequest. Firefox lists version 55 only behind the dom.payments.request.enabled preference. Check "PaymentRequest" in window and keep a normal checkout form as the fallback.
No, the API only collects the user's choice and passes details to your code. Response details come from the chosen payment method, so the shape of the details object differs per method. Your server still sends the data to a payment processor and confirms the charge.