Just-Eat spectrum-bottom spectrum-top facebook google-plus instagram linkedIn pinterest reddit rss twitter_like twitter_reply twitter_share twitter_veriviedtwitter vimeo whatsapp youtube error_filled error file info-filled info loading star tick arrow arrowLeft arrowRight close download minus-filled minus move play plus-filled plus searchIcon settings

Tag : javascript

1740 views

Unit testing front-end JavaScript with AVA and jsdom

Writing tests for JavaScript code that interacts with the DOM can be tricky. Luckily, using a combination of AVA and jsdom, writing those tests becomes a lot easier.

This article will walk you through how to set everything up so you can get started writing your tests today.

What is AVA?

AVA is described as a “Futuristic JavaScript test runner“. Sounds fancy, huh?! So, what is it exactly that makes it “futuristic“?!

Tests run quickly

AVA runs test files in parallel, each in its own separate process, with the tests inside those files running concurrently. This offers better performance than other test runners that run tests serially, such as Mocha. This also means that each test file is run in an isolated environment — great for writing atomic tests.

Simple API

AVA’s API is very small because, in AVA’s own words, it is “highly opinionated“. You won’t find any assertion aliases here! This reduces the cognitive load required when writing tests.

Write tests in ES2015

You don’t need to do anything to be able to write tests in ES2015, AVA supports this out of the box! Under the covers it’s using Babel to transpile with the es2015 and stage-2 presets.

No implicit globals

AVA has no implicit globals, simply import it into your test file and you have everything you need.

Other benefits

There are a whole host of other benefits which AVA offers such as:

  • Promise support
  • Generator function support
  • Async function support
  • Observable support
  • Enhanced assertion messages
  • Clean stack traces

All of this combined sounds very “futuristic” to me!

Getting off the launchpad with AVA

Now that we know more about AVA, let’s create a new project and start writing some tests.

Start by running npm init inside a new project folder. This will create a package.json file, which will contain various pieces of information about the project such as its name, authors, and dependencies, among others. Hitting enter for each question will fill in a default value.

Installing AVA

Add AVA to the project by typing npm install ava --save-dev, then update the scripts section in package.json:

The --verbose flag enables the verbose reporter, which means more information is displayed when the tests are run.

When using npm scripts, the path to AVA in the node_modules folder will be resolved for us, so all we need to do is type npm test on the command line. Doing so at the moment this will give us an exception:

Let’s fix that by adding a test.

Writing a test

Create a test directory, with a file named demo.test.js inside, then add a test:

First, AVA is imported into the module, then the test function is called, passing a string as the first parameter which describes what the test is doing. The second parameter is the test implementation function which contains the body of the test, this provides us with an object, t, from which we can call the assertion functions.

The is assertion is used here, which takes two values and checks that they are both equal (using === so there is no type conversion).

Note: You can choose any name you like for the t parameter, such as assert. However, using the t convention in AVA will wrap the assertions with power-assert which provides more descriptive messages.

Run npm test and the test result will be printed out

Success! Our test passed as expected. To see an example of what a failing test would look like change the test assertion to t.is(1 + 1, 1). Run the test now and you’ll see an error

As you can see, there is a lot of useful information provided in order to help us track down the issue.

Testing modules

To demonstrate how to test a module, create a new folder called src in the root of the project with a file inside called demo-module.js with the contents:

Update demo.test.js by first importing the module, then adding a new test:

Running npm test now will give you the following exception

Uh oh, what happened?

AVA will transpile ES2015 code in your tests; however, it won’t transpile code in modules imported from outside those tests. This is so that AVA has zero impact on your production environment.

If our source modules are written in ES2015, how do we tell AVA that we’d like them to be transpiled too?

Transpiling source files

To transpile source files, the quick and dirty option is to tell AVA to load babel-register which will automatically transpile the source files on the fly. This is ok if you have a small number of test files, but there is a performance cost which comes with loading babel-register in every forked process.

The other option is to transpile your sources before running the tests in order to improve performance.

The next two sections look at how each technique can be achieved.

Transpile with babel-register

Add babel-register by running npm install babel-register --save-dev, then add a "babel" config to package.json

Next, add "babel-register" to the AVA "require" section

Run npm test and the tests will once again pass, great!

The recommendation from the AVA team is to use babel-registeruntil the performance penalty becomes too great“. As your test base grows you’ll need to look into setting up a precompilation step.

Setting up a precompilation step

A precompilation step will transpile your source modules before the tests are run in order to improve performance. Let’s look at one way to set this up.

Note: If you were following along with the last section you’ll need to remove the references to babel-register. First run npm uninstall babel-register --save-dev, then remove "babel-register" from the AVA "require" section in package.json.

Start by adding the babel-cli and babel-preset-es2015 packages to the project: npm install babel-cli babel-preset-es2015 --save-dev.

Next, add a "babel" config to package.json

In order to run the tests, we need to update the npm scripts. Add a new npm script called precompile

The precompile npm script will tell Babel to take the files in the src directory, transpile them, then output the results to the dist directory.

Next, the test npm script needs to be updated so that it runs the precompile step before running the tests

The double ampersand (&&) tells npm to first run the precompile script and then the AVA tests.

The final task is to update the reference to demo-module inside demo.test.js to point at the compiled code, we do this by replacing ../src with ../dist:

Run npm test and we’re presented with all green tests!

Testing the DOM using Node

So far we have the ability to test JavaScript code, but what if we’d like to test a function which makes use of the DOM? Node doesn’t have a DOM tree, so how do we get around this?

One option is to use a combination of a test runner and a browser — a popular combination is Karma and PhantomJS. These offer a lot of benefits like being able to test against real browsers, run UI tests, take screenshots, and the ability to be run as part of a CI process.

However, they typically come with a fairly large overhead, so running lots of small tests can take minutes at a time. Wouldn’t it be great if there was a JavaScript implementation of the DOM?

