sepa/external/sepa.js

"use strict";

var wrapPromise = require("@braintree/wrap-promise");
var BraintreeError = require("../../lib/braintree-error");
var sepaErrors = require("../shared/errors");
var Promise = require("../../lib/promise");
var constants = require("../shared/constants");
var mandates = require("./mandate");
var hasMissingOption = require("../shared/has-missing-option");
var analytics = require("../../lib/analytics");
var VERSION = process.env.npm_package_version;

/**
 * @class
 * @param {object} options see {@link module:braintree-web/sepa.create|sepa.create}
 * @description <strong>Do not use this constructor directly. Use {@link module:braintree-web/sepa.create|braintree-web.sepa.create} instead.</strong>
 * @classdesc This class represents a SEPA component produced by {@link module:braintree-web/sepa.create|braintree-web.sepa.create}. Instances provide methods for tokenizing SEPA payments.
 */
function SEPA(options) {
  var getConfiguration = options.client.getConfiguration();

  this._client = options.client;
  this._assetsUrl =
    getConfiguration.gatewayConfiguration.assetsUrl + "/web/" + VERSION;
  this._isDebug = getConfiguration.isDebug;
  this._returnUrl = this._assetsUrl + "/html/redirect-frame.html?success=1";
  this._cancelUrl = this._assetsUrl + "/html/redirect-frame.html?cancel=1";

  analytics.sendEvent(this._client, "sepa.component.initialized");
}

/**
 * SEPA tokenize payload.
 * @typedef SEPA~tokenizePayload
 * @property {string} nonce The payment nonce.
 * @property {string} ibanLastFour The last four digits of the customer's IBAN.
 * @property {string} mandateType The specified mandateType used.
 * @property {string} customerId The provided customer id.
 */

/**
 * @static
 * @public
 * @function tokenize
 * @param {object} options All options for intiating the SEPA payment flow.
 * @param {string} [options.accountHolderName] The account holder name.
 * @param {string} [options.customerId] The customer's id.
 * @param {string} [options.iban] The customer's International Bank Account Number.
 * @param {string} [options.mandateType] Specify ONE_OFF or RECURRENT payment.
 * @param {string} [options.countryCode] The customer's country code.
 * @param {string} [options.merchantAccountId] The merchant's account id.
 * @param {callback} [callback] The first argument is an error object, where the second is a {@link SEPA~tokenizePayload|tokenizePayload}
 * @returns {(Promise<tokenizePayload|error>)} Returns a promise if no callback is provided.
 *
 * @example
 * button.addEventListener('click', function () {
 *   var tokenizeInputs = {
 *     accountHolderName: "some-accnt-holder-name",
 *     customerId: "a-customer-id",
 *     iban: "a-full-iban",
 *     mandateType: "ONE_OFF",
 *     countryCode: "LI",
 *     merchantAccountId: "a-merchant-account-id"
 *   }
 *   sepaInstance.tokenize(tokenizeInputs).then(function (payload) {
 *      // Submit payload.nonce to your server
 *   }).catch(function(tokenizationErr) {
 *     // Handle errors in the flow
 *   })
 * })
 */

SEPA.prototype.tokenize = function (options) {
  var self = this;

  if (!options || hasMissingOption(options, constants.REQUIRED_OPTIONS)) {
    analytics.sendEvent(self._client, "sepa.input-validation.missing-options");

    return Promise.reject(
      new BraintreeError(sepaErrors.SEPA_TOKENIZE_MISSING_REQUIRED_OPTION)
    );
  }

  if (!constants.MANDATE_TYPE_ENUM.includes(options.mandateType)) {
    analytics.sendEvent(self._client, "sepa.input-validation.invalid-mandate");

    return Promise.reject(
      new BraintreeError(sepaErrors.SEPA_INVALID_MANDATE_TYPE)
    );
  }

  return mandates
    .createMandate(self._client, {
      accountHolderName: options.accountHolderName,
      customerId: options.customerId,
      iban: options.iban,
      mandateType: options.mandateType,
      countryCode: options.countryCode,
      merchantAccountId: options.merchantAccountId,
      cancelUrl: self._cancelUrl,
      returnUrl: self._returnUrl,
    })
    .then(function (mandateResponse) {
      analytics.sendEvent(self._client, "sepa.create-mandate.success");
      options.last4 = mandateResponse.last4;
      options.bankReferenceToken = mandateResponse.bankReferenceToken;

      return mandates.openPopup(self._client, {
        approvalUrl: mandateResponse.approvalUrl,
        assetsUrl: self._assetsUrl,
      });
    })
    .then(function () {
      analytics.sendEvent(self._client, "sepa.mandate.approved");

      return mandates.handleApproval(self._client, {
        bankReferenceToken: options.bankReferenceToken,
        last4: options.last4,
        customerId: options.customerId,
        mandateType: options.mandateType,
        merchantAccountId: options.merchantAccountId,
      });
    })
    .then(function (approval) {
      analytics.sendEvent(self._client, "sepa.tokenization.success");

      return Promise.resolve(approval);
    })
    .catch(function (err) {
      analytics.sendEvent(self._client, "sepa." + err.details + ".failed");

      return Promise.reject(err);
    });
};

module.exports = wrapPromise.wrapPrototype(SEPA);