UNPKG

braintree-web

Version:

A suite of tools for integrating Braintree in the browser

444 lines (401 loc) 14.7 kB
"use strict"; var wrapPromise = require("@braintree/wrap-promise"); var analytics = require("../lib/analytics"); var BraintreeError = require("../lib/braintree-error"); var constants = require("./constants"); var errors = require("./errors"); var hasMissingOption = require("../lib/has-missing-option"); /** * @class * @param {object} options see {@link module:braintree-web/payment-ready.create|payment-ready.create} * @description <strong>Do not use this constructor directly. Use {@link module:braintree-web/payment-ready.create|braintree-web.payment-ready.create} instead.</strong> * @classdesc This class represents a Payment Ready component produced by {@link module:braintree-web/payment-ready.create|braintree-web.payment-ready.create}. Instances provide methods for Payment Ready methods. */ function PaymentReady(options) { this._instantiatedWithClient = Boolean(!options.useDeferredClient); this._client = options.client; this._createPromise = options.createPromise; } /** * Payment Ready Customer data object. * @typedef {object} PaymentReady~Customer * @property {string} [deviceFingerprintId] - Unique device identifier * @property {string} [hashedEmail] - This field must contain a Hex-encoded SHA-256 hashed value of the actual email. * The email address must be sanitized before hashing: * * email address in lowercase * * remove leading and trailing whitespace * @property {string} [hashedPhoneNumber] - This field must contain a Hex-encoded SHA-256 hashed value of the phone number; and the phone number must be in E.164 format. * * E.164 format example 1: '+12345551234' where country code is '1', area code is '234' and subscriber number is '555-1234' * * E.164 format example 2: '+6129876543X' where country code is '61', area code is '2' and subscriber number is '9876 543X' * @property {boolean} [paypalAppInstalled] - A flag indicating if customer device has paypal application installed. * @property {boolean} [venmoAppInstalled] - A flag indicating if customer device has venmo applicaition installed. * @property {string} [userAgent] - The user agent string of customer browser describing application, OS, vendor, and version of requesting user agent. */ /** * Creates a customer session using the Payment Ready API. * * @public * @param {object} options - The options for creating a customer session. * @param {PaymentReady~Customer} options.customer - The customer data object. At minimum, this object should contain either `hashedEmail` or `hashedPhoneNumber` field. * @param {string} [options.sessionId] - Optionally, specify a unique identifier for this customer session. It must be at least 36 characters, no more than 100 characters and match the following regex: `[A-Za-z0-9-_.]+`. * @returns {Promise<object>} A promise that resolves with the created customer session data. * @throws {BraintreeError} If the specified `sessionId` has previously been used or has an invalid format. * @example * paymentReadyInstance.createCustomerSession({ * customer: { * ... // Customer data * } * }).then(function (sessionData) { * // Handle the session data * console.log(sessionData.sessionId); * }).catch(function (err) { * // Handle errors * console.error(err); * }); */ PaymentReady.prototype.createCustomerSession = function (options) { var self = this; if ( !options || hasMissingOption(options, constants.REQUIRED_OPTIONS_CREATE_SESSION) ) { analytics.sendEvent( self._client, "payment-ready.create-customer-session.missing-options" ); return Promise.reject( new BraintreeError(errors.PAYMENT_READY_MISSING_REQUIRED_OPTION) ); } return self._client .request({ api: "graphQLApi", data: { query: constants.CREATE_PAYMENT_READY_SESSION_QUERY, variables: { input: { sessionId: options.sessionId, customer: options.customer, }, }, }, }) .then(function (response) { analytics.sendEvent( self._client, "payment-ready.create-customer-session.succeeded" ); return response.data.createCustomerSession; }) .catch(function (err) { analytics.sendEvent( self._client, "payment-ready.create-customer-session.failed" ); return Promise.reject( new BraintreeError({ type: err.type || errors.PAYMENT_READY_CREATE_SESSION_ERROR.type, code: err.code || errors.PAYMENT_READY_CREATE_SESSION_ERROR.code, message: err.message || errors.PAYMENT_READY_CREATE_SESSION_ERROR.message, details: { originalError: err }, }) ); }); }; /** * Updates an existing customer session using the Payment Ready API. * * @public * @param {object} options - The options for updating a customer session. * @param {string} options.sessionId - The ID of the session to update. * @param {PaymentReady~Customer} options.customer - The updated customer data object. * @returns {Promise<object>} A promise that resolves with the updated customer session data. * @example * paymentReadyInstance.updateCustomerSession({ * sessionId: "session-abc-123", * customer: { * ... // Updated customer data * } * }).then(function (sessionData) { * // Handle the updated session data * console.log(sessionData.sessionId); * }).catch(function (err) { * // Handle errors * console.error(err); * }); */ PaymentReady.prototype.updateCustomerSession = function (options) { var self = this; if ( !options || hasMissingOption(options, constants.REQUIRED_OPTIONS_UPDATE_SESSION) ) { analytics.sendEvent( self._client, "payment-ready.update-customer-session.missing-options" ); return Promise.reject( new BraintreeError(errors.PAYMENT_READY_MISSING_REQUIRED_OPTION) ); } return self._client .request({ api: "graphQLApi", data: { query: constants.UPDATE_PAYMENT_READY_SESSION_QUERY, variables: { input: { sessionId: options.sessionId, customer: options.customer, }, }, }, }) .then(function (response) { analytics.sendEvent( self._client, "payment-ready.update-customer-session.succeeded" ); return response.data.updateCustomerSession; }) .catch(function (err) { analytics.sendEvent( self._client, "payment-ready.update-customer-session.failed" ); return Promise.reject( new BraintreeError({ type: err.type || errors.PAYMENT_READY_UPDATE_SESSION_ERROR.type, code: err.code || errors.PAYMENT_READY_UPDATE_SESSION_ERROR.code, message: err.message || errors.PAYMENT_READY_UPDATE_SESSION_ERROR.message, details: { originalError: err }, }) ); }); }; /** * Generates customer payment recommendations using the Payment Ready API. * * @public * @param {object} options - The options for generating customer recommendations. * @param {string} options.sessionId - The ID of the session to get recommendations for. * @param {PaymentReady~Customer} [options.customer] - Optional customer data object. * @param {string} [options.domain] - Optional domain parameter. * @param {Array} [options.purchaseUnits] - Optional purchase units array. * @returns {Promise<object>} A promise that resolves with the customer recommendations data. * @example * paymentReadyInstance.getCustomerRecommendations({ * sessionId: "session-abc-123", * customer: { * // Optional customer data * }, * domain: "example.com", * purchaseUnits: [ * // Purchase unit data * ] * }).then(function (recommendations) { * // Handle the recommendations data * console.log(recommendations.paymentRecommendations); * }).catch(function (err) { * // Handle errors * console.error(err); * }); */ PaymentReady.prototype.getCustomerRecommendations = function (options) { var self = this; if ( !options || hasMissingOption(options, constants.REQUIRED_OPTIONS_GET_RECOMMENDATIONS) ) { analytics.sendEvent( self._client, "payment-ready.get-customer-recommendations.missing-options" ); return Promise.reject( new BraintreeError(errors.PAYMENT_READY_NO_SESSION_ID) ); } return self._client .request({ api: "graphQLApi", data: { query: constants.GENERATE_CUSTOMER_RECOMMENDATIONS_QUERY, variables: { input: { sessionId: options.sessionId, customer: options.customer, domain: options.domain, purchaseUnits: options.purchaseUnits, }, }, }, }) .then(function (response) { analytics.sendEvent( self._client, "payment-ready.get-customer-recommendations.succeeded" ); return response.data.generateCustomerRecommendations; }) .catch(function (err) { analytics.sendEvent( self._client, "payment-ready.get-customer-recommendations.failed" ); return Promise.reject( new BraintreeError({ type: err.type || errors.PAYMENT_READY_GET_RECOMMENDATIONS_ERROR.type, code: err.code || errors.PAYMENT_READY_GET_RECOMMENDATIONS_ERROR.code, message: err.message || errors.PAYMENT_READY_GET_RECOMMENDATIONS_ERROR.message, details: { originalError: err }, }) ); }); }; /** * Call this method when a payment button has been successfully displayed to the buyer. * This method sends analytics to help improve the Shopper Insights feature experience. * * @public * @param {object} options - The options for the event. * @param {string} options.buttonType Type of button presented to the user. * @param {string} options.paymentReadySessionId - The id of paymentReady session created with createCustomerSession method. * @param {object} [options.presentmentDetails] Details of presentment. Includes experimentType, pageType, and buttonOrder. * @param {PaymentReady~EXPERIMENT_TYPE} [options.presentmentDetails.experimentType] Experiment type being used. * @param {PaymentReady~PAGE_TYPE} [options.presentmentDetails.pageType] Page type where the SDK is being used. * @param {PaymentReady~BUTTON_ORDER} [options.presentmentDetails.buttonOrder] Where in the list of buttons this button was presented. * * @example * paymentReadyInstance.sendPresentedEvent({ * buttonType: "paypal", * paymentReadySessionId: "SESSION_ID_123", * presentmentDetails: { * experimentType: "control", * pageType: "checkout", * buttonOrder: "second" * }); * @returns {void} This method does not return any value. */ PaymentReady.prototype.sendPresentedEvent = function (options) { var experimentType = options.presentmentDetails ? options.presentmentDetails.experimentType : null; var pageType = options.presentmentDetails ? options.presentmentDetails.pageType : null; var buttonOrder = options.presentmentDetails ? options.presentmentDetails.buttonOrder : null; if ( !options.paymentReadySessionId || typeof options.paymentReadySessionId !== "string" ) { // eslint-disable-next-line no-console console.warn( "sendPresentedEvent: paymentReadySessionId is missing or invalid" ); return; } if (!options.buttonType || typeof options.buttonType !== "string") { // eslint-disable-next-line no-console console.warn("sendPresentedEvent: buttonType is missing"); return; } var buttonSelected = options.buttonType; var presentAnalyticFields; if (!Object.values(constants.BUTTON_TYPE).includes(buttonSelected)) { buttonSelected = constants.BUTTON_TYPE.OTHER; } if ( buttonOrder && !Object.values(constants.BUTTON_ORDER).includes(buttonOrder) ) { buttonOrder = constants.BUTTON_ORDER.OTHER; } if ( experimentType && !Object.values(constants.EXPERIMENT_TYPE).includes(experimentType) ) { experimentType = constants.EXPERIMENT_TYPE.TEST; } if (pageType && !Object.values(constants.PAGE_TYPE).includes(pageType)) { pageType = constants.PAGE_TYPE.OTHER; } presentAnalyticFields = buildPresentmentAnalyticFields({ buttonOrder: buttonOrder, buttonType: buttonSelected, experimentType: experimentType, pageType: pageType, sessionId: options.paymentReadySessionId, }); analytics.sendEventPlus( this._client, constants.EVENT_BUTTON_PRESENTED, presentAnalyticFields ); }; function buildPresentmentAnalyticFields(options) { var pkg = { // eslint-disable-next-line camelcase button_type: options.buttonType, // eslint-disable-next-line camelcase payment_ready_session_id: options.sessionId, }; if (options.buttonOrder) { pkg["payment_ready_button_order"] = options.buttonOrder; } if (options.experimentType) { pkg["payment_ready_experiment_type"] = options.experimentType; } if (options.pageType) { pkg["payment_ready_page_type"] = options.pageType; } return pkg; } /** * Use PayPal Payment Ready to optimize your checkout experience by sending analytics events. * * @public * @param {object} options - The options for the event * @param {string} options.paymentReadySessionId - The ID of the session to update. * @param {PaymentReady~BUTTON_TYPE} options.buttonType Type of button presented to the user. * @returns {void} * @example * <caption>Sending analytics event</caption> * paymentReadyInstance.sendSelectedEvent({ * paymentReadySessionId: "session-abc-123", * buttonType: "paypal" * }); */ PaymentReady.prototype.sendSelectedEvent = function (options) { if ( !options.paymentReadySessionId || typeof options.paymentReadySessionId !== "string" ) { // eslint-disable-next-line no-console console.warn( "sendSelectedEvent: paymentReadySessionId is missing or invalid" ); return; } if (!options.buttonType || typeof options.buttonType !== "string") { // eslint-disable-next-line no-console console.warn("sendSelectedEvent: buttonType is missing"); return; } var buttonSelected = options.buttonType; if (!Object.values(constants.BUTTON_TYPE).includes(buttonSelected)) { buttonSelected = constants.BUTTON_TYPE.OTHER; } analytics.sendEventPlus(this._client, constants.EVENT_BUTTON_SELECTED, { // eslint-disable-next-line camelcase payment_ready_session_id: options.paymentReadySessionId, // eslint-disable-next-line camelcase button_type: buttonSelected, }); }; module.exports = wrapPromise.wrapPrototype(PaymentReady);