Welcome to the stage; jsdom!

jsdom

jsdom is described as “A JavaScript implementation of the WHATWG DOM and HTML standards, for use with Node.js“.

It supports the DOM, HTML, canvas, and many other web platform APIs, making it ideal for our requirements.

Because it’s purely JavaScript, jsdom has very little overhead when creating a new document instance which means that tests run quickly.

There is a downside to using a JavaScript implementation over an actual browser – you are putting your trust in the standards being implemented and tested correctly, and any inconsistencies between browsers will not be detected. This is a deal breaker for some, but for the purposes of unit testing I think it is a reasonable risk to take; jsdom has been around since early 2010, is actively maintained, and thoroughly tested. If you are looking to write UI tests then a combination of something like Karma and PhantomJS may be a better fit for you.

Integrating jsdom

Setting up jsdom can be a daunting task, the documentation is great, but very lengthy and goes into a lot of detail (you should still read it!). Luckily a package called browser-env can help us out.

Add browser-env to the project npm install browser-env --save-dev.

Create a helpers directory (which is ignored by convention when using AVA) inside test, then add setup-browser-env.js with the contents

We need to tell AVA to require this module before any of the tests are run so that browser-env can create the full browser environment before any DOM references are encountered. Inside your package.json add

Note: You may have noticed that this file is written in ES5. This is because AVA will transpile ES2015 code in the tests, yet it won’t transpile any modules imported or, in this case, required from outside the tests — see the transpiling source files section.

Testing the DOM

Let’s write a test which makes use of the document global which has been provided thanks to jsdom. Add a new test to the end of demo.test.js:

First, we add a paragraph element with some text to the document body, then query for that element using document.querySelector, and finally, we verify that the selected paragraph tag has an innerHTML value equal to 'Hello, world'.

Run the tests with npm test

Congratulations, you’ve just unit-tested the (virtual) DOM!

Test coverage with nyc

As a bonus let’s quickly set up some test coverage. Because AVA runs each test file in a separate Node.js process, we need a code coverage tool which supports this. nyc ticks the box — it’s basically istanbul with support for subprocesses.

Add it to the project with npm install nyc --save-dev, then update the test npm script by adding nyc before the call to ava:

You’ll also need to update the Babel config to tell it to include source maps when developing so that the reporter can output the correct lines for the transpiled code:

Run the tests and witness the awesome code coverage table!

What next?

If you’re interested in what else you can do with AVA, have a look through the AVA readme, check out the AVA recipe docs, read about common pitfalls, and listen to this JavaScript Air podcast episode. I’d also recommend looking into setting up linting for your code.

You can browse the source code for this blog post on GitHub.

So, now you have no excuse for not testing your front-end JavaScript!


Damian Mullins is a UI Engineer at Just Eat. Progressive enhancement advocate, web standards supporter, JavaScript enthusiast.

712 views

Angular in Paris – ng-europe 2016

In Product Research at Just Eat we aim to research, design and prototype rapidly. The more prototypes we can put in users’ hands, the more chance we’ll have of discovering a good idea. Hopefully we’ll do just that and start to iterate, but in reality most prototypes are binned. So the more we build, the more likely we are to find something that people want.

Angular could be a great fit for us, and the recent excitement around the Angular2 release meant it was high time for us to immerse ourselves in the community for a few days at ng-europe. Here’s a quick summary of the bits that jumped out for me along with some notes on things I plan to research.

The first day

I woke up quite early on my first morning in Paris and headed down to breakfast. Happily, Miško Hevery, who created Angular, and his Google colleague Alex Eagle were also awake, (I think they had jet lag). Over a serendipitous breakfast we talked about Angular but also about sugar, autonomous cars and the number of atoms in the universe. This was a good start!

Keynote

In his keynote to a packed hall Miško’s talked about how Angular has grown, both the community and the platform itself in its ambition and potential. It’s now everywhere: we can build Angular web apps, iOS and Android apps and desktop apps. Performance is hugely improved and experiences are native.

Things to research: Benchpress, Augury.

RxJS

Rob Wormald gave a standout talk on RxJS and observables, here are his slides. I really like his style, making a complex subject accessible. Observables have had a massive impact on Angular’s architecture, and would feature in many of the later talks.

Things to research: RxJS; using the switchMap operator in requests and in the router; avoiding the elvis operator in templates with smart and dumb components.

ng-bootstrap

The ng-bootstrap project powers Bootstrap 4 components natively with Angular2. Pawel Kozlowski described how, because they were especially written for Angular2, these components get the most from the framework. A good example is only rendering expensive content when it’s needed. Here are his slides.

(He talked about how well tested the components are, but he didn’t mention accessibility, a special interest — I looked at the repo and don’t worry, it’s well in hand!)

Things to research: components with observable children; components with a programmatic API; alternatives like Material.

Data science

Ari Lerner first plugged his company’s book (looks good, I’ve a sample to read, I’ll let you know) and then really caught my attention in his talk on data science.

At Just Eat we have access to huge amounts of rich data which we analyse intelligently to drive and validate change. Ari described pages that randomly reorder themselves until an optimal variation — one that best suited conversion goals — is found with machine learning. I couldn’t track Ari down to ask him more about this, I have a load of questions, I’ll try and find him tomorrow.

Things to research: this work (it’s open source, I’ll update with a link once I get one).

TypeScript

Live coding while public speaking goes badly wrong for some, but not for Daniel Rosenwasser. His talk on TypeScript was excellent, lots of smiles in the crowd. I already liked the idea of TypeScript (a superset of JavaScript) but had no idea of how awesome it has become, especially in combination with good tooling.

Daniel renamed a .js file to a .ts file then quickly worked through the compiler warnings and refactored into way more robust idiomatic TypeScript. He showed off some new features of which improved type inference really impressed, clever compiler.

Things to research: VS Code, the latest TypeScript.

The rest of day one

