Aggiornato a novembre 2026. La prima versione di questo articolo, del giugno 2023, usava il metodo di pagamento basic-card e un vecchio formato di Google Pay. Oggi non funziona nessuno dei due, quindi il codice è stato riscritto.
La Payment Request API permette a una pagina di aprire la finestra di pagamento del browser invece di mostrare un proprio modulo per la carta. In Chrome quella finestra è Google Pay, in Safari è Apple Pay. L’utente sceglie una carta che ha già salvato, conferma e la pagina riceve un token da inviare al proprio fornitore di pagamenti. In questa serie in due parti vediamo come funziona oggi l’API, dove si può usare e che cosa bisogna ancora costruire da sé.
Cosa fa l’API e cosa non fa
L’API fa tre cose:
- mostra una finestra di pagamento disegnata dal browser, quindi uguale su tutti i siti;
- raccoglie i dati di pagamento e, se li chiedi, il nome, l’email, il telefono e l’indirizzo di spedizione di chi paga;
- restituisce tutto alla tua pagina in un oggetto
PaymentResponse.
Quello che non fa è addebitare il pagamento. Il token della risposta deve arrivare al tuo server, ed è il server ad addebitarlo tramite il tuo fornitore di pagamenti (Stripe, Adyen, Braintree, il gateway della tua banca…). L’API sostituisce solo il modulo di pagamento.
Dove funziona nel 2026
| Browser | Payment Request API | Metodi di pagamento |
|---|---|---|
| Chrome (desktop e Android) | Sì, da Chrome 60 (53 su Android) | Google Pay e altre app di pagamento di terze parti |
| Edge | Sì | Dipende dalle app di pagamento disponibili |
| Safari (macOS e iOS) | Sì, da Safari 11.1 | Solo Apple Pay |
| Firefox | No, solo con il flag dom.payments.request.enabled | — |
La documentazione di Google lo dice chiaramente: oggi Chrome è l’unico browser che supporta la Payment Request API con metodi di pagamento di terze parti, Google Pay compreso. Se ti serve Google Pay anche negli altri browser, la strada è la libreria JavaScript di Google Pay; la Payment Request API è la via nativa del browser.
Due requisiti valgono ovunque:
- HTTPS: l’API esiste solo nei contesti sicuri (
localhostconta come sicuro, comodo per i test); - un’azione dell’utente:
show()funziona solo in risposta a un clic o alla pressione di un tasto. Se lo chiami al caricamento della pagina, fallisce con unSecurityError.
Che fine ha fatto basic-card
La versione del 2023 di questo tutorial usava basic-card, un metodo con cui era il browser stesso a raccogliere il numero della carta. Il W3C Web Payments Working Group ha abbandonato quella specifica nell’agosto 2021 e i browser non la offrono più. Oggi le carte arrivano alla finestra di pagamento attraverso un wallet come Google Pay o Apple Pay. Se copi un vecchio esempio che elenca solo basic-card, show() viene rifiutato con un NotSupportedError.
Un esempio minimo con Google Pay in modalità di test
Alla pagina bastano due elementi: un pulsante con id pay-button, nascosto all’inizio con l’attributo hidden perché compaia solo se il browser può pagare, e un paragrafo vuoto con id result per i messaggi. La pagina completa, HTML compreso, è nella demo su CodePen, dove puoi anche provarla.
Il JavaScript:
const button = document.getElementById('pay-button');
const result = document.getElementById('result');
// Google Pay, nel formato che Google documenta per la Payment Request API (versione 2 dell'API).
// environment: 'TEST' restituisce carte fittizie: non viene addebitato nulla.
const googlePay = {
supportedMethods: 'https://google.com/pay',
data: {
environment: 'TEST',
apiVersion: 2,
apiVersionMinor: 0,
merchantInfo: {
merchantName: 'Example Merchant'
// merchantId: '12345678901234567890' (te lo dà Google per la produzione)
},
allowedPaymentMethods: [{
type: 'CARD',
parameters: {
allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
allowedCardNetworks: ['MASTERCARD', 'VISA']
},
tokenizationSpecification: {
type: 'PAYMENT_GATEWAY',
parameters: {
gateway: 'example',
gatewayMerchantId: 'exampleGatewayMerchantId'
}
}
}]
}
};
const details = {
total: { label: 'Totale', amount: { currency: 'EUR', value: '100.00' } },
displayItems: [
{ label: 'Prodotto A', amount: { currency: 'EUR', value: '50.00' } },
{ label: 'Prodotto B', amount: { currency: 'EUR', value: '50.00' } }
]
};
// 1. Mostrare il pulsante solo se il browser ha l'API e può usare il metodo
async function setUp() {
if (!window.PaymentRequest) {
return; // resta il checkout normale
}
const request = new PaymentRequest([googlePay], details);
if (await request.canMakePayment()) {
button.hidden = false;
}
}
// 2. Al clic, aprire la finestra di pagamento
button.addEventListener('click', async () => {
const request = new PaymentRequest([googlePay], details);
try {
const response = await request.show();
const charged = await sendToServer(response);
await response.complete(charged ? 'success' : 'fail');
result.textContent = charged ? 'Grazie per il tuo acquisto.' : 'Non è stato possibile completare il pagamento.';
} catch (error) {
if (error.name === 'AbortError') {
result.textContent = 'Pagamento annullato.';
} else if (error.name === 'NotSupportedError') {
result.textContent = 'Questo browser non ha nessuno dei metodi di pagamento che accettiamo.';
} else {
console.error(error);
result.textContent = 'Qualcosa è andato storto.';
}
}
});
// 3. Solo per la demo: in un negozio vero qui si invia response.details in POST al server,
// che addebita il token tramite il fornitore di pagamenti
async function sendToServer(response) {
console.log(response.methodName, response.details);
return true;
}
setUp();
L’esempio, passo per passo
I metodi di pagamento
Il primo argomento di new PaymentRequest() è un array di metodi di pagamento. Ognuno ha una stringa supportedMethods, un identificatore come 'https://google.com/pay' o 'https://apple.com/apple-pay', e un oggetto data il cui formato lo decide chi gestisce quel metodo. Per Google Pay, data è lo stesso oggetto di richiesta della Pay API di Google: apiVersion: 2, un blocco merchantInfo e un array allowedPaymentMethods di oggetti. Il tokenizationSpecification indica quale fornitore di pagamenti riceverà il token; i valori esatti di gateway e gatewayMerchantId te li dà il fornitore.
I tutorial più vecchi, compresa la prima versione di questo, scrivevano supportedMethods come array e allowedPaymentMethods come ['TOKENIZED_CARD', 'CARD']. Era il formato della prima versione dell’API di Google Pay; quella attuale si aspetta gli oggetti che vedi sopra.
I dettagli del pagamento
Il secondo argomento descrive cosa sta pagando l’utente: il total, obbligatorio, e volendo un elenco di displayItems che la finestra mostra come dettaglio. Gli importi sono stringhe ('100.00'), non numeri, e la valuta è un codice ISO 4217. Con Google Pay il totale va qui e non serve il campo transactionInfo che Google Pay usa fuori da questa API.
Controllare prima di mostrare il pulsante
canMakePayment() ti dice se il browser può usare almeno uno dei metodi che hai elencato. Non richiede un clic, quindi l’esempio lo chiama al caricamento della pagina e, se la risposta è no, il pulsante resta nascosto. Chi usa Firefox, per esempio, non vede mai un pulsante che fallirebbe e basta.
Mostrare la finestra e chiudere
Nel gestore del clic l’esempio crea una nuova richiesta e chiama show(). La promise si risolve con un PaymentResponse quando l’utente conferma. L’ordine dei due passi successivi è importante:
- inviare la risposta al server e aspettare di sapere se l’addebito è andato a buon fine o no;
- solo allora chiamare
response.complete('success')oresponse.complete('fail'), che chiude la finestra.
La versione del 2023 chiamava prima complete('success') e processava il pagamento dopo: diceva all’utente che era andato tutto bene prima ancora di tentare l’addebito.
Annullamenti ed errori
Quando l’utente chiude la finestra, show() non si risolve con un valore vuoto: viene rifiutata con un AbortError. Per questo l’esempio gestisce l’annullamento nel blocco catch e controlla error.name. NotSupportedError vuol dire che nessuno dei tuoi metodi è disponibile, e SecurityError di solito che show() è stato chiamato senza un clic.
Cosa arriva nella risposta
response.methodName ti dice quale metodo ha scelto l’utente, per esempio 'https://google.com/pay'. response.details contiene i dati di quel metodo. Con Google Pay è un oggetto con i dati di pagamento, in cui paymentMethodData.tokenizationData.token è quello che serve al fornitore per addebitare la carta. Se hai chiesto i dati di contatto, arrivano in payerName, payerEmail e payerPhone; ne parliamo nella parte 2.
Apple Pay
In Safari il metodo è 'https://apple.com/apple-pay', ma non basta un blocco di dati: servono un account esercente Apple Pay, un dominio verificato e un passaggio di validazione dell’esercente che il tuo server completa con Apple ogni volta che si apre la finestra. Nella parte 2 vediamo come quel passaggio si inserisce nello stesso codice.
Conviene usare l’API direttamente?
Per capire come funzionano i pagamenti nel browser, sì: tutto il flusso sta in una pagina di codice. In un negozio in produzione la maggior parte dei team usa il pulsante o il componente di checkout del proprio fornitore di pagamenti, che gestisce i formati di ogni wallet, la validazione dell’esercente e i browser senza l’API. Sapere cosa succede sotto, però, aiuta parecchio quando qualcosa non va.
Nella parte 2 chiediamo a chi paga il nome, l’email e l’indirizzo di spedizione, aggiorniamo il prezzo quando cambia l’indirizzo e aggiungiamo Apple Pay.
Per approfondire
- Payment Request API, PaymentRequest e PaymentResponse su MDN (in inglese)
- Google Pay con la Payment Request API, il tutorial di Google (in inglese)
- Can I use: Payment Request API