UNPKG

@wallfar/ocd-studio-core-sdk

Version:

Helper SDK for our OneClick Studio modules

196 lines (191 loc) 7.71 kB
import { v as validateAddress } from './shared/ocd-studio-core-sdk.ChPIrm-j.mjs'; import Stripe from 'stripe'; async function validatePromoCode(config, code) { try { const { db, promoCodes: promo_codes_collection } = config; let result = await db.collection(promo_codes_collection).where("code", "==", code).get(); if (result.empty || !result?.docs?.[0]) { throw new Error(); } let promo = result.docs[0].data(); if (promo.disabled) throw new Error(); if (promo.usageLimit !== 0 && promo.usageLimit <= promo.usageCount) throw new Error("Code is already used"); if (promo.startsAt?.seconds && new Date(promo.startsAt.seconds * 1e3) > /* @__PURE__ */ new Date()) throw new Error(); if (promo.expiresAt?.seconds && new Date(promo.expiresAt.seconds * 1e3) < /* @__PURE__ */ new Date()) throw new Error(); return { valid: true, promo: { id: result.docs[0].id, code: promo.code, type: promo.type, value: promo.value, minOrderAmount: promo.minOrderAmount } }; } catch (error) { return { valid: false, error: error.message || "Code is invalid" }; } } async function createPaymentLinkStripe(config, stripeConfig, checkoutData) { if (!stripeConfig.key) throw new Error("No stripe key provided"); const stripe = new Stripe(stripeConfig.key); const { cart, shippingOption, activePromoCode, deliveryAddress } = checkoutData; if (!cart || cart.length === 0) throw new Error("No products in cart"); if (!shippingOption) throw new Error("No shipping option selected"); if (!deliveryAddress) throw new Error("No delivery address"); let subtotal = 0; let discountObject = null; const products = await Promise.all(cart.map(async (item) => { const product = await config.db.collection(config.products).doc(item.productId).get(); return { id: product.id, ...product.data() }; })); cart.forEach((cart_item) => { let product = products.find((i) => i.id == cart_item.productId); let stock = product.stock, price = product.price; if (Array.isArray(product.options) && product.options.length > 0) { let variant = getVariant(product, cart_item.variant); stock = variant.stock; price = variant.price; } let new_item = { id: cart_item.productId, slug: product.slug, title: product.title, featuredMedia: product.featuredMedia, variant: cart_item.variant, quantity: stock < cart_item.quantity ? stock : cart_item.quantity, price }; subtotal += new_item.quantity * new_item.price; }); if (activePromoCode) { let { valid, promo, error } = await validatePromoCode(config, activePromoCode); if (!valid) throw new Error(error); discountObject = promo; } const { valid: isValidShippingOption, price: shippingFee } = await validateShippingOption(config, shippingOption, deliveryAddress, subtotal); if (!isValidShippingOption) throw new Error("Invalid shipping option"); const isValidAddress = validateAddress(deliveryAddress); if (isValidAddress.length > 0) throw new Error(isValidAddress.join(", ")); const discount = discountObject ? calculateDiscount(discountObject, subtotal) : 0; let total = subtotal - discount + (shippingFee || 0); if (total <= 0) total = 0; const session = await stripe.checkout.sessions.create({ customer_email: "maxim@wallfar.com", payment_method_types: ["card", "bancontact", "ideal", "sofort"], line_items: [{ price_data: { currency: "eur", unit_amount: parseInt((total * 100).toFixed(0)), product_data: { name: "Order", images: [] } }, quantity: 1 }], mode: "payment", success_url: "https://soakhandmade.be/checkout/success", cancel_url: "https://soakhandmade.be/checkout/cancel", metadata: { fbase_order_id: "fbase_order_id" }, payment_intent_data: { metadata: { fbase_order_id: "fbase_order_id" } } }); if (!session.url) throw new Error("Could not create payment link"); return session.url; } async function validateShippingOption(config, optionId, deliveryAddress, subtotal) { const shippingOptions = await getAllShippingOptions(config); const shippingOption = shippingOptions?.find((opt) => opt.id === optionId); if (!shippingOption) return { valid: false }; if (shippingOption.status !== "published") return { valid: false }; const validCountry = !shippingOption.countries || shippingOption.countries.length === 0 || shippingOption.countries.includes(deliveryAddress.country); if (!validCountry) return { valid: false }; let validCondition = shippingOption.condition === "always"; if (!validCondition) { if (shippingOption.condition === "price_based") { const MIN = shippingOption.condition_min || 0; const MAX = shippingOption.condition_max || Infinity; validCondition = subtotal >= MIN && subtotal <= MAX; } else if (shippingOption.condition === "weight_based") ; } return { valid: validCountry && validCondition, price: shippingOption.price || 0 }; } async function getAllShippingOptions(config) { const { shippingOptions: shipping_options_collection } = config; if (!shipping_options_collection) return null; if (shipping_options_collection.includes(":")) { const collectionParts = shipping_options_collection.split(":"); const pathParts = collectionParts[0].split("/"); const path = pathParts.slice(0, pathParts.length - 1); const docId = pathParts[pathParts.length - 1]; const docSnap = await config.db.collection(path.join("/")).doc(docId).get(); const options = docSnap.data()?.[collectionParts[1]]; return options?.filter((opt) => opt.status === "published") || null; } else { const snapshot = await config.db.collection(shipping_options_collection).get(); return !snapshot.empty ? snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() })).filter((opt) => opt.status === "published") : null; } } function getVariant(product, variant) { if (!product.variants || product.variants?.length === 0) { return { id: product.id, options: [], combinedOptions: "", stock: product.stock, price: product.price, featuredMedia: product.featuredMedia, disabled: false, title: product.title, slug: product.slug }; } return product.variants?.find( (v) => v.options.every((o) => o.value === variant[o.option]) ); } function calculateDiscount(promo, subtotal) { if (!promo) return 0; if (promo.type === "percentage") { return subtotal * ((promo.value || 0) / 100) || 0; } if (promo.type === "fixed") { return promo.value || 0; } return 0; } class WebshopServer { config; constructor(config) { this.config = config; if (!this.config.defaultBrand) this.config.defaultBrand = ""; if (!this.config.defaultCurrency) this.config.defaultCurrency = "USD"; if (!this.config.defaultLocale) this.config.defaultLocale = "en-US"; } async validatePromoCode(code) { if (this.config.provider === "firebase") { return await validatePromoCode(this.config.firebase, code); } throw new Error(`Provider ${this.config.provider} not supported.`); } async createPaymentLink(checkoutData) { if (this.config.provider === "firebase" && this.config.paymentProvider === "stripe") { return await createPaymentLinkStripe(this.config.firebase, this.config.stripe, checkoutData); } throw new Error(`Provider ${this.config.provider} not supported.`); } } const studioComponentsKey = Symbol("studioComponents"); export { WebshopServer, studioComponentsKey };