Other talks, all really interesting, covered BatScanner, unit testing, hybrid apps, security, migration and AngularFire2; all were recorded if you feel you’ve missed out. Looking forward to tomorrow!

The second day

Angular CLI

The day began with Hans Larsen reminding everyone that setting up tooling is really painful and robs way too much time from app development. Great to hear this isn’t just me.

The Angular team are taking this seriously. Angular CLI aims to stay out of your way, to understand your app and evolve with it by understanding your intent to generate, serve, test and build. You can generate classes, directives, enums, modules, pipes, services — and generation code adapts to its context. Here’s what it looks like:

Out of the box, Angular CLI can compile Less or Sass, transpile TypeScript, optimise assets, understand environments and serve a live reloading dev server. The compiler tree-shakes and compiles ahead of time, so the bundle is small and no renderer is needed on the client — users experience apps that load and render quickly.

Hans talked about the future, the CLI is getting smaller and more interoperable. Progressive web apps and server-side rendering will use ‘add-ons’ to change CLI behaviour. Add-ons and scripting will open the CLI to the community. I really like the fact that the CLI is part of Angular, it will always be in sync as Angular evolves. Learning just one tool is going to seriously speed up web app development.

Things to research: Angular CLI

Auguary

Vanessa Yuen showed us Augury, an open source Angular2 Chrome Extension built by Rangle.io and Google. Augury uses the debug API so that you can inspect and debug an Angular2 app in DevTools.

We watched as Vanessa used Augury to navigate an app’s component hierarchy, inspect state, change properties, invoke events, understand dependencies and view a router tree. One especially cool feature was using $a to reference a selected component in the console.

Augury looks pretty essential, especially when apps grow in complexity. Reassuring to hear how committed Rangle.io are to keep it in sync as Angular changes.

Things to research: Vanessa’s slides, Augury on GitHub.

Angular Universal

Next up, Wassim Chegham discussed Angular Universal: server-side rendering for Angular2. Not so relevant in product research, but still really impressive to hear about the latest.

SEO and link previews are the obvious problems that Universal solves for Angular apps. But more than that, it improves the user experience by removing the ‘web app gap’, the several seconds of blank screen a user experiences while an app bootstraps and renders. By rendering on the server the user sees the page immediately, before the app bootstraps. If they interact with the page before the app’s ready, those events are captured by Preboot until they may be safely replayed.

Things to research: Wassim’s slides, universal-starter seed.

The new router

Nir Kaufman gave us the lowdown on Angular2’s powerful root matching. This looks way easier to use with a consistent API and patterns. Lazy loading is new and as easy as replacing children with loadChildren. Params and query params now use observables which you pluck inside the component. It’s easier to secure states and prevent children activating or loading with ‘guards’. Especially cool, multiple views may now be serialised in the URL. I couldn’t quite get some of this talk, luckily Nir pointed us at some excellent guidance.

Things to research: Victor Savkin’s Articles and book.

Generative music

As a musician and someone who’s played around with music technology quite a bit, the talk by Tero Parviainen was by far the most entertaining and inspiring. Tero took us through his project In C, an Angular2 WebAudio app inspired by Terry Riley’s In C in which the user starts ‘players’ and advances them through patterns to produce a divergent generative composition.

Tero explained beautifully and with great clarity how Redux inspired the architecture of the app’s four decoupled component parts: a metronomic pulse, an immutable observable store, audio generation, and the UI. This architecture let him time travel through historic state and hot load code changes as he developed — awesome.

So no, this probably isn’t going to be relevant at work but just fantastic to see such a talent, much cheering and applause from everyone for Tero.

Things to research: Redux, ngrx/store.

Animation

Matias Niemelä discussed how animation in Angular2 uses a declarative domain specific language (DSL) inside the component. Testable, ahead-of-time compilable, independent from the DOM and web worker friendly.

This was the point in the conference when it really started to sink in for me what it means to for Angular to be platform agnostic. That was a nice moment, seriously impressive engineering.

The DSL composes states and transitions into triggers that are triggered from the component or from a property change in the template. For the web the implementation uses the Web Animations API — there’s a polyfill for lagging browsers.

Matias described a slew of upcoming features, a couple that jumped out were pulling animations from CSS and animating layout. I’m excited enough about playing around with what’s here today.

Things to research: Angular’s animation systemMatias’s slides.

Mobile apps

Adam Bradley from Ionic talked passionately about web standards and pointed out how fast and responsive the mobile web now is. Later, Christian Weyer and Thorsten Hans compiled the same codebase with Cordova and Electron yielding the same app across Mac OS, Windows, iOS, Android and web. Great talks but my issue with all these techniques is that apps don’t quite feel native enough, that you’re always dealing with web view’s shortcomings or chasing platform conventions and evolution.

So I was more interested in Marc Laval’s talk on custom renderers. In Angular2 the renderer is separated from Angular core and your app (in the browser they’re separated in the UI thread and a web worker). That renderer may be swapped so that Angular2 can work where there is no DOM, for example on Node, where Universal uses a server renderer, or on a mobile phone.

Both React Native and NativeScript work with Angular and render UIs natively with no web view. That’s a truly native experience and much more appealing to me. Neither need Angular to work, but how awesome would it be to master one framework for all three platforms?

Things to research: Marc’s slides, Angular2 &React Native, Angular2 & NativeScript.

Building RxJS

By now I was getting my head around observables, so André Staltz’s talk was well timed. Starting with an explanation of callback types, he flawlessly hacked away from simple JavaScript to a basic implementation of RxJS. He changed the code step-by-step, for example giveMeSomeData() became subscribe(). Really funny, and did the trick, definitely some lights going on all around.

Fast loading apps

Alex Eagle’s pointed at stats that give us about two seconds to make our apps responsive before users leave. At 2G or 250 kb/s that’s 500 k for JS and assets. That’s not very much so we need static analysis techniques, lazy loading, tree-shaking, bundling and minification to help us spend the budget.

