Adding an Apple Pay Button to React with Backend Support

Mo Sharif
3 min readJan 19, 2025

--

grok generated image

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.

--

--

Mo Sharif
Mo Sharif

Written by Mo Sharif

Hello, I'm Mo Sharif, a founder and passionate FE software engineer.

No responses yet