Backend Requirements
Apple Pay requires 2 backend API endpoints (described below) that you will need to implement.
You will specify these endpoints in the merchantValidation.url
and paymentAuthorization.url
properties when calling makePaymentRequest.
It is recommended to implement these in their own service. For reference take a look at this sample service.
#
Merchant Validation APIThe Merchant Validation API is only required if you want your application to run on the web. When your app runs natively it will never call this API.
The plugin will call the merchantValidation.url
with a http POST
method.
You may choose a name for the endpoint (eg https://api.my-company.com/session
)
To implement this API endpoint you code will implement a Http GET
call to the Apple Pay Gateway:
https://apple-pay-gateway.apple.com/paymentservices/paymentSession
The body of the request must follow:
{ "merchantIdentifier": "my-apple-pay-merchant-identifier", "displayName": "My Store", "initiative": "web", "initiativeContext": "www.my-company.com"}
merchantIdentifier
is the Merchant Identifier you chose in the Apple Developer portaldisplayName
is the name you want to appear for the Payment Sheetinitiative
should be hard coded toweb
initiativeContext
is the FQND (Fully Qualified Domain Name) where your payment page is served (not your API)
The response from apple-pay-gateway.apple.com
is opaque, you do not need to know what the response shape is so type it as any
. It should be returned as the response from your service.
You may get an error back from Apple which will be in the json
form of:
export interface PaymentSessionError { statusMessage: string; statusCode: string;}
IMPORTANT: When calling
apple-pay-gateway.apple.com
the request must:
- Use the Merchant Certificate and Private Key you defined in the Apple Developer portal.
- Use at a minimum TLS 1.2.
#
Sample CodeHere is a sample implementation of the merchant validation endpoint that which uses Node
and the node-fetch
package.
import fetch from 'node-fetch';import https from 'https';import fs from 'fs';
const response = await getApplePaySession(request);// You will pass response back to your applicationreturn { statusCode: 200, body: JSON.stringify(response) };
async function getApplePaySession(): Promise<any> { const response = await fetch( 'https://apple-pay-gateway.apple.com/paymentservices/paymentSession', { headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, method: 'post', body: JSON.stringify( { merchantIdentifier: 'my-apple-pay-merchant-identifier', displayName: 'My Store', initiative: 'web', initiativeContext: 'www.my-company.com' }), agent: getAgent() } ); return await response.json();}
function getAgent(): https.Agent { return new https.Agent({ rejectUnauthorized: false, keepAlive: false, minVersion: 'TLSv1.2',
// Password for the Merchant Certificate p12 file passphrase: 'super-secret',
// Note we dont use the fs `utf-8` encoding when reading a p12 file pfx: fs.readFileSync('./certs/pple-pay-merchant-id.p12') });}
In this example we have taken the Merchant Certicate downloaded from the Apple Developer portal, opened it in the Mac app
Keychain Access
and exported it asp12
file creating a password (dont usesuper-secret
). Thep12
file contains both the certificate and private key so we treat the password like any secret, and keep it out of our code repository rather than hard coding like in the sample above.
#
CORSAs your web application will call this API endpoint you must also deal with CORS otherwise you will get an error like: Failed to load resource: Request header field Content-Type is not allowed by Access-Control-Allow-Headers
.
Your backend can minimally implement CORS like:
if (event.httpMethod == 'OPTIONS') { return { statusCode: 200, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': '*' } }}
#
Payment Authorization APIThe plugin will call the paymentAuthorization.url
which you must implement.
This endpoint will authorize a transaction for a payment processor. Your code should accept the token data and make a payment request with your chosen payment processor. The response should indicate sucess if the payment was succcessful.
When a payment is processed with the plugin this endpoint will be called with a post
request and a body similar to this (values shorted for brevity):
{ "request": { "countryCode": "US", "currencyCode": "USD", "merchantCapabilities": [ "supports3DS" ], "supportedNetworks": [ "amex", "masterCard", "visa" ], "lineItems": [], "total": { "label": "Hot Dog", "amount": "5.00", "type": "final" } }, "token": { "paymentMethod": { "network": "MasterCard", "type": "credit", "displayName": "MasterCard 7951" }, "transactionIdentifier": "F9123768BEA8086F1FB011CD3C10079DE9FD635FC5F43DD1E4315A8F4264F1B3", "paymentData": { "data": "tE9equ6CGvj2b...GAM6mdBMARoBA==", "signature": "MIAGCSqGSIb3DQE...9gCMcSQWK9uQOp1gAAAAAAAA=", "header": { "publicKeyHash": "0/jmqztUB...Ms9xXBZQTY=", "ephemeralPublicKey": "MFkwEwYHKoZIzj0CA...rZUd7pOIawDDUYAoKw==", "transactionId": "f9123768fae8096f1fb011cd3c10079de9fd635fc5f63dd1e4315a8f4264f1b3" }, "version": "EC_v1" } }}
You can return a response and it will be returned to the app. So if you return a JSON response of:
{ "message": "Hello World"}
Then in the app your call for const response = await getApplePaySession(request);
will have a response
of:
{ "success": true, "data": {"message": "Hello World"}}