As he dug into the complexity of these things I felt massively relieved that people as smart as Alex are figuring out how to do all this – even more relieved to learn that Angular CLI will take care of everything and I don’t need to worry.

Wrapping up

And that was it! It’s all recorded if you any of that piqued your interest. The Q&A at the end was really interesting. What most stuck with me was that AngularJS to Angular2 has been painful for everyone, but that this kind of upgrade won’t happen again. Angular2 is now a platform that will just grow in its power and reach as it shields us from turbulent and rapidly changing platforms.

Farewell Paris

Thanks ng-europe for a fascinating couple of days, it was great to spend time with such a friendly, smart and supportive community.


Ben Glynn is a Principal UI Engineer for Product Research at Just Eat. He’s passionate about product discovery, the user interface and user experience.

1676 views

Bringing Apple Pay to the web

Using Apple Pay on the web from just-eat.co.uk

Using Apple Pay on the web from just-eat.co.uk.

Introduction

Back in June at WWDC, Apple announced that Apple Pay was expanding its reach. No longer just for apps and Wallet on TouchID compatible iOS devices and the Apple Watch, it would also be coming to Safari in iOS 10 and macOS Sierra in September 2016.

Just Eat was a launch partner when Apple Pay was released in the UK in our iOS app in 2015. We wanted to again be one of the first websites to support Apple Pay on the web by making it available within just-eat.co.uk. Our mission is to make food discovery exciting for everyone – and supporting Apple Pay for payment will make your experience even more dynamic and friction-free.

Alberto from our iOS team wrote a post about how we introduced Apple Pay into our iOS app last year, and this post follows on from that journey with a write-up of how we went about making Apple Pay available on our website to iOS and macOS users with the new Apple Pay JS SDK.

Getting set up

In the iOS world, due to the App Store review process and signed entitlements, once your app is in users’ hands you just use PassKit to get coding to accept payments. For the web things are a little different.

Due to the more loosely-coupled nature of the integration, instead trust between the merchant (Just Eat in our case) and Apple is provided through some additional means:

  • A valid SSL/TLS certificate
  • A validated domain name to prove a merchant owns a given domain
  • A Merchant Identify Certificate

 

As we already use Apple Pay here at Just Eat, the first few steps for getting up and running have already been achieved. We already have an Apple developer account and a merchant identifier via our iOS app development, and the Just Eat website is already served over HTTPS, so we have a valid SSL/TLS certificate.

We also do not need to worry about decrypting Apple Pay payment tokens ourselves. We use a third-party payment provider to offload our payment processing, and internal APIs for passing an Apple Pay token for processing via our payment provider already exists for handling iOS payments, so the website can integrate with those as well.

To get up and running and coding end-to-end, we need just need a Merchant Identity Certificate. This is used to perform two-way TLS authentication between our servers and the Apple Pay servers to validate the merchant session when the Apple Pay sheet is first displayed on a device.

The first step in getting a Merchant Identify Certificate is to validate a domain. This involves entering a domain name into the Apple Pay Developer Portal for the merchant identifier you want to set up Apple Pay on the web for – where you then get a file to download. This is just a text file that verifies the association between your domain and your merchant ID. You just need to deploy this file to the web server(s) hosting your domain so Apple can perform a one-time request to verify that the file can be found at your domain.

You need to do this for all domains you wish to use Apple Pay for, including internal ones for testing, so you may have to white list the Apple IP addresses so that the validation succeeds.

Once you have validated at least one domain, you can generate your Merchant Identify Certificate for your Merchant Identifier. This requires providing a Certificate Signing Request (CSR).

Uploading the CSR file in the Apple Developer Portal will generate a certificate file (merchant_id.cer) for you to download. This acts as the public key for your Merchant Identify Certificate. The private key is the CSR you provided. In order to create a valid TLS connection to the Apple Pay merchant validation server, you will need to create a public-private key pair using the CSR and the CER files, such as using a tool like OpenSSL. In our case we generated a .pfx file for use with .NET. Make sure you keep this file secure on your server and don’t expose it to your client-side code.

Separating concerns

So now we’ve got a validated domain and a Merchant Identify Certificate, we can start thinking about implementing the JavaScript SDK. At a high-level the components needed to create a working Apple Pay implementation in Safari are:

  1. JavaScript to test for the presence of Apple Pay, display the Apple Pay sheet and to respond to user interactions and receive the payment token
  2. CSS to render the Apple Pay button on a page
  3. An HTTPS resource to perform merchant validation

 
From the user’s point of view though, it’s just a button. So rather than add all the code for handling Apple Pay transactions directly into the codebase of our website, we decided instead to contain as much of the implementation as
possible in a separate service. This service presents its own API surface to our website, abstracting the detail of the Apple Pay JavaScript SDK itself away.

The high-level implementation from the website’s point of view is therefore like this:

  1. Render a hidden div on the appropriate page in the checkout flow to represent the Apple Pay button as well as some meta and link tags to drive our JavaScript API
  2. Reference a JavaScript file from the Apple Pay service via a script tag
  3. Provide some minimal CSS to make the Apple Pay button size and colour appropriate to the current page
  4. Call a function on our JavaScript API to test for whether Apple Pay is available
  5. If it is, call a second function passing in some parameters related to the current checkout page, such as the user’s basket, the DOM element for the div representing the Apple Pay button and some callback functions for when the payment is authorised, fails or an error occurs.

 
The rest of the Apple Pay implementation is handled by our JavaScript abstraction so that the Just Eat website itself never directly calls the Apple Pay JavaScript functions.

Our new Apple Pay service itself should have the following responsibilities:

  • Serve the JavaScript file for the abstraction for the website
  • Serve a file containing the base CSS for styling the Apple Pay button
  • Provide HTTP resources that support Cross Origin Resource Sharing (CORS) to:
    1. Provide the payment request properties to set up an Apple Pay sheet
    2. Validate merchant sessions
    3. Verify that a restaurant partner delivers to the selected delivery address
    4. Receive the Apple Pay payment token to capture funds from the user and place their order

 
