"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CreditCard = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
/*
 *  This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.
 *  License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
 */
/* Show payment methods.

TODO: we are only showing the credit card payment sources at present.
There are other types of sources, e.g., "ACH credit transfer".

In the *near* future we will support more payment methods!
*/
const react_1 = require("react");
const react_dom_1 = __importDefault(require("react-dom"));
const api_1 = __importDefault(require("lib/hooks/api"));
const loading_1 = __importDefault(require("components/share/loading"));
const help_email_1 = __importDefault(require("components/misc/help-email"));
const A_1 = __importDefault(require("components/misc/A"));
const antd_1 = require("antd");
const icon_1 = require("@cocalc/frontend/components/icon");
const post_1 = __importDefault(require("lib/api/post"));
const misc_1 = require("@cocalc/util/misc");
const use_customize_1 = __importDefault(require("lib/use-customize"));
const script_1 = __importDefault(require("next/script"));
const mounted_1 = __importDefault(require("lib/hooks/mounted"));
const site_name_1 = __importDefault(require("components/share/site-name"));
const sales_tax_1 = __importDefault(require("@cocalc/util/stripe/sales-tax"));
const STRIPE_CLIENT_LIBRARY = "https://js.stripe.com/v3/";
function Brand({ brand }) {
    return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [brand?.includes(" ") ? ("") : ((0, jsx_runtime_1.jsx)(icon_1.Icon, { name: `cc-${brand?.toLowerCase()}` })), " ", brand] }));
}
function Number({ last4 }) {
    return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: `**** **** **** ${last4}` });
}
function ExpirationDate({ exp_month, exp_year }) {
    return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: `${exp_month}/${exp_year}` });
}
function PaymentSourceActions({ onChange, default_source, brand, last4, id }) {
    const isMounted = (0, mounted_1.default)();
    const [error, setError] = (0, react_1.useState)("");
    return ((0, jsx_runtime_1.jsxs)("div", { children: [error && ((0, jsx_runtime_1.jsx)(antd_1.Alert, { type: "error", message: error, style: { marginBottom: "5px" } })), default_source ? ((0, jsx_runtime_1.jsx)(antd_1.Popconfirm, { placement: "topLeft", showCancel: false, title: (0, jsx_runtime_1.jsxs)("div", { style: { width: "400px" }, children: ["The default payment method is the", " ", (0, jsx_runtime_1.jsxs)("b", { children: [brand, " card ending in ...", last4] }), ". It will be used by default for subscriptions and new purchases."] }), okText: "OK", children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "primary", style: { marginRight: "5px", marginBottom: "5px" }, children: "Default" }) })) : ((0, jsx_runtime_1.jsx)(antd_1.Popconfirm, { placement: "topLeft", title: (0, jsx_runtime_1.jsxs)("div", { style: { width: "400px" }, children: ["Do you want to set the", " ", (0, jsx_runtime_1.jsxs)("b", { children: [brand, " card ending in ...", last4] }), " ", "to be the default for subscriptions and new purchases?"] }), onConfirm: async () => {
                    try {
                        setError("");
                        await (0, post_1.default)("/billing/set-default-source", {
                            default_source: id,
                        });
                        if (!isMounted.current)
                            return;
                        onChange?.();
                    }
                    catch (err) {
                        if (!isMounted.current)
                            return;
                        setError(err.message);
                    }
                }, okText: "Yes", cancelText: "No", children: (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "dashed", style: { marginRight: "5px", marginBottom: "5px" }, children: "Default" }) })), (0, jsx_runtime_1.jsx)(antd_1.Popconfirm, { placement: "topLeft", title: (0, jsx_runtime_1.jsxs)("div", { style: { width: "400px" }, children: ["Do you want to delete the", " ", (0, jsx_runtime_1.jsxs)("b", { children: [brand, " card ending in ...", last4] }), "? It will no longer be used for subscriptions and you will have to enter it again to use it to make a purchase."] }), onConfirm: async () => {
                    try {
                        setError("");
                        await (0, post_1.default)("/billing/delete-payment-method", { id });
                        onChange?.();
                    }
                    catch (err) {
                        setError(err.message);
                    }
                }, okText: "Yes, delete this card", cancelText: "Cancel", children: (0, jsx_runtime_1.jsxs)(antd_1.Button, { type: "dashed", children: [(0, jsx_runtime_1.jsx)(icon_1.Icon, { name: "trash" }), " Delete"] }) })] }));
}
const columns = (onChange) => [
    {
        responsive: ["xs"],
        title: "Card",
        render: (_, card) => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(CreditCard, { ...card }), (0, jsx_runtime_1.jsx)(PaymentSourceActions, { ...card, onChange: onChange })] })),
    },
    {
        responsive: ["sm"],
        title: "Type",
        dataIndex: "brand",
        render: (_, card) => (0, jsx_runtime_1.jsx)(Brand, { ...card }),
    },
    {
        responsive: ["sm"],
        title: "Number",
        dataIndex: "last4",
        render: (_, card) => (0, jsx_runtime_1.jsx)(Number, { ...card }),
    },
    {
        responsive: ["sm"],
        title: "Expiration Date",
        align: "center",
        render: (_, card) => (0, jsx_runtime_1.jsx)(ExpirationDate, { ...card }),
    },
    {
        responsive: ["sm"],
        title: "Country",
        dataIndex: "country",
        align: "center",
    },
    {
        responsive: ["sm"],
        title: "Postal Code",
        dataIndex: "address_zip",
        align: "center",
    },
    {
        responsive: ["sm"],
        title: "",
        render: (_, card) => (0, jsx_runtime_1.jsx)(PaymentSourceActions, { ...card, onChange: onChange }),
    },
];
function PaymentMethods({ startMinimized, setTaxRate }) {
    const [minimized, setMinimized] = (0, react_1.useState)(!!startMinimized);
    const { result, error, call } = (0, api_1.default)("billing/get-customer");
    if (error) {
        return (0, jsx_runtime_1.jsx)(antd_1.Alert, { type: "error", message: error });
    }
    if (!result) {
        return (0, jsx_runtime_1.jsx)(loading_1.default, { center: true });
    }
    // set default so can use in table
    const { default_source } = result;
    const cards = [];
    if (result.sources != null) {
        for (const row of result.sources.data) {
            if (row.id == default_source) {
                row.default_source = true;
                if (setTaxRate != null) {
                    // TODO: setTimeout as a quick fix. You can't change the state while the parent is rendering
                    // Cannot update a component (`Checkout`) while rendering a different component `PaymentMethods`).
                    setTimeout(() => setTaxRate((0, sales_tax_1.default)(row.address_zip)), 0);
                }
            }
            if (row.id.startsWith("card_")) {
                cards.push(row);
            }
        }
    }
    // sort by data rather than what comes back, so changing
    // default stays stable (since moving is confusing).
    cards.sort((x, y) => (0, misc_1.cmp)(x.id, y.id));
    if (minimized) {
        let defaultCard = undefined;
        for (const card of cards) {
            if (card.default_source) {
                defaultCard = card;
                break;
            }
        }
        if (defaultCard) {
            return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(A_1.default, { onClick: () => setMinimized(false), children: "Change" }), (0, jsx_runtime_1.jsx)(CreditCard, { ...defaultCard })] }));
        }
    }
    return ((0, jsx_runtime_1.jsxs)("div", { children: [startMinimized && cards.length > 0 && ((0, jsx_runtime_1.jsx)("div", { style: { float: "right", marginRight: "15px" }, children: (0, jsx_runtime_1.jsxs)(A_1.default, { onClick: () => setMinimized(true), children: ["Close ", (0, jsx_runtime_1.jsx)(icon_1.Icon, { name: "times" })] }) })), !startMinimized && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("h3", { children: ["Credit Cards (", cards.length, ")"] }), cards.length > 0 ? ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: "These are the credit cards that you have currently setup." })) : ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: "Please enter your credit card below." })), " "] })), (0, jsx_runtime_1.jsx)(AddPaymentMethod, { defaultAdding: cards.length == 0, onChange: call, style: { marginTop: "15px", marginBottom: "5px" } }), cards.length > 0 && ((0, jsx_runtime_1.jsx)(antd_1.Table, { columns: columns(call), dataSource: cards, rowKey: "id", style: { marginTop: "15px", overflowX: "auto" }, pagination: { hideOnSinglePage: true, pageSize: 100 } })), result.sources?.has_more && ((0, jsx_runtime_1.jsx)(antd_1.Alert, { type: "warning", showIcon: true, message: "WARNING: Some of your cards may not be displayed above, since there are so many." }))] }));
}
exports.default = PaymentMethods;
function AddPaymentMethod({ onChange, style, defaultAdding, }) {
    const [error, setError] = (0, react_1.useState)("");
    const [adding, setAdding] = (0, react_1.useState)(!!defaultAdding);
    const [creating, setCreating] = (0, react_1.useState)(false);
    const [stripe, setStripe] = (0, react_1.useState)(null);
    const cardRef = (0, react_1.useRef)();
    const { stripePublishableKey } = (0, use_customize_1.default)();
    const isMounted = (0, mounted_1.default)();
    (0, react_1.useEffect)(() => {
        if (!adding || !cardRef.current)
            return;
        if (stripe != null) {
            stripe.card.mount(react_dom_1.default.findDOMNode(cardRef.current));
        }
        else {
            if (window.Stripe == null)
                return;
            const stripe = window.Stripe(stripePublishableKey);
            const card = stripe.elements().create("card");
            if (card == null)
                throw Error("bug -- card cannot be null");
            setStripe({ stripe, card });
            card.mount(react_dom_1.default.findDOMNode(cardRef.current));
        }
    }, [adding, stripe]);
    return ((0, jsx_runtime_1.jsxs)("div", { style: style, children: [(0, jsx_runtime_1.jsxs)(antd_1.Button, { disabled: adding, onClick: () => setAdding(true), children: [(0, jsx_runtime_1.jsx)(icon_1.Icon, { name: "plus-circle" }), " Add Credit Card"] }), adding && ((0, jsx_runtime_1.jsxs)("div", { style: {
                    border: "1px solid #ddd",
                    padding: "15px",
                    borderRadius: "5px",
                    maxWidth: "600px",
                    margin: "15px auto",
                }, children: [(0, jsx_runtime_1.jsx)(script_1.default, { src: STRIPE_CLIENT_LIBRARY, onLoad: () => {
                            const stripe = window.Stripe(stripePublishableKey);
                            const card = stripe.elements().create("card");
                            if (card == null)
                                throw Error("bug -- card cannot be null");
                            setStripe({ stripe, card });
                        }, onError: () => {
                            setError(`Stripe script failed to load. Make sure your browser is not blocking ${STRIPE_CLIENT_LIBRARY}, then refresh this page.`);
                        } }), error && ((0, jsx_runtime_1.jsx)(antd_1.Alert, { type: "error", showIcon: true, message: error, style: { margin: "15px 0" } })), (0, jsx_runtime_1.jsxs)("div", { style: { textAlign: "center" }, children: [(0, jsx_runtime_1.jsx)("div", { ref: cardRef, style: {
                                    border: "1px solid lightgray",
                                    borderRadius: "5px",
                                    margin: "5px auto 20px auto",
                                    maxWidth: "400px",
                                    padding: "15px 10px",
                                    boxShadow: "5px 5px 5px lightgray",
                                } }), (0, jsx_runtime_1.jsx)(antd_1.Button, { style: { marginRight: "5px" }, onClick: () => setAdding(false), disabled: creating, children: "Cancel" }), (0, jsx_runtime_1.jsx)(antd_1.Button, { disabled: creating, type: "primary", onClick: async () => {
                                    setCreating(true);
                                    try {
                                        let token;
                                        try {
                                            const result = await stripe?.stripe.createToken(stripe.card);
                                            if (!isMounted.current)
                                                return;
                                            if (result.error) {
                                                throw Error(result.error.message);
                                            }
                                            ({ token } = result);
                                        }
                                        catch (err) {
                                            if (!isMounted.current)
                                                return;
                                            setError(err.message);
                                            return;
                                        }
                                        try {
                                            await (0, post_1.default)("/billing/create-payment-method", {
                                                id: token.id,
                                            });
                                            if (!isMounted.current)
                                                return;
                                            onChange?.();
                                            setAdding(false);
                                        }
                                        catch (err) {
                                            setError(err.message);
                                        }
                                    }
                                    finally {
                                        if (!isMounted.current)
                                            return;
                                        setCreating(false);
                                    }
                                }, children: creating ? ((0, jsx_runtime_1.jsx)(loading_1.default, { center: true, delay: 0, children: "Adding Card..." })) : ("Add Card") })] }), (0, jsx_runtime_1.jsxs)("div", { style: { color: "#666", marginTop: "15px" }, children: [(0, jsx_runtime_1.jsx)(site_name_1.default, {}), " does not directly store any credit card numbers; instead they are stored securely by", " ", (0, jsx_runtime_1.jsx)(A_1.default, { href: "https://stripe.com/", external: true, children: "Stripe" }), ". PayPal or wire transfers for non-recurring purchases above $100 are also possible. ", (0, jsx_runtime_1.jsx)(help_email_1.default, {}), "."] })] }))] }));
}
function CreditCard(props) {
    const { brand, last4, exp_month, exp_year, country, address_zip } = props;
    return ((0, jsx_runtime_1.jsxs)("div", { style: {
            backgroundColor: "#f0f0ff",
            border: "1px solid lightgrey",
            margin: "15px 0",
            padding: "10px",
            borderRadius: "5px",
            maxWidth: "300px",
        }, children: [(0, jsx_runtime_1.jsx)(Brand, { brand: brand }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)(Number, { last4: last4 }), (0, jsx_runtime_1.jsx)("br", {}), (0, jsx_runtime_1.jsx)(ExpirationDate, { exp_month: exp_month, exp_year: exp_year }), (0, jsx_runtime_1.jsx)("br", {}), country, " ", address_zip, (0, jsx_runtime_1.jsx)("br", {})] }));
}
exports.CreditCard = CreditCard;
//# sourceMappingURL=payment-methods.js.map