Apple Pay

With Tilled, you can accommodate all the ways customers wish to pay! Learn how to implement Apple Pay acceptance with ease.

Tilled allows Partners to accept Apple Pay payments, which Customers can use to make payments if they are using a compatible device and browser.

Tilled supports Apple Pay through the Tilled.js PaymentRequest object.


Requirements

Before you begin, you will need to:

  • Create a domain that meets the following requirements: - HTTPS secured (TLS 1.2 or later) [↪Source] - A valid SSL Certificate [↪Source]

  • Create an Apple Developer Account (this will be used when testing Apple Pay on your Website)

These requirements apply to both Sandbox and Production environments. For Sandbox testing, to get started, you can use a service like ngrok or Netlify.


Apple Pay Domain Verification

Apple's Documentation for Apple Pay on the Web instructs you to create a Merchant Identifier, Payment Processing Certificate, and to Register your Domain through them. Tilled takes care of all of this on your behalf when you verify your Domain through our API using our Apple Domain Verification File.
Verifying your Domain for Apple Pay
  1. Download Tilled's Apple Domain Verification File and host it on the domain at the path: /.well-known/apple-developer-merchantid-domain-association.

    • Example: https://example.com/.well-known/apple-developer-merchantid-domain-association
  2. After the Domain Verification File has been added, you will utilize Tilled's API to Create an Apple Domain.

Example: POST /v1/apple-pay-domains Request

$ curl -X POST 'https://sandbox-api.tilled.com/v1/apple-pay-domains' \
-H 'tilled-account: {{MERCHANT_ACCOUNT_ID}}' \
-H 'tilled-api-key: {{SECRET_KEY}}' \
-H 'Content-Type: application/json' \
--data-raw '{"hostname": "https://example.com"}'

Example: POST /v1/apple-pay-domains Response

{
  "updated_at": "2019-08-24T14:15:22Z",
  "created_at": "2019-08-24T14:15:22Z",
  "id": "string",
  "hostname": "string",
  "account_id": "string"
}
  1. After successful Domain Verification, you are now able to start accepting Payments on your site.

Including and Configuring Tilled on your Webpage

Including Tilled.js

To accept Apple Pay payments through Tilled, you will need to include Tilled.js on your page. Add the following HTML snippet to your web page, preferably in the <head> tag of your web page:

<script src="https://js.tilled.com/v2"></script>

Initializing Tilled.js

Instantiate an instance of Tilled by providing it with your publishable API key and the Tilled account id of the merchant account to perform the action on behalf of.

const tilled = new Tilled('pk_…', 'acct_…');

Use new Tilled(publishableKey, tilledAccount, options?) to create an instance of the Tilled object. The Tilled object provides access to the rest of the Tilled.js SDK. Your Tilled publishable API key is required when calling this function, replace the sample API key above with your actual API key. You can retrieve your API key by accessing https://app.tilled.com/api-keys.


Creating the PaymentRequest

In this example, we will be using the div container <div id="native-payment-element"> to inject the PaymentRequest Button (Apple Pay button) if the paymentRequest.canMakePayment(): Promise<boolean> returns as true.

PaymentRequest instances emit several different types of events, we will use tilled.paymentRequest to create a PaymentRequest object.

In Safari, tilled.paymentRequest uses Apple Pay.

PaymentRequest Example

const paymentRequest = tilled.paymentRequest({
  total: {
    label: 'Tilled tee',
    amount: paymentIntent.amount,
  },
  requestPayerName: true,
  requestPayerEmail: true,
});

When creating a PaymentRequest for Apple Pay, we highly recommend you collect the name as this also results in the collection of the billing address for Apple Pay, which can be used to perform address verification. A customer name can be collected by setting the requestPayerName option to true.

To view all PaymentRequest options properties, you can read more here.

Displaying The Apple Pay Button

To ensure that only customers who have an Apple Pay Wallet with an active card attached see the Apple Pay button, you will utilize paymentRequest.canMakePayment(): Promise<boolean> . This returns a Promise that resolves true if an enabled wallet is ready to pay. If no wallet is available, it resolves with false;

