Markdown Converter
Agent skill for markdown-converter
This file provides component-specific guidance for working with the Apple Pay component. For project-wide conventions and commands, see `/CLAUDE.md`.
Sign in to like and favorite skills
This file provides component-specific guidance for working with the Apple Pay component. For project-wide conventions and commands, see
/CLAUDE.md.
The Apple Pay component integrates with Apple's ApplePaySession API to enable Apple Pay payments on the web. It handles merchant validation, payment request creation, and tokenization of Apple Pay payments.
Key Features:
Docs: Braintree Apple Pay Guide
index.js - Component entry point with create() functionapple-pay.js - Main ApplePay class implementation (3 public methods)errors.js - Apple Pay error codes (6 errors)Note: This is a simple component (3 files) that acts as a bridge between Braintree and Apple's ApplePaySession API.
1. Create Apple Pay Instance ↓ 2. Check ApplePaySession Availability (window.ApplePaySession) ↓ 3. Create Payment Request (with Braintree defaults) ↓ 4. Create ApplePaySession ↓ 5. Merchant Validation Event ↓ 6. performValidation() (Braintree validates domain) ↓ 7. Payment Authorization Event ↓ 8. tokenize() (Convert Apple Pay token to Braintree nonce) ↓ 9. Send Nonce to Server
1. Apple Developer Setup:
2. Braintree Setup:
3. Browser Requirements:
4. HTTPS Required:
var applePay = require("braintree-web/apple-pay"); // 1. Check if Apple Pay is available if (window.ApplePaySession && ApplePaySession.canMakePayments()) { // 2. Create Apple Pay instance applePay.create( { client: clientInstance, }, function (err, applePayInstance) { if (err) { console.error("Error creating Apple Pay:", err); return; } // 3. Show Apple Pay button document.getElementById("apple-pay-button").style.display = "block"; document .getElementById("apple-pay-button") .addEventListener("click", function () { // 4. Create payment request var paymentRequest = applePayInstance.createPaymentRequest({ total: { label: "My Store", amount: "19.99", }, }); // 5. Create Apple Pay session var session = new ApplePaySession(3, paymentRequest); // 6. Handle merchant validation session.onvalidatemerchant = function (event) { applePayInstance.performValidation( { validationURL: event.validationURL, displayName: "My Store", }, function (validationErr, merchantSession) { if (validationErr) { console.error("Validation failed:", validationErr); session.abort(); return; } session.completeMerchantValidation(merchantSession); } ); }; // 7. Handle payment authorization session.onpaymentauthorized = function (event) { applePayInstance.tokenize( { token: event.payment.token, }, function (tokenizeErr, payload) { if (tokenizeErr) { console.error("Tokenization failed:", tokenizeErr); session.completePayment(ApplePaySession.STATUS_FAILURE); return; } // Send payload.nonce to server submitNonceToServer(payload.nonce) .then(function () { session.completePayment(ApplePaySession.STATUS_SUCCESS); }) .catch(function () { session.completePayment(ApplePaySession.STATUS_FAILURE); }); } ); }; // 8. Handle cancellation session.oncancel = function () { console.log("User canceled Apple Pay"); }; // 9. Start the session session.begin(); }); } ); }
applePay.create({ client: clientInstance, // Required (or authorization) authorization: "token", // Alternative to client useDeferredClient: true, // Optional: Immediate instance availability });
Required Fields:
{ total: { label: 'My Store', // Merchant name displayed to user amount: '19.99', // Total amount as string type: 'final' // 'final' or 'pending' } }
Optional Fields:
{ total: { /* ... */ }, // Line items (itemized display) lineItems: [ { label: 'Subtotal', amount: '17.99', type: 'final' }, { label: 'Shipping', amount: '2.00', type: 'final' } ], // Shipping methods shippingMethods: [ { label: 'Standard Shipping', detail: '5-7 business days', amount: '2.00', identifier: 'standard' }, { label: 'Express Shipping', detail: '2-3 business days', amount: '5.00', identifier: 'express' } ], // Required information requiredBillingContactFields: ['postalAddress', 'email'], requiredShippingContactFields: ['postalAddress', 'phone', 'email', 'name'], // Shipping type shippingType: 'shipping', // 'shipping', 'delivery', 'storePickup', 'servicePickup' // Application data (custom data) applicationData: btoa(JSON.stringify({ orderId: '123' })) }
The SDK automatically applies these defaults from gateway configuration:
{ countryCode: 'US', // From Braintree config currencyCode: 'USD', // From Braintree config merchantCapabilities: ['supports3DS'], // From Braintree config supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'] // From Braintree config }
Note:
supportedNetworks is automatically mapped (e.g., 'mastercard' → 'masterCard').
Creates an Apple Pay payment request with Braintree defaults merged in.
Signature:
var paymentRequest = applePayInstance.createPaymentRequest(options); // OR (with useDeferredClient) applePayInstance.createPaymentRequest(options).then(function (paymentRequest) { // Use paymentRequest });
Parameters:
options (object): Payment request options (see Configuration Options above)Returns:
object - Payment request ready for ApplePaySessionPromise<object> - Resolves with payment requestExample:
var paymentRequest = applePayInstance.createPaymentRequest({ total: { label: "My Company", amount: "19.99", }, requiredBillingContactFields: ["postalAddress"], }); var session = new ApplePaySession(3, paymentRequest);
Validates the merchant with Apple via Braintree's gateway.
Signature:
applePayInstance.performValidation(options, callback); // OR applePayInstance.performValidation(options).then(function (merchantSession) { // Use merchantSession });
Parameters:
options.validationURL (string, required): From ApplePayValidateMerchantEventoptions.displayName (string, optional): Merchant display nameoptions.merchantIdentifier (string, optional): Override merchant IDoptions.domainName (string, optional): Override domain nameReturns:
Promise<object> - Merchant session object to pass to session.completeMerchantValidation()Example:
session.onvalidatemerchant = function (event) { applePayInstance.performValidation( { validationURL: event.validationURL, displayName: "My Great Store", }, function (err, merchantSession) { if (err) { console.error(err); session.abort(); return; } session.completeMerchantValidation(merchantSession); } ); };
Converts an Apple Pay payment token to a Braintree payment method nonce.
Signature:
applePayInstance.tokenize(options, callback); // OR applePayInstance.tokenize(options).then(function (payload) { // Use payload });
Parameters:
options.token (object, required): The payment.token from ApplePayPaymentAuthorizedEventReturns:
Promise<tokenizePayload> - Tokenization result with noncePayload Structure:
{ nonce: 'tokencc_abc123_xyz789', type: 'ApplePayCard', description: 'Apple Pay', details: { cardType: 'Visa', // Card network cardHolderName: 'John Doe', // Cardholder name dpanLastTwo: '34', // Last 2 digits isDeviceToken: true // DPAN vs MPAN }, binData: { commercial: 'Unknown', countryOfIssuance: 'USA', debit: 'No', durbinRegulated: 'Yes', healthcare: 'No', issuingBank: 'Wells Fargo', payroll: 'No', prepaid: 'No', productId: '123', business: 'No', consumer: 'Yes', purchase: 'Yes', corporate: 'No' } }
Example:
session.onpaymentauthorized = function (event) { applePayInstance.tokenize( { token: event.payment.token, }, function (err, payload) { if (err) { console.error(err); session.completePayment(ApplePaySession.STATUS_FAILURE); return; } // Send payload.nonce to server submitToServer(payload.nonce); session.completePayment(ApplePaySession.STATUS_SUCCESS); } ); };
A special read-only property containing the Braintree merchant identifier.
Usage:
var canMakePayments = ApplePaySession.canMakePaymentsWithActiveCard( applePayInstance.merchantIdentifier ); canMakePayments.then(function (canMakePayments) { if (canMakePayments) { // Show Apple Pay button } });
Note: This is automatically set after client creation and is used to check if user has an active Apple Pay card.
From
errors.js:
Creation Errors:
APPLE_PAY_NOT_ENABLED (MERCHANT)
Validation Errors:
(MERCHANT)APPLE_PAY_VALIDATION_URL_REQUIRED
validationURL in performValidation() callvalidationURL from ApplePayValidateMerchantEvent
(MERCHANT)APPLE_PAY_MERCHANT_VALIDATION_FAILED
(NETWORK)APPLE_PAY_MERCHANT_VALIDATION_NETWORK
Tokenization Errors:
(MERCHANT)APPLE_PAY_PAYMENT_TOKEN_REQUIRED
token in tokenize() callevent.payment.token from ApplePayPaymentAuthorizedEvent
(NETWORK)APPLE_PAY_TOKENIZATION
1. Use Sandbox Account:
braintree.client .create({ authorization: SANDBOX_TOKENIZATION_KEY, }) .then(function (client) { return applePay.create({ client: client }); });
2. Apple Pay Sandbox Cards:
3. Domain Registration:
localhost for local developmentLocation:
test/apple-pay/unit/
Test Categories:
1. "Apple Pay is not available"
Symptoms:
window.ApplePaySession is undefinedcanMakePayments() returns falseDebug:
2. "Merchant validation failed"
Symptoms:
APPLE_PAY_MERCHANT_VALIDATION_FAILED errorDebug:
Fix:
// Braintree Control Panel → Settings → Processing → Apple Pay // Add domain: www.example.com (must match exactly)
3. "Session creation failed"
Symptoms:
new ApplePaySession() throws errorDebug:
Bad Example:
button.addEventListener("click", function () { fetchConfig().then(function (config) { // TOO LATE - not in direct response to click var session = new ApplePaySession(3, paymentRequest); }); });
Good Example:
button.addEventListener('click', function () { // Create session immediately in click handler var session = new ApplePaySession(3, paymentRequest); session.onvalidatemerchant = function (event) { // Async work here is fine applePayInstance.performValidation(...); }; session.begin(); });
4. "Tokenization failed"
Symptoms:
APPLE_PAY_TOKENIZATION errorDebug:
event.payment.token is passed correctly5. Amount Formatting
Symptoms:
Fix:
// GOOD: String with 2 decimal places total: { label: 'My Store', amount: '19.99' } // BAD: Number or wrong format total: { amount: 19.99 // Should be string } total: { amount: '19.9' // Should be '19.90' }
// Minimal implementation braintree.applePay .create({ client: clientInstance, }) .then(function (applePayInstance) { var paymentRequest = applePayInstance.createPaymentRequest({ total: { label: "My Store", amount: "10.00", }, }); var session = new ApplePaySession(3, paymentRequest); session.onvalidatemerchant = function (event) { applePayInstance .performValidation({ validationURL: event.validationURL, displayName: "My Store", }) .then(function (merchantSession) { session.completeMerchantValidation(merchantSession); }) .catch(function (err) { console.error(err); session.abort(); }); }; session.onpaymentauthorized = function (event) { applePayInstance .tokenize({ token: event.payment.token, }) .then(function (payload) { // Send payload.nonce to server return fetch("/checkout", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ nonce: payload.nonce }), }); }) .then(function () { session.completePayment(ApplePaySession.STATUS_SUCCESS); }) .catch(function (err) { console.error(err); session.completePayment(ApplePaySession.STATUS_FAILURE); }); }; session.begin(); });
var paymentRequest = applePayInstance.createPaymentRequest({ total: { label: 'My Store', amount: '22.00' }, lineItems: [ { label: 'Subtotal', amount: '20.00' }, { label: 'Shipping', amount: '2.00' } ], shippingMethods: [ { label: 'Standard Shipping', detail: '5-7 business days', amount: '2.00', identifier: 'standard' }, { label: 'Express Shipping', detail: '2-3 business days', amount: '5.00', identifier: 'express' } ], requiredShippingContactFields: ['postalAddress', 'email', 'phone'] }); var session = new ApplePaySession(3, paymentRequest); session.onshippingmethodselected = function (event) { var selectedShipping = event.shippingMethod; var subtotal = 20.00; var shippingCost = parseFloat(selectedShipping.amount); var total = subtotal + shippingCost; session.completeShippingMethodSelection({ newTotal: { label: 'My Store', amount: total.toFixed(2) }, newLineItems: [ { label: 'Subtotal', amount: subtotal.toFixed(2) }, { label: selectedShipping.label, amount: selectedShipping.amount } ] }); }; session.onshippingcontactselected = function (event) { var shippingContact = event.shippingContact; // Calculate shipping based on address var errors = []; var newShippingMethods = [...]; session.completeShippingContactSelection({ errors: errors, newShippingMethods: newShippingMethods, newTotal: { label: 'My Store', amount: '22.00' } }); }; // ... merchant validation and payment authorization as before
braintree.applePay .create({ authorization: CLIENT_TOKEN, useDeferredClient: true, }) .then(function (applePayInstance) { // Instance available immediately // Payment request returns promise with deferred client applePayInstance .createPaymentRequest({ total: { label: "My Store", amount: "10.00", }, }) .then(function (paymentRequest) { var session = new ApplePaySession(3, paymentRequest); // ... rest of implementation }); });
Browser Support:
Device Requirements:
Network Requirements: