'use strict';
var BraintreeError = require('../../lib/error');
var analytics = require('../../lib/analytics');
var methods = require('../../lib/methods');
var convertMethodsToError = require('../../lib/convert-methods-to-error');
var constants = require('../shared/constants');
var Bus = require('../../lib/bus');
var uuid = require('../../lib/uuid');
var deferred = require('../../lib/deferred');
var errors = require('../shared/errors');
var throwIfNoCallback = require('../../lib/throw-if-no-callback');
var events = require('../shared/events');
var version = require('package.version');
var iFramer = require('iframer');
var IFRAME_HEIGHT = 400;
var IFRAME_WIDTH = 400;
function ThreeDSecure(options) {
this._options = options;
this._assetsUrl = options.client.getConfiguration().gatewayConfiguration.assetsUrl;
this._client = options.client;
}
ThreeDSecure.prototype.verifyCard = function (options, callback) {
var url, addFrame, removeFrame, error, errorOption;
throwIfNoCallback(callback, 'verifyCard');
options = options || {};
callback = deferred(callback);
if (this._verifyCardInProgress === true) {
error = errors.THREEDS_AUTHENTICATION_IN_PROGRESS;
} else if (!options.nonce) {
errorOption = 'a nonce';
} else if (!options.amount) {
errorOption = 'an amount';
} else if (typeof options.addFrame !== 'function') {
errorOption = 'an addFrame function';
} else if (typeof options.removeFrame !== 'function') {
errorOption = 'a removeFrame function';
}
if (errorOption) {
error = {
type: errors.THREEDS_MISSING_VERIFY_CARD_OPTION.type,
code: errors.THREEDS_MISSING_VERIFY_CARD_OPTION.code,
message: 'verifyCard options must include ' + errorOption + '.'
};
}
if (error) {
callback(new BraintreeError(error));
return;
}
this._verifyCardInProgress = true;
addFrame = deferred(options.addFrame);
removeFrame = deferred(options.removeFrame);
url = 'payment_methods/' + options.nonce + '/three_d_secure/lookup';
this._client.request({
endpoint: url,
method: 'post',
data: {amount: options.amount}
}, function (err, response) {
if (err) {
this._verifyCardInProgress = false;
callback(err);
return;
}
this._lookupPaymentMethod = response.paymentMethod;
this._verifyCardCallback = function () {
this._verifyCardInProgress = false;
callback.apply(null, arguments);
}.bind(this);
this._handleLookupResponse({
lookupResponse: response,
addFrame: addFrame,
removeFrame: removeFrame
});
}.bind(this));
};
ThreeDSecure.prototype.cancelVerifyCard = function (callback) {
var error;
this._verifyCardInProgress = false;
if (typeof callback === 'function') {
if (!this._lookupPaymentMethod) {
error = new BraintreeError(errors.THREEDS_NO_VERIFICATION_PAYLOAD);
}
callback(error, this._lookupPaymentMethod);
}
};
ThreeDSecure.prototype._handleLookupResponse = function (options) {
var lookupResponse = options.lookupResponse;
if (lookupResponse.lookup && lookupResponse.lookup.acsUrl && lookupResponse.lookup.acsUrl.length > 0) {
options.addFrame(null, this._createIframe({
response: lookupResponse.lookup,
removeFrame: options.removeFrame
}));
} else {
this._verifyCardCallback(null, {
nonce: lookupResponse.paymentMethod.nonce,
verificationDetails: lookupResponse.threeDSecureInfo
});
}
};
ThreeDSecure.prototype._createIframe = function (options) {
var url, authenticationCompleteBaseUrl;
var parentURL = window.location.href;
var response = options.response;
this._bus = new Bus({
channel: uuid(),
merchantUrl: location.href
});
authenticationCompleteBaseUrl = this._assetsUrl + '/web/' + version + '/html/three-d-secure-authentication-complete-frame.html?channel=' + encodeURIComponent(this._bus.channel) + '&';
if (parentURL.indexOf('#') > -1) {
parentURL = parentURL.split('#')[0];
}
this._bus.on(Bus.events.CONFIGURATION_REQUEST, function (reply) {
reply({
acsUrl: response.acsUrl,
pareq: response.pareq,
termUrl: response.termUrl + '&three_d_secure_version=' + version + '&authentication_complete_base_url=' + encodeURIComponent(authenticationCompleteBaseUrl),
md: response.md,
parentUrl: parentURL
});
});
this._bus.on(events.AUTHENTICATION_COMPLETE, function (data) {
this._handleAuthResponse(data, options);
}.bind(this));
url = this._assetsUrl + '/web/' + version + '/html/three-d-secure-bank-frame@DOT_MIN.html';
this._bankIframe = iFramer({
src: url,
height: IFRAME_HEIGHT,
width: IFRAME_WIDTH,
name: constants.LANDING_FRAME_NAME + '_' + this._bus.channel
});
return this._bankIframe;
};
ThreeDSecure.prototype._handleAuthResponse = function (data, options) {
var authResponse = JSON.parse(data.auth_response);
this._bus.teardown();
options.removeFrame();
deferred(function () {
if (authResponse.success) {
this._verifyCardCallback(null, this._formatAuthResponse(authResponse.paymentMethod, authResponse.threeDSecureInfo));
} else if (authResponse.threeDSecureInfo && authResponse.threeDSecureInfo.liabilityShiftPossible) {
this._verifyCardCallback(null, this._formatAuthResponse(this._lookupPaymentMethod, authResponse.threeDSecureInfo));
} else {
this._verifyCardCallback(new BraintreeError({
type: BraintreeError.types.UNKNOWN,
code: 'UNKNOWN_AUTH_RESPONSE',
message: authResponse.error.message
}));
}
}.bind(this))();
};
ThreeDSecure.prototype._formatAuthResponse = function (paymentMethod, threeDSecureInfo) {
return {
nonce: paymentMethod.nonce,
details: paymentMethod.details,
description: paymentMethod.description,
liabilityShifted: threeDSecureInfo.liabilityShifted,
liabilityShiftPossible: threeDSecureInfo.liabilityShiftPossible
};
};
ThreeDSecure.prototype.teardown = function (callback) {
var iframeParent;
convertMethodsToError(this, methods(ThreeDSecure.prototype));
analytics.sendEvent(this._options.client, 'threedsecure.teardown-completed');
if (this._bus) {
this._bus.teardown();
}
if (this._bankIframe) {
iframeParent = this._bankIframe.parentNode;
if (iframeParent) {
iframeParent.removeChild(this._bankIframe);
}
}
if (typeof callback === 'function') {
callback = deferred(callback);
callback();
}
};
module.exports = ThreeDSecure;