Broadleaf Microservices
  • v1.0.0-latest-prod

ApplePay Checkout Integration Flow

Apple Pay Implementation Details in the Broadleaf Framework

It is possible to use the Apple Pay JS API or W3C Payment Request API to implement the Apple Pay payment. We used the Apple Pay JS API for our demo application and this documentation contains the examples for this API.

Providing Merchant Validation

When the user clicks on the Apple Pay button the event onvalidatemerchant is triggered to validate the merchant. The parameter of this function contains the validation URL - validationURL. This URL should be passed as the request parameter to the server - POST /api/payment/checkout-com/wallet-session/applepay/validate?validateSessionUrl={validationURL}. The response should be passed to the completeMerchantValidation method.

The example of the onvalidatemerchant event implementation:

    // The event that occurs when you click the Apple Pay button
    session.onvalidatemerchant = event => {
        const validationURL = event.validationURL;
      // "validateApplePaySession" should send the POST request to "/api/payment/checkout-com/wallet-session/applepay/validate?validateSessionUrl={validationURL}"
      validateApplePaySession(validationURL)
        .then(merchantSession => {
          session.completeMerchantValidation(merchantSession);
        })
        .catch(error => {
          console.error(error);
          session.completePayment(window.ApplePaySession.STATUS_FAILURE);
        });
    };

The endpoint com.broadleafcommerce.web.endpoint.CheckoutComWalletSessionEndpoint receives and handles this validation request.

See also Providing Merchant Validation documentation for more details.

Generate a Checkout.com token from the Apple Pay token

When the customer validates the transaction with biometrics, Apple will generate a payment token. This token should be converted into a Checkout.com card token. To do so, it has to be sent in the paymentMethodProperties of the Payment Request.

The example of the Payment Request with the Apple Pay token:

    // The event that occurs when the payment is authorized
    session.onpaymentauthorized = event => {
      const applePaymentToken = event.payment.token.paymentData;

        const paymentRequest = {
          name: 'Apple Pay',
          type: 'APPLE_PAY',
          gatewayType: 'CHECKOUT_COM',
          amount: '11.23',
          paymentMethodProperties: {
            apple_pay_token: JSON.stringify(applePaymentToken)
          }
        };

      ...
    };

On the server side before creation of the Payment object we execute the convert this token into a Checkout.com card token. The interface used to do so - com.broadleafcommerce.paymentgateway.service.PaymentGatewayExchangeWalletTokenService. By default, the exchanged token will be added to the com.broadleafcommerce.paymenttransaction.web.endpoint.domain.CreatePaymentRequest#paymentMethodProperties with the token key and the apple_pay_token will be removed. Using this converted token the standard payment request e.g. Authorize, can be executed.

See Apple Pay documentation for more details.

Set up Apple Pay

Before you get started please go to the Set up Apple Pay page and follow the steps to configure your Apple Pay integration.

After you’ve finished the setup process, you should have the following configuration:

  • A merchant ID (for example, merchant.com.mywebsite.sandbox).

  • Checkout.com linked to your merchant ID.

  • A domain verified by Apple.

  • A .key and a .pem certificate files.

Broadleaf Apple Pay Configuration

Default Certificates Provider

Our payment module includes an interface, com.broadleafcommerce.vendor.checkoutcom.service.sesseion.CheckoutComApplePayCertificateAndKeyProvider, that is responsible for reading the ApplePay certificates. The default implementation com.broadleafcommerce.vendor.checkoutcom.service.sesseion.DefaultCheckoutComApplePayCertificateAndKeyProvider uses ClassPathResource to resolve the certificates in the resources folder.

If you want to use another certificate storage, you can implement CheckoutComApplePayCertificateAndKeyProvider and override the default bean from com.broadleafcommerce.vendor.checkoutcom.autoconfigure.CheckoutComApplePayConfiguration. For example:

    @Bean
    public CheckoutComApplePayCertificateAndKeyProvider checkoutComApplePayCertificatesProvider() {
        return new MyCheckoutComApplePayCertificateAndKeyProvider();
    }

Add Certificates

Let’s assume that your certificate file names are private_key_sandbox.key and certificate_sandbox.pem.

