v149 · Web APIs · Handler Integration
Handler Integration
Complete code walkthrough: the payment handler service worker rejecting with a structured error, and the merchant page catching and acting on it. Before/after comparison for the Chrome 149 change.
before / after
Before Chrome 149 — handler (SW)
self.addEventListener('paymentrequest', ev => {
ev.respondWith(
// Only option: reject with an Error
// — no structured data
Promise.reject(
new Error('card expired')
)
);
});
// Merchant page
try {
await request.show();
} catch (err) {
// err is a generic AbortError
// err.detail is undefined
showGenericError(); // no info
}
Chrome 149+ — handler (SW)
self.addEventListener('paymentrequest', ev => {
ev.respondWith(
// Reject with structured error object
Promise.reject({
error: 'card_expired',
message: 'Card expired 2024-01'
})
);
});
// Merchant page
try {
await request.show();
} catch (err) {
// err.detail is the reject object
if (err.detail?.error === 'card_expired') {
showCardExpiredFlow(err.detail.message);
}
}
error object reference
| Property | Type | Description |
|---|---|---|
err.detail |
object | undefined |
The object the payment handler rejected with. undefined in pre-149 Chrome or when the handler uses a plain Error. |
err.detail.error |
string |
Handler-defined error code. Not standardised — choose codes meaningful to your handler/merchant pair. |
err.detail.message |
string |
Human-readable error string. May be shown to users or used for logging. |
err.name |
"AbortError" |
The DOMException name — unchanged from pre-149. Error type classification still uses err.name. |
full handler example
// payment-handler-sw.js
self.addEventListener('paymentrequest', async event => {
event.respondWith(handlePayment(event));
});
async function handlePayment(event) {
const details = event.total;
try {
// Open payment UI and wait for user action
const paymentWindow = await event.openWindow('/payment-ui');
// Wait for the UI to post back the result
const result = await waitForPaymentResult(paymentWindow);
if (!result.success) {
// Chrome 149+: reject with structured error
return Promise.reject({
error: result.errorCode, // e.g. 'card_expired'
message: result.errorMessage
});
}
// Resolve with PaymentResponse data
return {
methodName: event.methodData[0].supportedMethods,
details: { transactionId: result.transactionId }
};
} catch (internalErr) {
return Promise.reject({
error: 'internal_error',
message: internalErr.message
});
}
}
see also
implementation reference
Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