const { bigcommerceApiOAuth, bigcommerceStoreHash, channelId } = require("../config.js") /** * * @param {number} status */ function statusIsValid(status) { return !!status && status < 400 } /** * * @param {number} orderId * @returns {V2OrderProductsResponseBase[]} */ function getOrdersProducts(orderId) { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v2/orders/${orderId}/products`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText, } } return response.body.json() } /** * @param {number} orderId∂ * @returns {V2OrderResponseBase} */ function getOrderById(orderId) { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v2/orders/${orderId}`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText, } } /** * @type {V2OrderResponseBase} */ const order = response.body.json() order.productObjs = getOrdersProducts(orderId) order.shipping_addressObjs = getOrderShippingAddressesById(String(orderId)) return order } /** * * @param {number} orderId * @param {V2OrderResponseBase} order * @returns */ function updateOrderById(orderId, order) { delete order.id const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v2/orders/${orderId}`, { method: "PUT", headers: { "Content-Type": "application/json", Accept: "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, body: JSON.stringify(order), } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText, } } return response.body.json() } /** * @param {number} customerId */ function getOrdersForCustomer(customerId) { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v2/orders?customer_id=${customerId}`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText, } } /** * @type {V2OrderResponseBase[]} */ const orders = response.body.json() orders.forEach((order) => { order.productObjs = getOrdersProducts(order.id) order.shipping_addressObjs = getOrderShippingAddressesById(String(order.id)) }) // every order has date_created field, so we can sort by it (latest first) orders.sort((a, b) => new Date(b.date_created).getTime() - new Date(a.date_created).getTime()) return orders } /** * @returns {V2OrderProductsResponseBase[]} */ function getAllProducts() { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/catalog/products?limit=250`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText, } } return response.body.json().data } /** * @param {string} [orderId] * @returns {V2OrderShippingAddressesResponseBase[]} */ function getOrderShippingAddressesById(orderId) { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v2/orders/${orderId}/shipping_addresses`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText, } } return response.body.json() } /** * @param {string} [productId] * @returns {V2OrderProductsResponseBase} */ function getProductById(productId) { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/catalog/products/${productId}`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText, } } return response.body.json().data } /** * @param {string} orderId * @returns {V2OrderProductsResponseBase[]} */ function getOrderProductsById(orderId) { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v2/orders/${orderId}/products`, { method: "GET", headers: { "Content-Type": "application/json", Accept: "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText, } } return response.body.json() } /** * @param {string} [productId] * @returns {ProductImage[]} */ function getProductImages(productId) { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/catalog/products/${productId}/images`, { method: "GET", headers: { Accept: "application/json", "Content-Type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText, } } return response.body.json().data } /** * @param {string} [productId] * @param {string} [ratingId] */ function deleteRating(productId, ratingId) { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/catalog/products/${productId}/reviews/${ratingId}`, { method: "DELETE", headers: { Accept: "application/json", "Content-Type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText, } } } /** * @param {string} [cartId] * @returns {RestApiCart} */ function getCart(cartId) { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/carts/${cartId}`, { method: "GET", headers: { Accept: "application/json", "Content-Type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText + " cart", } } return response.body.json().data } /** * @param {string} [cartId] * @returns {any} */ function getRedirectUrl(cartId) { const response = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/carts/${cartId}/redirect_urls`, { method: "POST", headers: { accept: "application/json", "content-type": "application/json", "x-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(response.status)) { throw { status: response.status, error: response.statusText + "redirect_url", } } const data = response.body.json().data return { embeddedCheckoutURL: data?.embedded_checkout_url, checkoutURL: data?.checkout_url } } /** * * @param {string} cartId * @param { { merchandiseId: string; quantity: number; productId?: string }[]} items */ function addCartItem(cartId, items) { const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/carts/${cartId}/items`, { method: "POST", headers: { accept: "application/json", "content-type": "application/json", "x-Auth-Token": bigcommerceApiOAuth, }, body: JSON.stringify({ line_items: items }), } ) if (!statusIsValid(res.status)) throw { status: res.status, error: res.statusText, } return res.body.json().data } /** * * @param {string} cartId * @param {{ variant_id: string;product_id: string, quantity: number;} } item * @param {string} entityId * @returns {any} */ function updateCartItem(cartId, item, entityId) { const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/carts/${cartId}/items/${entityId}`, { method: "PUT", headers: { accept: "application/json", "content-type": "application/json", "x-Auth-Token": bigcommerceApiOAuth, }, body: JSON.stringify({ line_item: item }), } ) if (!statusIsValid(res.status)) throw { status: res.status, error: res.statusText, } return res.body.json().data } /** * * @param {string} cartId * @param {string} entityId * @returns {boolean} */ function deleteCartItem(cartId, entityId) { const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/carts/${cartId}/items/${entityId}`, { method: "DELETE", headers: { accept: "application/json", "content-type": "application/json", "x-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(res.status)) throw { status: res.status, error: res.statusText, } return true } /** * * @param {number} customerId * @param {string} email * @returns {BigCommerceCustomer} */ function getCustomerById(customerId, email = "", noException = false) { let res if (customerId) { res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/customers?id:in=${customerId}`, { method: "GET", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) } else { let encodedEmail = encodeURIComponent(email) res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/customers?email:in=${encodedEmail}`, { method: "GET", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) } if (!statusIsValid(res.status)) { if (noException) return null throw { status: res.status, error: res.statusText, } } if (res.body.json().data.length === 0) return null /** @type {BigCommerceCustomer} */ const customer = res.body.json().data[0] const formFields = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/customers/form-field-values?customer_id=${customer.id}`, { method: "GET", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(formFields.status)) { if (noException) return null throw { status: formFields.status, error: formFields.statusText, } } try { if (formFields.body && formFields.body.json()) customer.form_fields = formFields.body.json()?.data || [] } catch (e) {} return customer } /** * * @param {number} customerId * @param {number} addressId * @returns {BigCommerceAddress} */ function getCustomerAddressById(customerId, addressId) { const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/customers/addresses/?customer_id:in=${customerId}&id:in=${addressId}`, { method: "GET", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(res.status)) throw { status: res.status, error: res.statusText, } return res.body.json().data[0] } /** * * @param {number} customerId * @param {number} addressId * */ function deleteCustomerAddressById(customerId, addressId) { const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v2/customers/${customerId}/addresses/${addressId}`, { method: "DELETE", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) console.log(res.status, "status") if (!statusIsValid(res.status)) throw { status: res.status, error: res.statusText, } return true } /** * @param {BigCommerceAddress} address */ function updateCustomerAddressById(address) { const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/customers/addresses`, { method: "PUT", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, body: JSON.stringify([address]), } ) if (!statusIsValid(res.status)) throw { status: res.status, error: res.statusText, } return res.body.json().data[0] } /** * * @param {number} customerId * @returns {BigCommerceAddress[]} */ function getCustomerAddresses(customerId) { const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/customers/addresses?customer_id:in=${customerId}`, { method: "GET", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(res.status)) throw { status: res.status, error: res.statusText, } return res.body.json().data } /** * @param {BigCommerceAddress} address * @returns */ function addCustomerAddress(address) { const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/customers/addresses`, { method: "POST", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, body: JSON.stringify([address]), } ) if (!statusIsValid(res.status)) throw { status: res.status, error: res.statusText, } return res.body.json().data[0] } /** * * @param {string} email * @param {string} password * @returns {{is_valid: boolean}} */ function validateCredentials(email, password) { const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/customers/validate-credentials`, { method: "POST", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, body: JSON.stringify({ email, password, channelId, }), } ) if (res.status == 429) { throw { status: 429, error: "Too many requests", log: false, } } if (res.status == 422) { throw { status: 422, error: "Invalid email or password", log: false, } } if (!statusIsValid(res.status)) { throw { status: res.status, error: res.statusText, } } return res.body.json() } /** * * @param {Partial} customer * @returns {BigCommerceCustomer} */ function updateCustomer(customer) { const customerRes = context.http.fetch(`https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/customers`, { method: "PUT", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, body: JSON.stringify([customer]), }) if (!statusIsValid(customerRes.status)) throw { status: customerRes.status, error: customerRes.statusText, } return customerRes.body.json().data[0] } function generateJTI() { return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) } /** * * @param {number} customerId * @param {string} storeHash * @param {string} storeUrl * @param {string} clientId * @param {string} clientSecret * @param {string} [redirect_to] * @returns */ function getLoginUrl(customerId, storeHash, storeUrl, clientId, clientSecret, redirect_to = "") { const dateCreated = Math.round(new Date().getTime() / 1000) const payload = { iss: clientId, iat: dateCreated, jti: generateJTI(), operation: "customer_login", store_hash: storeHash, customer_id: customerId, redirect_to, } const token = context.jwt.create(payload, { secret: clientSecret, validityDuration: 600 }) return `${storeUrl}/login/token/${token}` } /** * @param {number} customerId * @returns {RestWishlist | false} */ function getWishlistEntries(customerId) { const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/wishlists?customer_id=${customerId}`, { method: "GET", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (res.status == 404) { return false } if (!statusIsValid(res.status)) { throw { status: res.status, error: res.statusText, } } if (res.body.json().data?.length === 0) return false return res.body.json().data[0] } /** * @param {number} customerId * @param {number} productId * @param {number} variantId * @returns {RestWishlist} */ function createWishlistEntry(productId, variantId, customerId) { const currentWishlist = getWishlistEntries(customerId) let res if (currentWishlist) { res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/wishlists/${currentWishlist?.id}/items`, { method: "POST", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, body: JSON.stringify({ items: [ { product_id: Number(productId), variant_id: Number(variantId), }, ], }), } ) } else { res = context.http.fetch(`https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/wishlists`, { method: "POST", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, body: JSON.stringify({ customer_id: Number(customerId), is_public: false, name: "Customer Wishlist", items: [ { product_id: Number(productId), variant_id: Number(variantId), }, ], }), }) } if (!statusIsValid(res.status)) { throw { status: res.status, error: res.statusText, } } return res.body.json().data } /** * @param {number} customerId * @param {number} productId * @param {number} variantId * @returns {boolean} */ function removeWishlistEntry(customerId, productId, variantId) { const currentWishlist = getWishlistEntries(customerId) if (!currentWishlist) { console.log("no wishlist at all") return true } const wishListEntry = currentWishlist.items.find( (item) => item.product_id == productId && item.variant_id == variantId ) if (!wishListEntry) { console.log("no wishlist entry") return true } const res = context.http.fetch( `https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/wishlists/${currentWishlist.id}/items/${wishListEntry.id}`, { method: "DELETE", headers: { accept: "application/json", "content-type": "application/json", "X-Auth-Token": bigcommerceApiOAuth, }, } ) if (!statusIsValid(res.status)) { throw { status: res.status, error: res.statusText, } } return true } /** * * @param {number} orderId * @returns {Partial} */ function createInternalOrderObject(orderId) { const orderProducts = getOrderProductsById(String(orderId)) const bigCommerceId = orderId const order = getOrderById(bigCommerceId) let [internalCustomer] = context.db.find("bigCommerceCustomer", { filter: { bigCommerceId: order?.customer_id, }, }) /** @type {Partial} */ const internalOrderReference = { bigCommerceId, customerBigCommerceId: order?.customer_id, cost: order?.total_inc_tax, customerTibiId: internalCustomer?.id, status: "draft", products: orderProducts.map((product) => { const internalProduct = context.db.find("bigCommerceProduct", { filter: { bigCommerceId: product.product_id, }, })[0] return { bigCommerceId: product.product_id, tibiId: internalProduct?.id, quantity: product.quantity, } }), } return internalOrderReference } module.exports = { updateCustomer, getOrderById, getOrderProductsById, getProductImages, getProductById, getOrderShippingAddressesById, deleteRating, getCart, getRedirectUrl, addCartItem, deleteCartItem, updateCartItem, getCustomerById, validateCredentials, updateCustomerAddressById, deleteCustomerAddressById, getCustomerAddressById, getCustomerAddresses, addCustomerAddress, getLoginUrl, getOrdersForCustomer, removeWishlistEntry, createWishlistEntry, getWishlistEntries, updateOrderById, getAllProducts, createInternalOrderObject, }