Progressive Web Apps (PWA)
Payment and Credential APIs
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:
- Implement a basic Payment Request API checkout flow with multiple payment options
- Integrate Google Pay or Apple Pay into your PWA (choose based on your target platform)
- Create a login form that stores credentials using Credential Management API
- Implement passwordless authentication using WebAuthn with device biometrics
- 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