Integrating with Apple Pay™️

Overview

When a cardholder submits a payment using Apple Pay (integrated with VGS), the following sequence occurs:

  • Apple establishes a secure connection with the merchant’s client-side and generates a DPAN associated with the card data.

  • Apple encrypts the card data using the merchant’s public key and forwards it to VGS.

  • VGS decrypts the card data using the merchant's private key, tokenizes it, and then forwards it to the merchant's server-side for storage and payment processing

To implement this integration, the merchant must set up an Apple Developer account, integrate the Apple Pay button on their client-side, and configure an Apple Pay inbound route in VGS.

Prerequisites

Before proceeding with the Apple Pay integration using VGS, get to know the following VGS products: Vaults, Proxies and Routing Data, and Tokenization.

Additional Requirements

This process requires specific security access to create necessary files in the Apple Developer Dashboard. For more details, refer to the Apple Support article

You will also need to generate two sets of keypairs:

  • Keypair A is required to create the Apple Pay Merchant Identity Certificate which enables your server-side to establish a secure TLS connection with Apple Pay

  • Keypair B is required to create the Apple Pay Payment Processing Certificate for Apple to encrypt the cardholder's DPAN before forwarding it to VGS

Additionally, you will provide the private key from Keypair B to VGS, which will be needed for decryption.

Full Process Diagram

Apple Pay Button Integration

To accept payments, you must host an Apple Pay button on your client. Below are some helpful integration resources provided by Apple and VGS:

Apple Pay Merchant Identity Certificate / Keypair A

  • Open your Apple Developer Dashboard and navigate to Certificates, Identifiers and Profiles

  • Select Identifiers from the left hand menu

  • Click the "+" button next to Identifiers and create new Merchant ID (or select an existing one)

  • Once new Merchant ID is created, click on it, scroll down to the bottom of the page, and add your domain under Merchant Domains. (This is where your Apple Pay button will be hosted).

  • Follow the necessary steps to verify your domain with Apple. Remember to download the file and save it at the specified location.

  • Once your domain is verified, reopen your Merchant ID and, under Apple Pay Merchant Identity Certificate select Create Certificate.

  • In your Terminal, create a .csr and .key files using this command: openssl req -out uploadMe.csr -new -newkey rsa:2048 -nodes -keyout certificate_sandbox.key. Follow the instructions in your Terminal.

  • Upload the generated uploadMe.csr file to your Apple Developer dashboard.

  • Download the merchant_id.cer file from Apple to your working project directory.

  • Convert the .cer file to a .pem file (required for establishing a merchant session with Apple). Enter the following command in your terminal: openssl x509 -inform der -in merchant_id.cer -out certificate_sandbox.pem

Apple Pay Payment Processing Certificate / Keypair B

  • Open your Apple Developer Dashboard and navigate to Certificates, Identifiers and Profiles at the bottom.

  • Select Certificates from the left hand menu

  • Click "+" button next to Certificates and select Apple Pay Payment Processing Certificate.

  • Select your Merchant ID and continue.

  • Under Apple Pay Payment Processing Certificate click Create Certificate.

  • In your Terminal, generate a set of ECC Keys. You can do this with OpenSSL: openssl ecparam -out applepay.key -name prime256v1 -genkey

  • With this key, generate a .csr file with: openssl req -new -sha256 -key applepay.key -nodes -out uploadMe2.csr -subj '/O=Company/C=US'

  • Upload the CSR to your Apple Pay dashboard. You should save your signed certificate as applepay.cer.

  • Download the apple_pay.cer file from Apple to your working project directory.

  • Convert your signed request. You should have applepay.cer downloaded from your Apple Pay dashboard. This file is in DER format. This will need to be converted to PEM format, and can be done with OpenSSL, remove unnecessary data and create a file named 'pubkey.txt': openssl x509 -in apple_pay.cer -text -inform DER -outform PEM -out applepay.pem && tr -d '\n' < applepay.pem | sed -e 's/.*-----BEGIN CERTIFICATE-----//g' -e 's/-----END CERTIFICATE-----//g' > pubkey.txt.

  • Generate a secure password. The PKCS12 Keystore must be password protected, or the integration will not work. Generate the PKCS12 Keystore. This will have the private ECC key and the Payment Processing Certificate that Apple Signed and associated with your merchant ID: openssl pkcs12 -export -out applepay.p12 -inkey applepay.key -in applepay.pem -passout pass:<password>. Save/Remember the password, as it will be required during the VGS setup.

