Skip to content

Commit

Permalink
Merge pull request #715 from recurly/fk-1314
Browse files Browse the repository at this point in the history
ApplePay through Braintree
  • Loading branch information
czombo authored Apr 6, 2022
2 parents 6d0a9b6 + c92c0cf commit 122302a
Show file tree
Hide file tree
Showing 7 changed files with 411 additions and 54 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ module.exports = {
env: {
browser: true,
es6: true,
node: true
node: true,
mocha: true
},
extends: 'eslint:recommended',
globals: {
Expand Down
86 changes: 86 additions & 0 deletions lib/recurly/apple-pay/apple-pay.braintree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import Promise from 'promise';
import loadScript from 'load-script';
import { ApplePay } from './apple-pay';

const debug = require('debug')('recurly:apple-pay:braintree');

const promisify = Promise.denodeify;

const CLIENT_VERSION = '3.76.0';
const LIBS = {
client: 'client',
applePay: 'apple-pay',
dataCollector: 'data-collector',
};

const loadBraintree = (...libs) => {
const loadLib = lib => {
const isLibPresent = window.braintree?.client?.VERSION === CLIENT_VERSION &&
lib in window.braintree;

return isLibPresent
? Promise.resolve()
: promisify(loadScript)(ApplePayBraintree.libUrl(lib));
};

return loadLib('client')
.then(() => Promise.all(libs.map(loadLib)));
};

export class ApplePayBraintree extends ApplePay {
static libUrl (lib) {
return `https://js.braintreegateway.com/web/${CLIENT_VERSION}/js/${LIBS[lib]}.min.js`;
}

configure (options) {
debug('Initializing client');

const authorization = options.braintree.clientAuthorization;

loadBraintree('applePay', 'dataCollector')
.then(() => window.braintree.client.create({ authorization }))
.then(client => Promise.all([
window.braintree.dataCollector.create({ client }),
window.braintree.applePay.create({ client })
]))
.then(([dataCollector, applePay]) => { this.braintree = { dataCollector, applePay }; })
.catch(err => {
this.initError = this.error('apple-pay-init-error', { err });
return Promise.reject(this.initError);
})
.then(() => super.configure(options));
}

onValidateMerchant (event) {
debug('Validating merchant session', event);

this.braintree.applePay
.performValidation({ validationURL: event.validationURL, displayName: 'My Store' })
.then(merchantSession => this.session.completeMerchantValidation(merchantSession))
.catch(err => this.error(err));
}

token (event, applePayPayment) {
debug('Creating token');

this.braintree.applePay
.tokenize({ token: event.payment.token })
.then(tokenizePayload => super.token(event, {
type: 'braintree',
payload: {
deviceData: this.braintree.dataCollector.deviceData,
applePayPayment,
tokenizePayload
}
}))
.catch(err => this.error('apple-pay-payment-failure', err));
}

onCancel (event) {
debug('Teardown payment', event);

this.braintree.applePay
.teardown()
.finally(() => super.onCancel(event));
}
}
30 changes: 12 additions & 18 deletions lib/recurly/apple-pay.js → lib/recurly/apple-pay/apple-pay.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Emitter from 'component-emitter';
import errors from './errors';
import { Pricing } from './pricing';
import PricingPromise from './pricing/promise';
import { normalize } from '../util/normalize';
import decimalize from '../util/decimalize';
import { FIELDS } from './token';
import errors from '../errors';
import { Pricing } from '../pricing';
import PricingPromise from '../pricing/promise';
import { normalize } from '../../util/normalize';
import decimalize from '../../util/decimalize';
import { FIELDS } from '../token';

const debug = require('debug')('recurly:apple-pay');

Expand All @@ -28,16 +28,6 @@ const I18N = {
giftCardLineItemLabel: 'Gift card'
};

/**
* Instantiation factory
*
* @param {Object} options
* @return {ApplePay}
*/
export function factory (options) {
return new ApplePay(Object.assign({}, options, { recurly: this }));
}

/**
* Initializes an Apple Pay session.
*
Expand All @@ -53,7 +43,7 @@ export function factory (options) {
* @public
*/

class ApplePay extends Emitter {
export class ApplePay extends Emitter {
constructor (options) {
super();

Expand Down Expand Up @@ -357,6 +347,10 @@ class ApplePay extends Emitter {

this.mapPaymentData(data, event.payment);

return this.token(event, data);
}

token (event, data) {
this.recurly.request.post({
route: '/apple_pay/token',
data,
Expand Down Expand Up @@ -398,7 +392,7 @@ class ApplePay extends Emitter {
*/
error (...params) {
let err = params[0] instanceof Error ? params[0] : errors(...params);
this.emit('error', err);
setTimeout(() => this.emit('error', err), 0);
return err;
}

Expand Down
16 changes: 16 additions & 0 deletions lib/recurly/apple-pay/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ApplePay } from './apple-pay';
import { ApplePayBraintree } from './apple-pay.braintree';

/**
* Instantiation factory
*
* @param {Object} options
* @return {ApplePay}
*/
export function factory (options) {
const factoryClass = options?.braintree?.clientAuthorization
? ApplePayBraintree
: ApplePay;

return new factoryClass(Object.assign({}, options, { recurly: this }));
}
6 changes: 4 additions & 2 deletions test/server/fixtures/apple_pay/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ const error = {

function token () {
const params = this.method === 'GET' ? this.query : this.request.body;
if (params.paymentData === 'valid-payment-data') return ok;
else return error;
const isValid = params.paymentData === 'valid-payment-data' ||
params?.payload?.applePayPayment?.paymentData === 'valid-payment-data';

return isValid ? ok : error;
}

token.ok = ok;
Expand Down
Loading

0 comments on commit 122302a

Please sign in to comment.