Put these certificate files into the resources folder depending on your Flex Package deployment type. You can use the inner folder e.g resources/applepay to separate these files from others. For example:

  • Min - ../flexpackages/min/src/main/resources/applepay

  • Balanced - ../flexpackages/balanced/cart/src/main/resources/applepay

  • Granular - You have to put the files to the resources of Payment Transaction Service

Add Configuration Properties:

broadleaf:
  checkout-com:
    apple-pay:
      merchant-identifier: merchant.com.mydomain.test (1)
      display-name: MyCommerce (2)
      initiative-context: mydomain.com (3)
      certificate: applepay/certificate_sandbox.pem (4)
      private-key: applepay/private_key_sandbox.key (5)
  cartoperation:
    service:
        checkout:
          checkout-payment-method-options:
            - payment-method-type: CREDIT_CARD
              payment-method-gateway-type: CHECKOUT_COM
            - payment-method-type: APPLE_PAY
              payment-method-gateway-type: CHECKOUT_COM
  1. The merchant ID configured in your Apple Pay Developer account

  2. A string of 64 or fewer UTF-8 characters containing the canonical name for your store, suitable for display

  3. This should be equal to the domain you verified in your Apple Pay Developer account

  4. The path to the Apple certificate

  5. The path to the Apple certificate private key

Apple Pay Domain Names for Merchant Validation

There is a list of available domain names to validate the merchant session object, see Setting Up Your Server.

By default, we allow the following domains:

  • apple-pay-gateway-cert.apple.com

  • apple-pay-gateway.apple.com

  • apple-pay-gateway-nc-pod1.apple.com

  • apple-pay-gateway-nc-pod2.apple.com

  • apple-pay-gateway-nc-pod3.apple.com

  • apple-pay-gateway-nc-pod4.apple.com

  • apple-pay-gateway-nc-pod5.apple.com

  • apple-pay-gateway-pr-pod1.apple.com

  • apple-pay-gateway-pr-pod2.apple.com

  • apple-pay-gateway-pr-pod3.apple.com

  • apple-pay-gateway-pr-pod4.apple.com

  • apple-pay-gateway-pr-pod5.apple.com

If you need to change this list use broadleaf.checkout-com.apple-pay.valid-merchant-validation-domains property.

For more information see Providing Merchant Validation

Apple Pay JS API Integration Example

In the following example we used the Apple Pay JS API. It is also possible to use the W3C Payment Request API.

Please note that this is just an example and doesn’t contain the fully functional code.

export const CheckoutComApplePayButton = () => {
  const applePaySession = window.ApplePaySession;

  const [canPayWithApplePay, setCanPayWithApplePay] = useState(false);

  useEffect(() => {
      // Check that the payment can be made with Apple Pay
    if (applePaySession && applePaySession.canMakePayments()) {
      setCanPayWithApplePay(true);
    } else {
      setCanPayWithApplePay(false);
    }
  }, [applePaySession]);

  const handleClick = useHandleClick();

  if (!canPayWithApplePay) {
    return null;
  }

  return (
    <div>
      <div
        className="apple-pay-button apple-pay-button-black"
        lang='en'
        onClick={handleClick}
      ></div>
      {error && (
        <strong className="block my-4 text-red-600 text-lg font-normal">
          {error}
        </strong>
      )}
    </div>
  );
};