Setting up an Inbound Route in VGS

In your VGS Dashboard, navigate to Addons. Enable Apple from the list and enter the following fields:

  • Merchant ID - Your full Apple Pay merchant identifier, e.g. merchant.verygoodsecurity.example.merchant

  • Keystore - Convert the keystore you created earlier to base64: cat applepay.p12 | base64

  • Keystore Password - Enter the password you created for Keypair B

  • Public Key - Paste the string from 'pubkey.txt' you created for Keypair B

After successfully completing these steps, you can confirm if the route has been set up by going to the route section on the VGS dashboard. VGS will be able to use your Keypair B Private Key to decrypt the data received from Apple.

Moving to a Live Vault

To go live with VGS, you must promote your sandbox vault to production. Learn more about the process here.

Currently, your Apple Pay inbound route uses sandbox-level tokens for your Apple Pay credentials, including the Merchant ID, Keystore, Keystore Password, and Public Key. Before moving to production, you need to generate live tokens for these credentials and update your Apple Pay .yaml file accordingly.

Use the Vault API's Create Aliases endpoint to generate live tokens for your Apple Pay credentials. Once you have these live tokens, replace the existing sandbox tokens in your YAML file before importing it into your production vault.

Below is an example curl for this request:

curl --request POST \
 --url 'https://VAULT_ID.live.vault-api.verygoodvault.com/aliases' \
 --header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
 --header 'Content-Type: application/json' \
--data '{
   "data": [
       {
           "value": "YOUR_KEYSTORE",
           "classifiers": [
               "keyStore"
           ],
           "format": "UUID"
       },
       {
           "value": "YOUR_KEYSTORE_PASSWORD",
           "classifiers": [
               "keyStorePassword"
           ],
           "format": "UUID"
       },
       {
           "value": "YOUR_BASE_64_PUBLIC_KEY",
           "classifiers": [
               "publicKey"
           ],
           "format": "UUID"
       },
       {
           "value": "YOUR_MERCHANT_ID",
           "classifiers": [
               "merchantID"
           ],
           "format": "UUID"
       }
   ]
}'

Cycling Keys Prior to Expiration

To cycle keys that are nearing expiration, follow the same steps outlined above for Keypair B, to generate new outputs. Once completed, tokenise the outputs using the Vault API and update the corresponding fields in your route.

Apple Pay may sometimes cache your keys and use them for payload generation. This would prevent VGS from decrypting your payload with a new key set. To avoid this, you can include two keypairs in the route via the configuration below:

To list two keypairs in the route, update the below section of your Apple Pay route config on the vGS Dashboard:

- - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_sandbox_oLtktbWQDpyJ4pD17UbFYW
      var: ctx.keystore
  - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_sandbox_jKv1oczf84RKw264LTHwyK
      var: ctx.keystorePassword
  - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_sandbox_rteKYhgSdnpRtvGS9V5cC8
      var: ctx.merchantId
  - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_sandbox_i6sgXMMNAAi7uKoXm6HJM2
      var: ctx.publicKey
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.merchantId
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.keystore
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.keystorePassword
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.publicKey
  - name: >-
      github.com/verygoodsecurity/common/utils/crypto/pay/apple/DecryptToken
    parameters:
      input: ctx.requestBody
      keystore: ctx.keystore
      keystorePassword: ctx.keystorePassword
      merchantId: ctx.merchantId
      publicKey: ctx.publicKey
      skipSignatureTimeValidation: false
      var: ctx.decryptedData

