install
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
namespace: bkdf_tibi_2024
|
||||
namespace: kontextwerk
|
||||
|
||||
meta:
|
||||
openapi:
|
||||
|
||||
@@ -1,919 +0,0 @@
|
||||
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<BigCommerceCustomer>} 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<Order>}
|
||||
*/
|
||||
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<Order>} */
|
||||
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,
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
const { DeepLkey } = require("../config.js")
|
||||
const { statusIsValid } = require("./utils.js")
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} text
|
||||
* @returns {string}
|
||||
*/
|
||||
function translateText(text) {
|
||||
const response = context.http.fetch(`https://api.deepl.com/v2/translate`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
Authorization: `DeepL-Auth-Key ${DeepLkey}`,
|
||||
"Content-Length": String(text.length),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
text: [text],
|
||||
target_lang: "DE",
|
||||
formality: "less",
|
||||
context:
|
||||
"Es handelt sich um eine Produktbeschreibung - insbesondere um die Größenbeschreibungen. Dinge wie A Länge, B Breite, C Ärmellänge etc. sollten präzise übersetzt werden, da die buchstaben hier auf ein Bild verweisen, welches du nicht kennst!",
|
||||
}),
|
||||
})
|
||||
|
||||
if (!statusIsValid(response.status)) {
|
||||
console.log(JSON.stringify(response), JSON.stringify(response.body.json()))
|
||||
throw {
|
||||
status: response.status,
|
||||
error: response.statusText,
|
||||
}
|
||||
}
|
||||
const res = response.body.json()
|
||||
const textMerged = res.translations.map((t) => t.text).join(" ")
|
||||
return textMerged
|
||||
}
|
||||
module.exports = {
|
||||
translateText,
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
const { fb_accessToken } = require("../config")
|
||||
|
||||
function postAddToCart() {
|
||||
const request = context.request()
|
||||
const headers = request.header("User-Agent")
|
||||
const ipAddress = request.clientIp()
|
||||
|
||||
const eventPayload = {
|
||||
data: [
|
||||
{
|
||||
event_name: "AddToCart",
|
||||
event_time: Math.floor(Date.now() / 1000),
|
||||
event_source_url: "https://www.binkrassdufass.de",
|
||||
action_source: "website",
|
||||
user_data: {
|
||||
client_user_agent: headers,
|
||||
client_ip_address: ipAddress,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
const res = context.http.fetch(
|
||||
"https://graph.facebook.com/v16.0/1117933239951751/events?access_token=" + fb_accessToken,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(eventPayload),
|
||||
}
|
||||
)
|
||||
console.log(JSON.stringify(res), "CARTADD")
|
||||
const response = res.body.json()
|
||||
console.log(JSON.stringify(response))
|
||||
}
|
||||
|
||||
function postUserRegister() {
|
||||
const request = context.request()
|
||||
const headers = request.header("User-Agent")
|
||||
const ipAddress = request.clientIp()
|
||||
const eventPayload = {
|
||||
data: [
|
||||
{
|
||||
event_name: "CompleteRegistration",
|
||||
event_time: Math.floor(Date.now() / 1000),
|
||||
event_source_url: "https://www.binkrassdufass.de",
|
||||
action_source: "website",
|
||||
user_data: {
|
||||
client_user_agent: headers,
|
||||
client_ip_address: ipAddress,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
const res = context.http.fetch(
|
||||
"https://graph.facebook.com/v16.0/1117933239951751/events?access_token=" + fb_accessToken,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(eventPayload),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} total
|
||||
*/
|
||||
function postPurchase(total) {
|
||||
const eventPayload = {
|
||||
data: [
|
||||
{
|
||||
event_name: "Purchase",
|
||||
event_time: Math.floor(Date.now() / 1000),
|
||||
event_source_url: "https://www.binkrassdufass.de",
|
||||
action_source: "website",
|
||||
user_data: {
|
||||
// dummy data bc inside webhook we don't have access to user data
|
||||
client_user_agent:
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||
client_ip_address: "192.168.1.1", // random IP address
|
||||
},
|
||||
custom_data: {
|
||||
currency: "EUR",
|
||||
value: total,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
const res = context.http.fetch(
|
||||
"https://graph.facebook.com/v12.0/1117933239951751/events?access_token=" + fb_accessToken,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(eventPayload),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
postAddToCart,
|
||||
postUserRegister,
|
||||
postPurchase,
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
const { omnisendApiKey } = require("../config.js")
|
||||
const { statusIsValid } = require("./utils.js")
|
||||
/**
|
||||
*
|
||||
* @param {string} email
|
||||
*/
|
||||
function createNewsletterSubscriber(email) {
|
||||
const res = context.http.fetch("https://api.omnisend.com/v5/contacts", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"X-API-KEY": omnisendApiKey,
|
||||
accept: "application/json",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
createdAt: new Date().toISOString(),
|
||||
identifiers: [
|
||||
{
|
||||
channels: {
|
||||
email: {
|
||||
status: "subscribed",
|
||||
statusDate: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
consent: {
|
||||
createdAt: new Date().toISOString(),
|
||||
source: "headless-frontend",
|
||||
},
|
||||
id: email,
|
||||
type: "email",
|
||||
},
|
||||
],
|
||||
}),
|
||||
})
|
||||
if (!statusIsValid(res.status)) {
|
||||
throw {
|
||||
status: res.status,
|
||||
error: res.statusText,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} email
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function checkIfNewsletterSubscriber(email) {
|
||||
const res = context.http.fetch("https://api.omnisend.com/v5/contacts", {
|
||||
headers: {
|
||||
"X-API-KEY": omnisendApiKey,
|
||||
accept: "application/json",
|
||||
},
|
||||
})
|
||||
const resJson = res.body.json()
|
||||
return resJson.contacts.some((contact) => contact.email === email)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createNewsletterSubscriber,
|
||||
checkIfNewsletterSubscriber,
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
const { printfulAPIToken, serverBaseURL } = require("../config")
|
||||
const { translateText } = require("./deepLRestApi")
|
||||
const { statusIsValid } = require("./utils")
|
||||
/**
|
||||
*
|
||||
* @param {number} bigCommerceOrderId
|
||||
*/
|
||||
function getPrintfulOrder(bigCommerceOrderId) {
|
||||
const response = context.http.fetch("https://api.printful.com/v2/orders/@" + bigCommerceOrderId, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${printfulAPIToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!statusIsValid(response.status)) {
|
||||
throw {
|
||||
status: response.status,
|
||||
error: response.statusText,
|
||||
}
|
||||
}
|
||||
return response.body.json().data
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} bigCommerceOrderId
|
||||
*/
|
||||
function getPrintfulOrderShipments(bigCommerceOrderId) {
|
||||
const response = context.http.fetch(`https://api.printful.com/v2/orders/@${bigCommerceOrderId}/shipments`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${printfulAPIToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!statusIsValid(response.status)) {
|
||||
throw {
|
||||
status: response.status,
|
||||
error: response.statusText,
|
||||
}
|
||||
}
|
||||
return response.body.json().data
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} bigCommerceOrderId
|
||||
* @param {any} data
|
||||
* @returns
|
||||
*/
|
||||
function patchPrintfulOrder(bigCommerceOrderId, data) {
|
||||
const response = context.http.fetch("https://api.printful.com/v2/orders/@" + bigCommerceOrderId, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${printfulAPIToken}`,
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
if (!statusIsValid(response.status)) {
|
||||
throw {
|
||||
status: response.status,
|
||||
error: response.statusText,
|
||||
}
|
||||
}
|
||||
return response.body.json()
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {string} type
|
||||
*/
|
||||
function createPrintfulWebhook(type) {
|
||||
const response = context.http.fetch("https://api.printful.com/v2/webhooks/" + type, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${printfulAPIToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type,
|
||||
url: `${serverBaseURL}webhook`,
|
||||
}),
|
||||
})
|
||||
console.log("response:", response.status)
|
||||
if (!statusIsValid(response.status)) {
|
||||
console.log("wtf?!")
|
||||
throw {
|
||||
status: response.status,
|
||||
error: response.statusText,
|
||||
}
|
||||
}
|
||||
return response.body.json()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
|
||||
*/
|
||||
function deletePrintfulWebhook(type) {
|
||||
const response = context.http.fetch("https://api.printful.com/v2/webhooks/" + type, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${printfulAPIToken}`,
|
||||
},
|
||||
})
|
||||
if (!statusIsValid(response.status)) {
|
||||
throw {
|
||||
status: response.status,
|
||||
error: response.statusText,
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} bigCommerceOrderId
|
||||
|
||||
*/
|
||||
|
||||
function cancelPrintfulOrder(bigCommerceOrderId) {
|
||||
const response = context.http.fetch("https://api.printful.com/orders/@" + bigCommerceOrderId, {
|
||||
method: "delete",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${printfulAPIToken}`,
|
||||
},
|
||||
})
|
||||
if (!statusIsValid(response.status)) {
|
||||
throw {
|
||||
status: response.status,
|
||||
error: response.statusText,
|
||||
}
|
||||
}
|
||||
return response.body.json()
|
||||
}
|
||||
/**
|
||||
* @param {string} printfulProductId
|
||||
*/
|
||||
function getPrintfulProductSizingChart(printfulProductId) {
|
||||
const response = context.http.fetch(
|
||||
`https://api.printful.com/v2/catalog-products/${printfulProductId}/sizes?unit=cm`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${printfulAPIToken}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
if (!statusIsValid(response.status)) {
|
||||
throw {
|
||||
status: response.status,
|
||||
error: response.statusText,
|
||||
}
|
||||
}
|
||||
return response.body.json().data
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} printfulProductId
|
||||
* @param {string} productId
|
||||
* @returns
|
||||
*/
|
||||
function extractSizingChart(printfulProductId, productId) {
|
||||
if (!printfulProductId) return null
|
||||
|
||||
const productSizingChart = getPrintfulProductSizingChart(printfulProductId)
|
||||
const sizeTable = productSizingChart.size_tables.find((t) => t.type === "measure_yourself")
|
||||
|
||||
if (!sizeTable) {
|
||||
throw {
|
||||
status: 404,
|
||||
message: "No sizing chart found for this product",
|
||||
}
|
||||
}
|
||||
|
||||
const productSizingImageDescriptionTranslation = translateText(sizeTable.image_description)
|
||||
const generalProductSizingDescription = translateText(sizeTable.description)
|
||||
|
||||
return {
|
||||
imageURL: sizeTable.image_url,
|
||||
imageDescription: productSizingImageDescriptionTranslation,
|
||||
availableSizes: productSizingChart.available_sizes,
|
||||
generalDescription: generalProductSizingDescription,
|
||||
columns: sizeTable.measurements.map((m) => {
|
||||
let label = context.db.find("module", {
|
||||
filter: {
|
||||
label: m.type_label,
|
||||
type: "sizeLabel",
|
||||
},
|
||||
})
|
||||
|
||||
if (label.length === 0) {
|
||||
const germanLabelTranslation = translateText(m.type_label)
|
||||
label[0] = context.db.create("module", {
|
||||
label: m.type_label,
|
||||
type: "sizeLabel",
|
||||
germanLabelTranslation: germanLabelTranslation,
|
||||
})
|
||||
} else {
|
||||
label = [...label]
|
||||
}
|
||||
|
||||
return {
|
||||
label: label[0].label,
|
||||
sizes: m.values.map((v) => {
|
||||
if (v.min_value && v.max_value) {
|
||||
if (sizeTable.unit === "inches") {
|
||||
return `${Math.round(Number(v.min_value) * 2.54)} - ${Math.round(
|
||||
Number(v.max_value) * 2.54
|
||||
)}`
|
||||
}
|
||||
return `${v.min_value} - ${v.max_value}`
|
||||
}
|
||||
if (sizeTable.unit === "inches") {
|
||||
return String(Math.round(Number(v.value) * 2.54))
|
||||
}
|
||||
return v.value
|
||||
}),
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
getPrintfulOrder,
|
||||
patchPrintfulOrder,
|
||||
createPrintfulWebhook,
|
||||
deletePrintfulWebhook,
|
||||
cancelPrintfulOrder,
|
||||
getPrintfulProductSizingChart,
|
||||
getPrintfulOrderShipments,
|
||||
extractSizingChart,
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
const { withAccount } = require("../lib/utils")
|
||||
const { getWishlistEntries } = require("../lib/bigcommerceRestAPI.js")
|
||||
;(function () {
|
||||
withAccount((login) => {
|
||||
const customerId = login.bigCommerceId
|
||||
const wishlist = getWishlistEntries(customerId)
|
||||
throw {
|
||||
status: 200,
|
||||
data: wishlist,
|
||||
}
|
||||
})
|
||||
})()
|
||||
@@ -1,35 +0,0 @@
|
||||
const { removeWishlistEntry } = require("../lib/bigcommerceRestAPI.js")
|
||||
const { withAccount } = require("../lib/utils")
|
||||
const { createWishlistEntry } = require("../lib/bigcommerceRestAPI.js")
|
||||
;(function () {
|
||||
withAccount((login) => {
|
||||
const productId = context.data.productId
|
||||
const variantId = context.data.variantId
|
||||
const customerId = login.bigCommerceId
|
||||
if (context.data.delete) {
|
||||
if (!productId || !variantId) {
|
||||
throw {
|
||||
message: "Invalid product or variant id",
|
||||
code: 400,
|
||||
}
|
||||
}
|
||||
const wishlist = removeWishlistEntry(customerId, productId, variantId)
|
||||
throw {
|
||||
status: 200,
|
||||
data: wishlist,
|
||||
}
|
||||
}
|
||||
|
||||
if (!productId || !variantId) {
|
||||
throw {
|
||||
message: "Invalid product or variant id",
|
||||
code: 400,
|
||||
}
|
||||
}
|
||||
const wishlist = createWishlistEntry(productId, variantId, customerId)
|
||||
throw {
|
||||
status: 200,
|
||||
data: wishlist,
|
||||
}
|
||||
})
|
||||
})()
|
||||
Reference in New Issue
Block a user