braintree-web
Version:
A suite of tools for integrating Braintree in the browser
1,299 lines (1,174 loc) • 113 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.braintree || (g.braintree = {})).client = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
"use strict";
var scriptPromiseCache = {};
function loadScript(options) {
var scriptLoadPromise;
var stringifiedOptions = JSON.stringify(options);
if (!options.forceScriptReload) {
scriptLoadPromise = scriptPromiseCache[stringifiedOptions];
if (scriptLoadPromise) {
return scriptLoadPromise;
}
}
var script = document.createElement("script");
var attrs = options.dataAttributes || {};
var container = options.container || document.head;
script.src = options.src;
script.id = options.id || "";
script.async = true;
if (options.type) {
script.setAttribute("type", "".concat(options.type));
}
if (options.crossorigin) {
script.setAttribute("crossorigin", "".concat(options.crossorigin));
}
if (options.integrity) {
script.setAttribute("integrity", "".concat(options.integrity));
}
Object.keys(attrs).forEach(function (key) {
script.setAttribute("data-".concat(key), "".concat(attrs[key]));
});
scriptLoadPromise = new Promise(function (resolve, reject) {
script.addEventListener("load", function () {
resolve(script);
});
script.addEventListener("error", function () {
reject(new Error("".concat(options.src, " failed to load.")));
});
script.addEventListener("abort", function () {
reject(new Error("".concat(options.src, " has aborted.")));
});
container.appendChild(script);
});
scriptPromiseCache[stringifiedOptions] = scriptLoadPromise;
return scriptLoadPromise;
}
loadScript.clearCache = function () {
scriptPromiseCache = {};
};
module.exports = loadScript;
},{}],2:[function(_dereq_,module,exports){
module.exports = _dereq_("./dist/load-script");
},{"./dist/load-script":1}],3:[function(_dereq_,module,exports){
"use strict";
function uuid() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0;
var v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
module.exports = uuid;
},{}],4:[function(_dereq_,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function deferred(fn) {
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
setTimeout(function () {
try {
fn.apply(void 0, args);
}
catch (err) {
/* eslint-disable no-console */
console.log("Error in callback function");
console.log(err);
/* eslint-enable no-console */
}
}, 1);
};
}
exports.deferred = deferred;
},{}],5:[function(_dereq_,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function once(fn) {
var called = false;
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
if (!called) {
called = true;
fn.apply(void 0, args);
}
};
}
exports.once = once;
},{}],6:[function(_dereq_,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable consistent-return */
function promiseOrCallback(promise, callback) {
if (!callback) {
return promise;
}
promise.then(function (data) { return callback(null, data); }).catch(function (err) { return callback(err); });
}
exports.promiseOrCallback = promiseOrCallback;
},{}],7:[function(_dereq_,module,exports){
"use strict";
var deferred_1 = _dereq_("./lib/deferred");
var once_1 = _dereq_("./lib/once");
var promise_or_callback_1 = _dereq_("./lib/promise-or-callback");
function wrapPromise(fn) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var callback;
var lastArg = args[args.length - 1];
if (typeof lastArg === "function") {
callback = args.pop();
callback = once_1.once(deferred_1.deferred(callback));
}
// I know, I know, this looks bad. But it's a quirk of the library that
// we need to allow passing the this context to the original function
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore: this has an implicit any
return promise_or_callback_1.promiseOrCallback(fn.apply(this, args), callback); // eslint-disable-line no-invalid-this
};
}
wrapPromise.wrapPrototype = function (target, options) {
if (options === void 0) { options = {}; }
var ignoreMethods = options.ignoreMethods || [];
var includePrivateMethods = options.transformPrivateMethods === true;
var methods = Object.getOwnPropertyNames(target.prototype).filter(function (method) {
var isNotPrivateMethod;
var isNonConstructorFunction = method !== "constructor" &&
typeof target.prototype[method] === "function";
var isNotAnIgnoredMethod = ignoreMethods.indexOf(method) === -1;
if (includePrivateMethods) {
isNotPrivateMethod = true;
}
else {
isNotPrivateMethod = method.charAt(0) !== "_";
}
return (isNonConstructorFunction && isNotPrivateMethod && isNotAnIgnoredMethod);
});
methods.forEach(function (method) {
var original = target.prototype[method];
target.prototype[method] = wrapPromise(original);
});
return target;
};
module.exports = wrapPromise;
},{"./lib/deferred":4,"./lib/once":5,"./lib/promise-or-callback":6}],8:[function(_dereq_,module,exports){
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.loadAxo = {}));
})(this, (function (exports) { 'use strict';
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
var dist = {};
var scriptPromiseCache = {};
function loadScript$1(options) {
var scriptLoadPromise;
var stringifiedOptions = JSON.stringify(options);
if (!options.forceScriptReload) {
scriptLoadPromise = scriptPromiseCache[stringifiedOptions];
if (scriptLoadPromise) {
return scriptLoadPromise;
}
}
var script = document.createElement("script");
var attrs = options.dataAttributes || {};
var container = options.container || document.head;
script.src = options.src;
script.id = options.id || "";
script.async = true;
if (options.type) {
script.setAttribute("type", "".concat(options.type));
}
if (options.crossorigin) {
script.setAttribute("crossorigin", "".concat(options.crossorigin));
}
Object.keys(attrs).forEach(function (key) {
script.setAttribute("data-".concat(key), "".concat(attrs[key]));
});
scriptLoadPromise = new Promise(function (resolve, reject) {
script.addEventListener("load", function () {
resolve(script);
});
script.addEventListener("error", function () {
reject(new Error("".concat(options.src, " failed to load.")));
});
script.addEventListener("abort", function () {
reject(new Error("".concat(options.src, " has aborted.")));
});
container.appendChild(script);
});
scriptPromiseCache[stringifiedOptions] = scriptLoadPromise;
return scriptLoadPromise;
}
loadScript$1.clearCache = function () {
scriptPromiseCache = {};
};
var loadScript_1$1 = loadScript$1;
var loadStylesheet$1 = function loadStylesheet(options) {
var stylesheet = document.querySelector("link[href=\"".concat(options.href, "\"]"));
if (stylesheet) {
return Promise.resolve(stylesheet);
}
stylesheet = document.createElement("link");
var container = options.container || document.head;
stylesheet.setAttribute("rel", "stylesheet");
stylesheet.setAttribute("type", "text/css");
stylesheet.setAttribute("href", options.href);
stylesheet.setAttribute("id", options.id);
if (container.firstChild) {
container.insertBefore(stylesheet, container.firstChild);
}
else {
container.appendChild(stylesheet);
}
return Promise.resolve(stylesheet);
};
Object.defineProperty(dist, "__esModule", { value: true });
dist.loadStylesheet = loadScript_1 = dist.loadScript = void 0;
var loadScript = loadScript_1$1;
var loadScript_1 = dist.loadScript = loadScript;
var loadStylesheet = loadStylesheet$1;
dist.loadStylesheet = loadStylesheet;
var CDNX_PROD = "https://www.paypalobjects.com";
var ASSET_NAME = {
minified: "axo.min",
unminified: "axo",
};
var FL_NAMESPACE = "fastlane";
var ASSET_PATH = "connect-boba";
var LOCALE_PATH = "".concat(ASSET_PATH, "/locales/");
var constants = {
AXO_ASSET_NAME: ASSET_NAME,
AXO_ASSET_PATH: ASSET_PATH,
LOCALE_PATH: LOCALE_PATH,
CDNX_PROD: CDNX_PROD,
};
var AxoSupportedPlatforms = {
BT: "BT",
PPCP: "PPCP",
};
/**
* Checks if the current environment is an AMD environment.
*
* @returns {boolean} True if the environment is AMD, false otherwise.
*/
function isAmdEnv() {
return typeof window.define === "function" && !!window.define.amd;
}
/**
* Checks if the current environment is a RequireJS environment.
*
* @returns {boolean} True if the environment is RequireJS, false otherwise.
*/
function isRequireJsEnv() {
return (isAmdEnv() &&
typeof window.requirejs === "function" &&
typeof window.requirejs.config === "function");
}
/**
* Safely loads BT modules by checking if the module already exists and verifying if versions mismatch
*
* @param loadConfig <BtModuleLoadConfig> Configuration of BT Module to load
* @param version <string> version that should be passed from the client getVersion
* @returns Promise<HTMLScriptElement>
* @returns Promise<true> when BT module with same version already exists
* @returns Promise.reject(err) when BT module already exists but versions mismatch or empty version passed in
*/
function safeLoadBtModule(loadConfig, version, minified) {
var _a, _b;
if (minified === void 0) { minified = true; }
return __awaiter(this, void 0, void 0, function () {
var bt, existingVersion;
return __generator(this, function (_c) {
bt = getBraintree();
if (bt && bt[loadConfig.module]) {
if (version && ((_a = bt[loadConfig.module]) === null || _a === void 0 ? void 0 : _a.VERSION) !== version) {
existingVersion = (_b = bt[loadConfig.module]) === null || _b === void 0 ? void 0 : _b.VERSION;
throw new Error("".concat(loadConfig.module, " already loaded with version ").concat(existingVersion, " cannot load version ").concat(version));
}
else {
return [2 /*return*/, true];
}
}
if (!version) {
throw new Error("Attempted to load ".concat(loadConfig.module, " without specifying version"));
}
return [2 /*return*/, loadBtModule(loadConfig, version, minified)];
});
});
}
/**
* Reads the version and to load the correct version of Bt module
*
* @param loadConfig <BtModuleLoadConfig> Configuration of BT Module to load
* @param version <string> Bt module version
* @returns Promise<HTMLScriptElement> or
*/
function loadBtModule(loadConfig, version, minified) {
if (minified === void 0) { minified = true; }
if (isAmdEnv()) {
var module_1 = minified
? loadConfig.amdModule.minified
: loadConfig.amdModule.unminified;
return new Promise(function (resolve, reject) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
window.require([module_1], resolve, reject);
});
}
var script = minified
? loadConfig.script.minified
: loadConfig.script.unminified;
return loadScript_1({
id: "".concat(loadConfig.id, "-").concat(version),
src: "https://js.braintreegateway.com/web/".concat(version, "/js/").concat(script),
});
}
/**
* Looks for the Braintree web sdk on the window object
*
* @returns Braintree web sdk
*/
function getBraintree() {
return window === null || window === void 0 ? void 0 : window.braintree;
}
var _a, _b;
/**
* Maps to the BT module namespace created on the window.braintree object
*/
var BtModule = {
Client: "client",
HostedCardFields: "hostedFields",
};
var BT_NAMESPACE = "braintree";
var BT_ASSET_NAME = (_a = {},
_a[BtModule.Client] = "client",
_a[BtModule.HostedCardFields] = "hosted-fields",
_a);
var btModulesLoadConfig = (_b = {},
_b[BtModule.Client] = {
id: "client",
module: BtModule.Client,
amdModule: {
unminified: "".concat(BT_NAMESPACE, "/").concat(BT_ASSET_NAME[BtModule.Client]),
minified: "".concat(BT_NAMESPACE, "/").concat(BT_ASSET_NAME[BtModule.Client], ".min"),
},
script: {
unminified: "".concat(BT_ASSET_NAME[BtModule.Client], ".js"),
minified: "".concat(BT_ASSET_NAME[BtModule.Client], ".min.js"),
},
},
_b[BtModule.HostedCardFields] = {
id: "hcf",
module: BtModule.HostedCardFields,
amdModule: {
unminified: "".concat(BT_NAMESPACE, "/").concat(BT_ASSET_NAME[BtModule.HostedCardFields]),
minified: "".concat(BT_NAMESPACE, "/").concat(BT_ASSET_NAME[BtModule.HostedCardFields], ".min"),
},
script: {
unminified: "".concat(BT_ASSET_NAME[BtModule.HostedCardFields], ".js"),
minified: "".concat(BT_ASSET_NAME[BtModule.HostedCardFields], ".min.js"),
},
},
_b);
/**
* Loads accelerated checkout components.
* @param options object with a minified parameter to determine if the script that is loaded should be minified or not (defaults to true if)
* @returns an object with metadata with a localeUrl parameter to be read by AXO SDK
*/
function loadAxo(options) {
return __awaiter(this, void 0, void 0, function () {
var btSdkVersion, minified, assetUrl, localeUrl;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
performance.mark("pp_axo_sdk_init_invoked");
btSdkVersion = options.btSdkVersion, minified = options.minified;
assetUrl = getAssetsUrl(options);
localeUrl = getLocaleUrl(options);
if (!(options.platform === AxoSupportedPlatforms.BT)) return [3 /*break*/, 2];
return [4 /*yield*/, Promise.all([
safeLoadBtModule(btModulesLoadConfig.hostedFields, btSdkVersion, minified),
loadAXOScript(assetUrl, minified),
])];
case 1:
_a.sent();
return [3 /*break*/, 5];
case 2:
if (!(options.platform === AxoSupportedPlatforms.PPCP)) return [3 /*break*/, 4];
return [4 /*yield*/, Promise.all([
safeLoadBtModule(btModulesLoadConfig.client, btSdkVersion, minified),
safeLoadBtModule(btModulesLoadConfig.hostedFields, btSdkVersion, minified),
loadAXOScript(assetUrl, minified),
])];
case 3:
_a.sent();
return [3 /*break*/, 5];
case 4: throw new Error("unsupported axo platform");
case 5: return [2 /*return*/, { metadata: { localeUrl: localeUrl } }];
}
});
});
}
/**
* Reads the url and to load the axo bundle script
* @param url (Required) string url for the correct axo asset
* @returns Promise<HTMLScriptElement>
*/
function loadAXOScript(url, minified) {
var _a;
if (minified === void 0) { minified = true; }
if (isAmdEnv()) {
// AMD environment
if (isRequireJsEnv()) {
// Let's configure RequireJS
requirejs.config({
paths: (_a = {},
_a[FL_NAMESPACE] = url,
_a),
});
}
var moduleName_1 = "".concat(FL_NAMESPACE, "/").concat(minified
? constants.AXO_ASSET_NAME.minified
: constants.AXO_ASSET_NAME.unminified);
return new Promise(function (resolve, reject) {
window.require([moduleName_1], resolve, reject);
});
}
// Not an AMD environment
return loadScript_1({
id: "axo-id",
src: url,
forceScriptReload: true,
});
}
/**
* Prepends the domain to the asset url
* @param options object with assetUrl and bundleid parameters to determine which URL to return
* @returns full domain and assets URL as string
*/
function generateAssetUrl(_a) {
var assetUrl = _a.assetUrl, bundleId = _a.bundleId;
return bundleId
? "https://cdn-".concat(bundleId, ".static.engineering.dev.paypalinc.com/").concat(assetUrl)
: "".concat(constants.CDNX_PROD, "/").concat(assetUrl);
}
/**
* Retrieves either the minified or unminified assets URL as specified
* @param options (Optional) object with a minified and metadata with bundleIdOverride parameters to determine which URL to return
* @returns assets URL as string
*/
function getAssetsUrl(options) {
var _a;
var assetName = (options === null || options === void 0 ? void 0 : options.minified) !== false
? constants.AXO_ASSET_NAME.minified
: constants.AXO_ASSET_NAME.unminified;
var assetUrl = isAmdEnv()
? constants.AXO_ASSET_PATH
: "".concat(constants.AXO_ASSET_PATH, "/").concat(assetName, ".js");
return generateAssetUrl({
assetUrl: assetUrl,
bundleId: (_a = options === null || options === void 0 ? void 0 : options.metadata) === null || _a === void 0 ? void 0 : _a.bundleIdOverride,
});
}
/**
* Retrieves the Locales URL, the path to our language files
* @param options (Optional) object with a minified and metadata with bundleIdOverride parameters to determine which URL to return
* @returns locale URL as string
*/
function getLocaleUrl(options) {
var _a;
return generateAssetUrl({
assetUrl: constants.LOCALE_PATH,
bundleId: (_a = options === null || options === void 0 ? void 0 : options.metadata) === null || _a === void 0 ? void 0 : _a.bundleIdOverride,
});
}
exports.constants = constants;
exports.loadAxo = loadAxo;
}));
},{}],9:[function(_dereq_,module,exports){
"use strict";
var BRAINTREE_VERSION = _dereq_("./constants").BRAINTREE_VERSION;
var GraphQL = _dereq_("./request/graphql");
var request = _dereq_("./request");
var isVerifiedDomain = _dereq_("../lib/is-verified-domain");
var BraintreeError = _dereq_("../lib/braintree-error");
var convertToBraintreeError = _dereq_("../lib/convert-to-braintree-error");
var getGatewayConfiguration = _dereq_("./get-configuration").getConfiguration;
var createAuthorizationData = _dereq_("../lib/create-authorization-data");
var metadata = _dereq_("../lib/add-metadata");
var wrapPromise = _dereq_("@braintree/wrap-promise");
var once = _dereq_("../lib/once");
var deferred = _dereq_("../lib/deferred");
var assign = _dereq_("../lib/assign").assign;
var analytics = _dereq_("../lib/analytics");
var errors = _dereq_("./errors");
var VERSION = _dereq_("../lib/constants").VERSION;
var GRAPHQL_URLS = _dereq_("../lib/constants").GRAPHQL_URLS;
var methods = _dereq_("../lib/methods");
var convertMethodsToError = _dereq_("../lib/convert-methods-to-error");
var assets = _dereq_("../lib/assets");
var FRAUDNET_FNCLS = _dereq_("../lib/constants").FRAUDNET_FNCLS;
var FRAUDNET_SOURCE = _dereq_("../lib/constants").FRAUDNET_SOURCE;
var FRAUDNET_URL = _dereq_("../lib/constants").FRAUDNET_URL;
var cachedClients = {};
/**
* This object is returned by {@link Client#getConfiguration|getConfiguration}. This information is used extensively by other Braintree modules to properly configure themselves.
* @typedef {object} Client~configuration
* @property {object} client The braintree-web/client parameters.
* @property {string} client.authorization A tokenizationKey or clientToken.
* @property {object} gatewayConfiguration Gateway-supplied configuration.
* @property {object} analyticsMetadata Analytics-specific data.
* @property {string} analyticsMetadata.sessionId Uniquely identifies a browsing session.
* @property {string} analyticsMetadata.sdkVersion The braintree.js version.
* @property {string} analyticsMetadata.merchantAppId Identifies the merchant's web app.
*/
/**
* @class
* @param {Client~configuration} configuration Options
* @description <strong>Do not use this constructor directly. Use {@link module:braintree-web/client.create|braintree.client.create} instead.</strong>
* @classdesc This class is required by many other Braintree components. It serves as the base API layer that communicates with our servers. It is also capable of being used to formulate direct calls to our servers, such as direct credit card tokenization. See {@link Client#request}.
*/
function Client(configuration) {
var configurationJSON, gatewayConfiguration;
configuration = configuration || {};
configurationJSON = JSON.stringify(configuration);
gatewayConfiguration = configuration.gatewayConfiguration;
if (!gatewayConfiguration) {
throw new BraintreeError(errors.CLIENT_MISSING_GATEWAY_CONFIGURATION);
}
["assetsUrl", "clientApiUrl", "configUrl"].forEach(function (property) {
if (
property in gatewayConfiguration &&
!isVerifiedDomain(gatewayConfiguration[property])
) {
throw new BraintreeError({
type: errors.CLIENT_GATEWAY_CONFIGURATION_INVALID_DOMAIN.type,
code: errors.CLIENT_GATEWAY_CONFIGURATION_INVALID_DOMAIN.code,
message: property + " property is on an invalid domain.",
});
}
});
/**
* Returns a copy of the configuration values.
* @public
* @returns {Client~configuration} configuration
*/
this.getConfiguration = function () {
return JSON.parse(configurationJSON);
};
this._request = request;
this._configuration = this.getConfiguration();
this._clientApiBaseUrl = gatewayConfiguration.clientApiUrl + "/v1/";
if (gatewayConfiguration.graphQL) {
if (!isVerifiedDomain(gatewayConfiguration.graphQL.url)) {
throw new BraintreeError({
type: errors.CLIENT_GATEWAY_CONFIGURATION_INVALID_DOMAIN.type,
code: errors.CLIENT_GATEWAY_CONFIGURATION_INVALID_DOMAIN.code,
message: "graphQL.url property is on an invalid domain.",
});
}
this._graphQL = new GraphQL({
graphQL: gatewayConfiguration.graphQL,
});
}
}
Client.initialize = function (options) {
var clientInstance, authData;
var promise = cachedClients[options.authorization];
if (promise) {
analytics.sendEvent(promise, "custom.client.load.cached");
return promise;
}
try {
authData = createAuthorizationData(options.authorization);
// eslint-disable-next-line no-unused-vars
} catch (err) {
return Promise.reject(
new BraintreeError(errors.CLIENT_INVALID_AUTHORIZATION)
);
}
promise = getGatewayConfiguration(authData, options.sessionId).then(
function (configuration) {
if (options.debug) {
configuration.isDebug = true;
}
configuration.authorization = options.authorization;
clientInstance = new Client(configuration);
return clientInstance;
}
);
cachedClients[options.authorization] = promise;
analytics.sendEvent(promise, "custom.client.load.initialized");
return promise
.then(function (client) {
analytics.sendEvent(clientInstance, "custom.client.load.succeeded");
return client;
})
.catch(function (err) {
delete cachedClients[options.authorization];
return Promise.reject(err);
});
};
// Primarily used for testing the client initalization call
Client.clearCache = function () {
cachedClients = {};
};
Client.prototype._findOrCreateFraudnetJSON = function (clientMetadataId) {
var el = document.querySelector('script[fncls="' + FRAUDNET_FNCLS + '"]');
var config, additionalData, authorizationFingerprint, parameters;
if (!el) {
el = document.body.appendChild(document.createElement("script"));
el.type = "application/json";
el.setAttribute("fncls", FRAUDNET_FNCLS);
}
config = this.getConfiguration();
additionalData = {
rda_tenant: "bt_card", // eslint-disable-line camelcase
mid: config.gatewayConfiguration.merchantId,
};
authorizationFingerprint = config.authorizationFingerprint;
if (authorizationFingerprint) {
authorizationFingerprint.split("&").forEach(function (pieces) {
var component = pieces.split("=");
if (component[0] === "customer_id" && component.length > 1) {
additionalData.cid = component[1];
}
});
}
parameters = {
f: clientMetadataId.substr(0, 32),
fp: additionalData,
bu: false,
s: FRAUDNET_SOURCE,
};
el.text = JSON.stringify(parameters);
};
/**
* Used by other modules to formulate all network requests to the Braintree gateway. It is also capable of being used directly from your own form to tokenize credit card information. However, be sure to satisfy PCI compliance if you use direct card tokenization.
* @public
* @param {object} options Request options:
* @param {string} options.method HTTP method, e.g. "get" or "post".
* @param {string} options.endpoint Endpoint path, e.g. "payment_methods".
* @param {object} options.data Data to send with the request.
* @param {number} [options.timeout=60000] Set a timeout (in milliseconds) for the request.
* @param {callback} [callback] The second argument, <code>data</code>, is the returned server data.
* @example
* <caption>Direct Credit Card Tokenization</caption>
* var createClient = require('braintree-web/client').create;
*
* createClient({
* authorization: CLIENT_AUTHORIZATION
* }, function (createErr, clientInstance) {
* var form = document.getElementById('my-form-id');
* var data = {
* creditCard: {
* number: form['cc-number'].value,
* cvv: form['cc-cvv'].value,
* expirationDate: form['cc-expiration-date'].value,
* billingAddress: {
* postalCode: form['cc-postal-code'].value
* },
* options: {
* validate: false
* }
* }
* };
*
* // Warning: For a merchant to be eligible for the easiest level of PCI compliance (SAQ A),
* // payment fields cannot be hosted on your checkout page.
* // For an alternative to the following, use Hosted Fields.
* clientInstance.request({
* endpoint: 'payment_methods/credit_cards',
* method: 'post',
* data: data
* }, function (requestErr, response) {
* // More detailed example of handling API errors: https://codepen.io/braintree/pen/MbwjdM
* if (requestErr) { throw new Error(requestErr); }
*
* console.log('Got nonce:', response.creditCards[0].nonce);
* });
* });
* @example
* <caption>Tokenizing Fields for AVS Checks</caption>
* var createClient = require('braintree-web/client').create;
*
* createClient({
* authorization: CLIENT_AUTHORIZATION
* }, function (createErr, clientInstance) {
* var form = document.getElementById('my-form-id');
* var data = {
* creditCard: {
* number: form['cc-number'].value,
* cvv: form['cc-cvv'].value,
* expirationDate: form['cc-date'].value,
* // The billing address can be checked with AVS rules.
* // See: https://articles.braintreepayments.com/support/guides/fraud-tools/basic/avs-cvv-rules
* billingAddress: {
* postalCode: form['cc-postal-code'].value,
* streetAddress: form['cc-street-address'].value,
* countryName: form['cc-country-name'].value,
* countryCodeAlpha2: form['cc-country-alpha2'].value,
* countryCodeAlpha3: form['cc-country-alpha3'].value,
* countryCodeNumeric: form['cc-country-numeric'].value
* },
* options: {
* validate: false
* }
* }
* };
*
* // Warning: For a merchant to be eligible for the easiest level of PCI compliance (SAQ A),
* // payment fields cannot be hosted on your checkout page.
* // For an alternative to the following, use Hosted Fields.
* clientInstance.request({
* endpoint: 'payment_methods/credit_cards',
* method: 'post',
* data: data
* }, function (requestErr, response) {
* // More detailed example of handling API errors: https://codepen.io/braintree/pen/MbwjdM
* if (requestErr) { throw new Error(requestErr); }
*
* console.log('Got nonce:', response.creditCards[0].nonce);
* });
* });
* @returns {(Promise|void)} Returns a promise if no callback is provided.
*/
Client.prototype.request = function (options, callback) {
var self = this;
var requestPromise = new Promise(function (resolve, reject) {
var optionName, api, baseUrl, requestOptions;
var shouldCollectData = Boolean(
options.endpoint === "payment_methods/credit_cards" &&
self.getConfiguration().gatewayConfiguration.creditCards
.collectDeviceData
);
if (options.api !== "graphQLApi") {
if (!options.method) {
optionName = "options.method";
} else if (!options.endpoint) {
optionName = "options.endpoint";
}
}
if (optionName) {
throw new BraintreeError({
type: errors.CLIENT_OPTION_REQUIRED.type,
code: errors.CLIENT_OPTION_REQUIRED.code,
message: optionName + " is required when making a request.",
});
}
if ("api" in options) {
api = options.api;
} else {
api = "clientApi";
}
requestOptions = {
method: options.method,
graphQL: self._graphQL,
timeout: options.timeout,
metadata: self._configuration.analyticsMetadata,
};
if (api === "clientApi") {
baseUrl = self._clientApiBaseUrl;
requestOptions.data = metadata.addMetadata(
self._configuration,
options.data
);
} else if (api === "graphQLApi") {
baseUrl =
GRAPHQL_URLS[self._configuration.gatewayConfiguration.environment];
options.endpoint = "";
requestOptions.method = "post";
requestOptions.data = assign(
{
clientSdkMetadata: {
platform: self._configuration.analyticsMetadata.platform,
source: self._configuration.analyticsMetadata.source,
integration: self._configuration.analyticsMetadata.integration,
sessionId: self._configuration.analyticsMetadata.sessionId,
version: VERSION,
},
},
options.data
);
requestOptions.headers = getAuthorizationHeadersForGraphQL(
self._configuration
);
} else {
throw new BraintreeError({
type: errors.CLIENT_OPTION_INVALID.type,
code: errors.CLIENT_OPTION_INVALID.code,
message: "options.api is invalid.",
});
}
requestOptions.url = baseUrl + options.endpoint;
requestOptions.sendAnalyticsEvent = function (kind, extraFields) {
if (extraFields) {
analytics.sendEventPlus(self, kind, extraFields);
} else {
analytics.sendEvent(self, kind);
}
};
self._request(requestOptions, function (err, data, status) {
var resolvedData, requestError;
requestError = formatRequestError(status, err);
if (requestError) {
reject(requestError);
return;
}
if (api === "graphQLApi" && data.errors) {
reject(
convertToBraintreeError(data.errors, {
type: errors.CLIENT_GRAPHQL_REQUEST_ERROR.type,
code: errors.CLIENT_GRAPHQL_REQUEST_ERROR.code,
message: errors.CLIENT_GRAPHQL_REQUEST_ERROR.message,
})
);
return;
}
resolvedData = assign({ _httpStatus: status }, data);
if (
shouldCollectData &&
resolvedData.creditCards &&
resolvedData.creditCards.length > 0
) {
self._findOrCreateFraudnetJSON(resolvedData.creditCards[0].nonce);
assets.loadScript({
src: FRAUDNET_URL,
forceScriptReload: true,
});
}
resolve(resolvedData);
});
});
if (typeof callback === "function") {
callback = once(deferred(callback));
requestPromise
.then(function (response) {
callback(null, response, response._httpStatus);
})
.catch(function (err) {
var status = err && err.details && err.details.httpStatus;
callback(err, null, status);
});
return;
}
return requestPromise; // eslint-disable-line consistent-return
};
// eslint-disable-next-line consistent-return
function formatRequestError(status, err) {
var requestError;
if (status === -1) {
requestError = new BraintreeError(errors.CLIENT_REQUEST_TIMEOUT);
} else if (status === 401) {
requestError = new BraintreeError(errors.CLIENT_AUTHORIZATION_INVALID);
} else if (status === 403) {
requestError = new BraintreeError(errors.CLIENT_AUTHORIZATION_INSUFFICIENT);
} else if (status === 429) {
requestError = new BraintreeError(errors.CLIENT_RATE_LIMITED);
} else if (status >= 500) {
requestError = new BraintreeError(errors.CLIENT_GATEWAY_NETWORK);
} else if (status < 200 || status >= 400) {
requestError = convertToBraintreeError(err, {
type: errors.CLIENT_REQUEST_ERROR.type,
code: errors.CLIENT_REQUEST_ERROR.code,
message: errors.CLIENT_REQUEST_ERROR.message,
});
}
if (requestError) {
requestError.details = requestError.details || {};
requestError.details.httpStatus = status;
return requestError;
}
}
Client.prototype.toJSON = function () {
return this.getConfiguration();
};
/**
* Returns the Client version.
* @public
* @returns {String} The created client's version.
* @example
* var createClient = require('braintree-web/client').create;
*
* createClient({
* authorization: CLIENT_AUTHORIZATION
* }, function (createErr, clientInstance) {
* console.log(clientInstance.getVersion()); // Ex: 1.0.0
* });
*/
Client.prototype.getVersion = function () {
return VERSION;
};
/**
* Cleanly tear down anything set up by {@link module:braintree-web/client.create|create}.
* @public
* @param {callback} [callback] Called once teardown is complete. No data is returned if teardown completes successfully.
* @example
* clientInstance.teardown();
* @example <caption>With callback</caption>
* clientInstance.teardown(function () {
* // teardown is complete
* });
* @returns {(Promise|void)} Returns a promise if no callback is provided.
*/
Client.prototype.teardown = wrapPromise(function () {
var self = this;
delete cachedClients[self.getConfiguration().authorization];
convertMethodsToError(self, methods(Client.prototype));
return Promise.resolve();
});
function getAuthorizationHeadersForGraphQL(configuration) {
var token =
configuration.authorizationFingerprint || configuration.authorization;
return {
Authorization: "Bearer " + token,
"Braintree-Version": BRAINTREE_VERSION,
};
}
module.exports = Client;
},{"../lib/add-metadata":29,"../lib/analytics":30,"../lib/assets":31,"../lib/assign":32,"../lib/braintree-error":33,"../lib/constants":34,"../lib/convert-methods-to-error":35,"../lib/convert-to-braintree-error":36,"../lib/create-authorization-data":37,"../lib/deferred":38,"../lib/is-verified-domain":43,"../lib/methods":45,"../lib/once":46,"./constants":10,"./errors":11,"./get-configuration":12,"./request":25,"./request/graphql":23,"@braintree/wrap-promise":7}],10:[function(_dereq_,module,exports){
"use strict";
module.exports = {
BRAINTREE_VERSION: "2018-05-10",
};
},{}],11:[function(_dereq_,module,exports){
"use strict";
/**
* @name BraintreeError.Client - Internal Error Codes
* @ignore
* @description These codes should never be experienced by the merchant directly.
* @property {MERCHANT} CLIENT_GATEWAY_CONFIGURATION_INVALID_DOMAIN An error to prevent client creation for domains that are not allowed in the JS.
* @property {INTERNAL} CLIENT_MISSING_GATEWAY_CONFIGURATION Occurs when the client is created without a gateway configuration. Should never happen.
*/
/**
* @name BraintreeError.Client - Create Error Codes
* @description Errors that may occur when [creating the client](./module-braintree-web_client.html#.create)
* @property {MERCHANT} CLIENT_INVALID_AUTHORIZATION Occurs when client token cannot be parsed.
*/
/**
* @name BraintreeError.Client - Request Error Codes
* @description Errors that may occur when [using the request method](./Client.html#request)
* @property {MERCHANT} CLIENT_OPTION_REQUIRED An option required in the request method was not provided. Usually `options.method` or `options.endpoint`
* @property {MERCHANT} CLIENT_OPTION_INVALID The request option provided is invalid.
* @property {MERCHANT} CLIENT_GATEWAY_NETWORK The Braintree gateway could not be contacted.
* @property {NETWORK} CLIENT_REQUEST_TIMEOUT The request took too long to complete and timed out.
* @property {NETWORK} CLIENT_REQUEST_ERROR The response from a request had status 400 or greater.
* @property {NETWORK} CLIENT_GRAPHQL_REQUEST_ERROR The response from a request to GraphQL contained an error.
* @property {MERCHANT} CLIENT_RATE_LIMITED The response from a request had a status of 429, indicating rate limiting.
* @property {MERCHANT} CLIENT_AUTHORIZATION_INSUFFICIENT The user associated with the client token or tokenization key does not have permissions to make the request.
* @property {MERCHANT} CLIENT_AUTHORIZATION_INVALID The provided authorization could not be found. Either the client token has expired and a new client token must be generated or the tokenization key used is set to be inactive or has been deleted.
*/
var BraintreeError = _dereq_("../lib/braintree-error");
module.exports = {
CLIENT_GATEWAY_CONFIGURATION_INVALID_DOMAIN: {
type: BraintreeError.types.MERCHANT,
code: "CLIENT_GATEWAY_CONFIGURATION_INVALID_DOMAIN",
},
CLIENT_OPTION_REQUIRED: {
type: BraintreeError.types.MERCHANT,
code: "CLIENT_OPTION_REQUIRED",
},
CLIENT_OPTION_INVALID: {
type: BraintreeError.types.MERCHANT,
code: "CLIENT_OPTION_INVALID",
},
CLIENT_MISSING_GATEWAY_CONFIGURATION: {
type: BraintreeError.types.INTERNAL,
code: "CLIENT_MISSING_GATEWAY_CONFIGURATION",
message: "Missing gatewayConfiguration.",
},
CLIENT_INVALID_AUTHORIZATION: {
type: BraintreeError.types.MERCHANT,
code: "CLIENT_INVALID_AUTHORIZATION",
message:
"Authorization is invalid. Make sure your client token or tokenization key is valid.",
},
CLIENT_GATEWAY_NETWORK: {
type: BraintreeError.types.NETWORK,
code: "CLIENT_GATEWAY_NETWORK",
message: "Cannot contact the gateway at this time.",
},
CLIENT_REQUEST_TIMEOUT: {
type: BraintreeError.types.NETWORK,
code: "CLIENT_REQUEST_TIMEOUT",
message: "Request timed out waiting for a reply.",
},
CLIENT_REQUEST_ERROR: {
type: BraintreeError.types.NETWORK,
code: "CLIENT_REQUEST_ERROR",
message: "There was a problem with your request.",
},
CLIENT_GRAPHQL_REQUEST_ERROR: {
type: BraintreeError.types.NETWORK,
code: "CLIENT_GRAPHQL_REQUEST_ERROR",
message: "There was a problem with your request.",
},
CLIENT_RATE_LIMITED: {
type: BraintreeError.types.MERCHANT,
code: "CLIENT_RATE_LIMITED",
message: "You are being rate-limited; please try again in a few minutes.",
},
CLIENT_AUTHORIZATION_INSUFFICIENT: {
type: BraintreeError.types.MERCHANT,
code: "CLIENT_AUTHORIZATION_INSUFFICIENT",
message: "The authorization used has insufficient privileges.",
},
CLIENT_AUTHORIZATION_INVALID: {
type: BraintreeError.types.MERCHANT,
code: "CLIENT_AUTHORIZATION_INVALID",
message:
"Either the client token has expired and a new one should be generated or the tokenization key has been deactivated or deleted.",
},
};
},{"../lib/braintree-error":33}],12:[function(_dereq_,module,exports){
"use strict";
var BraintreeError = _dereq_("../lib/braintree-error");
var wrapPromise = _dereq_("@braintree/wrap-promise");
var request = _dereq_("./request");
var uuid = _dereq_("@braintree/uuid");
var constants = _dereq_("../lib/constants");
var errors = _dereq_("./errors");
var GraphQL = _dereq_("./request/graphql");
var GRAPHQL_URLS = _dereq_("../lib/constants").GRAPHQL_URLS;
var isDateStringBeforeOrOn = _dereq_("../lib/is-date-string-before-or-on");
var BRAINTREE_VERSION = _dereq_("./constants").BRAINTREE_VERSION;
function getConfiguration(authData, inputSessionId) {
return new Promise(function (resolve, reject) {
var configuration, attrs, configUrl, reqOptions;
var sessionId = inputSessionId || uuid();
var analyticsMetadata = {
merchantAppId: window.location.host,
platform: constants.PLATFORM,
sdkVersion: constants.VERSION,
source: constants.SOURCE,
// NEXT_MAJOR_VERSION remove one of these to not duplicate data. Target parity with mobile platforms approach.
integration: constants.INTEGRATION,
integrationType: constants.INTEGRATION,
sessionId: sessionId,
};
attrs = authData.attrs;
configUrl = authData.configUrl;
attrs._meta = analyticsMetadata;
attrs.braintreeLibraryVersion = constants.BRAINTREE_LIBRARY_VERSION;
attrs.configVersion = "3";
reqOptions = {
url: configUrl,
method: "GET",
data: attrs,
};
if (attrs.authorizationFingerprint && authData.graphQL) {
if (isDateStringBeforeOrOn(authData.graphQL.date, BRAINTREE_VERSION)) {
reqOptions.graphQL = new GraphQL({
graphQL: {
url: authData.graphQL.url,
features: ["configuration"],
},
});
}
reqOptions.metadata = analyticsMetadata;
} else if (attrs.tokenizationKey) {
reqOptions.graphQL = new GraphQL({
graphQL: {
url: GRAPHQL_URLS[authData.environment],
features: ["configuration"],
},
});
reqOptions.metadata = analyticsMetadata;
}
request(reqOptions, function (err, response, status) {
var errorTemplate;
if (err) {
if (status === 403) {
errorTemplate = errors.CLIENT_AUTHORIZATION_INSUFFICIENT;
} else if (status === 401) {
errorTemplate = errors.CLIENT_AUTHORIZATION_INVALID;
} else {
errorTemplate = errors.CLIENT_GATEWAY_NETWORK;
}
reject(
new BraintreeError({
type: errorTemplate.type,
code: errorTemplate.code,
message: errorTemplate.message,
details: {
originalError: err,
},
})
);
return;
}
configuration = {
authorizationType: attrs.tokenizationKey
? "TOKENIZATION_KEY"
: "CLIENT_TOKEN",
authorizationFingerprint: attrs.authorizationFingerprint,
analyticsMetadata: analyticsMetadata,
gatewayConfiguration: response,
};
resolve(configuration);
});
});
}
module.exports = {
getConfiguration: wrapPromise(getConfiguration),
};
},{"../lib/braintree-error":33,"../lib/constants":34,"../lib/is-date-string-before-or-on":41,"./constants":10,"./errors":11,"./request":25,"./request/graphql":23,"@braintree/uuid":3,"@braintree/wrap-promise":7}],13:[function(_dereq_,module,exports){
"use strict";
var BraintreeError = _dereq_("../lib/braintree-error");
var Client = _der