Show / Hide Table of Contents

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

  1. You need a membership of the Apple Developer Program, and to sign in as the Account Holder or an Admin for the steps below.
  2. 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.
  3. 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 .cer to 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 .p12 that holds the certificate and its key (with its password), or the .cer with 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.

  1. In your Apple account, under your merchant id, Merchant Domains, Add Domain, and enter the fully qualified name.
  2. 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.
  3. 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 paymentRequest from Payment/WalletConfig as in Wallets: on path C its countryCode is 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 to Payment/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

  1. In the Google Pay & Wallet Console, choose the business type Merchant and complete your business profile.
  2. In the Google Pay API section, accept the terms of service and the acceptable use policy.
  3. 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 .pem or as base64 PKCS#8 text. (Google's documentation shows how to make one with OpenSSL, curve prime256v1.)

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_3DS in production. In Google's TEST environment add PAN_ONLY (["CRYPTOGRAM_3DS", "PAN_ONLY"]), as Payment/WalletConfig does: Google's test cards have no cryptogram, so without it the TEST sheet has no card to offer. PAN_ONLY is accepted in TEST only.
  • totalPrice and currencyCode must 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:

  1. 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/WalletConfig keeps serving its public key. The Portal shows the pending public key next to the current one.
  2. 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.
  3. Your administrator clicks Make current. From then on Payment/WalletConfig serves 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.
  4. 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

  1. Path A first. Add WithGooglePay or WithApplePay to a hosted payment on a test merchant with a simulated (Dummy) account, and see a wallet payment end to end.
  2. Path B on a test merchant. Build your own page with Payment/WalletConfig, Payment/ApplePayValidate and Payment/PayWithGoogle / Payment/PayWithApple against the Dummy processor, with Google's TEST environment and Apple's sandbox.
  3. 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.
  4. 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; environment PRODUCTION; 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 WAL and RTE05 errors 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: .01 declines, .02 is an error, anything else is approved.
  • Payment/GetPaymentStatus shows cardData.wallet (ApplePay or GooglePay).

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.

In this article
Back to top Generated by DocFX