const useHandleClick = () => {
    const { createPayment } = useCreatePayment();
  return () => {
    // Create the Apple Pay session
    // see https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession
    const session = new window.ApplePaySession(6, {
      countryCode: 'US',
      currencyCode: 'USD',
      supportedNetworks: ['visa', 'masterCard'],
      merchantCapabilities: ['supports3DS'],
      total: {
        label: 'My Store',
        amount: '11.23'
      }
    });

    // The event that occurs when you click the Apple Pay button
    session.onvalidatemerchant = event => {
        const validationURL = event.validationURL;
      // "validateApplePaySession" should send the POST request to "/api/payment/checkout-com/wallet-session/applepay/validate?validateSessionUrl={validationURL}"
      validateApplePaySession(validationURL)
        .then(merchantSession => {
          session.completeMerchantValidation(merchantSession);
        })
        .catch(error => {
          console.error(error);
          session.completePayment(window.ApplePaySession.STATUS_FAILURE);
        });
    };

    // The event that occurs when the payment is authorized
    session.onpaymentauthorized = event => {
      const applePaymentToken = event.payment.token.paymentData;

      createPayment(JSON.stringify(applePaymentToken), session);
    };

    session.begin();
  };
};
const useCreatePayment = () => {

  const createPayment = async (
    applePaymentToken,
    appleSession
  ) => {
    const paymentRequest = {
      name: 'Apple Pay',
      type: 'APPLE_PAY',
      gatewayType: 'CHECKOUT_COM',
      amount: '11.23',
      paymentMethodProperties: {
        apple_pay_token: applePaymentToken
      }
    };

    let paymentSummary;

    try {
      paymentSummary = await handleSubmitPaymentInfo(paymentRequest);
      if (paymentSummary) {
        appleSession.completePayment(window.ApplePaySession.STATUS_SUCCESS);
      }
    } catch (err) {
      console.error('There was an error adding payment information', err);

      appleSession.completePayment(window.ApplePaySession.STATUS_FAILURE);
    }
  };

  return { createPayment };
};

Testing Locally

In this example we use the Ngrok to have the ability to open the storefront started locally from the Apple device. Please install and configure it before testing the Apple Pay integration.

To test the Apple Pay integration follow the next steps:

  1. Go to Testing Apple Pay page and configure your Apple Pay device.

  2. Configure your environment as described in this documentation above. Do not validate any domains in the Apple Developer, you will need to do it later. Please note that the property broadleaf.checkout-com.apple-pay.initiative-context will need to be changed later with the domain generated by ngrok, for example, c746-176-115-97-170.eu.ngrok.io without https:// (we will use this URL for this documentation).

  3. Add the following configuration:

     broadleaf:
       tenant:
         urlresolver:
           application:
             port: '443'
             domain: 'eu.ngrok.io'
  4. Run your app locally as usual

  5. Execute ngrok http https://{yourAppUri}/ to build the tunnel to your local app. Replace yourAppUri with your app URI e.g myapp.localhost:8456

  6. Change the broadleaf.checkout-com.apple-pay.initiative-context property with the generated domain - c746-176-115-97-170.eu.ngrok.io and restart your server.

  7. In the Admin panel go to the Security → Authorization Servers, open your app configuration and click on Authorized Clients tab (/authorization-servers/{yourAppId}?form=authorizedClients). Add the Redirect Urls - https://c746-176-115-97-170.eu.ngrok.io/callback and https://c746-176-115-97-170.eu.ngrok.io/silent-callback.html to your Authorized Client.

  8. In the Admin panel go to the Tenant Management → Applications and open your application configuration from the list. In the Application Identifier field replace the value with eu.ngrok.io. This step is needed to resolve your application. See com.broadleafcommerce.tenant.web.endpoint.TenantResolverEndpoint.

  9. Go to the Apple Developer and validate the domain - c746-176-115-97-170.eu.ngrok.io. See Validate your domain. During the validation the text file will be generated, and it should be put to the .well-known folder in the root of your server. If you are using React to build your app, you can put it to the public folder in the root of your project.

If you made everything correctly, you should be able to open your app by https://c746-176-115-97-170.eu.ngrok.io URL on your Apple Device.

Troubleshooting

If your page is blank please make sure you configured your content security policy with Content-Security-Policy correctly or disable it temporary. It can restrict the domain generated by ngrok.

If the Apple Pay button is displayed but when you click on it and see Processing for a long period of time, make sure the request to validate the session is executed without any errors, see com.broadleafcommerce.vendor.checkoutcom.service.sesseion.CheckoutComWalletSessionService.validateApplePaySession.

If the session validation looks good but the payment failed, make sure you specified broadleaf.checkout-com.apple-pay.initiative-context with the domain you verified on the Apple Developer. Make sure the value doesn’t contain https://, for example instead of https://c746-176-115-97-170.eu.ngrok.io use c746-176-115-97-170.eu.ngrok.io.

If the request to create the Payment failed, make sure you are sending apple_pay_token: applePaymentToken in the paymentMethodProperties. Check if this token is exchanged without any errors, see com.broadleafcommerce.vendor.checkoutcom.service.token.ExchangeWalletTokenService.exchangeWalletToken.