Implementing Apple Pay & Stripe on the Web

**This tutorial assumes basic knowledge of HTML, CSS, JS and PHP.**

I was recently engaged by a client to implement Apple Pay with their existing website in order to accept donations. As I don’t really tend to interact with the Apple ecosystem for development (beyond testing sites using Safari and some previous work on a multi-platform app using Ionic) I was slightly dubious about taking on the challenge, however, not one to let people down if I can help it, I accepted the job and jumped into research mode.

During my investigations I found out that my reasoning for staying away from Apple development is still very much justified, but that eventually you can uncover the information you need with a vast amount of digging and, of course, giving some money to Apple. Here are the most useful things I learned, along with some code for good measure!

Apple Pay is NOT a payment solution

First off, if you’re looking to implement Apple Pay on your site, you will also need a 3rd party payment provider to complete the transaction. This is not at all clear until you read further into the Apple Pay JS documentation and can be vastly confusing for clients, particularly when this is not mentioned after speaking directly to Apple employees.

All the Apple Pay part of the process does is take the card details stored on a user’s device and tokenise them, allowing for this information to be passed securely to a third party payment provider, such as (and in this case) Stripe. Once you understand this, things become a little easier to navigate, and, if you’re lucky, a little more removed from Apple’s development infrastructure.

One important thing to note is that only certain payment providers are allowed, and these vary by location. You can find a list of accepted payment providers by region at the very bottom of this page: https://developer.apple.com/apple-pay/ 

Apple Pay requirements & enabled devices

In order to use Apple Pay JS you will need the following accounts:

You will also need to configure your server to:

  • Serve all pages containing Apple Pay over HTTPS
  • Have a valid SSL certificate
  • Support TLS 1.2 and a cipher suite from the following:
    Table of Apple Pay JS Cipher Suites
    Taken from https://developer.apple.com/documentation/applepayjs/setting_up_your_server

     

For testing, you will also need an Apple device which supports iOS 10 or later, or macOS 10.12 or later. Additionally the device must have Touch ID, otherwise you need yet another device which does to authorise the payment anyway. This was the bit where I had to part with some cash, I managed to pick up the iPad 2017 for £339 directly from Apple, which wasn’t too bad for an Apple product, but still a good chunk of money. Here is a handy list of currently available devices which fit the bill*:

  • MacBook Pro with optional Touch Bar added
  • iPad Pro
  • iPad 2017
  • iPad mini 4
  • iPad mini 3
  • iPad Air 2
  • iPhone X (although this uses Face ID, instead of Touch)
  • iPhone 8
  • iPhone 7
  • iPhone 6s
  • iPhone SE
  • Apple Watch (presumably any model)

*This information is surprisingly hard to find, so please let me know if I’ve missed any devices.

I’d also point out that, unless you also have a mac (macBook, iMac, Mac mini, etc) available then debugging is going to be nigh-on impossible. Luckily, my client was able to lend me a macBook which saved me some time, but if you’re not in a position to borrow one, then you may have a long and frustrating road ahead.

Check out your payment provider docs!

Always check your chosen payment provider’s Apple Pay documentation as this may save you a whole load of headaches. Stripe has all the Apple Pay stuff baked right into its JS API which you can include in your site using the following line of code:

<script type="text/javascript" src="https://js.stripe.com/v2/"></script>

Their documentation for getting and Apple Pay button up and running is also excellent and can be found here: https://stripe.com/docs/apple-pay/web/v2

Write some code

The elements you’ll need to code to get an Apple Pay button showing on your site and accepting payments are relatively straightforward.

Along with the included Stripe JS library from above, make sure you are also including jQuery.

First, add a button element to your site using HTML:

<button id="apple-pay-button"></button>

Then add related styling into your CSS file, in order to show the button. The code below will add a basic, plain Apple Pay button, however, if you want a differently styled button, you can change the background-image property and if you need a Donate button, rather than a Pay button you can add a -apple-pay-button-type property with a value of donate. Other button types are plain, buy and set-up; the styling implications can be found here: https://developer.apple.com/documentation/applepayjs/displaying_apple_pay_buttons

