UNPKG

braintree-web

Version:

A suite of tools for integrating Braintree in the browser

1,517 lines (1,352 loc) 98.4 kB
(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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PromiseGlobal = void 0; var promise_polyfill_1 = __importDefault(_dereq_("promise-polyfill")); var PromiseGlobal = // eslint-disable-next-line no-undef typeof Promise !== "undefined" ? Promise : promise_polyfill_1.default; exports.PromiseGlobal = PromiseGlobal; },{"promise-polyfill":15}],2:[function(_dereq_,module,exports){ "use strict"; var promise_1 = _dereq_("./lib/promise"); 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.crossorigin) { script.setAttribute("crossorigin", "" + options.crossorigin); } Object.keys(attrs).forEach(function (key) { script.setAttribute("data-" + key, "" + attrs[key]); }); scriptLoadPromise = new promise_1.PromiseGlobal(function (resolve, reject) { script.addEventListener("load", function () { resolve(script); }); script.addEventListener("error", function () { reject(new Error(options.src + " failed to load.")); }); script.addEventListener("abort", function () { reject(new Error(options.src + " has aborted.")); }); container.appendChild(script); }); scriptPromiseCache[stringifiedOptions] = scriptLoadPromise; return scriptLoadPromise; } loadScript.clearCache = function () { scriptPromiseCache = {}; }; module.exports = loadScript; },{"./lib/promise":1}],3:[function(_dereq_,module,exports){ module.exports = _dereq_("./dist/load-script"); },{"./dist/load-script":2}],4:[function(_dereq_,module,exports){ "use strict"; var isIE11 = _dereq_("./is-ie11"); module.exports = function isIE(ua) { ua = ua || window.navigator.userAgent; return ua.indexOf("MSIE") !== -1 || isIE11(ua); }; },{"./is-ie11":5}],5:[function(_dereq_,module,exports){ "use strict"; module.exports = function isIe11(ua) { ua = ua || window.navigator.userAgent; return ua.indexOf("Trident/7") !== -1; }; },{}],6:[function(_dereq_,module,exports){ "use strict"; module.exports = function isIe9(ua) { ua = ua || window.navigator.userAgent; return ua.indexOf("MSIE 9") !== -1; }; },{}],7:[function(_dereq_,module,exports){ module.exports = _dereq_("./dist/is-ie"); },{"./dist/is-ie":4}],8:[function(_dereq_,module,exports){ module.exports = _dereq_("./dist/is-ie9"); },{"./dist/is-ie9":6}],9:[function(_dereq_,module,exports){ "use strict"; var GlobalPromise = (typeof Promise !== "undefined" ? Promise // eslint-disable-line no-undef : null); var ExtendedPromise = /** @class */ (function () { function ExtendedPromise(options) { var _this = this; if (typeof options === "function") { this._promise = new ExtendedPromise.Promise(options); return; } this._promise = new ExtendedPromise.Promise(function (resolve, reject) { _this._resolveFunction = resolve; _this._rejectFunction = reject; }); options = options || {}; this._onResolve = options.onResolve || ExtendedPromise.defaultOnResolve; this._onReject = options.onReject || ExtendedPromise.defaultOnReject; if (ExtendedPromise.shouldCatchExceptions(options)) { this._promise.catch(function () { // prevents unhandled promise rejection warning // in the console for extended promises that // that catch the error in an asynchronous manner }); } this._resetState(); } ExtendedPromise.defaultOnResolve = function (result) { return ExtendedPromise.Promise.resolve(result); }; ExtendedPromise.defaultOnReject = function (err) { return ExtendedPromise.Promise.reject(err); }; ExtendedPromise.setPromise = function (PromiseClass) { ExtendedPromise.Promise = PromiseClass; }; ExtendedPromise.shouldCatchExceptions = function (options) { if (options.hasOwnProperty("suppressUnhandledPromiseMessage")) { return Boolean(options.suppressUnhandledPromiseMessage); } return Boolean(ExtendedPromise.suppressUnhandledPromiseMessage); }; // start Promise methods documented in: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Methods ExtendedPromise.all = function (args) { return ExtendedPromise.Promise.all(args); }; ExtendedPromise.allSettled = function (args) { return ExtendedPromise.Promise.allSettled(args); }; ExtendedPromise.race = function (args) { return ExtendedPromise.Promise.race(args); }; ExtendedPromise.reject = function (arg) { return ExtendedPromise.Promise.reject(arg); }; ExtendedPromise.resolve = function (arg) { return ExtendedPromise.Promise.resolve(arg); }; ExtendedPromise.prototype.then = function () { var _a; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return (_a = this._promise).then.apply(_a, args); }; ExtendedPromise.prototype.catch = function () { var _a; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return (_a = this._promise).catch.apply(_a, args); }; ExtendedPromise.prototype.resolve = function (arg) { var _this = this; if (this.isFulfilled) { return this; } this._setResolved(); ExtendedPromise.Promise.resolve() .then(function () { return _this._onResolve(arg); }) .then(function (argForResolveFunction) { _this._resolveFunction(argForResolveFunction); }) .catch(function (err) { _this._resetState(); _this.reject(err); }); return this; }; ExtendedPromise.prototype.reject = function (arg) { var _this = this; if (this.isFulfilled) { return this; } this._setRejected(); ExtendedPromise.Promise.resolve() .then(function () { return _this._onReject(arg); }) .then(function (result) { _this._setResolved(); _this._resolveFunction(result); }) .catch(function (err) { return _this._rejectFunction(err); }); return this; }; ExtendedPromise.prototype._resetState = function () { this.isFulfilled = false; this.isResolved = false; this.isRejected = false; }; ExtendedPromise.prototype._setResolved = function () { this.isFulfilled = true; this.isResolved = true; this.isRejected = false; }; ExtendedPromise.prototype._setRejected = function () { this.isFulfilled = true; this.isResolved = false; this.isRejected = true; }; ExtendedPromise.Promise = GlobalPromise; return ExtendedPromise; }()); module.exports = ExtendedPromise; },{}],10:[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; },{}],11:[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; },{}],12:[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; },{}],13:[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; },{}],14:[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":11,"./lib/once":12,"./lib/promise-or-callback":13}],15:[function(_dereq_,module,exports){ 'use strict'; /** * @this {Promise} */ function finallyConstructor(callback) { var constructor = this.constructor; return this.then( function(value) { // @ts-ignore return constructor.resolve(callback()).then(function() { return value; }); }, function(reason) { // @ts-ignore return constructor.resolve(callback()).then(function() { // @ts-ignore return constructor.reject(reason); }); } ); } function allSettled(arr) { var P = this; return new P(function(resolve, reject) { if (!(arr && typeof arr.length !== 'undefined')) { return reject( new TypeError( typeof arr + ' ' + arr + ' is not iterable(cannot read property Symbol(Symbol.iterator))' ) ); } var args = Array.prototype.slice.call(arr); if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { if (val && (typeof val === 'object' || typeof val === 'function')) { var then = val.then; if (typeof then === 'function') { then.call( val, function(val) { res(i, val); }, function(e) { args[i] = { status: 'rejected', reason: e }; if (--remaining === 0) { resolve(args); } } ); return; } } args[i] = { status: 'fulfilled', value: val }; if (--remaining === 0) { resolve(args); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); } // Store setTimeout reference so promise-polyfill will be unaffected by // other code modifying setTimeout (like sinon.useFakeTimers()) var setTimeoutFunc = setTimeout; function isArray(x) { return Boolean(x && typeof x.length !== 'undefined'); } function noop() {} // Polyfill for Function.prototype.bind function bind(fn, thisArg) { return function() { fn.apply(thisArg, arguments); }; } /** * @constructor * @param {Function} fn */ function Promise(fn) { if (!(this instanceof Promise)) throw new TypeError('Promises must be constructed via new'); if (typeof fn !== 'function') throw new TypeError('not a function'); /** @type {!number} */ this._state = 0; /** @type {!boolean} */ this._handled = false; /** @type {Promise|undefined} */ this._value = undefined; /** @type {!Array<!Function>} */ this._deferreds = []; doResolve(fn, this); } function handle(self, deferred) { while (self._state === 3) { self = self._value; } if (self._state === 0) { self._deferreds.push(deferred); return; } self._handled = true; Promise._immediateFn(function() { var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { (self._state === 1 ? resolve : reject)(deferred.promise, self._value); return; } var ret; try { ret = cb(self._value); } catch (e) { reject(deferred.promise, e); return; } resolve(deferred.promise, ret); }); } function resolve(self, newValue) { try { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.'); if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { var then = newValue.then; if (newValue instanceof Promise) { self._state = 3; self._value = newValue; finale(self); return; } else if (typeof then === 'function') { doResolve(bind(then, newValue), self); return; } } self._state = 1; self._value = newValue; finale(self); } catch (e) { reject(self, e); } } function reject(self, newValue) { self._state = 2; self._value = newValue; finale(self); } function finale(self) { if (self._state === 2 && self._deferreds.length === 0) { Promise._immediateFn(function() { if (!self._handled) { Promise._unhandledRejectionFn(self._value); } }); } for (var i = 0, len = self._deferreds.length; i < len; i++) { handle(self, self._deferreds[i]); } self._deferreds = null; } /** * @constructor */ function Handler(onFulfilled, onRejected, promise) { this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; this.onRejected = typeof onRejected === 'function' ? onRejected : null; this.promise = promise; } /** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ function doResolve(fn, self) { var done = false; try { fn( function(value) { if (done) return; done = true; resolve(self, value); }, function(reason) { if (done) return; done = true; reject(self, reason); } ); } catch (ex) { if (done) return; done = true; reject(self, ex); } } Promise.prototype['catch'] = function(onRejected) { return this.then(null, onRejected); }; Promise.prototype.then = function(onFulfilled, onRejected) { // @ts-ignore var prom = new this.constructor(noop); handle(this, new Handler(onFulfilled, onRejected, prom)); return prom; }; Promise.prototype['finally'] = finallyConstructor; Promise.all = function(arr) { return new Promise(function(resolve, reject) { if (!isArray(arr)) { return reject(new TypeError('Promise.all accepts an array')); } var args = Array.prototype.slice.call(arr); if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { try { if (val && (typeof val === 'object' || typeof val === 'function')) { var then = val.then; if (typeof then === 'function') { then.call( val, function(val) { res(i, val); }, reject ); return; } } args[i] = val; if (--remaining === 0) { resolve(args); } } catch (ex) { reject(ex); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); }; Promise.allSettled = allSettled; Promise.resolve = function(value) { if (value && typeof value === 'object' && value.constructor === Promise) { return value; } return new Promise(function(resolve) { resolve(value); }); }; Promise.reject = function(value) { return new Promise(function(resolve, reject) { reject(value); }); }; Promise.race = function(arr) { return new Promise(function(resolve, reject) { if (!isArray(arr)) { return reject(new TypeError('Promise.race accepts an array')); } for (var i = 0, len = arr.length; i < len; i++) { Promise.resolve(arr[i]).then(resolve, reject); } }); }; // Use polyfill for setImmediate for performance gains Promise._immediateFn = // @ts-ignore (typeof setImmediate === 'function' && function(fn) { // @ts-ignore setImmediate(fn); }) || function(fn) { setTimeoutFunc(fn, 0); }; Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { if (typeof console !== 'undefined' && console) { console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console } }; module.exports = Promise; },{}],16:[function(_dereq_,module,exports){ "use strict"; var isIe = _dereq_("@braintree/browser-detection/is-ie"); var isIe9 = _dereq_("@braintree/browser-detection/is-ie9"); module.exports = { isIe: isIe, isIe9: isIe9, }; },{"@braintree/browser-detection/is-ie":7,"@braintree/browser-detection/is-ie9":8}],17:[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 addMetadata = _dereq_("../lib/add-metadata"); var Promise = _dereq_("../lib/promise"); 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) { 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); } catch (err) { return Promise.reject( new BraintreeError(errors.CLIENT_INVALID_AUTHORIZATION) ); } promise = getGatewayConfiguration(authData).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; // eslint-disable-line no-invalid-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 = 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) { 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 * }); * @returns {void} */ 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; // eslint-disable-line no-invalid-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":38,"../lib/analytics":39,"../lib/assets":40,"../lib/assign":41,"../lib/braintree-error":42,"../lib/constants":43,"../lib/convert-methods-to-error":44,"../lib/convert-to-braintree-error":45,"../lib/create-authorization-data":46,"../lib/deferred":47,"../lib/is-verified-domain":51,"../lib/methods":53,"../lib/once":54,"../lib/promise":55,"./constants":18,"./errors":19,"./get-configuration":20,"./request":32,"./request/graphql":30,"@braintree/wrap-promise":14}],18:[function(_dereq_,module,exports){ "use strict"; module.exports = { BRAINTREE_VERSION: "2018-05-10", }; },{}],19:[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":42}],20:[function(_dereq_,module,exports){ "use strict"; var BraintreeError = _dereq_("../lib/braintree-error"); var Promise = _dereq_("../lib/promise"); 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) { return new Promise(function (resolve, reject) { var configuration, attrs, configUrl, reqOptions; var sessionId = uuid(); var analyticsMetadata = { merchantAppId: window.location.host, platform: constants.PLATFORM, sdkVersion: constants.VERSION, source: constants.SOURCE, 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; } // NEXT_MAJOR_VERSION // there are various issues with the config endpoint where the values returned // do not match the values from the merchant account id passed into a client token // we need to update the configuration request endpoint to be able to pass the // correct values. The following ones are incorrect // * applePayWeb - definitely supportedNetworks, which compiles all the card // networks from all the merchant accounts instead of providing just the // ones from the specified one. The same is probably true for ios apple pay // Also the merchantidentifier // NEXT_MAJOR_VERSION Allow passing in merchant account id when creating the component // to fetch the config for that merchant account id (particularly helpful when using // a tokenization key for authorization) 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":42,"../lib/constants":43,"../lib/is-date-string-before-or-on":50,"../lib/promise":55,"./constants":18,"./errors":19,"./request":32,"./request/graphql":30,"@braintree/uuid":10,"@braintree/wrap-promise":14}],21:[function(_dereq_,module,exports){ "use strict"; var BraintreeError = _dereq_("../lib/braintree-error"); var Client = _dereq_("./client"); var VERSION = "3.87.0"; var Promise = _dereq_("../lib/promise"); var wrapPromise = _dereq_("@braintree/wrap-promise"); var sharedErrors = _dereq_("../lib/errors"); /** @module braintree-web/client */ /** * @function create * @description This function is the entry point for the <code>braintree.client</code> module. It is used for creating {@link Client} instances that service communication to Braintree servers. * @param {object} options Object containing all {@link Client} options: * @param {string} options.authorization A tokenizationKey or clientToken. * @param {callback} [callback] The second argument, <code>data</code>, is the {@link Client} instance. * @returns {(Promise|void)} Returns a promise if no callback is provided. * @example * var createClient = require('braintree-web/client').create; * * createClient({ * authorization: CLIENT_AUTHORIZATION * }, function (createErr, clientInstance) { * if (createErr) { * if (createErr.code === 'CLIENT_AUTHORIZATION_INVALID') { * // either the client token has expired, and a new one should be generated * // or the tokenization key was deactivated or deleted * } else { * console.log('something went wrong creating the client instance', createErr); * } * return; * } * * // set up other components * }); * @static */ function create(options) { if (!options.authorization) { return Promise.reject( new BraintreeError({ type: sharedErrors.INSTANTIATION_OPTION_REQUIRED.type, code: sharedErrors.INSTANTIATION_OPTION_REQUIRED.code, message: "options.authorization is required when instantiating a client.", }) ); } return Client.initialize(options); } module.exports = { create: wrapPromise(create), /** * @description The current version of the SDK, i.e. `{@pkg version}`. * @type {string} */ VERSION: VERSION, }; },{"../lib/braintree-error":42,"../lib/errors":49,"../lib/promise":55,"./client":17,"@braintree/wrap-promise":14}],22:[function(_dereq_,module,exports){ "use strict"; var querystring = _dereq_("../../lib/querystring"); var assign = _dereq_("../../lib/assign").assign; var prepBody = _dereq_("./prep-body"); var parseBody = _dereq_("./parse-body"); var xhr = _dereq_("./xhr"); var isXHRAvailable = xhr.isAvailable; var GraphQLRequest = _dereq_("./graphql/request"); var DefaultRequest = _dereq_("./default-request"); var MAX_TCP_RETRYCOUNT = 1; var TCP_PRECONNECT_BUG_STATUS_CODE = 408; function requestShouldRetry(status) { return !status || status === TCP_PRECONNECT_BUG_STATUS_CODE; } function graphQLRequestShouldRetryWithClientApi(body) { var errorClass = !body.data && body.errors && body.errors[0] && body.errors[0].extensions && body.errors[0].extensions.errorClass; return errorClass === "UNKNOWN" || errorClass === "INTERNAL"; } function _requestWithRetry(options, tcpRetryCount, cb) { var status, resBody, ajaxRequest, body, method, headers, parsedBody; var url = options.url; var graphQL = options.graphQL; var timeout = options.timeout; var req = xhr.getRequestObject(); var callback = cb; var isGraphQLRequest = Boolean( graphQL && graphQL.isGraphQLRequest(url, options.data) ); options.headers = assign( { "Content-Type": "application/json" }, options.headers ); if (isGraphQLRequest) { ajaxRequest = new GraphQLRequest(options); } else { ajaxRequest = new DefaultRequest(options); } url = ajaxRequest.getUrl(); body = ajaxRequest.getBody(); method = ajaxRequest.getMethod(); headers = ajaxRequest.getHeaders(); if (method === "G