Implementing an Apple Pay button in your React app involves setting up a frontend component and a backend to handle merchant validation and payment processing. Here’s how you can do it step by step.
Step 1: Frontend Apple Pay Button
Payment Request Setup
The first step is to configure the Apple Pay payment request. This includes details like the country, currency, supported networks, and the total amount.
const paymentRequest = {
countryCode: "US",
currencyCode: "USD",
supportedNetworks: ["visa", "masterCard", "amex", "discover"],
merchantCapabilities: ["supports3DS"],
total: {
label: "Your Store",
amount: "10.00",
},
};
Handling Merchant Validation
Apple Pay requires merchant validation before initiating the payment session. Here’s how you handle the onvalidatemerchant
event.
const handleMerchantValidation = async (event, session, onValidationFailed) => {
try {
const response = await fetch("/validate-apple-pay", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ validationURL: event.validationURL }),
});
const merchantSession = await response.json();
session.completeMerchantValidation(merchantSession);
} catch (error) {
console.error("Merchant validation failed:", error);
onValidationFailed();
}
};
Handling Payment Authorization
Once the payment is authorized, send the payment token to your backend for processing. Use the onpaymentauthorized
event to handle this step.
const handlePaymentAuthorization = async (event, session, onPaymentAuthorized) => {
try {
const response = await fetch("/process-apple-pay", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
token: event.payment.token,
amount: 1000, // Amount in cents
currency: "USD",
}),
});
const result = await response.json();
if (result.success) {
session.completePayment(ApplePaySession.STATUS_SUCCESS);
onPaymentAuthorized();
} else {
session.completePayment(ApplePaySession.STATUS_FAILURE);
}
} catch (error) {
console.error("Payment authorization failed:", error);
session.completePayment(ApplePaySession.STATUS_FAILURE);
}
};
Full Apple Pay Button Component
Combine these pieces into the complete button component.
const ApplePayButton = ({ onPaymentAuthorized, onValidationFailed }) => {
const handleClick = () => {
const session = new ApplePaySession(3, paymentRequest);
session.onvalidatemerchant = (event) =>
handleMerchantValidation(event, session, onValidationFailed);
session.onpaymentauthorized = (event) =>
handlePaymentAuthorization(event, session, onPaymentAuthorized);
session.begin();
};
return (
<button
onClick={handleClick}
className="apple-pay-button-with-text apple-pay-button-black-with-text"
>
<span className="text">Pay with</span>
<span className="logo"></span>
</button>
);
};
export default ApplePayButton;
Step 2: Styling the Apple Pay Button
Here’s the CSS to style the Apple Pay button:
@supports (-webkit-appearance: -apple-pay-button) {
.apple-pay-button-with-text {
width: 100%;
height: 53px;
border-radius: 8px;
display: inline-block;
-webkit-appearance: -apple-pay-button;
-apple-pay-button-type: buy;
}
.apple-pay-button-with-text > * {
display: none;
}
.apple-pay-button-black-with-text {
-apple-pay-button-style: black;
}
}
@supports not (-webkit-appearance: -apple-pay-button) {
.apple-pay-button-with-text {
display: inline-flex;
justify-content: center;
font-size: 12px;
border-radius: 8px;
padding: 0px;
box-sizing: border-box;
min-width: 200px;
min-height: 32px;
max-height: 64px;
}
.apple-pay-button-black-with-text {
background-color: black;
color: white;
}
.apple-pay-button-with-text > .text {
font-family: -apple-system;
font-size: 1em;
font-weight: 300;
margin-right: 5px;
}
.apple-pay-button-with-text > .logo {
width: 35px;
height: 100%;
background-size: 100% 60%;
background-repeat: no-repeat;
background-position: center;
}
}
Step 3: Backend Setup for Merchant Validation
Apple Pay requires a backend to validate the merchant and authorize payments. Here’s how you can implement it.
Merchant Validation Endpoint
import https from 'https';
import fs from 'fs';
import path from 'path';
const MERCHANT_ID_CERT = fs.readFileSync(path.resolve(__dirname, './merchant_id.cert.pem'));
const MERCHANT_ID_KEY = fs.readFileSync(path.resolve(__dirname, './merchant_id.key.pem'));
export const validateApplePay = (req, res) => {
const { validationURL } = req.body;
const options = {
hostname: new URL(validationURL).hostname,
port: 443,
path: new URL(validationURL).pathname,
method: 'POST',
key: MERCHANT_ID_KEY,
cert: MERCHANT_ID_CERT,
};
const appleReq = https.request(options, (appleRes) => {
let data = '';
appleRes.on('data', (chunk) => (data += chunk));
appleRes.on('end', () => res.json(JSON.parse(data)));
});
appleReq.on('error', (err) => res.status(500).json({ error: err.message }));
appleReq.end();
};
Payment Processing Endpoint
import Stripe from 'stripe';
const stripe = new Stripe('your-stripe-secret-key');
export const processApplePay = async (req, res) => {
const { token, amount, currency } = req.body;
try {
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency,
payment_method_data: {
type: 'card',
card: { token },
},
confirm: true,
});
res.json(paymentIntent);
} catch (err) {
res.status(500).json({ error: err.message });
}
};
Step 4: Testing the Integration
- Add the
ApplePayButton
component to your app. - Ensure your backend endpoints (
/validate-apple-pay
and/process-apple-pay
) are running. - Test the integration on a device that supports Apple Pay.