Such that it becomes:

- - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_sandbox_oLtktbWQDpyJ4pD17UbFYW
      var: ctx.keystore
  - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_sandbox_jKv1oczf84RKw264LTHwyK
      var: ctx.keystorePassword
  - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_sandbox_rteKYhgSdnpRtvGS9V5cC8
      var: ctx.merchantId
  - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_sandbox_i6sgXMMNAAi7uKoXm6HJM2
      var: ctx.publicKey
  - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_YOURNEWKEYSTOREPASSWORDTOKEN
      var: ctx.additionalKeystorePassword
  - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_YOURNEWPUBLICKEYTOKEN
      var: ctx.additionalPublicKey
  - name: github.com/verygoodsecurity/common/vars/Set
    parameters:
      value: tok_YOURNEWKEYSTORETOKEN
      var: ctx.additionalKeystore
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.merchantId
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.keystore
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.keystorePassword
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.publicKey
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.additionalKeystore
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.additionalKeystorePassword
- - name: github.com/verygoodsecurity/common/vars/Reveal
    parameters:
      var: ctx.additionalPublicKey
  - name: >-
    github.com/verygoodsecurity/common/utils/crypto/pay/apple/DecryptToken
  parameters:
    input: ctx.requestBody
    keystore: ctx.keystore
    keystorePassword: ctx.keystorePassword
    merchantId: ctx.merchantId
    additionalKeystore: ctx.additionalKeystore
    additionalKeystorePassword: ctx.additionalKeystorePassword 
    additionalPublicKey: ctx.additionalPublicKey  
    publicKey: ctx.publicKey
    skipSignatureTimeValidation: false
    var: ctx.decryptedData

After the first set of keys expires and is no longer used by Apple Pay to generate encrypted payloads, update all tokens in the route to reflect the current key set. This approach simplifies future key rotations.

Requesting a Merchant Session

To validate that the merchant is authorized to host the Apple Pay button on the client-side, your server-side must request a Merchant Session from Apple (for further details, see the Apple Pay Demo and Payment Session Documentation).

When making a call to Apple, include the certificate_sandbox.pem and certificate_sandbox.key from the Keypair A files as shown in the example below:

const httpsAgent = new https.Agent({
  rejectUnauthorized: false,
  cert: fs.readFileSync(path.join(__dirname, '.../certificate_sandbox.pem')),
  key: fs.readFileSync(path.join(__dirname, '.../certificate_sandbox.key')),
});

const response = await axios.post(
  appleUrl,
  {
    merchantIdentifier: <YOUR_MERCHANT_ID> // e.g. "merchant.com.vgs.sample.app",
    initiativeContext: <YOUR_VERIFIED_DOMAIN> // e.g. "merchant.com",
    initiative: "web",
    displayName: <MERCHANT_NAME> // e.g. "Merchant LLC"
  },
  { httpsAgent }
);

Routing Encrypted Data to VGS

Once your client-side is able to retrieve encrypted data from Apple, forward it to VGS so that it can be decrypted, tokenized and forwarded to your server-side (For more details on forwarding data to third parties, refer to the Apple Pay Demo).

Configure the upstream host to point to your Apple Pay Inbound Route URL as shown in the example below:

session.onpaymentauthorized = event => {

    const paymentPayload = event.payment.token;

    fetch('https://<YOUR_VAULT_ID>.sandbox.verygoodproxy.com/post', { 
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({"token":paymentPayload})
    })

    // Define ApplePayPaymentAuthorizationResult
    const result = {
        "status": ApplePaySession.STATUS_SUCCESS
    };
    session.completePayment(result);
};

Samples

Check out our sample applications below to see how to collect Apple Pay™️ data with VGS.

This examples shows how easily you can integrate VGS + Apple Pay™️ into your React application and secure sensitive payments data. Check our sample app to see how to collect Apple Pay™️ data with the VGS Collect iOS SDK.

Last updated