UNPKG

braintree-web

Version:

A suite of tools for integrating Braintree in the browser

408 lines (407 loc) 13.7 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function (t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __importDefault = (this && this.__importDefault) || function (mod) { return mod && mod.__esModule ? mod : { default: mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var framebus_1 = __importDefault(require("framebus")); var iframer_1 = __importDefault(require("@braintree/iframer")); var uuid_1 = __importDefault(require("@braintree/uuid")); var events_1 = require("../shared/events"); var queries_1 = require("./queries"); var VENMO_DESKTOP_POLLING_INTERVAL = 1000; // 1 second var VISUAL_DELAY_BEFORE_SIGNALLING_COMPLETION = 2000; // 2 seconds var VenmoDesktop = /** @class */ (function () { function VenmoDesktop(options) { this.isHidden = true; this.env = options.environment; this.id = uuid_1.default(); this.profileId = options.profileId; this.displayName = options.displayName; this.paymentMethodUsage = options.paymentMethodUsage; this.shouldUseLegacyQRCodeMutation = !this.paymentMethodUsage; var frameUrl = options.url + "#" + this.env + "_" + this.id; this.bus = new framebus_1.default({ channel: this.id, verifyDomain: options.verifyDomain, targetFrames: [], }); this.apiRequest = options.apiRequest; this.sendEvent = options.sendEvent; this.Promise = options.Promise; this.alertBox = document.createElement("div"); this.alertBox.setAttribute("data-venmo-desktop-id", this.id); this.alertBox.setAttribute("role", "alert"); this.alertBox.style.position = "fixed"; this.alertBox.style.display = "none"; this.alertBox.style.height = "1px"; this.alertBox.style.width = "1px"; this.alertBox.style.overflow = "hidden"; this.alertBox.style.zIndex = "0"; this.iframe = iframer_1.default({ src: frameUrl, name: "venmo-desktop-iframe", style: { display: "none", position: "fixed", top: "0", bottom: "0", right: "0", left: "0", height: "100%", width: "100%", zIndex: "9999999", }, title: "Venmo Desktop", }); this.bus.addTargetFrame(this.iframe); } VenmoDesktop.prototype.initialize = function () { var _this = this; return new this.Promise(function (resolve) { _this.bus.on(events_1.VENMO_DESKTOP_IFRAME_READY, function () { resolve(_this); }); _this.bus.on(events_1.VENMO_DESKTOP_REQUEST_NEW_QR_CODE, function () { _this.sendEvent("venmo.tokenize.desktop.restarted-from-error-view"); _this.startPolling(); }); document.body.appendChild(_this.iframe); document.body.appendChild(_this.alertBox); }); }; VenmoDesktop.prototype.launchDesktopFlow = function () { var _this = this; this.isHidden = false; var promise = new this.Promise(function (resolve, reject) { _this.launchDesktopPromiseRejectFunction = reject; var removeListeners = function () { /* eslint-disable @typescript-eslint/no-use-before-define */ _this.bus.off( events_1.VENMO_DESKTOP_CUSTOMER_CANCELED, customerCancelledHandler ); _this.bus.off( events_1.VENMO_DESKTOP_UNKNOWN_ERROR, unknownErrorHandler ); /* eslint-enable @typescript-eslint/no-use-before-define */ }; var unknownErrorHandler = function (err) { removeListeners(); _this.sendEvent("venmo.tokenize.desktop.unknown-error"); reject({ allowUIToHandleError: false, reason: "UNKNOWN_ERROR", err: err, }); }; var customerCancelledHandler = function () { removeListeners(); _this.updateVenmoDesktopPaymentContext("CANCELED"); _this.sendEvent( "venmo.tokenize.desktop.status-change.canceled-from-modal" ); reject({ allowUIToHandleError: false, reason: "CUSTOMER_CANCELED", }); }; _this.completedHandler = function (payload) { removeListeners(); resolve(payload); }; _this.bus.on( events_1.VENMO_DESKTOP_CUSTOMER_CANCELED, customerCancelledHandler ); _this.bus.on(events_1.VENMO_DESKTOP_UNKNOWN_ERROR, unknownErrorHandler); }); this.iframe.style.display = "block"; this.setAlert("Generating a QR code, get your Venmo app ready"); this.iframe.focus(); this.startPolling(); return promise .then(function (result) { delete _this.venmoContextId; delete _this.launchDesktopPromiseRejectFunction; return result; }) .catch(function (err) { delete _this.venmoContextId; delete _this.launchDesktopPromiseRejectFunction; return _this.Promise.reject(err); }); }; VenmoDesktop.prototype.triggerCompleted = function (result) { var _this = this; if (this.isHidden) { return; } setTimeout(function () { if (_this.completedHandler) { _this.completedHandler(result); } delete _this.completedHandler; }, VISUAL_DELAY_BEFORE_SIGNALLING_COMPLETION); }; VenmoDesktop.prototype.triggerRejected = function (err) { if (this.launchDesktopPromiseRejectFunction) { this.launchDesktopPromiseRejectFunction(err); } }; VenmoDesktop.prototype.hideDesktopFlow = function () { this.setAlert(""); this.iframe.style.display = "none"; this.bus.emit(events_1.VENMO_DESKTOP_CLOSED_FROM_PARENT); this.isHidden = true; }; VenmoDesktop.prototype.displayError = function (message) { if (this.isHidden) { return; } this.bus.emit(events_1.VENMO_DESKTOP_DISPLAY_ERROR, { message: message, }); this.setAlert(message); }; VenmoDesktop.prototype.displayQRCode = function (id, merchantId) { if (this.isHidden) { return; } this.bus.emit(events_1.VENMO_DESKTOP_DISPLAY_QR_CODE, { id: id, merchantId: merchantId, }); this.setAlert("To scan the QR code, open your Venmo app"); }; VenmoDesktop.prototype.authorize = function () { if (this.isHidden) { return; } this.bus.emit(events_1.VENMO_DESKTOP_AUTHORIZE); this.setAlert("Venmo account authorized"); }; VenmoDesktop.prototype.authorizing = function () { if (this.isHidden) { return; } this.bus.emit(events_1.VENMO_DESKTOP_AUTHORIZING); this.setAlert("Authorize on your Venmo app"); }; VenmoDesktop.prototype.startPolling = function () { var _this = this; return this.createVenmoDesktopPaymentContext() .then(function (result) { var expiresIn = new Date(result.expiresAt).getTime() - new Date(result.createdAt).getTime(); var expiredTime = Date.now() + expiresIn; _this.displayQRCode(result.id, result.merchantId); return _this.pollForStatusChange(result.status, expiredTime); }) .then(function (result) { if (!result) { return; } // since we are manually adding a prepended @ sign // we want to make sure that the username does not // start giving us the @ sign up front in the future var username = result.userName || ""; username = "@" + username.replace("@", ""); _this.triggerCompleted({ paymentMethodNonce: result.paymentMethodId, username: username, payerInfo: result.payerInfo, id: _this.venmoContextId || "", }); }) .catch(function (err) { if (err.allowUIToHandleError) { // noop here and let the UI handle the customer error return; } _this.sendEvent("venmo.tokenize.desktop.unhandled-error"); _this.triggerRejected(err); }); }; VenmoDesktop.prototype.pollForStatusChange = function (status, expiredTime) { var _this = this; if (!this.venmoContextId) { return this.Promise.resolve(); } if (Date.now() > expiredTime) { return this.updateVenmoDesktopPaymentContext("EXPIRED").then(function () { _this.displayError("Something went wrong"); _this.sendEvent("venmo.tokenize.desktop.status-change.sdk-timeout"); return _this.Promise.reject({ allowUIToHandleError: true, reason: "TIMEOUT", }); }); } return this.lookupVenmoDesktopPaymentContext().then(function (response) { if (!_this.venmoContextId || !response) { return _this.Promise.resolve(); } var newStatus = response.status; if (newStatus !== status) { status = newStatus; _this.sendEvent( "venmo.tokenize.desktop.status-change." + status.toLowerCase() ); switch (status) { case "CREATED": // noop, no need to do anything here // should never be able to get to this point // but we'll keep it in to enumerate the statuses break; case "EXPIRED": case "FAILED": case "CANCELED": var message = status === "CANCELED" ? "The authorization was canceled" : "Something went wrong"; _this.displayError(message); // these are all terminal states, so we end it here return _this.Promise.reject({ allowUIToHandleError: true, reason: status, }); case "SCANNED": _this.authorizing(); break; case "APPROVED": _this.authorize(); return _this.Promise.resolve(response); default: // any other statuses are irrelevant to the polling // and can just be ignored } } return new _this.Promise(function (resolve, reject) { setTimeout(function () { _this .pollForStatusChange(status, expiredTime) .then(resolve) .catch(reject); }, VENMO_DESKTOP_POLLING_INTERVAL); }); }); }; VenmoDesktop.prototype.teardown = function () { this.bus.teardown(); if (this.iframe.parentNode) { this.iframe.parentNode.removeChild(this.iframe); } if (this.alertBox.parentNode) { this.alertBox.parentNode.removeChild(this.alertBox); } }; VenmoDesktop.prototype.setAlert = function (message) { this.alertBox.style.display = message ? "block" : "none"; this.alertBox.textContent = message; }; VenmoDesktop.prototype.createPaymentContextFromGraphqlLegacyQRCodeMutation = function (intent) { return this.apiRequest(queries_1.LEGACY_CREATE_PAYMENT_CONTEXT_QUERY, { input: { environment: this.env, intent: intent, }, }).then(function (response) { return response .createVenmoQRCodePaymentContext.venmoQRCodePaymentContext; }); }; VenmoDesktop.prototype.createPaymentContextFromGraphQL = function (intent) { var input = { intent: intent, paymentMethodUsage: this.paymentMethodUsage, customerClient: "DESKTOP", }; if (this.profileId) { input.merchantProfileId = this.profileId; } if (this.displayName) { input.displayName = this.displayName; } return this.apiRequest(queries_1.CREATE_PAYMENT_CONTEXT_QUERY, { input: input, }).then(function (response) { return response.createVenmoPaymentContext.venmoPaymentContext; }); }; VenmoDesktop.prototype.createVenmoDesktopPaymentContext = function () { var _this = this; var contextPromise = this.shouldUseLegacyQRCodeMutation ? this.createPaymentContextFromGraphqlLegacyQRCodeMutation("PAY_FROM_APP") : this.createPaymentContextFromGraphQL("PAY_FROM_APP"); return contextPromise.then(function (context) { _this.venmoContextId = context.id; var merchantId = _this.profileId || context.merchantId; return { id: context.id, status: context.status, merchantId: merchantId, createdAt: context.createdAt, expiresAt: context.expiresAt, }; }); }; VenmoDesktop.prototype.updateVenmoDesktopPaymentContext = function ( status, additionalOptions ) { if (additionalOptions === void 0) { additionalOptions = {}; } if (!this.venmoContextId) { return this.Promise.resolve(); } var data = { input: __assign( { id: this.venmoContextId, status: status }, additionalOptions ), }; var query = this.shouldUseLegacyQRCodeMutation ? queries_1.LEGACY_UPDATE_PAYMENT_CONTEXT_QUERY : queries_1.UPDATE_PAYMENT_CONTEXT_QUERY; return this.apiRequest(query, data).then(function () { // noop so we can resolve without any data to match the type }); }; VenmoDesktop.prototype.lookupVenmoDesktopPaymentContext = function () { if (!this.venmoContextId) { return this.Promise.resolve(); } var query = this.shouldUseLegacyQRCodeMutation ? queries_1.LEGACY_VENMO_PAYMENT_CONTEXT_STATUS_QUERY : queries_1.VENMO_PAYMENT_CONTEXT_STATUS_QUERY; return this.apiRequest(query, { id: this.venmoContextId, }).then(function (response) { return response.node; }); }; return VenmoDesktop; })(); exports.default = VenmoDesktop;