Setting up your own Apple Pay and Google Pay
This page is for integrators who want to take Apple Pay or Google Pay with their own Apple and Google registrations. Most merchants do not need to: PayLolly's registrations work on the hosted page and on your own page. Read section 1 first to find out which path you need.
Important
Check that your account can take wallet payments before you build anything. Wallet payments go only to merchant accounts whose processor has been enabled for the wallet. Today only simulated (test) accounts take wallet payments. For a live acquirer account, ask PayLolly whether and when the wallet is enabled for it before you build anything for production, and in particular before you register with Apple or Google yourself. Until then a live request is refused with RTE05, and the hosted page shows no wallet button.
1. Which path do you need?
| Path | Where the button is | Apple and Google registration | What you do |
|---|---|---|---|
| A. Hosted payment page | PayLolly's hosted page | PayLolly's | Add WithApplePay / WithGooglePay to mode. Nothing to obtain. Your websites only matter when the page is in an iframe. |
| B. Your own page, PayLolly's registration | Your page, through the Wallet API | PayLolly's | Give PayLolly your websites. Payment/WalletConfig gives you PayLolly's merchant id and key; for Apple Pay PayLolly also validates your sessions (Payment/ApplePayValidate). |
| C. Your own page, your own registration | Your page, through the Wallet API | Yours | Register with Apple and Google yourself, and validate Apple Pay sessions on your own server. PayLolly holds the keys that decrypt your tokens, decrypts them and takes the payments. |
All three need the permissions described in Wallets (for B and C also the Wallet API permission) and an account that can take the wallet (see the box above).
When is C worth it?
- You want your own legal or trading name and your own merchant registration with Apple and Google, rather than PayLolly's.
- You want your website registrations to be yours, independent of PayLolly's, and not to depend on PayLolly registering each of your websites.
What C costs you:
- An Apple Developer Program membership and a Google Pay & Wallet Console business profile, and Google's production review of your website.
- Certificates that expire (Apple, every 25 months) and keys to rotate (Google, yearly), each on your calendar.
- Your own session validation code for Apple Pay (section 3.5), with your own Merchant Identity certificate on your server.
- For Google Pay, Google's requirements for a Direct integration (section 4.1), which are strict.
The hosted payment page (path A) always uses PayLolly's registrations, also for a merchant that has its own for the Wallet API. You can do C for one wallet and B for the other.
2. Who does what (path C)
| Who | What |
|---|---|
| You (the merchant or integrator) | Apple Developer account, Apple merchant id, the two Apple certificates (the Payment Processing certificate is requested with PayLolly's help), your domains registered and verified with Apple, your Apple Pay session validation code. Google Pay & Wallet Console business profile and merchant id, the public key registered with Google, your website submitted for Google's review. Your own page and server code. |
| The PayLolly administrator of your merchant | Adds the own configuration of the wallet to your merchant in the PayLolly Portal, generates the Apple certificate request or the Google key (or uploads yours), gives you the Apple certificate request and the Google public key, makes a new Google key current once you have registered it with Google, and switches the configuration on (Enabled). |
| PayLolly | Keeps the Apple Payment Processing key and the Google private key encrypted, decrypts your tokens and takes the payments. PayLolly never holds your Apple Merchant Identity certificate. |
Once the own configuration of a wallet is switched on, it is used for every Wallet API call of that wallet, except Payment/ApplePayValidate, which is refused with WAL09. If it is switched off, or not complete, the Wallet API refuses the wallet with WAL03: it does not fall back to PayLolly's registration. The Portal does not let an administrator switch on a configuration that is not complete.
3. Apple Pay, path C, step by step
sequenceDiagram
Browser->>+Website: Open the checkout
Website->>+Api: Payment/WalletConfig (ApplePay, amount, currency)
Api->>+Website: paymentRequest
Browser->>+Website: Validation url (onvalidatemerchant)
Website->>+Apple: Payment session request (your Merchant Identity certificate)
Apple->>+Website: Merchant session
Website->>+Browser: Merchant session (completeMerchantValidation)
Browser->>+Website: Token (paymentData)
Website->>+Api: Payment/PayWithApple
Api->>+Website: Outcome
3.1 Apple Developer account and merchant id
- You need a membership of the Apple Developer Program, and to sign in as the Account Holder or an Admin for the steps below.
- In Certificates, Identifiers & Profiles, add a Merchant ID (for example
merchant.com.example.shop). It never expires, and you can use it for several websites. - Give the merchant id to your PayLolly administrator: it is entered as Apple merchant id in the Portal.
3.2 Payment Processing certificate (PayLolly decrypts with it)
Apple encrypts every payment token with the public key of this certificate; PayLolly decrypts it with the private key. Apple requires an elliptic curve P-256 key for it. There are two ways:
- Recommended: the key is made in PayLolly. Your PayLolly administrator clicks Generate CSR in the Apple Pay section of your merchant (the key never leaves PayLolly, and this is saved at once) and gives you the certificate signing request. In your Apple account, under your merchant id, Apple Pay Payment Processing Certificate, Create Certificate, upload the request and download the issued
.cer. Give the.certo your administrator, who uploads it alone and saves the merchant. - Your own key. Make a P-256 key and request yourself (Apple's instructions: ECC, 256 bit; with OpenSSL the curve
prime256v1), have Apple issue the certificate, and give your administrator either a.p12that holds the certificate and its key (with its password), or the.cerwith its private key as an unencrypted.pem. Send them through a secure channel.
PayLolly checks the upload before it is used: the key must be the certificate's, on P-256, not encrypted, the certificate must not have expired and must be a Payment Processing certificate (not the Merchant Identity certificate), and the key must work. A certificate for another Apple merchant id than the one entered is reported as a warning.
Note
Each Generate CSR makes a new key and replaces any earlier key that is still waiting for its certificate: upload the certificate for the latest request.
3.3 Merchant Identity certificate (stays on your server)
This TLS client certificate proves to Apple that a payment session request comes from you. It is used only on your own server (section 3.5); do not give it to PayLolly.
In your Apple account, under your merchant id, Apple Pay Merchant Identity Certificate, Create Certificate: upload a certificate signing request made with a key you keep, and download the .cer. Apple's account help describes how to make the request; check there for the key type it currently expects. Keep the key and the certificate together on your server, for example as a .p12:
openssl x509 -inform der -in merchant_id.cer -out merchant_id.pem
openssl pkcs12 -export -inkey merchant_id.key -in merchant_id.pem -out merchant_id.p12
3.4 Register and verify your websites
Every website (top-level domain or subdomain) that shows the Apple Pay button must be registered and verified under your merchant id. For a page in an iframe, it is the website the payer sees in the address bar.
- In your Apple account, under your merchant id, Merchant Domains, Add Domain, and enter the fully qualified name.
- Download the domain verification file and serve it on that website at
https://<your domain>/.well-known/apple-developer-merchantid-domain-association, over HTTPS, publicly, with no redirect and no proxy in front of it. - Click Verify.
Apple's requirements for your server include HTTPS with a valid certificate and TLS 1.2 or later. A verification lasts as long as the website's TLS certificate: renew that certificate before it expires, or verify the website again.
3.5 Validate the session on your server
When the payer taps the button, the sheet raises onvalidatemerchant with a validation url. Your page sends it to your server; your server sends a payment session request to that url with your Merchant Identity certificate as the TLS client certificate, and returns Apple's answer to the page unchanged, which hands it to session.completeMerchantValidation. A session is valid for five minutes.
Payment/ApplePayValidate is not used on path C: for a merchant whose own Apple Pay configuration is switched on it is refused with WAL09.
The request body:
| Field | Value |
|---|---|
merchantIdentifier |
Your Apple merchant id |
displayName |
Your store's name, at most 64 characters, the same every time |
initiative |
web |
initiativeContext |
The website the button is on (a registered domain, without https:// or a path) |
Only call Apple. Before you send anything, check that the validation url is https and that its host is one of Apple's Apple Pay gateways listed in Apple's "Setting up your server" (for example apple-pay-gateway.apple.com), and refuse anything else: the url comes from the browser, and your certificate must never be shown to another server. The -cert gateways (for example apple-pay-gateway-cert.apple.com) are Apple's sandbox: allow them only on servers you test with, never in production.
curl
# VALIDATION_URL is the url the sheet gave, after you have checked its host
curl --fail --silent --show-error \
--cert merchant_id.pem --key merchant_id.key \
-H 'Content-Type: application/json' \
-d '{"merchantIdentifier":"merchant.com.example.shop","displayName":"Example Shop","initiative":"web","initiativeContext":"shop.example.com"}' \
"$VALIDATION_URL"
Node.js
const https = require('https');
const fs = require('fs');
// Apple's production gateways; add the -cert (sandbox) hosts on test servers only
const appleHosts = new Set(['apple-pay-gateway.apple.com', 'cn-apple-pay-gateway.apple.com']);
const cert = fs.readFileSync('merchant_id.pem');
const key = fs.readFileSync('merchant_id.key');
function appleMerchantSession(validationUrl, domain) {
const url = new URL(validationUrl);
if (url.protocol !== 'https:' || !appleHosts.has(url.hostname))
return Promise.reject(new Error('not an Apple Pay gateway'));
const body = JSON.stringify({
merchantIdentifier: 'merchant.com.example.shop',
displayName: 'Example Shop',
initiative: 'web',
initiativeContext: domain, // a domain you registered with Apple
});
return new Promise((resolve, reject) => {
const req = https.request(url, {
method: 'POST', cert, key, timeout: 10000,
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
}, res => {
let text = '';
res.on('data', c => text += c);
res.on('end', () => res.statusCode === 200 ? resolve(text) : reject(new Error('Apple answered ' + res.statusCode)));
});
req.on('timeout', () => req.destroy(new Error('timeout')));
req.on('error', reject);
req.end(body);
});
}
// your /apple-pay-session endpoint returns the text unchanged (Content-Type: application/json)
C#
using System.Net.Http.Json;
using System.Security.Cryptography.X509Certificates;
public sealed class AppleMerchantSession
{
// Apple's production gateways; add the -cert (sandbox) hosts on test servers only
static readonly HashSet<string> AppleHosts = new(StringComparer.OrdinalIgnoreCase)
{ "apple-pay-gateway.apple.com", "cn-apple-pay-gateway.apple.com" };
readonly HttpClient http;
public AppleMerchantSession(string p12Path, string p12Password)
{
// a .p12 (certificate and key); on Windows a certificate loaded from PEM files
// may need re-exporting to PKCS#12 before it can be used as a client certificate
var certificate = X509CertificateLoader.LoadPkcs12FromFile(p12Path, p12Password);
var handler = new HttpClientHandler { ClientCertificateOptions = ClientCertificateOption.Manual };
handler.ClientCertificates.Add(certificate);
http = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(10) }; // keep one instance
}
public async Task<string> RequestAsync(string validationUrl, string domain)
{
if (!Uri.TryCreate(validationUrl, UriKind.Absolute, out var url)
|| url.Scheme != Uri.UriSchemeHttps || !AppleHosts.Contains(url.Host))
throw new ArgumentException("not an Apple Pay gateway");
using var response = await http.PostAsJsonAsync(url, new
{
merchantIdentifier = "merchant.com.example.shop",
displayName = "Example Shop",
initiative = "web",
initiativeContext = domain, // a domain you registered with Apple
});
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync(); // give it to the page unchanged
}
}
(X509CertificateLoader is .NET 9 or later; on earlier versions use new X509Certificate2(p12Path, p12Password).)
The page side is the one in Wallets: only the endpoint your page posts the validation url to now calls Apple instead of Payment/ApplePayValidate.
3.6 Show the sheet and pay
- Get the
paymentRequestfromPayment/WalletConfigas in Wallets: on path C itscountryCodeis the one of your own configuration. Or build the same object yourself. - When the payer authorises, send
event.payment.token.paymentData, as a json string, to your server, and from there toPayment/PayWithApple(see Wallets). PayLolly decrypts it with your Payment Processing key.
4. Google Pay, path C, step by step
4.1 Before you start: Google's Direct integration
With your own Google merchant id, your website asks Google to encrypt the payment data for a public key whose private key PayLolly holds. In Google's terms this is a Direct integration (tokenization type DIRECT), not a gateway integration. Google's documentation sets requirements for Direct integrations, including:
- PCI DSS compliance validated by a Qualified Security Assessor, and a PCI attestation provided through the Google Pay & Wallet Console;
- registering your public encryption key in the console, and rotating it once a year.
Google's documentation also says that third parties providing gateway or processing services to other merchants are not eligible for a Direct integration. Confirm with Google and with PayLolly that path C for Google Pay is open to you, and which PCI documentation is expected, before you rely on it. If it is not, use path A or B for Google Pay.
4.2 Business profile and merchant id
- In the Google Pay & Wallet Console, choose the business type Merchant and complete your business profile.
- In the Google Pay API section, accept the terms of service and the acceptable use policy.
- Your merchant id is shown in the console. Give it to your PayLolly administrator, with the merchant name you are registered under and your two letter country code (for Google, the country of the acquiring bank).
4.3 The key
Your PayLolly administrator, in the Google Pay section of your merchant:
- clicks Generate key (the private key never leaves PayLolly; this is saved at once), or
- uploads a private key you made: an unencrypted P-256 key, as a
.pemor as base64 PKCS#8 text. (Google's documentation shows how to make one with OpenSSL, curveprime256v1.)
The Portal then shows the public key (base64, uncompressed point). Register it in the Google Pay & Wallet Console (Google Pay API, Direct integration, public encryption key), and use it in your payment data request.
The first key of your merchant becomes its current key straight away: nothing is served before it, so there is nothing to protect. Every later key (generated or uploaded while a current key is in use) is pending, as in section 4.7: it is not served until your administrator clicks Make current.
Payment/WalletConfig always serves the public key of the current key. PayLolly decrypts tokens made for the current key, for a pending key and for the other (previous) key.
4.4 Build the payment data request
The simplest is to keep calling Payment/WalletConfig as in Wallets: on path C its paymentDataRequest already holds your merchant id, merchant name and public key. You can also build it yourself:
{
"apiVersion": 2,
"apiVersionMinor": 0,
"allowedPaymentMethods": [
{
"type": "CARD",
"parameters": {
"allowedAuthMethods": ["CRYPTOGRAM_3DS"],
"allowedCardNetworks": ["MASTERCARD", "VISA"],
"billingAddressRequired": true,
"billingAddressParameters": { "format": "MIN", "phoneNumberRequired": false }
},
"tokenizationSpecification": {
"type": "DIRECT",
"parameters": {
"protocolVersion": "ECv2",
"publicKey": "BExamplePublicKeyFromThePortal0000000000000000000000000000000000000000000000000000000000="
}
}
}
],
"merchantInfo": { "merchantId": "BCR2DN4TEXAMPLE0", "merchantName": "Example Shop Ltd" },
"transactionInfo": {
"countryCode": "MT",
"currencyCode": "EUR",
"totalPriceStatus": "FINAL",
"totalPrice": "10.50",
"totalPriceLabel": "Total"
},
"emailRequired": true
}
publicKey: the current public key the Portal shows, exactly (or, while you test a new key, its pending public key: see section 4.7). Another key gives tokens PayLolly cannot decrypt (WAL01).merchantId: your Google merchant id, the one entered in the Portal: tokens are made for it.allowedCardNetworks: Visa and Mastercard only.allowedAuthMethods:CRYPTOGRAM_3DSin production. In Google's TEST environment addPAN_ONLY(["CRYPTOGRAM_3DS", "PAN_ONLY"]), asPayment/WalletConfigdoes: Google's test cards have no cryptogram, so without it the TEST sheet has no card to offer.PAN_ONLYis accepted in TEST only.totalPriceandcurrencyCodemust be those of the payment you then send to PayLolly.
Show the sheet as in Wallets. Use TEST or PRODUCTION as environment as Payment/WalletConfig says: it follows the PayLolly environment you call, not your merchant.
4.5 Production review
In the TEST environment no review is needed and any merchant id works. For production, in the console under Google Pay API, Integrations, add your website (the top-level domain that calls the Google Pay API), choose the Direct integration type, upload the screenshots Google asks for and submit it for approval. Go through Google's integration checklist first, including its brand guidelines (show the button only after isReadyToPay).
4.6 Pay
Send paymentData.paymentMethodData.tokenizationData.token from your server to Payment/PayWithGoogle (see Wallets).
4.7 Key rotation
Google asks for the key to be rotated once a year. With PayLolly:
- Your administrator clicks Generate key (or uploads a new key and saves the merchant). The new key is pending: the key in use stays the current key and
Payment/WalletConfigkeeps serving its public key. The Portal shows the pending public key next to the current one. - Register the pending public key with Google (Google Pay & Wallet Console). PayLolly already decrypts tokens made for it, so if you build the payment data request yourself you can test the new key before the switch.
- Your administrator clicks Make current. From then on
Payment/WalletConfigserves the new public key, and the key that was in use is kept as the other key: tokens made with the old public key still decrypt. - Keep the other key for at least eight days after the old public key stopped being used (Google's rule), then have your administrator click Clear other key, and delete the old public key in the console.
Only one pending key and one other key are kept: generating again replaces the pending key (register the newest one), and making a key current while an other key is kept drops that older key. A pending key you no longer want can be removed with Discard pending key (then save the merchant).
5. A suggested order
- Path A first. Add
WithGooglePayorWithApplePayto a hosted payment on a test merchant with a simulated (Dummy) account, and see a wallet payment end to end. - Path B on a test merchant. Build your own page with
Payment/WalletConfig,Payment/ApplePayValidateandPayment/PayWithGoogle/Payment/PayWithAppleagainst the Dummy processor, with Google's TEST environment and Apple's sandbox. - Path C only when you need it, once you know that your live account can take the wallet and (for Google Pay) that a Direct integration is open to you.
- Go-live checklist:
- The Wallet API permission and the wallet permission are switched on for your production merchant.
- Your live account takes the wallet in your currencies (confirmed by PayLolly: otherwise
RTE05). - Apple: every website registered and verified under your merchant id; your session validation allows only Apple's production gateways; the own configuration is Enabled.
- Google: website approved for production; the public key registered in the console;
environmentPRODUCTION; the own configuration is Enabled. - Dates in your diary: the Apple Payment Processing and Merchant Identity certificates (25 months each), your websites' TLS certificates, the yearly Google key rotation.
- Monitoring of the
WALandRTE05errors in your server's logs.
6. Testing
- Google Pay: use the TEST environment. Google shows its own test cards; they arrive as a card number without a cryptogram, which PayLolly accepts in TEST only.
- Apple Pay: use Apple's sandbox: a sandbox tester account (created in App Store Connect) signed in on an Apple device, with Apple's test cards added to Wallet; your session validation calls the sandbox gateway the sheet gives. Ask PayLolly whether sandbox tokens are accepted in the PayLolly environment you test in.
- The Dummy processor takes wallet payments at once. The result is decided by the cents of the amount, as for the test cards:
.01declines,.02is an error, anything else is approved. Payment/GetPaymentStatusshowscardData.wallet(ApplePayorGooglePay).
7. Troubleshooting
| Code | Likely cause on path C | What to do |
|---|---|---|
WAL01 |
The token was not made for this configuration: another Apple merchant id or Payment Processing certificate, a Google merchant id or public key other than the one in the Portal, an expired token, or Apple's certificate replaced in Apple's account but not yet uploaded to PayLolly | Compare the merchant id and the public key with the Portal; upload the current Apple certificate; send tokens to your server at once |
WAL02 |
A network other than Visa or Mastercard, no cryptogram (Google PAN_ONLY outside TEST), or for Apple Pay an amount or currency other than those of the sheet |
Send the amount and currency the payer was shown |
WAL03 |
No permission for the wallet, or your own configuration of the wallet is switched off, not complete or cannot be read. This includes Payment/ApplePayValidate while your own Apple configuration is switched off |
Ask your PayLolly administrator: the Portal shows what the configuration lacks |
WAL05 |
Payment/ApplePayValidate for a website not registered with PayLolly (path B only) |
On path C, validate on your own server |
WAL07 |
The Wallet API permission is not switched on | Ask PayLolly |
WAL09 |
Payment/ApplePayValidate called for a merchant whose own Apple configuration is switched on. The usual checks come first: without the Wallet API permission the answer is WAL07, without the Apple Pay permission WAL03, and with no account that takes Apple Pay RTE05 |
Validate the session on your own server (section 3.5) |
RTE05 |
None of your accounts can take this wallet payment: the account's processor does not take the wallet, or the request has something that account's processor does not support (for example a descriptor), or the currency does not match. Today only simulated (test) accounts take wallet payments |
Ask PayLolly which of your accounts can take the wallet |
Apple refusing your own payment session request is between your server and Apple: check the merchant id, the Merchant Identity certificate (not expired, for that merchant id), and that initiativeContext is a verified website.
8. Renewals
- Apple Payment Processing certificate (25 months). The Portal warns 60 days before it expires. To renew, your administrator clicks Generate CSR, you have Apple issue a new certificate for that request, and your administrator uploads it and saves the merchant. Do that before the new certificate is used in Apple's account (check Apple's account for how and when a new certificate becomes active), so that PayLolly can decrypt tokens made with it. The certificate in use is kept as the previous certificate, so tokens made for it still decrypt; only one previous certificate is kept. Once no more tokens are made for it, your administrator can click Clear previous certificate.
- Apple Merchant Identity certificate (25 months). It is yours, on your server: renew it in your Apple account and replace it on your server before it expires. PayLolly is not involved.
- Website verification with Apple lasts as long as the website's TLS certificate: renew the TLS certificate in time, or verify the website again.
- Google key, yearly: see section 4.7.
Apple also sends reminders to the Account Holder and Admins of your Apple account before certificates and verifications expire.
References
Apple:
- Configuring your environment: https://developer.apple.com/documentation/applepayontheweb/configuring-your-environment
- Setting up your server: https://developer.apple.com/documentation/applepayontheweb/setting-up-your-server
- Maintaining your environment: https://developer.apple.com/documentation/applepayontheweb/maintaining-your-environment
- Requesting an Apple Pay payment session: https://developer.apple.com/documentation/applepayontheweb/requesting-an-apple-pay-payment-session
- Configure Apple Pay (merchant id, Payment Processing certificate): https://developer.apple.com/help/account/capabilities/configure-apple-pay
- Configure Apple Pay on the web (domains, Merchant Identity certificate): https://developer.apple.com/help/account/capabilities/configure-apple-pay-on-the-web
- Create a certificate signing request: https://developer.apple.com/help/account/certificates/create-a-certificate-signing-request
- Preparing merchant domains for verification (path of the verification file): https://developer.apple.com/documentation/applepaywebmerchantregistrationapi/preparing-merchant-domains-for-verification
- Payment token format reference: https://developer.apple.com/documentation/passkit/payment-token-format-reference
- Sandbox testing: https://developer.apple.com/apple-pay/sandbox-testing/
Google:
- Payment data cryptography (ECv2, keys, rotation): https://developers.google.com/pay/api/web/guides/resources/payment-data-cryptography
- Request objects (DIRECT tokenization, merchant info, transaction info): https://developers.google.com/pay/api/web/reference/request-objects
- Request production access: https://developers.google.com/pay/api/web/guides/test-and-deploy/request-prod-access
- Integration checklist: https://developers.google.com/pay/api/web/guides/test-and-deploy/integration-checklist
- Test card suite: https://developers.google.com/pay/api/web/guides/resources/test-card-suite
Apple and Google change these procedures from time to time: where this page and theirs differ, follow theirs, and tell PayLolly.