'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

const index = require('./index-cd6fab62.js');
const defaultI18n = require('./default-i18n-072e75d7.js');
require('./index-20b57ec7.js');
const fetch$1 = require('./fetch-a8709225.js');
const index$1 = require('./index-2fb0a3b9.js');
require('./_commonjsHelpers-537d719a.js');
require('./add-query-args-17c551b6.js');

/*!
 * paypal-js v5.0.4 (2022-04-06T22:09:33.042Z)
 * Copyright 2020-present, PayPal, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
function findScript(url, attributes) {
    var currentScript = document.querySelector("script[src=\"".concat(url, "\"]"));
    if (currentScript === null)
        return null;
    var nextScript = createScriptElement(url, attributes);
    // ignore the data-uid-auto attribute that gets auto-assigned to every script tag
    var currentScriptClone = currentScript.cloneNode();
    delete currentScriptClone.dataset.uidAuto;
    // check if the new script has the same number of data attributes
    if (Object.keys(currentScriptClone.dataset).length !==
        Object.keys(nextScript.dataset).length) {
        return null;
    }
    var isExactMatch = true;
    // check if the data attribute values are the same
    Object.keys(currentScriptClone.dataset).forEach(function (key) {
        if (currentScriptClone.dataset[key] !== nextScript.dataset[key]) {
            isExactMatch = false;
        }
    });
    return isExactMatch ? currentScript : null;
}
function insertScriptElement(_a) {
    var url = _a.url, attributes = _a.attributes, onSuccess = _a.onSuccess, onError = _a.onError;
    var newScript = createScriptElement(url, attributes);
    newScript.onerror = onError;
    newScript.onload = onSuccess;
    document.head.insertBefore(newScript, document.head.firstElementChild);
}
function processOptions(options) {
    var sdkBaseURL = "https://www.paypal.com/sdk/js";
    if (options.sdkBaseURL) {
        sdkBaseURL = options.sdkBaseURL;
        delete options.sdkBaseURL;
    }
    processMerchantID(options);
    var _a = Object.keys(options)
        .filter(function (key) {
        return (typeof options[key] !== "undefined" &&
            options[key] !== null &&
            options[key] !== "");
    })
        .reduce(function (accumulator, key) {
        var value = options[key].toString();
        if (key.substring(0, 5) === "data-") {
            accumulator.dataAttributes[key] = value;
        }
        else {
            accumulator.queryParams[key] = value;
        }
        return accumulator;
    }, {
        queryParams: {},
        dataAttributes: {},
    }), queryParams = _a.queryParams, dataAttributes = _a.dataAttributes;
    return {
        url: "".concat(sdkBaseURL, "?").concat(objectToQueryString(queryParams)),
        dataAttributes: dataAttributes,
    };
}
function objectToQueryString(params) {
    var queryString = "";
    Object.keys(params).forEach(function (key) {
        if (queryString.length !== 0)
            queryString += "&";
        queryString += key + "=" + params[key];
    });
    return queryString;
}
/**
 * Parse the error message code received from the server during the script load.
 * This function search for the occurrence of this specific string "/* Original Error:".
 *
 * @param message the received error response from the server
 * @returns the content of the message if the string string was found.
 *          The whole message otherwise
 */
function parseErrorMessage(message) {
    var originalErrorText = message.split("/* Original Error:")[1];
    return originalErrorText
        ? originalErrorText.replace(/\n/g, "").replace("*/", "").trim()
        : message;
}
function createScriptElement(url, attributes) {
    if (attributes === void 0) { attributes = {}; }
    var newScript = document.createElement("script");
    newScript.src = url;
    Object.keys(attributes).forEach(function (key) {
        newScript.setAttribute(key, attributes[key]);
        if (key === "data-csp-nonce") {
            newScript.setAttribute("nonce", attributes["data-csp-nonce"]);
        }
    });
    return newScript;
}
function processMerchantID(options) {
    var merchantID = options["merchant-id"], dataMerchantID = options["data-merchant-id"];
    var newMerchantID = "";
    var newDataMerchantID = "";
    if (Array.isArray(merchantID)) {
        if (merchantID.length > 1) {
            newMerchantID = "*";
            newDataMerchantID = merchantID.toString();
        }
        else {
            newMerchantID = merchantID.toString();
        }
    }
    else if (typeof merchantID === "string" && merchantID.length > 0) {
        newMerchantID = merchantID;
    }
    else if (typeof dataMerchantID === "string" &&
        dataMerchantID.length > 0) {
        newMerchantID = "*";
        newDataMerchantID = dataMerchantID;
    }
    options["merchant-id"] = newMerchantID;
    options["data-merchant-id"] = newDataMerchantID;
    return options;
}

/**
 * Load the Paypal JS SDK script asynchronously.
 *
 * @param {Object} options - used to configure query parameters and data attributes for the JS SDK.
 * @param {PromiseConstructor} [PromisePonyfill=window.Promise] - optional Promise Constructor ponyfill.
 * @return {Promise<Object>} paypalObject - reference to the global window PayPal object.
 */
