'use strict';
var BraintreeError = require('../lib/braintree-error');
var analytics = require('../lib/analytics');
var deferred = require('../lib/deferred');
var sharedErrors = require('../lib/errors');
var errors = require('./errors');
/**
* An Apple Pay Payment Authorization Event object.
* @typedef {object} ApplePayPaymentAuthorizedEvent
* @external ApplePayPaymentAuthorizedEvent
* @see {@link https://developer.apple.com/reference/applepayjs/applepaypaymentauthorizedevent ApplePayPaymentAuthorizedEvent}
*/
/**
* An Apple Pay Payment Request object.
* @typedef {object} ApplePayPaymentRequest
* @external ApplePayPaymentRequest
* @see {@link https://developer.apple.com/reference/applepayjs/1916082-applepay_js_data_types/paymentrequest PaymentRequest}
*/
/**
* @class
* @param {object} options Options
* @description <strong>You cannot use this constructor directly. Use {@link module:braintree-web/apple-pay.create|braintree.applePay.create} instead.</strong>
* @classdesc This class represents an Apple Pay component. Instances of this class have methods for validating the merchant server and tokenizing payments.
*/
function ApplePay(options) {
this._client = options.client;
/**
* @name ApplePay#merchantIdentifier
* @description A special merchant ID which represents the merchant association with Braintree. Required when using `ApplePaySession.canMakePaymentsWithActiveCard`.
* @example
* var promise = ApplePaySession.canMakePaymentsWithActiveCard(applePayInstance.merchantIdentifier);
* promise.then(function (canMakePaymentsWithActiveCard) {
* if (canMakePaymentsWithActiveCard) {
* // Set up Apple Pay buttons
* }
* });
*/
Object.defineProperty(this, 'merchantIdentifier', {
value: this._client.getConfiguration().gatewayConfiguration.applePayWeb.merchantIdentifier,
configurable: false,
writable: false
});
}
/**
* Merges a payment request with Braintree defaults to return an {external:ApplePayPaymentRequest}.
*
* The following properties are assigned to `paymentRequest` if not already defined. Their default values come from the Braintree gateway.
* - `countryCode`
* - `currencyCode`
* - `merchantCapabilities`
* - `supportedNetworks`
* @public
* @param {external:ApplePayPaymentRequest} paymentRequest The payment request details to apply on top of those from Braintree.
* @returns {external:ApplePayPaymentRequest} The decorated `paymentRequest` object.
* @example
* var applePay = require('braintree-web/apple-pay');
*
* applePay.create({client: clientInstance}, function (applePayErr, applePayInstance) {
* if (applePayErr) {
* // Handle error here
* return;
* }
*
* var paymentRequest = applePayInstance.createPaymentRequest({
* total: {
* label: 'My Company',
* amount: '19.99'
* }
* });
*
* var session = new ApplePaySession(1, paymentRequest);
*
* // ...
*/
ApplePay.prototype.createPaymentRequest = function (paymentRequest) {
var applePay = this._client.getConfiguration().gatewayConfiguration.applePayWeb;
var defaults = {
countryCode: applePay.countryCode,
currencyCode: applePay.currencyCode,
merchantCapabilities: applePay.merchantCapabilities || ['supports3DS'],
supportedNetworks: applePay.supportedNetworks.map(function (network) {
return network === 'mastercard' ? 'masterCard' : network;
})
};
return Object.assign({}, defaults, paymentRequest);
};
/**
* Validates your merchant website, as required by `ApplePaySession` before payment can be authorized.
* @public
* @param {object} options Options
* @param {string} options.validationURL The validationURL fram an `ApplePayValidateMerchantEvent`.
* @param {string} options.displayName The canonical name for your store. Use a non-localized name. This parameter should be a UTF-8 string that is a maximum of 128 characters. The system may display this name to the user.
* @param {callback} callback The second argument, <code>data</code>, is the Apple Pay merchant session object.
* Pass the merchant session to your Apple Pay session's `completeMerchantValidation` method.
* @returns {void}
* @example
* var applePay = require('braintree-web/apple-pay');
*
* applePay.create({client: clientInstance}, function (applePayErr, applePayInstance) {
* if (applePayErr) {
* // Handle error here
* return;
* }
*
* var paymentRequest = applePayInstance.createPaymentRequest({
* total: {
* label: 'My Company',
* amount: '19.99'
* }
* });
* var session = new ApplePaySession(1, paymentRequest);
*
* session.onvalidatemerchant = function (event) {
* applePayInstance.performValidation({
* validationURL: event.validationURL,
* displayName: 'My Great Store'
* }, function (validationErr, validationData) {
* if (validationErr) {
* console.error(validationErr);
* session.abort();
* return;
* }
*
* session.completeMerchantValidation(validationData);
* });
* };
* });
*/
ApplePay.prototype.performValidation = function (options, callback) {
var applePayWebSession;
if (typeof callback !== 'function') {
throw new BraintreeError({
type: sharedErrors.CALLBACK_REQUIRED.type,
code: sharedErrors.CALLBACK_REQUIRED.code,
message: 'performValidation requires a callback.'
});
}
callback = deferred(callback);
if (!options || !options.validationURL) {
callback(new BraintreeError(errors.APPLE_PAY_VALIDATION_URL_REQUIRED));
return;
}
applePayWebSession = {
validationUrl: options.validationURL,
domainName: options.domainName || global.location.hostname,
merchantIdentifier: options.merchantIdentifier || this.merchantIdentifier
};
if (options.displayName != null) {
applePayWebSession.displayName = options.displayName;
}
this._client.request({
method: 'post',
endpoint: 'apple_pay_web/sessions',
data: {
_meta: {source: 'apple-pay'},
applePayWebSession: applePayWebSession
}
}, function (err, response) {
if (err) {
if (err.code === 'CLIENT_REQUEST_ERROR') {
callback(new BraintreeError({
type: errors.APPLE_PAY_MERCHANT_VALIDATION_FAILED.type,
code: errors.APPLE_PAY_MERCHANT_VALIDATION_FAILED.code,
message: errors.APPLE_PAY_MERCHANT_VALIDATION_FAILED.message,
details: {
originalError: err.details.originalError
}
}));
} else {
callback(new BraintreeError({
type: errors.APPLE_PAY_MERCHANT_VALIDATION_NETWORK.type,
code: errors.APPLE_PAY_MERCHANT_VALIDATION_NETWORK.code,
message: errors.APPLE_PAY_MERCHANT_VALIDATION_NETWORK.message,
details: {
originalError: err
}
}));
}
analytics.sendEvent(this._client, 'applepay.performValidation.failed');
} else {
callback(null, response);
analytics.sendEvent(this._client, 'applepay.performValidation.succeeded');
}
}.bind(this));
};
/**
* Tokenizes an Apple Pay payment. This will likely be called in your `ApplePaySession`'s `onpaymentauthorized` callback.
* @public
* @param {object} options Options
* @param {object} options.token The `payment.token` property of an {@link external:ApplePayPaymentAuthorizedEvent}.
* @param {callback} callback The second argument, <code>data</code>, is the tokenized payload.
* @returns {void}
* @example
* var applePay = require('braintree-web/apple-pay');
*
* applePay.create({client: clientInstance}, function (applePayErr, applePayInstance) {
* if (applePayErr) {
* // Handle error here
* return;
* }
*
* var paymentRequest = applePayInstance.createPaymentRequest({
* total: {
* label: 'My Company',
* amount: '19.99'
* }
* });
* var session = new ApplePaySession(1, paymentRequest);
*
* session.onpaymentauthorized = function (event) {
* applePayInstance.tokenize({
* token: event.payment.token
* }, function (tokenizeErr, tokenizedPayload) {
* if (tokenizeErr) {
* session.completePayment(ApplePaySession.STATUS_FAILURE);
* return;
* }
* session.completePayment(ApplePaySession.STATUS_SUCCESS);
*
* // Send the tokenizedPayload to your server here!
* });
* };
*
* // ...
* });
*/
ApplePay.prototype.tokenize = function (options, callback) {
if (typeof callback !== 'function') {
throw new BraintreeError({
type: sharedErrors.CALLBACK_REQUIRED.type,
code: sharedErrors.CALLBACK_REQUIRED.code,
message: 'tokenize requires a callback.'
});
}
callback = deferred(callback);
if (!options.token) {
callback(new BraintreeError(errors.APPLE_PAY_PAYMENT_TOKEN_REQUIRED));
return;
}
this._client.request({
method: 'post',
endpoint: 'payment_methods/apple_payment_tokens',
data: {
_meta: {
source: 'apple-pay'
},
applePaymentToken: Object.assign({}, options.token, {
// The gateway requires this key to be base64-encoded.
paymentData: btoa(JSON.stringify(options.token.paymentData))
})
}
}, function (err, response) {
if (err) {
callback(new BraintreeError({
type: errors.APPLE_PAY_TOKENIZATION.type,
code: errors.APPLE_PAY_TOKENIZATION.code,
message: errors.APPLE_PAY_TOKENIZATION.message,
details: {
originalError: err
}
}));
analytics.sendEvent(this._client, 'applepay.tokenize.failed');
} else {
callback(null, response.applePayCards[0]);
analytics.sendEvent(this._client, 'applepay.tokenize.succeeded');
}
}.bind(this));
};
module.exports = ApplePay;