Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
alec-paulson committed Jan 13, 2017
0 parents commit 0f1e1cf
Show file tree
Hide file tree
Showing 18 changed files with 903 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"presets": [
"es2015"
]
}
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
.DS_Store
.vscode
npm-debug.log
acme-challenge
cert.pem
key.pem
98 changes: 98 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Using Node and Heroku to demo ApplePay on the Web

Site adapted from [EmporiumWeb](https://developer.apple.com/library/content/samplecode/EmporiumWeb/Introduction/Intro.html)

## Requirements:

* iOS 10 device that supports ApplePay (has TouchID or is an Apple Watch)
* (Optionally) macOS Sierra computer that supports Handoff (mid 2012 or newer, requires Bluetooth LE support)
* Development can be largely done even with an OS version below Sierra and without
Handoff support, though testing on it won't work.
* Apple developer account ($99/yr)
* Heroku account (free)
* Git, Node and NPM installed

## How to host the ApplePay Web example for free using Heroku:

### Sign up for [Heroku](https://www.heroku.com) (no CC needed)

Sign up then install the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli).

Next from a terminal run `heroku login` and login.

Next clone this project to your computer and in your terminal change directories to that folder.

In the `lib/config.js` file change the `vantivIPAuth` value from `<Base64 Encoded Auth>` to the value provided by Vantiv IP.

In the `public/js/eprotectsupport.js` file change line 74 to contain the PaypageId provided by Vantiv IP.

Run `heroku create` to create a new Heroku app with a random name (that you can change later).

Next let's do some Apple setup.

### Get your Merchant Identity Certificate