function loadScript(options, PromisePonyfill) {
    if (PromisePonyfill === void 0) { PromisePonyfill = getDefaultPromiseImplementation(); }
    validateArguments(options, PromisePonyfill);
    // resolve with null when running in Node
    if (typeof window === "undefined")
        return PromisePonyfill.resolve(null);
    var _a = processOptions(options), url = _a.url, dataAttributes = _a.dataAttributes;
    var namespace = dataAttributes["data-namespace"] || "paypal";
    var existingWindowNamespace = getPayPalWindowNamespace(namespace);
    // resolve with the existing global paypal namespace when a script with the same params already exists
    if (findScript(url, dataAttributes) && existingWindowNamespace) {
        return PromisePonyfill.resolve(existingWindowNamespace);
    }
    return loadCustomScript({
        url: url,
        attributes: dataAttributes,
    }, PromisePonyfill).then(function () {
        var newWindowNamespace = getPayPalWindowNamespace(namespace);
        if (newWindowNamespace) {
            return newWindowNamespace;
        }
        throw new Error("The window.".concat(namespace, " global variable is not available."));
    });
}
/**
 * Load a custom script asynchronously.
 *
 * @param {Object} options - used to set the script url and attributes.
 * @param {PromiseConstructor} [PromisePonyfill=window.Promise] - optional Promise Constructor ponyfill.
 * @return {Promise<void>} returns a promise to indicate if the script was successfully loaded.
 */
function loadCustomScript(options, PromisePonyfill) {
    if (PromisePonyfill === void 0) { PromisePonyfill = getDefaultPromiseImplementation(); }
    validateArguments(options, PromisePonyfill);
    var url = options.url, attributes = options.attributes;
    if (typeof url !== "string" || url.length === 0) {
        throw new Error("Invalid url.");
    }
    if (typeof attributes !== "undefined" && typeof attributes !== "object") {
        throw new Error("Expected attributes to be an object.");
    }
    return new PromisePonyfill(function (resolve, reject) {
        // resolve with undefined when running in Node
        if (typeof window === "undefined")
            return resolve();
        insertScriptElement({
            url: url,
            attributes: attributes,
            onSuccess: function () { return resolve(); },
            onError: function () {
                var defaultError = new Error("The script \"".concat(url, "\" failed to load."));
                if (!window.fetch) {
                    return reject(defaultError);
                }
                // Fetch the error reason from the response body for validation errors
                return fetch(url)
                    .then(function (response) {
                    if (response.status === 200) {
                        reject(defaultError);
                    }
                    return response.text();
                })
                    .then(function (message) {
                    var parseMessage = parseErrorMessage(message);
                    reject(new Error(parseMessage));
                })
                    .catch(function (err) {
                    reject(err);
                });
            },
        });
    });
}
function getDefaultPromiseImplementation() {
    if (typeof Promise === "undefined") {
        throw new Error("Promise is undefined. To resolve the issue, use a Promise polyfill.");
    }
    return Promise;
}
function getPayPalWindowNamespace(namespace) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    return window[namespace];
}
function validateArguments(options, PromisePonyfill) {
    if (typeof options !== "object" || options === null) {
        throw new Error("Expected an options object.");
    }
    if (typeof PromisePonyfill !== "undefined" &&
        typeof PromisePonyfill !== "function") {
        throw new Error("Expected PromisePonyfill to be a function.");
    }
}

const paypalButtonsCss = ":host{display:block}.paypal-buttons{position:relative}";