#apple-pay-button{
    display: none;
    background-color: black;
    background-image: -webkit-named-image(apple-pay-logo-white);
    background-size: 100% 100;
    background-origin: content-box;
    background-repeat: no-repeat;
    width: 100%;
    height: 44px;
    padding: 10px 0;
    border-radius: 10px;
  }

The actual CSS I ended up with is below as I wanted a black donate button. I found I had issues on Safari changing the visibility of the button unless I added the visibility: visible; rule.

#apple-pay-button {
    -webkit-appearance: -apple-pay-button;
    -apple-pay-button-type: donate;
    visibility: visible;
    display: none;
    width: 200px;
    min-height: 30px;
    border: 1px solid black;
    background-image: -webkit-named-image(apple-pay-logo-black);
    background-size: 100% calc(60% + 2px);
    background-repeat: no-repeat;
    background-color: black;
    background-position: 50% 50%;
    border-radius: 5px;
    padding: 0px;
    margin: 5px auto;
    transition: background-color .15s;
  }

If you do change the styling of the button, make sure you’re not violating any of Apple’s Human Interface Guidelines, which can be found here: https://developer.apple.com/apple-pay/web-human-interface-guidelines/

Because the amount being donated needed to be specified by the user, I also added a simple form to my implementation which allowed the user to input a donation amount. This code is not included here.

Add some JavaScript

Next you’ll need a .js file to start making your button functional. Create one and make sure it’s linked within your HTML file, then start with a window onload function to define your Stripe Public Key and check whether Apple Pay is available on the current device or browser.

(function () {
   window.onload = function() {        
        Stripe.setPublishableKey('YOUR KEY HERE');

        Stripe.applePay.checkAvailability(function(available) {
            if (available) {
                document.getElementById('apple-pay-button').style.display = 'block';
                document.getElementById('apple-pay-button').addEventListener('click', beginApplePay);
            } else {
                //add some feedback for Apple Pay unavailable
            }
        });
    };
})(this);

If Apple Pay is available on the user’s device, then the button will display. When the user clicks the button, the function beginApplePay will be called. That function doesn’t exist yet, so let’s write it.

function beginApplePay() {
    var paymentRequest = {
        countryCode: 'GB',
        currencyCode: 'GBP',
        total: {
            label: 'My Donation',
            amount: '20.00'
        }
    };
}

The first thing we’re doing here is setting up a paymentRequest object. This requires properties of total, countryCode and currencyCode to be set. Note that the amount is set as a formatted string, and not an integer. The full list of available properties can be found here: https://developer.apple.com/documentation/applepayjs/applepaypaymentrequest

My actual code takes the amount from the user input field in my form and also performs validation on this field before the payment request is instantiated, this is not shown above.

Next we need to set up an Apple Pay session. We will send the session our paymentRequest, and it will return a token that we can use to make our payment with Stripe.

var session = Stripe.applePay.buildSession(paymentRequest, function(result, completion) {

    //process payment here

}, function(error) {
   document.getElementById('apple-pay-failed').style.display = 'block';
   console.log(error.message);
});

The above code builds the session and returns a result object. If the call fails, we display some error feedback to the user and console log the error for debugging. That’s great, but now we need to actually make the payment.

To do this, we need to make a post request to a processing file. You can do this in any language allowed by the Stripe API, mine is done using PHP. So, the above code changes to:

var session = Stripe.applePay.buildSession(paymentRequest, function(result, completion) {

    jQuery.post('payment.php', { token: result.token.id, amount: paymentRequest.total.amount, currency: paymentRequest.currencyCode, description: paymentRequest.total.label }).done(function() {
        document.getElementById('apple-pay-success').style.display = 'block';
        completion(ApplePaySession.STATUS_SUCCESS);
    }).fail(function() {
        document.getElementById('apple-pay-failed').style.display = 'block';
        completion(ApplePaySession.STATUS_FAILURE);
    });

}, function(error) {
   document.getElementById('apple-pay-failed').style.display = 'block';
   console.log(error.message);
});

