Progressive Web Apps (PWA)

Payment and Credential APIs

18 min Lesson 17 of 30

Payment and Credential APIs

Modern web applications can handle payments and authentication securely using native browser APIs. This lesson covers the Payment Request API for processing payments and the Credential Management API for secure authentication.

Payment Request API

The Payment Request API provides a consistent, browser-native payment experience that works across platforms and payment methods.

<!-- Basic Payment Request --> <button id="buyButton">Buy Now - $29.99</button> <script> const buyButton = document.getElementById('buyButton'); // Check if Payment Request is supported if (window.PaymentRequest) { buyButton.addEventListener('click', async () => { // Define payment methods const supportedPaymentMethods = [ { supportedMethods: 'basic-card', data: { supportedNetworks: ['visa', 'mastercard', 'amex'], supportedTypes: ['debit', 'credit'] } } ]; // Define payment details const paymentDetails = { total: { label: 'Total Amount', amount: { currency: 'USD', value: '29.99' } }, displayItems: [ { label: 'PWA Course', amount: { currency: 'USD', value: '24.99' } }, { label: 'Tax', amount: { currency: 'USD', value: '5.00' } } ] }; // Optional: Request shipping information const options = { requestPayerName: true, requestPayerEmail: true, requestPayerPhone: true, requestShipping: false }; try { // Create payment request const request = new PaymentRequest( supportedPaymentMethods, paymentDetails, options ); // Show payment UI const paymentResponse = await request.show(); // Process payment const result = await processPayment(paymentResponse); if (result.success) { await paymentResponse.complete('success'); console.log('Payment successful!'); } else { await paymentResponse.complete('fail'); console.error('Payment failed'); } } catch (error) { console.error('Payment error:', error); } }); } else { // Fallback for browsers without Payment Request API buyButton.addEventListener('click', () => { window.location.href = '/checkout'; }); } // Simulate payment processing async function processPayment(paymentResponse) { const paymentData = { methodName: paymentResponse.methodName, details: paymentResponse.details, payerName: paymentResponse.payerName, payerEmail: paymentResponse.payerEmail }; // Send to server for processing const response = await fetch('/api/process-payment', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(paymentData) }); return await response.json(); } </script>
Note: Payment Request API must be triggered by user interaction (button click) and requires HTTPS in production. Never store card details in your application.

Google Pay Integration

Integrate Google Pay for a streamlined checkout experience on Android and web.

<script> // Google Pay configuration const googlePayConfig = { apiVersion: 2, apiVersionMinor: 0, allowedPaymentMethods: [ { type: 'CARD', parameters: { allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'], allowedCardNetworks: ['MASTERCARD', 'VISA'] }, tokenizationSpecification: { type: 'PAYMENT_GATEWAY', parameters: { gateway: 'example', gatewayMerchantId: 'exampleGatewayMerchantId' } } } ], merchantInfo: { merchantId: 'BCR2DN4TR6G2RXXX', merchantName: 'Your Store Name' }, transactionInfo: { totalPriceStatus: 'FINAL', totalPrice: '29.99', currencyCode: 'USD', countryCode: 'US' } }; // Initialize Google Pay async function initGooglePay() { if (window.PaymentRequest) { const supportedPaymentMethods = [ { supportedMethods: 'https://google.com/pay', data: googlePayConfig } ]; const paymentDetails = { total: { label: 'Total', amount: { currency: 'USD', value: '29.99' } } }; const request = new PaymentRequest( supportedPaymentMethods, paymentDetails ); // Check if Google Pay is available const canMakePayment = await request.canMakePayment(); if (canMakePayment) { document.getElementById('googlePayButton').style.display = 'block'; } } } // Process Google Pay payment async function onGooglePayButtonClick() { const request = new PaymentRequest( [{ supportedMethods: 'https://google.com/pay', data: googlePayConfig }], { total: { label: 'Total', amount: { currency: 'USD', value: '29.99' } } } ); try { const paymentResponse = await request.show(); const paymentToken = paymentResponse.details.paymentMethodData.tokenizationData.token; // Send token to server const result = await fetch('/api/process-google-pay', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: paymentToken }) }); if (result.ok) { await paymentResponse.complete('success'); } else { await paymentResponse.complete('fail'); } } catch (error) { console.error('Google Pay error:', error); } } initGooglePay(); </script>

Apple Pay Integration

Enable Apple Pay for iOS and macOS Safari users.

<script> // Check Apple Pay availability if (window.ApplePaySession && ApplePaySession.canMakePayments()) { document.getElementById('applePayButton').style.display = 'block'; document.getElementById('applePayButton').addEventListener('click', () => { // Apple Pay payment request const request = { countryCode: 'US', currencyCode: 'USD', supportedNetworks: ['visa', 'masterCard', 'amex'], merchantCapabilities: ['supports3DS'], total: { label: 'Your Store Name', amount: '29.99' } }; // Create Apple Pay session const session = new ApplePaySession(3, request); // Validate merchant session.onvalidatemerchant = async (event) => { const merchantSession = await fetch('/api/apple-pay-session', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ validationURL: event.validationURL }) }).then(r => r.json()); session.completeMerchantValidation(merchantSession); }; // Handle payment authorization session.onpaymentauthorized = async (event) => { const payment = event.payment; // Process payment on server const result = await fetch('/api/process-apple-pay', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: payment.token }) }).then(r => r.json()); if (result.success) { session.completePayment(ApplePaySession.STATUS_SUCCESS); } else { session.completePayment(ApplePaySession.STATUS_FAILURE); } }; session.begin(); }); } </script>
Warning: Apple Pay requires domain verification and merchant certificates. Payment tokens must be processed on your server, never client-side. Always use HTTPS.