The example below injects paymentRequestButton (Apple Pay button) to the div container <div id="native-payment-element"> if paymentRequest.canMakePayment(): Promise<boolean> resolves true.

var prButton = form.createField('paymentRequestButton', {
  paymentRequest: paymentRequest,
});

paymentRequest.canMakePayment().then((result) => {
  if (result) {
    // Inject paymentRequestButton Form Field to the DOM
    prButton.inject('#native-payment-element');
  }
});

PaymentMethod Creation

Tilled.js automatically creates a payment method after the customer is done interacting with the browser’s payment interface. To access the created payment method, listen for this event by utilizing paymentRequest.on('paymentmethod', handler): void

It's important to note that payment methods created through Apple Pay will have a "type" of "card" and a validity period of 5 minutes.

For further details on PaymentMethod Creation, please refer to the Tilled.js documentation here.

Creating and Confirming The PaymentIntent

Before displaying your checkout form and confirming the payment, your backend server will need to make an API call to Tilled to create a Payment Intent with the payment amount. You will pass the intent’s client_secret to your front end. Use the tilled.confirmPayment(client_secret, { payment_method }) method to process the payment with the newly created Payment Method.

tilled.confirmPayment(clientSecret: string, params): Promise<PaymentIntent> confirms a Payment Intent and, optionally, creates a Payment Method from the Form Fields in the DOM.

This method returns a Promise which will resolve with a Payment Intent object.

paymentRequest.on('paymentmethod', (ev) => {
  let paymentMethod = ev.paymentMethod;
  tilled
    .confirmPayment(paymentIntentClientSecret, {
      payment_method: paymentMethod.id,
    })
    .then(
      (paymentIntent) => {
        // The payment intent confirmation occurred, but the
        // actual charge may still have failed. Check
        if (
          paymentIntent.status === 'succeeded' ||
          paymentIntent.status === 'processing'
        ) {
          ev.complete('success');
          alert('Successul payment');
        } else {
          ev.complete('fail');
          const errMsg = paymentIntent.last_payment_error?.message;
          alert('Payment failed: ' + errMsg);
        }
      },
      (err) => {
        ev.complete('fail');
      },
    );
});

Testing your Apple Pay integration

Apple allows developers to test Apple Pay before or after implementation by using their Apple Developer Account to create a Sandbox Tester Account. To start testing your Apple Pay Integration you will need to do the following:

  1. Make sure that your domain:

  2. Enroll in the Apple Developer Program if you haven't already.

  3. Create a Sandbox Tester Account

  4. Sign in to your Sandbox Tester Account on a Compatible Device you would like to test out your integration on.


Examples

See our Apple Pay Examples below:

Apple Pay | PaymentRequest Example

/**
 * Example assumptions:
 * The paymentRequestButton field has a div defined in the DOM
 * <div id="native-payment-element"></div> 
 * 
 */
const form = tilled.form({
  payment_method_type: 'card',
});

const paymentRequest = tilled.paymentRequest({
  total: {
    label: 'Tilled tee',
    amount: secretData.amount,
  },
});

const prButton = form.createField('paymentRequestButton', {
  paymentRequest: paymentRequest,
});

paymentRequest.canMakePayment().then((result) => {
  if (result) {
    prButton.inject('#native-payment-element');
  } else {
    document.getElementById('native-payment-element').style.display =
      'none';
  }
});

paymentRequest.on('paymentmethod', (ev) => {
  let paymentMethod = ev.paymentMethod;
  tilled
    .confirmPayment(paymentIntentClientSecret, {
      payment_method: paymentMethod.id,
    })
    .then(
      (paymentIntent) => {
        // The payment intent confirmation occurred, but the
        // actual charge may still have failed. Check
        if (
          paymentIntent.status === 'succeeded' ||
          paymentIntent.status === 'processing'
        ) {
          ev.complete('success');
          alert('Successul payment');
        } else {
          ev.complete('fail');
          const errMsg = paymentIntent.last_payment_error?.message;
          alert('Payment failed: ' + errMsg);
        }
      },
      (err) => {
        ev.complete('fail');
      },
    );
});