'use strict';
var frameService = require('../../lib/frame-service/external');
var BraintreeError = require('../../lib/error');
var VERSION = require('package.version');
var constants = require('../shared/constants');
var INTEGRATION_TIMEOUT_MS = require('../../lib/constants').INTEGRATION_TIMEOUT_MS;
var analytics = require('../../lib/analytics');
var methods = require('../../lib/methods');
var deferred = require('../../lib/deferred');
var convertMethodsToError = require('../../lib/convert-methods-to-error');
/**
* @typedef {object} PayPal~tokenizePayload
* @property {string} nonce The payment method nonce.
* @property {object} details Additional PayPal account details.
* @property {string} details.email User's email address.
* @property {string} details.payerId User's payer ID, the unique identifier for each PayPal account.
* @property {string} details.firstName User's given name.
* @property {string} details.lastName User's surname.
* @property {?string} details.countryCode User's 2 character country code.
* @property {?string} details.phone User's phone number (e.g. 555-867-5309).
* @property {?object} details.shippingAddress User's shipping address details, only available if shipping address is enabled.
* @property {string} details.shippingAddress.recipientName Recipient of postage.
* @property {string} details.shippingAddress.line1 Street number and name.
* @property {string} details.shippingAddress.line2 Extended address.
* @property {string} details.shippingAddress.city City or locality.
* @property {string} details.shippingAddress.state State or region.
* @property {string} details.shippingAddress.postalCode Postal code.
* @property {string} details.shippingAddress.countryCode 2 character country code (e.g. US).
*/
/**
* @typedef {object} PayPal~tokenizeReturn
* @property {Function} close A handle to close the PayPal checkout flow.
*/
/**
* @class
* @param {module:braintree-web/paypal~createOptions} options see {@link module:braintree-web/paypal.create|paypal.create}
* @classdesc This class represents a PayPal component. Instances of this class have methods for launching auth dialogs and other programmatic interactions with the PayPal component.
*/
function PayPal(options) {
this._options = options;
this._assetsUrl = options.client.getConfiguration().gatewayConfiguration.paypal.assetsUrl + '/web/' + VERSION;
this._authorizationInProgress = false;
}
PayPal.prototype._initialize = function (callback) {
var options = this._options;
var failureTimeout = setTimeout(function () {
analytics.sendEvent(options.client, 'web.paypal.load.timed-out');
}, INTEGRATION_TIMEOUT_MS);
frameService.create(
this._options,
{
name: constants.LANDING_FRAME_NAME,
url: this._assetsUrl,
landingFrameHTML: '/html/paypal-landing-frame@DOT_MIN.html'
},
function (service) {
this._frameService = service;
clearTimeout(failureTimeout);
analytics.sendEvent(options.client, 'web.paypal.load.succeeded');
callback();
}.bind(this)
);
};
/**
* Launches the PayPal login flow and returns a nonce payload.
* @public
* @param {callback} callback The second argument, <code>data</code>, is a {@link PayPal~tokenizePayload|tokenizePayload}.
* @returns {PayPal~tokenizeReturn} A handle to close the PayPal checkout frame.
*/
PayPal.prototype.tokenize = function (callback) {
var client;
if (typeof callback !== 'function') {
throw new BraintreeError({
type: BraintreeError.types.MERCHANT,
message: 'tokenize must include a callback function.'
});
}
client = this._options.client;
callback = deferred(callback);
if (this._authorizationInProgress) {
analytics.sendEvent(client, 'web.paypal.tokenization.error.already-opened');
callback(new BraintreeError({
type: BraintreeError.types.MERCHANT,
message: 'Another tokenization request is active.'
}));
} else {
this._authorizationInProgress = true;
analytics.sendEvent(client, 'web.paypal.tokenization.opened');
this._frameService.open(function (err) {
if (err) {
if (err.message === constants.FRAME_CLOSED_ERROR_MESSAGE) {
analytics.sendEvent(client, 'web.paypal.tokenization.closed.by-user');
} else {
analytics.sendEvent(client, 'web.paypal.tokenization.failed');
}
} else {
analytics.sendEvent(client, 'web.paypal.tokenization.success');
}
this._authorizationInProgress = false;
callback.apply(null, arguments);
}.bind(this));
}
return {
close: function () {
analytics.sendEvent(client, 'web.paypal.tokenization.closed.by-merchant');
this._frameService.close();
}.bind(this)
};
};
/**
* Cleanly tear down anything set up by {@link module:braintree-web/paypal.create|create}.
* @public
* @param {callback} [callback] Called once teardown is complete. No data is returned if teardown completes successfully.
* @returns {void}
*/
PayPal.prototype.teardown = function (callback) {
this._frameService.teardown();
convertMethodsToError(this, methods(PayPal.prototype));
analytics.sendEvent(this._options.client, 'web.paypal.teardown-completed');
if (typeof callback === 'function') {
callback = deferred(callback);
callback();
}
};
module.exports = PayPal;