UNPKG

bitsnap-checkout

Version:

This is Bitsnap Checkout React library for easy integration with any website which is using React framework

1,417 lines (1,400 loc) 82.7 kB
import { AddressSchema, BillingAddressSchema, GetPreOrderDetailsRequestSchema, PreOrderItemSchema, PublicApiService, file_common_v1_address, file_common_v1_gateway } from "./chunk-FLQW553F.mjs"; import "./chunk-ZD7AOCMD.mjs"; // src/components/checkout/ApplePay.tsx import ApplePayButton from "apple-pay-button"; // src/public.api.client.ts import { createClient } from "@connectrpc/connect"; import { createConnectTransport } from "@connectrpc/connect-web"; var PublicApiClient; ((PublicApiClient2) => { function get(host) { return createClient(PublicApiService, getTransport(host)); } PublicApiClient2.get = get; let transport; function getTransport(host) { if (transport == null) { transport = createConnectTransport({ useBinaryFormat: true, baseUrl: host + "/api/rpc" }); } return transport; } })(PublicApiClient || (PublicApiClient = {})); // src/components/checkout/CartProvider.tsx import { create as create2 } from "@bufbuild/protobuf"; import { createContext, useContext } from "react"; import { QueryClient, QueryClientProvider } from "react-query"; import zod from "zod"; // src/components/checkout/constants.ts var HOST = "https://bitsnap.pl"; function setCustomHost(host) { HOST = host; } // src/components/checkout/helper.methods.ts function buildURL(projectID, path) { return `${HOST}/api/integrations/${projectID}/public-commerce${path}`; } // src/components/checkout/lib/err.ts function isErr(x) { return typeof x === "object" && x != null && "ERR" in x; } function Err(message, type) { return { ERR: true, error: message, type }; } // src/components/checkout/lib/round.number.ts function round(num, numberOfDecimals = 2) { return Number( +(Math.round(Number(num + "e+" + numberOfDecimals)) + "e-" + numberOfDecimals) ); } function formatCurrency(amount, currency) { const formatter = Intl.NumberFormat(navigator.language, { style: "currency", currency, currencyDisplay: "symbol" }); return formatter.format(amount / 100); } // src/components/checkout/state.ts import { create } from "zustand"; var useCheckoutStore = create((set) => ({ isCartVisible: false, numberOfProductsInCart: 2, showCart: () => set((state) => ({ ...state, isCartVisible: true })), hideCart: () => set((state) => ({ ...state, isCartVisible: false })), setNumberOfProductsInCart: (numberOfProductsInCart) => set((state) => ({ ...state, numberOfProductsInCart })) })); // src/components/checkout/methods.ts async function addProductToCart(id, quantity = 1, metadata) { return Bitsnap.addProductToCart(id, quantity, metadata); } function showCart() { return Bitsnap.showCart(); } function hideCart() { return Bitsnap.hideCart(); } var Bitsnap; ((Bitsnap2) => { async function addProductToCart2(id, quantity = 1, metadata) { const projectID = getProjectID(); if (projectID == null) { throw new Error("No project ID found"); } const methods = getCheckoutMethods(projectID); const err = await methods.addProduct({ productID: id, quantity, metadata }); if (err != null) { return err; } return void 0; } Bitsnap2.addProductToCart = addProductToCart2; function showCart2() { useCheckoutStore.setState({ isCartVisible: true }); } Bitsnap2.showCart = showCart2; function hideCart2() { useCheckoutStore.setState({ isCartVisible: false }); } Bitsnap2.hideCart = hideCart2; })(Bitsnap || (Bitsnap = {})); async function createPaymentURL(request) { const projectID = getProjectID(); if (projectID == null) { throw new Error("No project ID found"); } request = injectReferenceToRequestIfNeeded(request); const result = await fetch(buildURL(projectID, "/buy"), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request) }); if (result.status != 200) { console.warn( "result", await result.text(), result.status, result.statusText ); return Err("internal-error", "internal"); } const response = await result.json(); return { url: response.url }; } async function createCheckout(request) { const projectID = getProjectID(); if (projectID == null) { throw new Error("No project ID found"); } const headers = { "Content-Type": "application/json", ...request.apiKey != null ? { Authorization: `Bearer ${request.apiKey}` } : {} }; const path = request.testMode ? `/api/payment/link/auto/${projectID}/test` : `/api/payment/link/auto/${projectID}`; delete request.apiKey; delete request.testMode; const response = await fetch(HOST + path, { method: "POST", headers, body: JSON.stringify(request) }); const payload = await response.json(); return { status: "ok", redirectURL: payload.url }; } function getReferenceIfPossible() { if (typeof localStorage == "undefined") { return void 0; } const refLink = localStorage.getItem("bitsnap-ref"); if (refLink == null) { return void 0; } return refLink; } function injectReferenceToRequestIfNeeded(request) { const ref = getReferenceIfPossible(); if (ref == null) { return request; } if (request.metadata == null) { request.metadata = {}; } request.metadata["ref"] = ref; return request; } // src/components/checkout/google.pay.mapper.ts function mapGooglePayConfiguration(args) { var _a; const { items, googlePayConfig, checkout, result, cartRequiresShipping } = args; if (result.totalAmount == null) { return void 0; } let intents = []; if (cartRequiresShipping) { intents.push("SHIPPING_ADDRESS"); intents.push("SHIPPING_OPTION"); } const returnValue = { apiVersion: 2, apiVersionMinor: 0, callbackIntents: intents, allowedPaymentMethods: [ { type: "CARD", parameters: { allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"], allowedCardNetworks: ["MASTERCARD", "VISA"] }, tokenizationSpecification: { type: "PAYMENT_GATEWAY", parameters: { gateway: googlePayConfig.gateway, gatewayMerchantId: googlePayConfig.gatewayId } } } ], emailRequired: true, shippingAddressParameters: { phoneNumberRequired: cartRequiresShipping }, shippingAddressRequired: cartRequiresShipping, shippingOptionRequired: cartRequiresShipping, shippingOptionParameters: cartRequiresShipping ? { shippingOptions: result.methods.slice(0, 5).map((method) => { var _a2; return { id: method.id, label: method.name + ` - ${formatCurrency(method.amount, result.currency)}`, description: (_a2 = method.description) != null ? _a2 : "" }; }), defaultSelectedOptionId: result.selectedDeliveryMethod } : void 0, merchantInfo: { merchantId: googlePayConfig.merchantId + "", merchantName: googlePayConfig.merchantName }, transactionInfo: { totalPriceStatus: "FINAL", totalPriceLabel: "P\u0142atno\u015B\u0107 za koszyk", totalPrice: `${round(result.totalAmount / 100, 2)}`, currencyCode: result.currency, countryCode: result.country, checkoutOption: "COMPLETE_IMMEDIATE_PURCHASE", displayItems: mapGooglePayDisplayItems(items, result.methods, (_a = result.selectedDeliveryMethod) != null ? _a : result.methods.length > 0 ? result.methods[0].id : void 0, checkout.couponCode, result.couponValue) } }; console.log("returnValue", returnValue); return returnValue; } function mapGooglePayDisplayItems(items, methods, deliveryMethod, couponCode, couponValue) { const displayItems = []; let subtotalAmount = 0; for (const item of items) { if (item.details == null) { continue; } subtotalAmount += item.details.price * item.quantity; displayItems.push({ label: item.details.name + " x " + item.quantity, price: `${round(item.details.price / 100, 2)}`, type: "LINE_ITEM", status: "FINAL" }); } const selectedDeliveryMethod = methods.find((method) => method.id === deliveryMethod); if (selectedDeliveryMethod != null) { displayItems.push({ label: selectedDeliveryMethod.name, price: `${round(selectedDeliveryMethod.amount / 100, 2)}`, type: "SHIPPING_OPTION", status: "FINAL" }); } if (couponCode != null && couponValue != null) { displayItems.push({ label: `Kupon ${couponCode}`, price: `${round(couponValue / 100, 2)}`, type: "DISCOUNT", status: "FINAL" }); } if (subtotalAmount > 0) { displayItems.push({ label: "Subtotal", price: `${round(subtotalAmount / 100, 2)}`, type: "SUBTOTAL", status: "FINAL" }); } return displayItems; } // src/components/checkout/CartProvider.tsx import { Fragment, jsx } from "react/jsx-runtime"; var MARKETING_AGREEMENT_ID = "__m_a"; var CartProviderContext = createContext(void 0); var bitsnapProjectID = void 0; function setProjectID(projectID) { bitsnapProjectID = projectID; } function getProjectID() { if (bitsnapProjectID != null) { return bitsnapProjectID; } const me = document.querySelector( 'script[data-id][data-name="internal-cart"]' ); const projectID = me == null ? void 0 : me.getAttribute("data-id"); return projectID != null ? projectID : void 0; } function getNewHostIfExist() { const me = document.querySelector( 'script[data-id][data-name="internal-cart"]' ); const customHost = me == null ? void 0 : me.getAttribute("data-custom-host"); return customHost != null ? customHost : void 0; } var CartProvider = ({ children }) => { const queryClient = new QueryClient(); const projectID = getProjectID(); if (projectID == null) { return /* @__PURE__ */ jsx(Fragment, {}); } const checkoutMethods = getCheckoutMethods(projectID); return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(CartProviderContext.Provider, { value: checkoutMethods, children }) }); }; var getProducts = async (projectID) => { var _a, _b; const products = (_b = (_a = getCheckout()) == null ? void 0 : _a.products) != null ? _b : []; const productIds = Array.from( new Set(products.map((product) => product.productID)) ); const params = new URLSearchParams(); params.set("ids", productIds.join(",")); const result = await fetch( buildURL(projectID, `/products?${params.toString()}`), { method: "GET" } ); if (result.status != 200) { return []; } const payload = await result.json(); products.forEach((product) => { var _a2; product["details"] = (_a2 = payload.result) == null ? void 0 : _a2.find((el) => { if (el.id === product.productID) { return true; } if (el.variants != null && el.variants.length > 0) { const index = el.variants.findIndex( (variant) => variant.id === product.productID ); return index !== -1; } return false; }); }); return products.filter((el) => "details" in el).map((el) => { el.details = resolveProductDetailsFromSingleProduct( el.productID, el.details ); return el; }); }; var useCartProvider = () => { const context = useContext(CartProviderContext); if (context === void 0) { throw new Error("useCartProvider must be used within a CartProvider"); } return context; }; var CartProvider_default = CartProvider; var googlePayConfigSchema = zod.object({ isAvailable: zod.boolean(), merchantId: zod.string(), merchantName: zod.string(), gateway: zod.string(), gatewayId: zod.string() }); var checkoutSchema = zod.object({ country: zod.string().optional(), couponCode: zod.string().optional(), selectedDeliveryMethod: zod.string().optional(), postalCode: zod.string().optional(), email: zod.string().optional(), products: zod.array( zod.object({ id: zod.string(), productID: zod.string(), quantity: zod.number(), metadata: zod.record(zod.string(), zod.string().optional()).optional() }) ).optional(), googlePayConfig: googlePayConfigSchema.optional() }); var emptyCheckout = { country: void 0, couponCode: void 0, email: void 0, selectedDeliveryMethod: void 0, postalCode: void 0, products: [], googlePayConfig: void 0 }; var checkoutKey = "checkout"; function getCheckout() { try { const value = localStorage.getItem(checkoutKey); if (value == null) { return emptyCheckout; } return checkoutSchema.parse(JSON.parse(value)); } catch (e) { return emptyCheckout; } } function addProducts(products) { const checkout = getCheckout(); if (checkout.products == null) { checkout.products = []; } checkout.products.push(...products.map((el) => ({ id: Math.random().toString(36).substring(7), productID: el.productID, quantity: el.quantity, metadata: el.metadata }))); saveCheckout(checkout); } function removeProductFromCheckout(ids) { var _a; const checkout = getCheckout(); const newCheckout = { ...checkout, products: (_a = checkout == null ? void 0 : checkout.products) == null ? void 0 : _a.filter( (product) => !ids.includes(product.productID) && !ids.includes(product.id) ) }; saveCheckout(newCheckout); } function saveCheckout(model) { localStorage.setItem(checkoutKey, JSON.stringify(model)); } var getCheckoutMethods = (projectID) => { const newHost = getNewHostIfExist(); if (newHost != null) { setCustomHost(newHost); } return { async checkIfApplePayIsAvailable() { if (typeof window == "undefined" || typeof document == "undefined") { return false; } if ("ApplePaySession" in window === false) { return false; } try { const result = await PublicApiClient.get(HOST).isOneClickPaymentAvailable({ projectId: projectID }); return result.applePay; } catch (e) { console.error("Error checking if Apple Pay is available", e); return false; } }, async getGooglePayConfiguration(args) { if (typeof window == "undefined" || typeof document == "undefined") { return { isAvailable: false }; } try { const googlePayConfig = await resolveGooglePayConfiguration(projectID); if ((googlePayConfig == null ? void 0 : googlePayConfig.isAvailable) != true) { return { isAvailable: false }; } const checkout = getCheckout(); if (checkout == null) { console.log("checkout is null"); return { isAvailable: false }; } if (args && args.items != null && args.items.length > 0) { if (checkout.products) { removeProductFromCheckout(checkout.products.map((el) => el.productID)); } addProducts(args.items); } const products = await getProducts(projectID); if (isErr(products)) { return { isAvailable: false }; } const result = await PublicApiClient.get(HOST).getPreOrderDetails({ items: products.map( (el) => create2(PreOrderItemSchema, { id: el.productID, quantity: el.quantity }) ), projectId: projectID, // we can detect 5 the closest inpost pickup point based on shipping address. postCode: checkout.postalCode, couponCode: checkout.couponCode, countryCode: checkout.country, selectedDeliveryMethod: checkout.selectedDeliveryMethod }); if (result.totalAmount == null) { return { isAvailable: false }; } const cartRequiresShipping = products.find((el) => { var _a; return ((_a = el.details) == null ? void 0 : _a.isDeliverable) == true; }) != null; const mappedPaymentRequest = await mapGooglePayConfiguration({ items: products, googlePayConfig, checkout, cartRequiresShipping, result }); if (mappedPaymentRequest == null) { return { isAvailable: false }; } return { isAvailable: true, config: mappedPaymentRequest }; } catch (e) { console.error("Error resolving Google Pay configuration", e); return { isAvailable: false }; } }, async clearCart() { var _a; const empty = structuredClone(emptyCheckout); empty.country = (_a = getCheckout()) == null ? void 0 : _a.country; saveCheckout(empty); }, async getAvailableCountries() { const result = await fetch(buildURL(projectID, "/countries"), { method: "GET" }); if (result.status != 200) { return []; } try { return zod.array( zod.object({ name: zod.string(), code: zod.string() }) ).parse(await result.json()); } catch (e) { return Err(e.toString(), "internal"); } }, async getCountry() { var _a; let country = (_a = getCheckout()) == null ? void 0 : _a.country; if (country == null) { country = "PL"; const checkout = getCheckout(); checkout.country = country; saveCheckout(checkout); } return country; }, async getNumberOfElementsInCart() { var _a, _b, _c; return (_c = (_b = (_a = getCheckout()) == null ? void 0 : _a.products) == null ? void 0 : _b.reduce( (acc, product) => acc + product.quantity, 0 )) != null ? _c : 0; }, async getProducts() { return await getProducts(projectID); }, async removeProductFromCart(args) { return removeProductFromCheckout([args.id]); }, async setCountry(country) { const checkout = getCheckout(); checkout.country = country; saveCheckout(checkout); }, async setCouponCodeIfPossible(couponCode) { const checkout = getCheckout(); checkout.couponCode = couponCode; saveCheckout(checkout); }, async setPostalCode(postalCode) { const checkout = getCheckout(); checkout.postalCode = postalCode; saveCheckout(checkout); }, async setDeliveryMethod(deliveryMethod) { const checkout = getCheckout(); checkout.selectedDeliveryMethod = deliveryMethod; saveCheckout(checkout); }, async setEmail(email) { const checkout = getCheckout(); checkout.email = email; saveCheckout(checkout); }, async addProduct(args) { return addProducts([args]); }, async updateQuantity(args) { var _a; const checkout = getCheckout(); checkout.products = (_a = checkout.products) == null ? void 0 : _a.map((product) => { if (product.id === args.id) { product.quantity = args.quantity; } return product; }); saveCheckout(checkout); }, async redirectToNextStep() { const checkout = getCheckout(); if (checkout.products == null || checkout.products.length == 0) { return Err("cart-is-empty", "badInput"); } const mergedMetadata = checkout.products.reduce( (acc, product) => { if (product.metadata != null) { Object.keys(product.metadata).forEach((key) => { var _a; const value = (_a = product.metadata) == null ? void 0 : _a[key]; if (value != null) { acc[key] = value; } }); } return acc; }, {} ); const payload = { items: checkout.products.map((el) => { return { id: el.productID, quantity: el.quantity }; }), askForNote: true, countries: checkout.country ? [checkout.country] : void 0, metadata: mergedMetadata }; const paymentResponse = await createPaymentURL(payload); if (isErr(paymentResponse)) { console.warn("cannot create payment URL", paymentResponse.error); return paymentResponse; } return { url: paymentResponse.url }; }, async justRedirectToPayment(args) { let payload = { items: [ { id: args.productID, quantity: 1 } ], askForNote: false, details: args.email || args.name ? { name: args.name, email: args.email } : void 0 }; payload = injectReferenceToRequestIfNeeded(payload); if (args.country == null) { args.country = "pl"; } if (payload.details == null) { payload.details = {}; } if (payload.details.address == null) { payload.details.address = {}; } payload.details.address.country = args.country; if (args.marketingAgreement === true) { payload.additionalAgreements = [ { id: MARKETING_AGREEMENT_ID, name: "", required: true, answer: true } ]; } const result = await fetch(buildURL(projectID, "/buy"), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); console.log("CODE", result.status); if (result.status != 200) { console.warn( "result", await result.text(), result.status, result.statusText ); return Err("internal-error", "internal"); } const response = await result.json(); console.log(response.url); return { url: response.url }; }, async completeApplePayPayment(args) { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J; const checkout = getCheckout(); if (checkout == null) { return Err("Checkout not found"); } const products = await getProducts(projectID); if (isErr(products)) { return products; } try { let shippingName = (_a = args.shippingContact) == null ? void 0 : _a.givenName; if (((_b = args.shippingContact) == null ? void 0 : _b.familyName) != null) { shippingName += " " + ((_c = args.shippingContact) == null ? void 0 : _c.familyName); } else if (((_d = args.billingContact) == null ? void 0 : _d.familyName) != null) { shippingName += " " + ((_e = args.billingContact) == null ? void 0 : _e.familyName); } let metadata = {}; const ref = getReferenceIfPossible(); if (ref != null) { metadata["ref"] = ref; } const result = await PublicApiClient.get(HOST).authorizeOneClickPayment( { gateway: 0 /* APPLE_PAY */, paymentData: JSON.stringify(args.token.paymentData), paymentMethod: JSON.stringify(args.token.paymentMethod), transactionIdentifier: args.token.transactionIdentifier, order: create2(GetPreOrderDetailsRequestSchema, { items: products.map((el) => ({ id: el.productID, quantity: el.quantity })), couponCode: checkout.couponCode, email: (_j = (_i = (_h = (_f = args.shippingContact) == null ? void 0 : _f.emailAddress) != null ? _h : (_g = args.billingContact) == null ? void 0 : _g.emailAddress) != null ? _i : checkout.email) != null ? _j : void 0, phone: (_n = (_m = (_k = args.shippingContact) == null ? void 0 : _k.phoneNumber) != null ? _m : (_l = args.billingContact) == null ? void 0 : _l.phoneNumber) != null ? _n : void 0, selectedDeliveryMethod: checkout.selectedDeliveryMethod, postCode: checkout.postalCode, projectId: projectID }), shippingAddress: create2(AddressSchema, { line1: (_q = (_p = (_o = args.shippingContact) == null ? void 0 : _o.addressLines) == null ? void 0 : _p[0]) != null ? _q : "", city: (_s = (_r = args.shippingContact) == null ? void 0 : _r.locality) != null ? _s : "", country: (_u = (_t = args.shippingContact) == null ? void 0 : _t.countryCode) != null ? _u : "", zipCode: (_w = (_v = args.shippingContact) == null ? void 0 : _v.postalCode) != null ? _w : "", name: shippingName }), billingAddress: create2(BillingAddressSchema, { name: (_A = (_x = args.billingContact) == null ? void 0 : _x.givenName) != null ? _A : " " + ((_z = (_y = args.billingContact) == null ? void 0 : _y.familyName) != null ? _z : ""), line1: (_D = (_C = (_B = args.billingContact) == null ? void 0 : _B.addressLines) == null ? void 0 : _C[0]) != null ? _D : "", city: (_F = (_E = args.billingContact) == null ? void 0 : _E.locality) != null ? _F : "", country: (_H = (_G = args.billingContact) == null ? void 0 : _G.countryCode) != null ? _H : "", zipCode: (_J = (_I = args.billingContact) == null ? void 0 : _I.postalCode) != null ? _J : "" }), metadata: Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : void 0 } ); return { isSuccess: result.isSuccess, redirectURL: result.redirectUrl }; } catch (error) { console.error(error); return Err("Failed to authorize payment"); } }, async completeGooglePayPayment(args) { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C; const checkout = getCheckout(); if (checkout == null) { return Err("Checkout not found"); } const products = await getProducts(projectID); if (isErr(products)) { return products; } try { let shippingName = (_a = args.paymentData.shippingAddress) == null ? void 0 : _a.name; let metadata = {}; const ref = getReferenceIfPossible(); if (ref != null) { metadata["ref"] = ref; } const result = await PublicApiClient.get(HOST).authorizeOneClickPayment( { gateway: 1 /* GOOGLE_PAY */, paymentData: args.paymentData.paymentMethodData.tokenizationData.token, paymentMethod: args.paymentData.paymentMethodData.type, transactionIdentifier: "", order: create2(GetPreOrderDetailsRequestSchema, { items: products.map((el) => ({ id: el.productID, quantity: el.quantity })), couponCode: checkout.couponCode, email: (_b = args.paymentData.email) != null ? _b : void 0, phone: (_d = (_c = args.paymentData.shippingAddress) == null ? void 0 : _c.phoneNumber) != null ? _d : void 0, selectedDeliveryMethod: (_f = (_e = args.paymentData.shippingOptionData) == null ? void 0 : _e.id) != null ? _f : checkout.selectedDeliveryMethod, postCode: (_g = args.paymentData.shippingAddress) == null ? void 0 : _g.postalCode, projectId: projectID }), shippingAddress: create2(AddressSchema, { line1: (_i = (_h = args.paymentData.shippingAddress) == null ? void 0 : _h.address1) != null ? _i : "", city: (_k = (_j = args.paymentData.shippingAddress) == null ? void 0 : _j.locality) != null ? _k : "", country: (_m = (_l = args.paymentData.shippingAddress) == null ? void 0 : _l.countryCode) != null ? _m : "", zipCode: (_o = (_n = args.paymentData.shippingAddress) == null ? void 0 : _n.postalCode) != null ? _o : "", name: shippingName }), billingAddress: create2(BillingAddressSchema, { name: (_q = (_p = args.paymentData.paymentMethodData.info) == null ? void 0 : _p.billingAddress) == null ? void 0 : _q.name, line1: (_t = (_s = (_r = args.paymentData.paymentMethodData.info) == null ? void 0 : _r.billingAddress) == null ? void 0 : _s.address1) != null ? _t : "", city: (_w = (_v = (_u = args.paymentData.paymentMethodData.info) == null ? void 0 : _u.billingAddress) == null ? void 0 : _v.locality) != null ? _w : "", country: (_z = (_y = (_x = args.paymentData.paymentMethodData.info) == null ? void 0 : _x.billingAddress) == null ? void 0 : _y.countryCode) != null ? _z : "", zipCode: (_C = (_B = (_A = args.paymentData.paymentMethodData.info) == null ? void 0 : _A.billingAddress) == null ? void 0 : _B.postalCode) != null ? _C : "" }), metadata: Object.keys(metadata).length > 0 ? JSON.stringify(metadata) : void 0 } ); return { isSuccess: result.isSuccess, redirectURL: result.redirectUrl }; } catch (error) { console.error(error); return Err("Failed to authorize payment"); } return { isSuccess: true }; }, async getApplePayPaymentRequest(args) { var _a, _b; const checkout = getCheckout(); if (checkout == null) { return Err("Checkout not found"); } if (args && args.expectedItems != null && args.expectedItems.length > 0) { if (checkout.products) { removeProductFromCheckout(checkout.products.map((el) => el.productID)); } addProducts(args.expectedItems); } const products = await getProducts(projectID); if (isErr(products)) { return products; } const result = await PublicApiClient.get(HOST).getPreOrderDetails({ items: products.map( (el) => create2(PreOrderItemSchema, { id: el.productID, quantity: el.quantity }) ), projectId: projectID, // we can detect 5 the closest inpost pickup point based on shipping address. postCode: checkout.postalCode, couponCode: checkout.couponCode, countryCode: checkout.country, selectedDeliveryMethod: checkout.selectedDeliveryMethod }); const items = products.map((el) => { var _a2, _b2, _c; return { amount: `${round(el.quantity * ((_b2 = (_a2 = el.details) == null ? void 0 : _a2.price) != null ? _b2 : 0) / 100, 2)}`, label: ((_c = el.details) == null ? void 0 : _c.name) + " - " + el.quantity, type: "final" }; }); if (result.selectedDeliveryMethod != null) { const deliveryMethod = result.methods.find((el) => el.id == result.selectedDeliveryMethod); if (deliveryMethod != null) { items.push({ amount: `${round(deliveryMethod.amount / 100, 2)}`, label: deliveryMethod.name, type: "final" }); } } if (result.couponCode != null && result.couponValue != null) { items.push({ amount: `-${round(result.couponValue / 100, 2)}`, label: result.couponCode, type: "final" }); } return { countryCode: (_a = checkout.country) != null ? _a : "PL", merchantCapabilities: [ "supports3DS", "supportsCredit", "supportsDebit" ], supportedNetworks: ["visa", "masterCard"], total: { amount: `${round(((_b = result.totalAmount) != null ? _b : 0) / 100, 2)}`, label: "P\u0142atno\u015B\u0107 za koszyk", type: "final" }, shippingMethods: result.methods.map((el) => { var _a2, _b2, _c; return { amount: `${round(el.amount / 100, 2)}`, detail: (_a2 = el.description) != null ? _a2 : "", identifier: el.id, label: el.name, dateComponentsRange: { startDateComponents: { days: (_b2 = el.minDays) != null ? _b2 : 1, hours: 0, months: 0, years: 0 }, endDateComponents: { days: (_c = el.maxDays) != null ? _c : 14, hours: 0, months: 0, years: 0 } } }; }), lineItems: items, currencyCode: "PLN" }; } }; }; async function resolveGooglePayConfiguration(projectID) { var _a; const checkout = getCheckout(); if (((_a = checkout.googlePayConfig) == null ? void 0 : _a.isAvailable) != true) { const result = await PublicApiClient.get(HOST).isOneClickPaymentAvailable({ projectId: projectID }); if (result.googlePay == true && result.googlePayConfig != null) { checkout.googlePayConfig = { isAvailable: true, gateway: result.googlePayConfig.gateway, gatewayId: result.googlePayConfig.gatewayMerchantId, merchantName: result.googlePayConfig.merchantName, merchantId: result.googlePayConfig.merchantId }; } else { checkout.googlePayConfig = { isAvailable: false, gateway: "", gatewayId: "", merchantName: "", merchantId: "" }; } } saveCheckout(checkout); return checkout.googlePayConfig; } function resolveProductDetailsFromSingleProduct(id, product) { var _a, _b, _c, _d; if (id == product.id) { const variantIndex = (_a = product.variants) == null ? void 0 : _a.findIndex((v) => v.id === id); if (variantIndex != null && variantIndex != -1) { const variant2 = product.variants[variantIndex]; return { ...product, availableQuantity: variant2.availableQuantity, isDeliverable: variant2.isDeliverable, images: (_b = variant2.images) != null ? _b : product.images, name: product.name + " " + variant2.name, price: variant2.price, currency: variant2.currency }; } return product; } const variant = (_c = product.variants) == null ? void 0 : _c.find((v) => v.id === id); if (variant == null) { return product; } return { ...product, id: variant.id, name: product.name + " " + variant.name, price: variant.price, currency: variant.currency, metadata: product.metadata, availableQuantity: variant.availableQuantity, isDeliverable: variant.isDeliverable, images: (_d = variant.images) != null ? _d : product.images }; } // src/components/checkout/ApplePay.tsx import { useQuery } from "react-query"; import { Fragment as Fragment2, jsx as jsx2 } from "react/jsx-runtime"; function ApplePayButtonComponent({ style, buttonType, colorType, items, onClick }) { const { checkIfApplePayIsAvailable, getApplePayPaymentRequest, completeApplePayPayment, setCountry, setCouponCodeIfPossible, setDeliveryMethod, setEmail, setPostalCode, clearCart } = useCartProvider(); const { data: isApplePayAvailable } = useQuery("is-apple-pay-available", checkIfApplePayIsAvailable); if (isApplePayAvailable != true) { return null; } const expectedItems = items.map((el) => ({ productID: el.id, quantity: el.quantity, metadata: el.metadata })); async function beginSession() { const requiresShipping = items.find((el) => el.isDeliverable === true) != null; const session = new ApplePaySession(14, { countryCode: "PL", merchantCapabilities: ["supports3DS"], supportedNetworks: ["visa", "masterCard"], currencyCode: "PLN", total: { amount: `${round(items.reduce((acc, item) => acc + item.price * item.quantity, 0) / 100, 2)}`, label: "P\u0142atno\u015B\u0107 za koszyk", type: "pending" }, lineItems: [ ...items.map((item) => ({ amount: `${round(item.price * item.quantity / 100, 2)}`, label: item.name, type: "final" })), requiresShipping ? { amount: `16.99`, type: "pending", label: "Dostawa" } : void 0 ].filter((el) => el != null), ...requiresShipping ? { shippingMethods: [], supportsCouponCode: true, requiredBillingContactFields: ["name", "email", "postalAddress", "phone"], requiredShippingContactFields: ["name", "email", "postalAddress", "phone"] } : {} }); await (onClick == null ? void 0 : onClick()); const apRequest = await getApplePayPaymentRequest({ expectedItems }); if (isErr(apRequest)) { console.error("Error creating ApplePaySession:", apRequest.error); return; } session.oncancel = (event) => { console.log("ApplePaySession cancelled", event); }; session.onvalidatemerchant = async (event) => { try { const result = await PublicApiClient.get(HOST).applePayValidateMerchant( { validationUrl: event.validationURL, projectId: getProjectID() } ); console.log("merchantSession", result.merchantSession); if (result.merchantSession.length > 0) { console.log("completing merchant session"); session.completeMerchantValidation( JSON.parse(result.merchantSession) ); } else { session.abort(); } } catch (error) { session.abort; } }; session.onshippingmethodselected = async (event) => { console.log("shipping method selected", event); await setDeliveryMethod(event.shippingMethod.identifier); const apRequest2 = await getApplePayPaymentRequest({ expectedItems }); console.log("apRequest", apRequest2); if (isErr(apRequest2)) { session.abort(); throw new Error("Failed to get Apple Pay payment request"); } session.completeShippingMethodSelection({ newTotal: apRequest2.total, newLineItems: apRequest2.lineItems, newShippingMethods: apRequest2.shippingMethods }); }; session.onshippingcontactselected = async (event) => { try { if (event.shippingContact.countryCode != null) { await setCountry(event.shippingContact.countryCode); } if (event.shippingContact.postalCode != null) { await setPostalCode(event.shippingContact.postalCode); } if (event.shippingContact.phoneNumber != null) { await setEmail(event.shippingContact.emailAddress); } const apRequest2 = await getApplePayPaymentRequest({ expectedItems }); if (isErr(apRequest2)) { session.abort(); throw new Error("Failed to get Apple Pay payment request"); } console.log("apRequest", apRequest2); session.completeShippingContactSelection({ newLineItems: apRequest2.lineItems, newTotal: apRequest2.total, newShippingMethods: apRequest2.shippingMethods }); } catch (error) { console.log("shipping contact selected error", error); session.abort(); } }; session.oncouponcodechanged = async (event) => { try { console.log("coupon code changed", event); const result = await setCouponCodeIfPossible(event.couponCode); const apRequest2 = await getApplePayPaymentRequest({ expectedItems }); console.log("apRequest", apRequest2); if (isErr(apRequest2)) { session.abort(); throw new Error("Failed to get Apple Pay payment request"); } session.completeCouponCodeChange({ errors: isErr(result) ? [new ApplePayError("couponCodeInvalid")] : void 0, newTotal: apRequest2.total, newLineItems: apRequest2.lineItems, newShippingMethods: apRequest2.shippingMethods }); } catch (error) { console.log("coupon code changed error", error); session.abort(); } }; session.onpaymentauthorized = async (event) => { try { const result = await completeApplePayPayment({ token: event.payment.token, billingContact: event.payment.billingContact, shippingContact: event.payment.shippingContact }); if (isErr(result)) { console.log("apple pay error", result); session.completePayment({ status: ApplePaySession.STATUS_FAILURE }); return; } if (result.redirectURL) { setTimeout(() => { open(result.redirectURL, "_self"); }, 2e3); } session.completePayment({ status: result.isSuccess ? ApplePaySession.STATUS_SUCCESS : ApplePaySession.STATUS_FAILURE }); result.isSuccess && clearCart(); } catch (e) { session.completePayment({ status: ApplePaySession.STATUS_FAILURE }); } }; session.begin(); } return /* @__PURE__ */ jsx2(ApplePayButton, { style, buttonStyle: colorType, type: buttonType, onClick: beginSession }); } function Wrapper(props) { return /* @__PURE__ */ jsx2(Fragment2, { children: /* @__PURE__ */ jsx2(CartProvider_default, { children: /* @__PURE__ */ jsx2(ApplePayButtonComponent, { ...props }) }) }); } var ApplePay_default = Wrapper; // src/components/checkout/GooglePay.tsx import GooglePayButton from "@google-pay/button-react"; import { useMutation, useQuery as useQuery2 } from "react-query"; import { Fragment as Fragment3, jsx as jsx3, jsxs } from "react/jsx-runtime"; function GooglePayButtonComponent({ items, style, buttonColor, buttonSizeMode, test }) { const { getGooglePayConfiguration, completeGooglePayPayment, setCountry, setCouponCodeIfPossible, setDeliveryMethod, setEmail, setPostalCode, clearCart } = useCartProvider(); const { data: googlePayConfiguration } = useQuery2(["get-google-pay-configuration", items.map((el) => el.id).join(",")], () => getGooglePayConfiguration({ items: items.map((el) => ({ ...el, id: void 0, productID: el.id })) })); const { mutateAsync: completeGooglePayPaymentAsync, isLoading } = useMutation(completeGooglePayPayment); if (googlePayConfiguration == null || googlePayConfiguration.isAvailable != true) { console.log("google pay configuration is not available", googlePayConfiguration); return null; } return /* @__PURE__ */ jsxs(Fragment3, { children: [ /* @__PURE__ */ jsx3( GooglePayButton, { buttonSizeMode, buttonColor, style, existingPaymentMethodRequired: true, environment: test === true ? "TEST" : "PRODUCTION", onPaymentDataChanged: async (paymentData) => { var _a, _b, _c, _d, _e; console.log("payment data changed", paymentData); if (((_a = paymentData.shippingAddress) == null ? void 0 : _a.postalCode) != null) { await setPostalCode((_b = paymentData.shippingAddress) == null ? void 0 : _b.postalCode); } if (((_c = paymentData.shippingAddress) == null ? void 0 : _c.countryCode) != null) { await setCountry((_d = paymentData.shippingAddress) == null ? void 0 : _d.countryCode); } if (((_e = paymentData.shippingOptionData) == null ? void 0 : _e.id) != null) { await setDeliveryMethod(paymentData.shippingOptionData.id); } const config = await getGooglePayConfiguration({ items: [] }); console.log("PAYMENT DATA CHANGED CONFIG", config); if (config.isAvailable) { return { error: void 0, newShippingOptionParameters: config.config.shippingOptionParameters, newTransactionInfo: config.config.transactionInfo }; } return { error: void 0 }; }, paymentRequest: googlePayConfiguration.config, onLoadPaymentData: async (paymentRequest) => { try { const result = await completeGooglePayPaymentAsync({ paymentData: paymentRequest }); if (isErr(result)) { console.log("apple pay error", result); return; } result.isSuccess && clearCart(); if (result.redirectURL) { open(result.redirectURL, "_self"); } } catch (e) { console.warn("error completing google pay payment", e); } } } ), isLoading ? "\u0141adowanie..." : null ] }); } function Wrapper2(props) { return /* @__PURE__ */ jsx3(Fragment3, { children: /* @__PURE__ */ jsx3(CartProvider_default, { children: /* @__PURE__ */ jsx3(GooglePayButtonComponent, { ...props }) }) }); } var GooglePay_default = Wrapper2; // src/components/checkout/BitsnapCart.tsx import { useEffect as useEffect3 } from "react"; import zod2 from "zod"; // src/components/checkout/CartComponent.tsx import { useAutoAnimate as useAutoAnimate2 } from "@formkit/auto-animate/react"; import { createPortal } from "react-dom"; // src/components/checkout/CartComponentContent.tsx import { useAutoAnimate } from "@formkit/auto-animate/react"; import React from "react"; import { useMutation as useMutation2, useQuery as useQuery3 } from "react-query"; // src/components/checkout/CountrySelector.tsx import { AnimatePresence, motion } from "framer-motion"; import { useEffect, useRef, useState } from "react"; import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime"; function CountrySelector({ id, open: open2, disabled = false, onToggle, onChange, selectedValue, countries }) { var _a; const ref = useRef(null); useEffect(() => { const mutableRef = ref; const handleClickOutside = (event) => { if (mutableRef.current && // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error !mutableRef.current.contains(event.target) && open2) { onToggle(); setQuery(""); } }; window.document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [ref]); useEffect(() => { if (selectedValue == null && countries.length > 0) { onChange(countries[0].code); } }, []); const [query, setQuery] = useState(""); return /* @__PURE__ */ jsx4("div", { ref, children: /* @__PURE__ */ jsxs2("div", { className: "relative", children: [ /* @__PURE__ */ jsxs2( "button", { type: "button", className: `${disabled ? "ics-bg-neutral-100" : "ics-bg-extra-light-white"} ics-relative ics-w-full ics-rounded-2xl ics-shadow-sm ics-pl-8 ics-pr-10 ics-py-3 ics-text-left ics-cursor-default focus:ics-outline-none focus:ics-ring-1 focus:ics-ring-blue-500 focus:ics-border-blue-500 sm:ics-text-sm`, "aria-haspopup": "listbox", "aria-expanded": "true", "aria-labelledby": "listbox-label", onClick: onToggle, disabled, children: [ /* @__PURE__ */ jsxs2("span", { className: "ics-truncate ics-flex ics-items-center", children: [ /* @__PURE__ */ jsx4( "img", { alt: `${selectedValue}`, src: `https://purecatamphetamine.github.io/country-flag-icons/3x2/${selectedValue}.svg`, className: "ics-inline ics-mr-2 ics-h-4 ics-rounded-sm" } ), (_a = countries.find((el) => el.code === selectedValue)) == null ? void 0 : _a.name ] }), /* @__PURE__ */ jsx4( "span", { className: `ics-absolute ics-inset-y-0 ics-right-0 ics-flex ics-items-center ics-pr-2 ics-pointer-events-none ${disabled ? "ics-hidden" : ""}`, children: /* @__PURE__ */ jsx4( "svg", { className: "h-5 w-5 text-light-purple", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx4( "path", { fillRule: "evenodd", d: "M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z", clipRule: "evenodd" } ) } ) } ) ] } ), /* @__PURE__ */ jsx4(AnimatePresence, { children: open2 && /* @__PURE__ */ jsxs2( motion.ul, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.1 }, className: "ics-absolute ics-z-10 -ics-mt-80 ics-w-full dark:ics-bg-neutral-800 ics-bg-white ics-shadow-lg ics-max-h-80 ics-rounded-md ics-text-body-regular ics-ring-1 ics-ring-black ics-ring-opacity-5 focus:ics-outline-none sm:ics-text-body-regular", tabIndex: -1, role: "listbox", "aria-labelledby": "listbox-label", "aria-activedescendant": "listbox-option-3", children: [ /* @__PURE__ */ jsxs2("div", { className: "ics-sticky ics-top-0 ics-z-10 ics-bg-white dark:ics-bg-neutral-800", children: [ /* @__PURE__ */ jsx4("li", { className: "dark:ics-text-neutral-200 ics-text-neutral-900 ics-cursor-default ics-select-none ics-relative ics-py-2 ics-px-3", chi