session.oncancel = function() {
    console.log("User hit the cancel button in the payment window");
};

session.begin();

Taking the result token from the Apple Pay session, and the amount, currency and description from our paymentRequest object, and sending them through to a payment.php file for processing. If the payment file returns a successful payment, the Apple Pay session’s status is set to success, otherwise it’s set to failure. Depending on the payment outcome, feedback is displayed to the user.

We also need to check whether the user cancels out of the process before payment. We can do this using the session.oncancel function defined above. This logs the message ‘User hit the cancel button in the payment window’ to the console.

There is one more thing that needs to be done in our JS file, and that is to begin the session. This is done after our session variable has been set up, and within the begineApplePay() function by adding the line session.begin(); as seen above.

Our full beginApplePay() function now looks like this:

function beginApplePay() {
    var paymentRequest = {
        countryCode: 'GB',
        currencyCode: 'GBP',
        total: {
            label: 'My Donation',
            amount: '20.00'
        }
    };

    var session = Stripe.applePay.buildSession(paymentRequest, function(result, completion) {

        jQuery.post('payment.php', { token: result.token.id, amount: paymentRequest.total.amount, currency: paymentRequest.currencyCode, description: paymentRequest.total.label }).done(function() {
            document.getElementById('apple-pay-success').style.display = 'block';
            completion(ApplePaySession.STATUS_SUCCESS);
        }).fail(function() {
            document.getElementById('apple-pay-failed').style.display = 'block';
            completion(ApplePaySession.STATUS_FAILURE);
        });

    }, function(error) {
       document.getElementById('apple-pay-failed').style.display = 'block';
       console.log(error.message);
    });

    session.oncancel = function() {
        console.log("User hit the cancel button in the payment window");
    };

    session.begin();
}

Make the payment

Finally we need to process the payment using Stripe’s API. As mentioned, I’m doing this using PHP, so create the payment.php file referenced above, and add the following code:

$apikey = 'YOUR STRIPE SECRET KEY';
$token = $_POST['token'];
$amount = $_POST['amount'] * 100; //convert to pence for Stripe
$currency = $_POST['currency'];
$description = $_POST['description'];

$url = 'https://api.stripe.com/v1/charges' . '?amount='.$amount.'&currency='.$currency.'&source='.$token.'&description='. urlencode($description);
$headers = array('Content-Type' =>'application/json', 'Authorization' => 'Bearer ' . $apikey);
 

Set up your variables using the posted parameters to make a cURL request to the Stripe API. Add a variable for your Stripe Secret Key, found in your Stripe account. Build your URL string using the variables and use your API key to set up the request Authorization header. Next, make your cURL request and deal with the response:

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => $url,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_HTTPHEADER => $headers,
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  //do some error processing here
  echo "cURL Error #:" . $err;
} else {
  //return the response
  echo $response;
}

And you’re done! You should now have a working implementation of Apple Pay using Stripe on your website.

One more issue…

One extra stumbling block I encountered which appears to be a Safari issue was a jQuery undefined error. If you’re hitting this, you can overcome it by including jQuery within your JS file by adding the following code before your window.onload function.

if (typeof jQuery === "undefined") {
    var script = document.createElement('script');
    script.src = 'https://code.jquery.com/jquery-latest.min.js';
    script.type = 'text/javascript';
    document.getElementsByTagName('head')[0].appendChild(script);
}

Wrapping up

Hopefully you’ve stumbled across this tutorial during your own frustration installing Apple Pay on your site, and this has helped in one way or another. Otherwise, considering you’ve read this far, I hope you found it interesting!

**NOTE: This implementation uses Stripe’s API v2. Since the implementation was completed, v3 has been released and uses a slightly different method. All Apple information remains correct at time of publishing.**