Credential Management API

Simplify user authentication with browser-managed credentials, including passwords and federated logins.

<!-- Login Form with Credential Management --> <form id="loginForm"> <input type="email" id="email" name="email" autocomplete="username"> <input type="password" id="password" name="password" autocomplete="current-password"> <button type="submit">Sign In</button> </form> <script> // Check if Credential Management is supported if (window.PasswordCredential || window.FederatedCredential) { // Auto sign-in with stored credentials navigator.credentials.get({ password: true, federated: { providers: [ 'https://accounts.google.com', 'https://www.facebook.com' ] }, mediation: 'optional' // 'silent', 'optional', or 'required' }).then(credential => { if (credential) { if (credential.type === 'password') { // Use password credential return signInWithPassword(credential); } else if (credential.type === 'federated') { // Use federated credential return signInWithFederated(credential); } } }); // Store credentials after successful login document.getElementById('loginForm').addEventListener('submit', async (e) => { e.preventDefault(); const email = document.getElementById('email').value; const password = document.getElementById('password').value; // Authenticate user const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); if (response.ok) { // Store credential const credential = new PasswordCredential({ id: email, password: password, name: 'User Name', iconURL: 'https://example.com/avatar.jpg' }); await navigator.credentials.store(credential); console.log('Credential stored'); } }); } async function signInWithPassword(credential) { const response = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: credential.id, password: credential.password }) }); if (response.ok) { console.log('Signed in successfully'); } } </script>

WebAuthn (Web Authentication API)

Implement passwordless authentication using biometrics, security keys, or device credentials.

<button id="registerButton">Register with Biometrics</button> <button id="authenticateButton">Sign In with Biometrics</button> <script> // Register new credential (e.g., fingerprint, face ID) document.getElementById('registerButton').addEventListener('click', async () => { try { // Get challenge from server const challengeResponse = await fetch('/api/webauthn/register-challenge'); const options = await challengeResponse.json(); // Create credential const credential = await navigator.credentials.create({ publicKey: { challenge: Uint8Array.from(options.challenge, c => c.charCodeAt(0)), rp: { name: 'Your App Name', id: 'example.com' }, user: { id: Uint8Array.from(options.userId, c => c.charCodeAt(0)), name: 'user@example.com', displayName: 'User Name' }, pubKeyCredParams: [ { alg: -7, type: 'public-key' }, // ES256 { alg: -257, type: 'public-key' } // RS256 ], authenticatorSelection: { authenticatorAttachment: 'platform', // 'platform' or 'cross-platform' userVerification: 'required' }, timeout: 60000 } }); // Send credential to server await fetch('/api/webauthn/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: credential.id, rawId: Array.from(new Uint8Array(credential.rawId)), response: { clientDataJSON: Array.from(new Uint8Array(credential.response.clientDataJSON)), attestationObject: Array.from(new Uint8Array(credential.response.attestationObject)) } }) }); console.log('Biometric registration successful'); } catch (error) { console.error('Registration failed:', error); } }); // Authenticate with existing credential document.getElementById('authenticateButton').addEventListener('click', async () => { try { // Get challenge from server const challengeResponse = await fetch('/api/webauthn/login-challenge'); const options = await challengeResponse.json(); // Get credential const assertion = await navigator.credentials.get({ publicKey: { challenge: Uint8Array.from(options.challenge, c => c.charCodeAt(0)), rpId: 'example.com', userVerification: 'required', timeout: 60000 } }); // Send assertion to server const response = await fetch('/api/webauthn/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: assertion.id, rawId: Array.from(new Uint8Array(assertion.rawId)), response: { clientDataJSON: Array.from(new Uint8Array(assertion.response.clientDataJSON)), authenticatorData: Array.from(new Uint8Array(assertion.response.authenticatorData)), signature: Array.from(new Uint8Array(assertion.response.signature)) } }) }); if (response.ok) { console.log('Authentication successful'); } } catch (error) { console.error('Authentication failed:', error); } }); </script>
Tip: WebAuthn provides the highest level of security for authentication. It's phishing-resistant and eliminates password vulnerabilities. Always implement it with server-side verification.
Exercise:
  1. Implement a basic Payment Request API checkout flow with multiple payment options
  2. Integrate Google Pay or Apple Pay into your PWA (choose based on your target platform)
  3. Create a login form that stores credentials using Credential Management API
  4. Implement passwordless authentication using WebAuthn with device biometrics
  5. Build a complete checkout experience with dynamic pricing and payment processing

Security Best Practices

  • HTTPS Required: All payment and credential APIs require secure contexts
  • Server Validation: Always validate and process payments on the server, never client-side
  • Token Handling: Never store payment tokens in localStorage or cookies
  • PCI Compliance: Follow PCI DSS standards when handling card data
  • Error Messages: Avoid exposing sensitive information in error messages
  • Rate Limiting: Implement rate limiting on authentication endpoints
  • Audit Logs: Log all payment and authentication attempts for security monitoring