'use strict';
var frameService = require('../../lib/frame-service/external');
var BraintreeError = require('../../lib/braintree-error');
var useMin = require('../../lib/use-min');
var VERSION = process.env.npm_package_version;
var INTEGRATION_TIMEOUT_MS = require('../../lib/constants').INTEGRATION_TIMEOUT_MS;
var analytics = require('../../lib/analytics');
var methods = require('../../lib/methods');
var convertMethodsToError = require('../../lib/convert-methods-to-error');
var convertToBraintreeError = require('../../lib/convert-to-braintree-error');
var Promise = require('../../lib/promise');
var querystring = require('../../lib/querystring');
var wrapPromise = require('@braintree/wrap-promise');
var constants = require('./constants');
var errors = require('../shared/errors');
function LocalPayment(options) {
this._client = options.client;
this._assetsUrl = options.client.getConfiguration().gatewayConfiguration.assetsUrl + '/web/' + VERSION;
this._isDebug = options.client.getConfiguration().isDebug;
this._loadingFrameUrl = this._assetsUrl + '/html/local-payment-landing-frame' + useMin(this._isDebug) + '.html';
this._authorizationInProgress = false;
this._paymentType = 'unknown';
this._merchantAccountId = options.merchantAccountId;
}
LocalPayment.prototype._initialize = function () {
var self = this;
var client = this._client;
var failureTimeout = setTimeout(function () {
analytics.sendEvent(client, 'local-payment.load.timed-out');
}, INTEGRATION_TIMEOUT_MS);
return new Promise(function (resolve) {
frameService.create({
name: 'localpaymentlandingpage',
dispatchFrameUrl: self._assetsUrl + '/html/dispatch-frame' + useMin(self._isDebug) + '.html',
openFrameUrl: self._loadingFrameUrl
}, function (service) {
self._frameService = service;
clearTimeout(failureTimeout);
analytics.sendEvent(client, 'local-payment.load.succeeded');
resolve(self);
});
});
};
LocalPayment.prototype.startPayment = wrapPromise(function (options) {
var address, params;
var self = this;
var serviceId = this._frameService._serviceId;
if (hasMissingOption(options)) {
return Promise.reject(new BraintreeError(errors.LOCAL_PAYMENT_START_PAYMENT_MISSING_REQUIRED_OPTION));
}
address = options.address || {};
params = {
intent: 'sale',
returnUrl: querystring.queryify(self._assetsUrl + '/html/local-payment-redirect-frame' + useMin(self._isDebug) + '.html', {
channel: serviceId,
r: options.fallback.url,
t: options.fallback.buttonText
}),
cancelUrl: querystring.queryify(self._assetsUrl + '/html/local-payment-cancel-frame' + useMin(self._isDebug) + '.html', {
channel: serviceId
}),
experienceProfile: {
noShipping: !options.shippingAddressRequired
},
fundingSource: options.paymentType,
amount: options.amount,
currencyIsoCode: options.currencyCode,
firstName: options.givenName,
lastName: options.surname,
payerEmail: options.email,
phone: options.phone,
line1: address.streetAddress,
line2: address.extendedAddress,
city: address.locality,
state: address.region,
postalCode: address.postalCode,
countryCode: address.countryCode,
merchantAccountId: self._merchantAccountId
};
self._paymentType = options.paymentType.toLowerCase();
if (self._authorizationInProgress) {
analytics.sendEvent(self._client, self._paymentType + '.local-payment.start-payment.error.already-opened');
return Promise.reject(new BraintreeError(errors.LOCAL_PAYMENT_ALREADY_IN_PROGRESS));
}
self._authorizationInProgress = true;
return new Promise(function (resolve, reject) {
self._startPaymentCallback = self._createStartPaymentCallback(resolve, reject);
self._frameService.open({}, self._startPaymentCallback);
self._client.request({
method: 'post',
endpoint: 'local_payments/create',
data: params
}).then(function (response) {
analytics.sendEvent(self._client, self._paymentType + '.local-payment.start-payment.opened');
self._startPaymentOptions = options;
options.onPaymentStart({paymentId: response.paymentResource.paymentToken}, function () {
self._frameService.redirect(response.paymentResource.redirectUrl);
});
}).catch(function (err) {
var status = err.details && err.details.httpStatus;
self._frameService.close();
self._authorizationInProgress = false;
if (status === 422) {
reject(new BraintreeError({
type: errors.LOCAL_PAYMENT_INVALID_PAYMENT_OPTION.type,
code: errors.LOCAL_PAYMENT_INVALID_PAYMENT_OPTION.code,
message: errors.LOCAL_PAYMENT_INVALID_PAYMENT_OPTION.message,
details: {
originalError: err
}
}));
return;
}
reject(convertToBraintreeError(err, {
type: errors.LOCAL_PAYMENT_START_PAYMENT_FAILED.type,
code: errors.LOCAL_PAYMENT_START_PAYMENT_FAILED.code,
message: errors.LOCAL_PAYMENT_START_PAYMENT_FAILED.message
}));
});
});
});
LocalPayment.prototype.tokenize = function (params) {
var self = this;
var client = this._client;
params = params || querystring.parse();
return client.request({
endpoint: 'payment_methods/paypal_accounts',
method: 'post',
data: this._formatTokenizeData(params)
}).then(function (response) {
var payload = self._formatTokenizePayload(response);
if (global.popupBridge) {
analytics.sendEvent(client, self._paymentType + '.local-payment.tokenization.success-popupbridge');
} else {
analytics.sendEvent(client, self._paymentType + '.local-payment.tokenization.success');
}
return payload;
}).catch(function (err) {
analytics.sendEvent(client, self._paymentType + '.local-payment.tokenization.failed');
return Promise.reject(convertToBraintreeError(err, {
type: errors.LOCAL_PAYMENT_TOKENIZATION_FAILED.type,
code: errors.LOCAL_PAYMENT_TOKENIZATION_FAILED.code,
message: errors.LOCAL_PAYMENT_TOKENIZATION_FAILED.message
}));
});
};
LocalPayment.prototype.closeWindow = function () {
if (this._authoriztionInProgress) {
analytics.sendEvent(this._client, this._paymentType + '.local-payment.start-payment.closed.by-merchant');
}
this._frameService.close();
};
LocalPayment.prototype.focusWindow = function () {
this._frameService.focus();
};
LocalPayment.prototype._createStartPaymentCallback = function (resolve, reject) {
var self = this;
var client = this._client;
return function (err, params) {
self._authorizationInProgress = false;
if (err) {
if (err.code === 'FRAME_SERVICE_FRAME_CLOSED') {
analytics.sendEvent(client, self._paymentType + '.local-payment.tokenization.closed.by-user');
reject(new BraintreeError(errors.LOCAL_PAYMENT_WINDOW_CLOSED));
} else if (err.code && err.code.indexOf('FRAME_SERVICE_FRAME_OPEN_FAILED') > -1) {
reject(new BraintreeError({
code: errors.LOCAL_PAYMENT_WINDOW_OPEN_FAILED.code,
type: errors.LOCAL_PAYMENT_WINDOW_OPEN_FAILED.type,
message: errors.LOCAL_PAYMENT_WINDOW_OPEN_FAILED.message,
details: {
originalError: err
}
}));
}
} else if (params) {
if (!global.popupBridge) {
self._frameService.redirect(self._loadingFrameUrl);
}
self.tokenize(params).then(resolve).catch(reject).then(function () {
self._frameService.close();
});
}
};
};
LocalPayment.prototype._formatTokenizePayload = function (response) {
var payload;
var account = {};
if (response.paypalAccounts) {
account = response.paypalAccounts[0];
}
payload = {
nonce: account.nonce,
details: {},
type: account.type
};
if (account.details) {
if (account.details.payerInfo) {
payload.details = account.details.payerInfo;
}
if (account.details.correlationId) {
payload.correlationId = account.details.correlationId;
}
}
return payload;
};
LocalPayment.prototype.hasTokenizationParams = function () {
var params = querystring.parse();
return Boolean(params.btLpToken && params.btLpPaymentId && params.btLpPayerId);
};
LocalPayment.prototype._formatTokenizeData = function (params) {
var clientConfiguration = this._client.getConfiguration();
var gatewayConfiguration = clientConfiguration.gatewayConfiguration;
var data = {
merchantAccountId: this._merchantAccountId,
paypalAccount: {
correlationId: params.btLpToken || params.token,
paymentToken: params.btLpPaymentId || params.paymentId,
payerId: params.btLpPayerId || params.PayerID,
unilateral: gatewayConfiguration.paypal.unvettedMerchant,
intent: 'sale'
}
};
return data;
};
function hasMissingOption(options) {
var i, option;
if (!options) {
return true;
}
for (i = 0; i < constants.REQUIRED_OPTIONS_FOR_START_PAYMENT.length; i++) {
option = constants.REQUIRED_OPTIONS_FOR_START_PAYMENT[i];
if (!options.hasOwnProperty(option)) {
return true;
}
}
if (!(options.fallback.url && options.fallback.buttonText)) {
return true;
}
return false;
}
LocalPayment.prototype.teardown = wrapPromise(function () {
var self = this;
self._frameService.teardown();
convertMethodsToError(self, methods(LocalPayment.prototype));
analytics.sendEvent(self._client, 'local-payment.teardown-completed');
return Promise.resolve();
});
module.exports = LocalPayment;