Request your Merchant Identity Certificate by logging into your [Apple Developer Site](https://developer.apple.com) and going to
your Merchant ID and clicking "Create Certificate" under "Merchant Identity Certificate" and following the instructions. After you have
the certificate, follow these instructions to convert it to .PEM format and place it in the `certificates` folder.

#### Convert your Merchant Identity Certificate (.CER) to .PEM format

See this [link](http://stackoverflow.com/questions/21250510/generate-pem-file-used-to-setup-apple-push-notification) on SO

Ignore all the stuff about push certificates, this works for converting our Merchant Identity Certificate as well.

##### Basic steps:

1. Import the .cer into Keychain, export both the certificate and private key as one .p12 format
file then run:

`openssl pkcs12 -in <p12filename>.p12 -out <pemfilename>.pem -nodes -clcerts`

2. Then put the pem file in `/certificates` named `applePayCert.pem`

### Testing Locally

First install all prerequisites by running `npm install`.

Then run `npm run dev` and browse to `http://localhost:4567/`. You should be able to open the page, but ApplePay won't work locally.

The `app.js` will run using HTTP instead of HTTPS because Heroku will automatically serve the page over HTTPS.
This also has the nice side benefit that we don't need the sslKey or sslCert like we would with original EmporiumWeb example
(but we still need the applePayCert).

If running locally using `npm run dev` then the server will spin up on port 4567 otherwise `process.env.PORT` will be used
(which Heroku will have set to 80).

### Deploying to Heroku

Now deploy this project to Heroku by running `git push heroku master`.

You should now be able to hit `https://<name>.herokuapp.com` though ApplePay won't work yet.

If any changes are made to any files in the `lib` folder be sure to run `npm run build` before deploying to Heroku, in order to generate
the files in the `dist` folder (which Heroku will run).

### Validate your domain

Next, follow the instructions [here](https://developer.apple.com/reference/applepayjs/) to validate your merchant domain.
Apple will provide a file with which to perform the validation.

Place the file in the `.well-known` folder, commit the file with

`git add . && git commit -m 'added apple verification file'`

then deploy to Heroku again with `git push heroku master`. Then return to your Apple Developer page and complete verification.

Finally, with all that done you can hit `https://<name>.herokuapp.com` and perform an ApplePay transaction in two ways:

* From Safari on an iOS 10 device with ApplePay support
* From Safari on a Mac running macOS Sierra that [supports Handoff](https://support.apple.com/kb/PH25169?locale=en_US) (2012 and later hardware)

After completing a transaction you should see, first, a registration id appear, followed by the results of the transaction to Vantiv IP.

## Apple Resources

* [Apple Pay Developer Site](https://developer.apple.com/apple-pay/)
* [Apple Pay on the web WWDC Session Video](https://developer.apple.com/videos/play/wwdc2016/703/)
* [Apple Pay Domain Verification](https://developer.apple.com/support/apple-pay-domain-verification/)
5 changes: 5 additions & 0 deletions certificates/CERTIFICATES_GO_HERE
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/*
Copyright (C) 2016 Vantiv. All Rights Reserved.
*/

Place your Apple Pay merchant certificate in this folder.
157 changes: 157 additions & 0 deletions dist/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
'use strict';

var _express = require('express');

var _express2 = _interopRequireDefault(_express);

var _bodyParser = require('body-parser');

var _bodyParser2 = _interopRequireDefault(_bodyParser);

var _fs = require('fs');

var _fs2 = _interopRequireDefault(_fs);

var _https = require('https');

var _https2 = _interopRequireDefault(_https);

var _http = require('http');

var _http2 = _interopRequireDefault(_http);

var _request = require('request');

var _request2 = _interopRequireDefault(_request);

var _path = require('path');

var _path2 = _interopRequireDefault(_path);

var _config = require('./config');

var _config2 = _interopRequireDefault(_config);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
* IMPORTANT
* Change these paths to your own SSL and Apple Pay certificates,
* with the appropriate merchant identifier and domain
* See the README for more information.
*/
/*
Copyright (C) 2016 Vantiv. All Rights Reserved.
Adapted from EmporiumWeb:
https://developer.apple.com/library/content/samplecode/EmporiumWeb/Introduction/Intro.html
Abstract:
Sets up a simple Express HTTP server to host the example page, and handles requesting
the Apple Pay merchant session from Apple's servers.
*/

var APPLE_PAY_CERTIFICATE_PATH = "./certificates/applePayCert.pem";
//const SSL_CERTIFICATE_PATH = "./certificates/cert.pem";
//const SSL_KEY_PATH = "./certificates/key.pem";
var MERCHANT_IDENTIFIER = "merchant.com.mercury.prelive";
var MERCHANT_DOMAIN = "vantivapplepayweb.herokuapp.com";

try {
_fs2.default.accessSync(APPLE_PAY_CERTIFICATE_PATH);
// fs.accessSync(SSL_CERTIFICATE_PATH);
// fs.accessSync(SSL_KEY_PATH);
} catch (e) {
throw new Error('You must generate your SSL and Apple Pay certificates before running this example.');
}

//const sslKey = fs.readFileSync(SSL_KEY_PATH);
//const sslCert = fs.readFileSync(SSL_CERTIFICATE_PATH);
var applePayCert = _fs2.default.readFileSync(APPLE_PAY_CERTIFICATE_PATH);

/**
* Set up our server and static page hosting
*/
console.log('__dirname: ' + __dirname);
var options = {
index: 'index.html'
};
var app = (0, _express2.default)();
app.use(_express2.default.static('public'));
app.use('/.well-known', _express2.default.static('.well-known'));
app.use(_bodyParser2.default.json());

/**
* A POST endpoint to obtain a merchant session for Apple Pay.
* The client provides the URL to call in its body.
* Merchant validation is always carried out server side rather than on
* the client for security reasons.
*/
app.post('/getApplePaySession', function (req, res) {

// We need a URL from the client to call
if (!req.body.url) return res.sendStatus(400);

console.log(req.body.url);
// We must provide our Apple Pay certificate, merchant ID, domain name, and display name
var options = {
url: req.body.url,
cert: applePayCert,
key: applePayCert,
method: 'post',
body: {
merchantIdentifier: MERCHANT_IDENTIFIER,
domainName: MERCHANT_DOMAIN,
displayName: 'My Store'
},
json: true
};

// Send the request to the Apple Pay server and return the response to the client
(0, _request2.default)(options, function (err, response, body) {
if (err) {
console.log('Error generating Apple Pay session!');
console.log(err, response, body);
res.status(500).send(body);
}
res.send(body);
});
});

app.post('/makeVantivIPCall', function (req, res) {
console.log('hit /makeVantivIPCall');
console.log(req.body.registrationId + " " + req.body.amount);

var options = {
url: _config2.default.vantivIPURL + "/PaymentsAPI/Credit/SaleByRecordNo",
method: 'post',
headers: {
"content-type": "application/json",
"accept": "*/*",
"authorization": "Basic " + _config2.default.vantivIPAuth
},
body: {
"OperatorID": "TEST",
"InvoiceNo": "123456",
"Purchase": req.body.amount,
"Tax": "0.00",
"TokenType": "RegistrationId",
"RecordNo": req.body.registrationId,
"Frequency": "OneTime"
},
json: true
};

(0, _request2.default)(options, function (err, response, body) {
if (err) {
console.log(err, response, body);
res.status(500).send(body);
}
res.send(body);
});
});

/**
* Start serving the app.
*/
_http2.default.createServer(app).listen(process.env.PORT || 4567);
15 changes: 15 additions & 0 deletions dist/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';

/*
Copyright (C) 2016 Vantiv. All Rights Reserved.
Adapted from EmporiumWeb:
https://developer.apple.com/library/content/samplecode/EmporiumWeb/Introduction/Intro.html
*/

var config = {
vantivIPAuth: '<Base64 Encoded Auth>',
vantivIPURL: 'https://w1.mercurycert.net'
};

module.exports = config;
Loading

0 comments on commit 0f1e1cf

Please sign in to comment.