Separating the CSS, JavaScript and back-end implementation allows us to decouple the implementation from our website itself allowing for more discrete changes. For example, the current Apple Pay version is 1. By abstracting things away we could make changes to support a future version 2 transparently from the website’s point-of-view.

Delving into the implementation

As mentioned in the high-level design above, integrating Apple Pay into a website requires a mix of client-side and server-side implementation. We need to implement some JavaScript, make some CSS available and provide some server-side HTTP resources to handle merchant validation of payment processing. There’s also some HTTP meta and link tags you can add to enhance your integration.

Let’s delve into the different layers and things we need to add…

HTML

Well first we need an Apple Pay button. You can add one with some HTML like this:

Ignore the apple-pay-* CSS classes for now as I’ll come back to them, but the hide class (or some other similar approach) ensures that the div for the button is not visible when the page first loads. This allows us to display it as appropriate once we have detected that Apple Pay is available in the browser using JavaScript.

HTML metadata

Apple Pay supports a number of different HTML meta and link tags that you can use to improve the user experience for your integration.

First, there’s some link tags you can add to provide an icon for use on an iPhone or iPad when a confirmation message is shown to the user initiating a payment from macOS:

These link elements can even be added dynamically by scripts when you detect the Apple Pay is available, provided that they are in the DOM before you create an ApplePaySession object.