const ScPaypalButtons = class {
  constructor(hostRef) {
    index.registerInstance(this, hostRef);
    this.scError = index.createEvent(this, "scError", 7);
    this.scSetState = index.createEvent(this, "scSetState", 7);
    this.scPaid = index.createEvent(this, "scPaid", 7);
    /** Is this busy? */
    this.busy = false;
    /** Buttons to render */
    this.buttons = ['paypal', 'card'];
    /** Label for the button. */
    this.label = 'paypal';
    /** Button color. */
    this.color = 'gold';
  }
  handleOrderChange() {
    this.cardContainer.innerHTML = '';
    this.paypalContainer.innerHTML = '';
    this.loadScript();
  }
  /** Load the script */
  async loadScript() {
    var _a;
    if (!this.clientId || !this.merchantId)
      return;
    const subscriptionOrder = index$1.hasSubscription(this.order);
    try {
      const paypal = await loadScript({
        'client-id': this.clientId.replace(/ /g, ''),
        ...(!subscriptionOrder ? { 'merchant-id': this.merchantId.replace(/ /g, '') } : {}),
        'commit': false,
        'intent': subscriptionOrder ? 'tokenize' : 'capture',
        'vault': subscriptionOrder,
        'currency': ((_a = this.order) === null || _a === void 0 ? void 0 : _a.currency.toUpperCase()) || 'USD',
      });
      this.renderButtons(paypal);
    }
    catch (err) {
      console.error('failed to load the PayPal JS SDK script', err);
    }
  }
  /** Load the script on component load. */
  componentDidLoad() {
    this.loadScript();
  }
  /** Render the buttons. */
  renderButtons(paypal) {
    const createFunc = index$1.hasSubscription(this.order) ? 'createBillingAgreement' : 'createOrder';
    const config = {
      /**
       * Validate the form, client-side when the button is clicked.
       */
      onClick: async (_, actions) => {
        const form = this.el.closest('sc-checkout');
        const isValid = await form.validate();
        return isValid ? actions.resolve() : actions.reject();
      },
      onInit: () => {
        this.loaded = true;
      },
      onCancel: () => {
        this.scSetState.emit('REJECT');
      },
      /**
       * The transaction has been approved.
       * We can capture it.
       */
      onApprove: async () => {
        var _a, _b;
        try {
          const intent = (await fetch$1.apiFetch({
            method: 'PATCH',
            path: `surecart/v1/payment_intents/${(_b = (_a = this.order) === null || _a === void 0 ? void 0 : _a.payment_intent) === null || _b === void 0 ? void 0 : _b.id}/capture`,
          }));
          if (['succeeded', 'pending', 'requires_approval'].includes(intent === null || intent === void 0 ? void 0 : intent.status)) {
            this.scPaid.emit();
          }
          else {
            this.scError.emit({ code: 'could_not_capture', message: defaultI18n.__('The payment did not process. Please try again.', 'surecart') });
            this.scSetState.emit('REJECT');
          }
        }
        catch (err) {
          console.error(err);
          this.scError.emit({ code: 'could_not_capture', message: defaultI18n.__('The payment did not process. Please try again.', 'surecart') });
          this.scSetState.emit('REJECT');
        }
      },
      /**
       * Transaction errored.
       * @param err
       */
      onError: err => {
        this.scError.emit({ code: err === null || err === void 0 ? void 0 : err.code, message: err === null || err === void 0 ? void 0 : err.message });
        this.scSetState.emit('REJECT');
      },
    };
    config[createFunc] = async () => {
      return new Promise(async (resolve, reject) => {
        var _a, _b;
        // get the checkout component.
        const checkout = this.el.closest('sc-checkout');
        // submit and get the finalized order
        const order = (await checkout.submit());
        // an error occurred. reject with the error.
        if (order instanceof Error) {
          return reject(order);
        }
        // assume there was a validation issue here.
        // that is handled by our fetch function.
        if ((order === null || order === void 0 ? void 0 : order.status) !== 'finalized') {
          return reject(new Error('Something went wrong. Please try again.'));
        }
        // resolve the payment intent id.
        if ((_a = order === null || order === void 0 ? void 0 : order.payment_intent) === null || _a === void 0 ? void 0 : _a.external_intent_id) {
          return resolve((_b = order === null || order === void 0 ? void 0 : order.payment_intent) === null || _b === void 0 ? void 0 : _b.external_intent_id);
        }
        // we don't have the correct payment intent for some reason.
        this.scError.emit({ code: 'missing_payment_intent', message: defaultI18n.__('Something went wrong. Please contact us for payment.', 'surecart') });
        return reject();
      });
    };
    if (paypal.FUNDING.PAYPAL) {
      const paypalButton = paypal.Buttons({
        fundingSource: paypal.FUNDING.PAYPAL,
        style: {
          label: this.label,
          color: this.color,
        },
        ...config,
      });
      if (paypalButton.isEligible()) {
        paypalButton.render(this.paypalContainer);
      }
    }
    if (paypal.FUNDING.CARD) {
      const cardButton = paypal.Buttons({
        fundingSource: paypal.FUNDING.CARD,
        style: {
          color: 'black',
        },
        ...config,
      });
      if (cardButton.isEligible()) {
        cardButton.render(this.cardContainer);
      }
    }
  }
  render() {
    return (index.h("div", { class: { 'paypal-buttons': true, 'paypal-buttons--busy': this.busy } }, (!this.loaded || this.busy) && index.h("sc-skeleton", { style: { 'height': '55px', '--border-radius': '4px', 'cursor': 'wait' } }), index.h("div", { class: "sc-paypal-button-container", hidden: !this.loaded || this.busy }, index.h("div", { hidden: !this.buttons.includes('card'), class: "sc-paypal-card-button", ref: el => (this.cardContainer = el) }), index.h("div", { hidden: !this.buttons.includes('paypal'), class: "sc-paypal-button", ref: el => (this.paypalContainer = el) }))));
  }
  get el() { return index.getElement(this); }
  static get watchers() { return {
    "order": ["handleOrderChange"]
  }; }
};
ScPaypalButtons.style = paypalButtonsCss;

exports.sc_paypal_buttons = ScPaypalButtons;
