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
JavaScript
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