There’s also some meta tags you can add so that crawlers (such as Googlebot can identify your website as supporting payment through Apple Pay:

Integrating the Apple Pay JavaScript SDK

So now we’ve got the HTML for the Apple Pay button and some metadata tags, we need some JavaScript to drive the integration.

In our case we have placed all of our Apple Pay-related JavaScript into a single file. This allows us to use server-side feature flags to decide to render the script tag for it (or not), so that the relevant file is only fetched when the feature is enabled.

Within this JavaScript file, there are functions for dealing with the Apple Pay workflow and calling the Safari functions in the browser.

The psuedo-code for an implementation within a consuming website would be:

First we have functions in je.applePay that contain simple functions for feature detection. For example, the isSupportedByDevice() function tests if the current browser supports Apple Pay at all, where as the isSupportedForCheckout() function additionally tests if the Just Eat specific information (such as the ID of the basket to pay for) is available to the current page.

The controller is the top-level object in our abstraction that the containing page uses to handle the Apple Pay payment flow. This handles things so that when the user clicks the Apple Pay button, we create an Apple Pay session with the appropriate payment information, do callbacks to the server to validate the merchant session and capture payment – and invoke the website-supplied callback functions when the payment process ends.

Within our abstraction, we use the ApplePaySession object to drive our integration. For example, to test for Apple Pay support, we use code similar to this (logging removed for brevity):

Assuming that the device supports Apple Pay then we’ll want to display the Apple Pay button. However before we do that we’ll need to wire-up an onclick event handler to invoke the JavaScript to handle the payment process itself when it is clicked or pressed. For example with jQuery:

Now the Apple Pay button will be displayed. The rendering of the button itself is handled by the CSS provided by Apple. There are four possible variants. First there’s a choice between a black or a white button, then there’s the choice of either an Apple Pay logo only, or the logo prefixed by “By with” (CSS).

The logo itself is provided by resources built into Safari, such as shown in this snippet:

The CSS file for this is loaded dynamically by our JavaScript abstraction so users with devices that do not support Apple Pay do not pay the penalty of a network request to get the CSS file. This also removes the need for the consuming website to explicitly load the CSS itself with a link tag and allows the location of the CSS file itself to be modified at any time in our Apple Pay service.

So when the user either taps or clicks the button, that’s when the work to start the Apple Pay session begins. First you need to create a properly set up payment request object to create an instance of ApplePaySession along with the Apple Pay version (currently 1).

Be careful here – Apple Pay only allows an ApplePaySession object to be created when invoked as part of a user gesture. So, if you want to do any interaction with your server-side implementation here, ensure you do not make use of asynchronous code such as with a Promise object. Otherwise creating the ApplePaySession may occur outside the scope of the gesture handler, which will cause a JavaScript exception to be thrown and the session creation to fail.

We haven’t done enough to show the sheet yet though. Next we need to register the callback functions for the events we want to receive callbacks for. At a minimum you will need two of these:

 
onvalidatemerchant is called after the sheet is displayed to the user. It provides you with a URL to pass to the server-side of your implementation to validate the merchant session.

An example of how you could do this in jQuery is shown in the snippet below:

onpaymentauthorized is called after payment is authorised by the user either with a fingerprint from an iPhone or iPad or by pressing a button on their Apple Watch. This provides the payment token for capturing the funds from the user.

An example of how you could do this in jQuery is shown in the snippet below:

The functionality to actually capture funds from the user is outside the scope of this blog post – information about decrypting Apple Pay payment tokens can be found here.

There’s also events for payment selection, shipping method selection, shipping contact selection and cancellation. This allows you to do things such as:

  • Dynamically adjust pricing based on payment method or shipping address
  • Validate that the shipping address is valid, for example whether a restaurant delivers to the specified shipping address

 
Note that before the payment is authorised by the user, not all of the shipping contact and billing contact information is yet available to you via the parameters passed to the event handlers. For example, the country, locality (eg a city or town), administrative area (eg a county or state) and the first part of the postal code (eg outward code in the UK, such as EC4M 7RF). This is for privacy reasons as before the user authorises the payment it is still a request for payment, and as such the full information is only revealed to use you the integrator by the onpaymentauthorized event.

Once you’ve registered all your event handlers, you just need to call the begin function to display the Apple Pay sheet.

HTTP resources

Our server-side implementation has 4 main resources that we consume from our JavaScript code for all flows:

  1. GET /applepay/metadata
  2. GET /applepay/basket/{id}
  3. POST /applepay/validate
  4. POST /applepay/payment

 
The metadata resource is used to test whether Apple Pay is available on the current domain (for example www.just-eat.co.uk). The JSON response returned indicates whether the Apple Pay feature is enabled for the referring domain, the merchant capabilities, the supported payment networks, the country and currency code and the available Apple Pay touch icons and their URIs. This allows our JavaScript example to build up the link tags for the touch icons dynamically, deferring the need for them until necessary.

The basket resource is used to fetch details about the user’s current basket so that we can render the Apple Pay sheet to show the items for their order, the total, the shipping method and the required shipping contact fields. For example, we require the user’s postal address for delivery orders but that isn’t required for collection orders. This removes the need for the JavaScript to determine any of this information itself, as it can just copy the fields into the payment request object for the ApplePaySession constructor directly from the JSON response.

The validate resource is used to implement the merchant session validation with the Apple Pay servers. This posts the Apple validation URL to our back-end which then calls the specified URL using the Merchant Identify Certificate associated with the requesting domain to validate the merchant session. The JSON response then returns a MerchantSession dictionary for consumption by the JavaScript to pass to the completeMerchantValidation function.

The payment resource is used to POST the encrypted payment token, as well as the basket ID and billing and shipping contact details to our server to place the order. This resource then returns either an order ID (and optionally a token if a guest user account was created) if the payment was authorised successful or an error code otherwise.

For delivery orders we also have a POST /applepay/basket/{id}/validatepostalcode resource to check that the user’s chosen shipping address can be delivered to.

Merchant Validation

Initiating the POST to Apple’s servers to validate the session is relatively simple in ASP.NET Core (more about that later), provided you’ve already performed the steps to create a .pfx file for your Merchant Identify Certificate.

First we need to load the certificate, whether that’s from the certificate store or from a file on disk. In our service we store the certificate as an embedded resource as we have multiple certificates for different environments, but the simplest form is loading from disk.

This was the approach I was using in some local initial testing, but when I deployed the code to a Microsoft Azure App Service to leverage the free SSL certificate, this stopped working. After some digging around I found that this was because on Windows you need to be able to load the user profile to access private keys in certificates, and this isn’t possible by default in IIS as it isn’t loaded. This is easy enough to fix when you have full control of the infrastructure (such as our Amazon Web Services (AWS) Elastic Cloud Compute (EC2) instances), but there’s no option available to enable this in Azure.

Luckily there is a way around this. First, you upload the certificate that has a private key that you wish to use to the App Service using the “SSL certificates” tab in the Azure Portal. Next, you add the WEBSITE_LOAD_CERTIFICATES App setting to the “Application settings” tab and set its value to the thumbprint of the certificate you want to use. This causes the App Service to make the specified certificate available in the “My” store in the “Current User” location so it can be read by the identity associated with the IIS App Pool. Note that the validOnly parameter value is set to false; if it is not the Merchant Identifier Certificate will not be loaded as it is not considered valid for use by Windows, even though it is valid from Apple’s perspective.

The next step in the merchant validation process is to construct the payload to POST to the Apple server. For this we need our domain name, the store display name (in our case “Just Eat”) and the merchant identifier. While we could configure the merchant identifier to use per domain, we can be smart about it and read it from the Merchant Identifier Certificate instead. Thanks to Tom Dale’s node.js example implementation, we discovered that this can be found from the 1.2.840.113635.100.6.32 X.509 extension field, so we can read it out of our X509Certificate2 like so:

Now we can POST to the validation URL we received from the JavaScript. As mentioned previously we need to provide the Merchant Identifier Certificate with the request for two-way TLS authentication. This is achieved by using the HttpClientHandler class which provides a ClientCertificates property where we can use our certificate, and then pass it into the constructor of HttpClient to handle authentication for use when we POST the data:

Assuming we get a valid response from the Apple server, then we just need to deserialise the JSON containing the merchant session and return it to the client from our API controller method:

Now our JavaScript needs to consume the response body as mentioned earlier in the JavaScript implementation to pass it to the ApplePaySession.completeMerchantValidation function to allow the user to authorise the payment.

New Tricks with ASP.NET Core

When we started implementing Apple Pay for our website, ASP.NET Core 1.0.0 had just been released, and as such we were running all our C#-based code on the full .NET Framework. We decided that given the relatively small size and self-contained nature of the service for Apple Pay (plus there being no legacy code to worry about) that we’d dip our toes into the new world of ASP.NET Core for implementing the service for Apple Pay.

There are a number of capabilities and enhancements of ASP.NET Core that made it attractive for the implementation, but the main one was the improved integration with client-side focused technologies, such as Bower, Gulp and npm. Given that a bulk of the implementation is in JavaScript, this made it easier to use best-practice tools for JavaScript (and CSS) that provide features such as concatenation, minification, linting and testing. This made implementing the JavaScript part of the integration much easier to implement that the equivalent workflow in an ASP.NET MVC project in Visual Studio.

Getting cut at the bleeding edge

Of course, going with a new version of a well-established technology isn’t all plain-sailing. There’s been a few trade-offs moving to ASP.NET Core that have made us go back a few steps in some areas. These are gaps we hope to address in the near future to obtain feature parity with our existing ASP.NET applications. Some of these trade-offs are detailed below.

Dependencies

Here at Just Eat we have a variety of shared libraries that we add as dependencies into our .NET applications to share a common best-practice and allow services to focus on their primary purpose, rather than also have to worry about boiler-plate code, such as for logging, monitoring and communicating with other Just Eat services over HTTP.

Unfortunately a number of these dependencies are not quite in the position to support consumption from .NET Core-based applications. In most cases this is due to dependencies we consume ourselves not supporting .NET Core (such as Autofixture used in tests), or using .NET APIs that are not present in .NET Core’s surface area (such as changes to the UdpClient class).

We’re planning to move such libraries over to support .NET Core in due course (example), but the structure of the dependencies makes this a non-trivial task. The plan is to move our Apple Pay service over to versions of our libraries supporting .NET Core as they become available, for now it uses its own .NET Core forks of these libraries.

Monitoring

At Just Eat we have a very mature monitoring and logging solutions using Kibana and Grafana, amongst other tools. Part of our monitoring solution involves a custom service that is installed on our AWS EC2 Amazon Machine Images (AMIs) which collects performance counter data to publish to StatsD.

Unfortunately ASP.NET Core does not currently implement performance counters on Windows. In ASP.NET, there are various performance counters available that we collect as part of our monitoring, such as the number of current IIS connections, request execution times, etc. Even though ASP.NET Core can be hosted via IIS, because the .NET Framework is not used, these performance counters are of no use when it comes to monitoring an ASP.NET Core application.

Testing the implementation

So once we’ve gotten our server-side implementation to get details for rendering the Apple Pay sheet, validating merchant sessions and processing payment in place, as well as our JavaScript abstraction and base CSS, we can start going about testing it out.

But how do we test Apple Pay without using our own personal credit/debit card?

Luckily with iOS 10, watchOS 3 and macOS Sierra, Apple have provided us with a way to do this. It’s called the Apple Pay Sandbox. This provides us with a way to set up users with “real” payment cards that allow us to test transaction processing (at least up to the point of trying to capture funds). You can find more details on the website, but the main steps are:

  1. Setup a sandbox tester account in iTunes Connect
  2. Sign into iCloud on your test device(s) using your sandbox tester
  3. Add one or more test card(s) to Wallet on you test device(s)

 

Using the Apple Pay sandbox then allows you to test as many transactions as you like on your test devices without worrying about spending a fortune or misplacing your personal payment card details.

Stubbing Out the SDK

With the majority of Just Eat’s back-end services (and our website) being written in ASP.NET, this posed a bit of a challenge for testing. Of course the interactions with the sheet and the rendering need to be tested on a real Apple Pay-supporting device, but how could we run the full-back end stack on our local Windows 10 machines and use Apple Pay for local testing of changes without setting up lots of proxying to macOS and iOS test devices?

Well luckily in JavaScript it’s quite simple to add a polyfill to a browser to provide a native API where there would otherwise not be one available. So that’s what we did.

You can find it in a here on GitHub.

Effectively the polyfill provides the ApplePaySession object if it does not already exist, and functions in a way that makes the functions behave as if Apple Pay is available on the current device and chains the events and their handlers together to make it appear that a user is interacting with the Apple Pay sheet.

Of course it is no substitute for testing with a real device, but the polyfill provides enough of an implementation to test feature detection (i.e. only adding the button if Apple Pay is supported) and the server side implementation for fetching and rendering the basket, performing merchant validation, and passing on a valid sandbox payment token.

You can get a valid payment token for a sandbox transaction that you can embed within your own copy of the Polyfill by adding some JavaScript logging to print out the text representation of the object passed as the event parameter to the onpaymentauthorized function, as well as populating it with some appropriate billing and payment contact details.

We use the polyfill for testing in our QA environments by loading it into the browser via a script tag in our checkout-related pages where the Apple Pay button would appear.

Deployment

So we’ve got our new service, and we’ve integrated it into our website and it’s all working locally. Now it just needs deploying to our QA environments for testing, and then eventually onto our production environment.

We have our own deployment pipeline here at Just Eat that sets up deploying IIS applications from ZIP packages and we also build our own custom AWS AMIs to deploy our services onto, so that’s all taken care of by our Platform Engineering team.

Our AMIs do not yet have .NET Core installed on them though, so if we tried to use the deployed in IIS it would return an HTTP 502. That’s easy enough to resolve though, we just need to make a new AMI with .NET Core on it.

This is nice and easy as Chocolatey provides packages for both the .NET Core runtime and the Windows Server Hosting installer for IIS hosting.

Now there’s just a few more things we need to do to get our feature ready to run:

  1. We need to set the ASPNETCORE_ENVIRONMENT environment variable so that the application runs with the right configuration
  2. We need to set up the registry hives required for the ASP.NET Core data protection system (used for things like antiforgery tokens)
  3. We need to adjust the App Pool configuration

 

Our deployment process already provides us with hooks to run PowerShell scripts post-deployment, so we just need to write some small scripts to do the steps.

Setting the environment name

We can set the environment name machine-wide because we deploy each service on its own EC2 instance. There are other approaches available, like setting environment variables in the ASP.NET Core Module, but this was simpler:

Configuring the App Pool

We also need to amend the IIS App Pool for the website to disable the .NET Framework (because we don’t need it) and to load the user profile so we can load the private keys in our Merchant Identifier Certificates.

Setting Up Data Protection

The process for setting up Data Protection for IIS, which in turn provides a link to a PowerShell script, can be found here.

After these three steps are done, then IIS just needs to be restarted (such as with iisreset) to pick up the configuration changes.

The (Apple) pay off

So now with Apple Pay integrated into our website, it’s possible for the user to pay using the cards loaded into Wallet on either their iPhone running iOS 10 or their Apple Watch running watchOS 3 when paired with a MacBook running macOS Sierra.

iPhone payment flow

At the start of the checkout flow the user is prompted to select what time they would like their food delivered for (or be ready for collection) and an optional note for the restaurant.

At first the user is shown the Apple Pay button in additional to the usual button to continue through checkout to provide their delivery and payment details.

The user taps the Apple Pay button and the Apple sheet is displayed. Then the user selects their payment card as well as their delivery address. While this happens we asynchronously validate the merchant session to enable TouchID to authorize payment as well as validate that the restaurant selected delivers to the postcode provided by the user in the case of a delivery order.

Once the user authorizes payment with their finger or thumb, the sheet is dismissed, they are logged in to a guest account if not already logged in, and redirected to the order confirmation page.

The Apple Pay button displayed during checkout in Safari on iOS 10.

The Apple Pay button displayed during checkout in Safari on iOS 10.

The Apple Pay payment sheet in iOS.

The Apple Pay payment sheet in iOS.

macOS payment flow

At the start of the checkout flow the user is prompted to select what time they would like their food delivered for (or be ready for collection) and an optional note for the restaurant.

Here the user is shown the Apple Pay button in additional to the usual button to continue through checkout to provide their delivery and payment details.

The Apple Pay button displayed during checkout in Safari on macOS Sierra.

The Apple Pay button displayed during checkout in Safari on macOS Sierra.

The user clicks the Apple Pay button and the Apple sheet is displayed. The user selects their payment card as well as their delivery address. While this happens we asynchronously validate the merchant session to enable the ability to authorize payment using an iPhone, iPad or Apple Watch paired with the signed in iCloud account, as well as validate that the restaurant selected delivers to the postcode provided by the user in the case of a delivery order.

The Apple Pay payment sheet in macOS Sierra.

The Apple Pay payment sheet in macOS Sierra.

Once the merchant session is validated, the user is then prompted to authorize the payment on their paired device, for example using either an iPhone with TouchID or an Apple Watch.

Payment confirmation for a purchase from macOS using Touch ID on an iPhone.

Payment confirmation for a purchase from macOS using Touch ID on an iPhone.

Payment confirmation for a purchase from macOS using Apple Watch.

Payment confirmation for a purchase from macOS using Apple Watch.

Once the user authorizes payment with their finger or thumb with TouchID or by pressing a button on their Apple Watch, the sheet is dismissed, they are logged in to a guest account if not already logged in, and redirected to the order confirmation page.

Now the user just needs to wait for their food with their inner food mood to be prepared.

Example integration

An example integration of Apple Pay JS adapted from our own implementation is available on GitHub. You should be able to use it as a guide to implementing Apple Pay into your website by viewing the JavaScript for creating an ApplePaySession and the C# for validating a merchant session. Also, provided you have an Apple Developer account so that you can generate your own merchant identifier and the associated certificates, you should also be able to run it yourself and see Apple Pay in action.

Conclusion

We hope you’ve found this post about how we brought Apple Pay to the Just Eat website informative and interesting, and that the example integration is a useful resource if you’re thinking about implementing Apple Pay into your own e-commerce solution yourself.

It’s been an interesting SDK to integrate with a number of challenges along the way, but we’ve also learned a lot in the process, particularly about Apple Pay itself, as well as the differences between ASP.NET and ASP.NET Core (the good and the not so good).

Just Eat is here to help you find your flavour, and with Apple Pay as a payment option in our website now, we hope you’ll now be able to find it even easier!

1363 views

Creating a heat-map with D3

Here at JUST EAT we have regular 3-day hackathons, where we get the chance to work on whatever we believe will help empower consumers to love their takeaway experience. I’ve found they are a great opportunity to work in an area outside that of my current day-to-day work, and also to familiarise myself with new technologies. My first hackathon at JUST EAT was a great example of this. Along with others I spent the 3 days putting together a reporting dashboard using the following stack; most of which were new to me at the time:

  • Redshift – AWS’s data warehouse service
  • ServiceStack – framework for the API that queries our data in Redshift
  • Backbone – providing the framework for our client-side application which consumes the API
  • Rickshaw – graphing toolkit for displaying time-series (built on top of D3)
  • D3.js (Data-Driven-Documents) – JavaScript library for manipulating documents based on data

Seeing as we already had D3 in the mix, and having postcode dimensions in our data warehouse, I thought it’d be fairly simple to add a D3 choropleth (or heat-map) to our prototype to add a bit of visual flair. After reading the excellent Let’s Make a Map tutorial by Mike Bostock, I knew that the hardest part was going to be sourcing the postcode shape-files.

Sourcing the data

Shapefiles are the standard data format for describing geospatial information – shapefiles of UK postcodes are available to buy from the O/S – unfortunately outside of the budget for the prototype we were creating.

After much experimentation, and learning some new tools, I managed to cobble together some UK postcode area data. I found that there is an amazing array of open source geospatial tools – www.maptools.org is an excellent resource for these; the most useful of which was QGIS for visualising and editing geospatial data.

Once I had the shapefile, it was an easy process to simplify the geometries (hence reducing the file-size), and export to the TopoJSON format that D3 maps consume.

I’ve posted a full description of what was involved to create the TopoJSON at github.com/roblascelles/uk-postcode-map/wiki/Cobbling-together-UK-postcode-area-data

Displaying the data

Drawing the postcode map was simply a case of replacing the boundary data in the tutorial. After that, it was almost trivial to add a fill value for each postcode-shape based on external data. The following snippets are from github.com/roblascelles/uk-postcode-map/wiki/Displaying-the-data, which describe all the steps of how to display an example choropleth.

Assuming that we’ve already obtained a JavaScript associative array called “areadata” that holds the values we need to represent for each postcode – we can use D3 to create a function that will map each value to a colour like this:

Note, the RGB values are a nice range of blues from learning-d3-part-7-choropleth-maps. Also, we’re using the excellent underscore.js library here to flatten-out our data into an array so D3 can use it.

Now we just need to pass the value in the color function for each postcode (the .style(“fill”.. section below)

As an example, let’s generate some test data based purely on the first character of the postcode:

We can now see those character values, represented as colours on our choropleth:

uk-postcode-areas

We’re not going to win an information is beautiful award, but it’s much better than a table.

The actual map I finished with during the hackathon was obviously loading actual data from the service; I also added a zoom function from this click-to-zoom via transform example (was just a few extra lines of code). My next step is to display more details on the map as you zoom in – but that’s for a future hackathon.