medusa-storefront-data 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +31 -0
- package/dist/cookies.d.ts +23 -0
- package/dist/cookies.d.ts.map +1 -0
- package/dist/cookies.js +140 -0
- package/dist/server/cart.d.ts +92 -0
- package/dist/server/cart.d.ts.map +1 -0
- package/dist/server/cart.js +827 -0
- package/dist/server/categories.d.ts +3 -0
- package/dist/server/categories.d.ts.map +1 -0
- package/dist/server/categories.js +71 -0
- package/dist/server/collections.d.ts +8 -0
- package/dist/server/collections.d.ts.map +1 -0
- package/dist/server/collections.js +84 -0
- package/dist/server/customer-registration.d.ts +142 -0
- package/dist/server/customer-registration.d.ts.map +1 -0
- package/dist/server/customer-registration.js +295 -0
- package/dist/server/customer.d.ts +48 -0
- package/dist/server/customer.d.ts.map +1 -0
- package/dist/server/customer.js +462 -0
- package/dist/server/dynamic-config.d.ts +125 -0
- package/dist/server/dynamic-config.d.ts.map +1 -0
- package/dist/server/dynamic-config.js +263 -0
- package/dist/server/fulfillment.d.ts +4 -0
- package/dist/server/fulfillment.d.ts.map +1 -0
- package/dist/server/fulfillment.js +72 -0
- package/dist/server/guest.d.ts +109 -0
- package/dist/server/guest.d.ts.map +1 -0
- package/dist/server/guest.js +304 -0
- package/dist/server/index.d.ts +21 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +20 -0
- package/dist/server/locale-actions.d.ts +14 -0
- package/dist/server/locale-actions.d.ts.map +1 -0
- package/dist/server/locale-actions.js +63 -0
- package/dist/server/locales.d.ts +10 -0
- package/dist/server/locales.d.ts.map +1 -0
- package/dist/server/locales.js +20 -0
- package/dist/server/notifications.d.ts +2 -0
- package/dist/server/notifications.d.ts.map +1 -0
- package/dist/server/notifications.js +20 -0
- package/dist/server/onboarding.d.ts +2 -0
- package/dist/server/onboarding.d.ts.map +1 -0
- package/dist/server/onboarding.js +8 -0
- package/dist/server/orders.d.ts +69 -0
- package/dist/server/orders.d.ts.map +1 -0
- package/dist/server/orders.js +371 -0
- package/dist/server/payment-details.d.ts +5 -0
- package/dist/server/payment-details.d.ts.map +1 -0
- package/dist/server/payment-details.js +53 -0
- package/dist/server/payment.d.ts +2 -0
- package/dist/server/payment.d.ts.map +1 -0
- package/dist/server/payment.js +25 -0
- package/dist/server/products.d.ts +58 -0
- package/dist/server/products.d.ts.map +1 -0
- package/dist/server/products.js +285 -0
- package/dist/server/regions.d.ts +5 -0
- package/dist/server/regions.d.ts.map +1 -0
- package/dist/server/regions.js +54 -0
- package/dist/server/returns.d.ts +29 -0
- package/dist/server/returns.d.ts.map +1 -0
- package/dist/server/returns.js +236 -0
- package/dist/server/swaps.d.ts +14 -0
- package/dist/server/swaps.d.ts.map +1 -0
- package/dist/server/swaps.js +123 -0
- package/dist/server/variants.d.ts +3 -0
- package/dist/server/variants.d.ts.map +1 -0
- package/dist/server/variants.js +26 -0
- package/dist/util/get-locale-header.d.ts +4 -0
- package/dist/util/get-locale-header.d.ts.map +1 -0
- package/dist/util/get-locale-header.js +7 -0
- package/dist/util/medusa-error.d.ts +2 -0
- package/dist/util/medusa-error.d.ts.map +1 -0
- package/dist/util/medusa-error.js +18 -0
- package/package.json +152 -0
- package/src/config.ts +39 -0
- package/src/cookies.ts +171 -0
- package/src/middleware.ts +2 -0
- package/src/server/cart.ts +1054 -0
- package/src/server/categories.ts +94 -0
- package/src/server/collections.ts +113 -0
- package/src/server/customer-registration.ts +349 -0
- package/src/server/customer.ts +581 -0
- package/src/server/dynamic-config.ts +403 -0
- package/src/server/fulfillment.ts +97 -0
- package/src/server/guest.ts +333 -0
- package/src/server/index.ts +21 -0
- package/src/server/locale-actions.ts +74 -0
- package/src/server/locales.ts +28 -0
- package/src/server/notifications.ts +22 -0
- package/src/server/onboarding.ts +9 -0
- package/src/server/orders.ts +467 -0
- package/src/server/payment-details.ts +69 -0
- package/src/server/payment.ts +35 -0
- package/src/server/products.ts +378 -0
- package/src/server/regions.ts +66 -0
- package/src/server/returns.ts +294 -0
- package/src/server/swaps.ts +150 -0
- package/src/server/variants.ts +38 -0
- package/src/server/wishlist.ts +64 -0
- package/src/services/middleware.ts +54 -0
- package/src/util/get-locale-header.ts +8 -0
- package/src/util/medusa-error.ts +19 -0
- package/src/util/sort-products.ts +47 -0
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import { sdk } from "../config";
|
|
3
|
+
import medusaError from "../util/medusa-error";
|
|
4
|
+
import { revalidateTag, revalidatePath } from "next/cache";
|
|
5
|
+
import { redirect } from "next/navigation";
|
|
6
|
+
import { getAuthHeaders, getCacheOptions, getCacheTag, getCartId, removeCartId, setCartId, getHoldCartId, setBuyNowCartId, removeBuyNowCartId, } from "../cookies";
|
|
7
|
+
import { getRegion } from "./regions";
|
|
8
|
+
import { getLocale } from "./locale-actions";
|
|
9
|
+
import { retrieveCustomer } from "./customer";
|
|
10
|
+
import { cache } from "react";
|
|
11
|
+
/**
|
|
12
|
+
* Retrieves a cart by its ID. If no ID is provided, it will use the cart ID from the cookies.
|
|
13
|
+
* @param cartId - optional - The ID of the cart to retrieve.
|
|
14
|
+
* @returns The cart object if found, or null if not found.
|
|
15
|
+
*/
|
|
16
|
+
export const retrieveCart = cache(async (cartId, fields) => {
|
|
17
|
+
let id = cartId || (await getCartId());
|
|
18
|
+
const heldId = await getHoldCartId();
|
|
19
|
+
// AUTO-RESTORE LOGIC:
|
|
20
|
+
// If we're not explicitly asking for a specific cartId and we have an active cart
|
|
21
|
+
if (!cartId && id) {
|
|
22
|
+
try {
|
|
23
|
+
const headerList = await headers();
|
|
24
|
+
const pathname = headerList.get("x-pathname") || headerList.get("referer") || "";
|
|
25
|
+
// Skip metadata check if we are clearly in checkout to avoid extra API call
|
|
26
|
+
if (!pathname.includes("/checkout")) {
|
|
27
|
+
const currentCart = await sdk.client
|
|
28
|
+
.fetch(`/store/carts/${id}`, {
|
|
29
|
+
method: "GET",
|
|
30
|
+
query: { fields: "metadata" },
|
|
31
|
+
headers: await getAuthHeaders(),
|
|
32
|
+
cache: "no-store",
|
|
33
|
+
})
|
|
34
|
+
.then(({ cart }) => cart)
|
|
35
|
+
.catch(() => null);
|
|
36
|
+
if (currentCart?.metadata?.is_buy_now || currentCart?.metadata?.is_reorder) {
|
|
37
|
+
await removeBuyNowCartId();
|
|
38
|
+
id = await getCartId(); // Re-fetch ID which will now fall back to the real main cart
|
|
39
|
+
revalidateTag("carts");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
// Silent fail
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
fields ??=
|
|
48
|
+
"*items, *region, *items.product, *items.product.thumbnail, *items.product.images, *items.product.options, *items.product.variants, +items.product.variants.inventory_quantity, +items.product.variants.manage_inventory, +items.product.variants.allow_backorder, *items.product.variants.options, *items.variant, +items.variant.inventory_quantity, +items.variant.manage_inventory, +items.variant.allow_backorder, *items.variant.images, *items.variant.product, *items.variant.product.thumbnail, *items.variant.product.images, *items.variant.options, *items.thumbnail, *items.metadata, +items.total, +items.adjustments, *promotions, +shipping_methods.name, +shipping_methods.adjustments";
|
|
49
|
+
if (!id) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const headers = {
|
|
53
|
+
...(await getAuthHeaders()),
|
|
54
|
+
};
|
|
55
|
+
const next = {
|
|
56
|
+
...(await getCacheOptions("carts")),
|
|
57
|
+
};
|
|
58
|
+
return await sdk.client
|
|
59
|
+
.fetch(`/store/carts/${id}`, {
|
|
60
|
+
method: "GET",
|
|
61
|
+
query: {
|
|
62
|
+
fields,
|
|
63
|
+
},
|
|
64
|
+
headers,
|
|
65
|
+
next,
|
|
66
|
+
cache: "no-store",
|
|
67
|
+
})
|
|
68
|
+
.then(({ cart }) => {
|
|
69
|
+
return cart;
|
|
70
|
+
})
|
|
71
|
+
.catch(() => null);
|
|
72
|
+
});
|
|
73
|
+
export async function getOrSetCart(countryCode) {
|
|
74
|
+
const region = await getRegion(countryCode);
|
|
75
|
+
if (!region) {
|
|
76
|
+
throw new Error(`Region not found for country code: ${countryCode}`);
|
|
77
|
+
}
|
|
78
|
+
let cart = await retrieveCart(undefined, "id,region_id");
|
|
79
|
+
const headers = {
|
|
80
|
+
...(await getAuthHeaders()),
|
|
81
|
+
};
|
|
82
|
+
if (!cart) {
|
|
83
|
+
const locale = await getLocale();
|
|
84
|
+
const cartResp = await sdk.store.cart.create({ region_id: region.id, locale: locale || undefined }, {}, headers);
|
|
85
|
+
cart = cartResp.cart;
|
|
86
|
+
await setCartId(cart.id);
|
|
87
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
88
|
+
revalidateTag(cartCacheTag);
|
|
89
|
+
}
|
|
90
|
+
if (cart && cart?.region_id !== region.id) {
|
|
91
|
+
await sdk.store.cart.update(cart.id, { region_id: region.id }, {}, headers);
|
|
92
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
93
|
+
revalidateTag(cartCacheTag);
|
|
94
|
+
}
|
|
95
|
+
return cart;
|
|
96
|
+
}
|
|
97
|
+
export async function updateCart(data) {
|
|
98
|
+
const cartId = await getCartId();
|
|
99
|
+
if (!cartId) {
|
|
100
|
+
throw new Error("No existing cart found, please create one before updating");
|
|
101
|
+
}
|
|
102
|
+
const headers = {
|
|
103
|
+
...(await getAuthHeaders()),
|
|
104
|
+
};
|
|
105
|
+
return sdk.store.cart
|
|
106
|
+
.update(cartId, data, {}, headers)
|
|
107
|
+
.then(async ({ cart }) => {
|
|
108
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
109
|
+
revalidateTag(cartCacheTag);
|
|
110
|
+
const fulfillmentCacheTag = await getCacheTag("fulfillment");
|
|
111
|
+
revalidateTag(fulfillmentCacheTag);
|
|
112
|
+
return cart;
|
|
113
|
+
})
|
|
114
|
+
.catch(medusaError);
|
|
115
|
+
}
|
|
116
|
+
export async function addToCart({ variantId, quantity, countryCode, }) {
|
|
117
|
+
if (!variantId) {
|
|
118
|
+
throw new Error("Missing variant ID when adding to cart");
|
|
119
|
+
}
|
|
120
|
+
const cart = await getOrSetCart(countryCode);
|
|
121
|
+
if (!cart) {
|
|
122
|
+
throw new Error("Error retrieving or creating cart");
|
|
123
|
+
}
|
|
124
|
+
const headers = {
|
|
125
|
+
...(await getAuthHeaders()),
|
|
126
|
+
};
|
|
127
|
+
await sdk.store.cart
|
|
128
|
+
.createLineItem(cart.id, {
|
|
129
|
+
variant_id: variantId,
|
|
130
|
+
quantity,
|
|
131
|
+
}, {}, headers)
|
|
132
|
+
.then(async () => {
|
|
133
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
134
|
+
revalidateTag(cartCacheTag);
|
|
135
|
+
const fulfillmentCacheTag = await getCacheTag("fulfillment");
|
|
136
|
+
revalidateTag(fulfillmentCacheTag);
|
|
137
|
+
})
|
|
138
|
+
.catch(medusaError);
|
|
139
|
+
}
|
|
140
|
+
export async function buyNow({ variantId, quantity, countryCode, }) {
|
|
141
|
+
if (!variantId) {
|
|
142
|
+
throw new Error("Missing variant ID when adding to cart");
|
|
143
|
+
}
|
|
144
|
+
const region = await getRegion(countryCode);
|
|
145
|
+
if (!region) {
|
|
146
|
+
throw new Error(`Region not found for country code: ${countryCode}`);
|
|
147
|
+
}
|
|
148
|
+
const headers = {
|
|
149
|
+
...(await getAuthHeaders()),
|
|
150
|
+
};
|
|
151
|
+
const locale = await getLocale();
|
|
152
|
+
// 1. Create a NEW cart regardless of existing one
|
|
153
|
+
const cartResp = await sdk.store.cart.create({
|
|
154
|
+
region_id: region.id,
|
|
155
|
+
locale: locale || undefined,
|
|
156
|
+
metadata: { is_buy_now: true }
|
|
157
|
+
}, {}, headers);
|
|
158
|
+
const cart = cartResp.cart;
|
|
159
|
+
// Save the new cart id in "buy_now_cart_id" cookie
|
|
160
|
+
await removeBuyNowCartId();
|
|
161
|
+
await setBuyNowCartId(cart.id);
|
|
162
|
+
// Ensure this new cart is not merged with existing ones
|
|
163
|
+
try {
|
|
164
|
+
await sdk.client.fetch(`/store/carts/merge/skip`, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
body: { cart_id: cart.id },
|
|
167
|
+
headers,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
console.error("Failed to skip cart merge for buy now cart:", e);
|
|
172
|
+
}
|
|
173
|
+
// 2. Add the item to this new cart
|
|
174
|
+
await sdk.store.cart.createLineItem(cart.id, {
|
|
175
|
+
variant_id: variantId,
|
|
176
|
+
quantity,
|
|
177
|
+
}, {}, headers);
|
|
178
|
+
// 3. (REMOVED: No longer overwriting the main cart ID, because getCartId handles buy_now_cart_id priority)
|
|
179
|
+
// 4. OPTIMIZATION: If user is logged in and has addresses, auto-fill and skip to payment
|
|
180
|
+
const customer = await retrieveCustomer().catch(() => null);
|
|
181
|
+
const defaultAddress = customer?.addresses?.find(a => a.is_default_shipping || a.metadata?.is_default === "true" || a.metadata?.is_default === true) || customer?.addresses?.[0];
|
|
182
|
+
let skipToPayment = false;
|
|
183
|
+
if (defaultAddress) {
|
|
184
|
+
try {
|
|
185
|
+
// Set addresses
|
|
186
|
+
await sdk.store.cart.update(cart.id, {
|
|
187
|
+
shipping_address: {
|
|
188
|
+
first_name: defaultAddress.first_name,
|
|
189
|
+
last_name: defaultAddress.last_name,
|
|
190
|
+
address_1: defaultAddress.address_1,
|
|
191
|
+
address_2: defaultAddress.address_2,
|
|
192
|
+
city: defaultAddress.city,
|
|
193
|
+
country_code: defaultAddress.country_code,
|
|
194
|
+
postal_code: defaultAddress.postal_code,
|
|
195
|
+
province: defaultAddress.province,
|
|
196
|
+
phone: defaultAddress.phone,
|
|
197
|
+
company: defaultAddress.company,
|
|
198
|
+
},
|
|
199
|
+
billing_address: {
|
|
200
|
+
first_name: defaultAddress.first_name,
|
|
201
|
+
last_name: defaultAddress.last_name,
|
|
202
|
+
address_1: defaultAddress.address_1,
|
|
203
|
+
address_2: defaultAddress.address_2,
|
|
204
|
+
city: defaultAddress.city,
|
|
205
|
+
country_code: defaultAddress.country_code,
|
|
206
|
+
postal_code: defaultAddress.postal_code,
|
|
207
|
+
province: defaultAddress.province,
|
|
208
|
+
phone: defaultAddress.phone,
|
|
209
|
+
company: defaultAddress.company,
|
|
210
|
+
},
|
|
211
|
+
email: customer.email
|
|
212
|
+
}, {}, headers);
|
|
213
|
+
// Fetch and set first shipping method
|
|
214
|
+
const { shipping_options } = await sdk.client.fetch("/store/shipping-options", {
|
|
215
|
+
query: { cart_id: cart.id },
|
|
216
|
+
headers,
|
|
217
|
+
cache: "no-store",
|
|
218
|
+
});
|
|
219
|
+
if (shipping_options?.length > 0) {
|
|
220
|
+
await sdk.store.cart.addShippingMethod(cart.id, { option_id: shipping_options[0].id }, {}, headers);
|
|
221
|
+
skipToPayment = true;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
// Fallback to normal checkout if anything fails
|
|
226
|
+
console.error("Failed to auto-fill address and shipping:", e);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
230
|
+
revalidateTag(cartCacheTag);
|
|
231
|
+
revalidatePath(`/${countryCode}/checkout`, "page");
|
|
232
|
+
// 5. Redirect to checkout (default or skipped to payment)
|
|
233
|
+
if (skipToPayment) {
|
|
234
|
+
redirect(`/${countryCode}/checkout?step=payment&cart_id=${cart.id}`);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
redirect(`/${countryCode}/checkout?cart_id=${cart.id}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export async function updateLineItem({ lineId, quantity, }) {
|
|
241
|
+
if (!lineId) {
|
|
242
|
+
throw new Error("Missing lineItem ID when updating line item");
|
|
243
|
+
}
|
|
244
|
+
const cartId = await getCartId();
|
|
245
|
+
if (!cartId) {
|
|
246
|
+
throw new Error("Missing cart ID when updating line item");
|
|
247
|
+
}
|
|
248
|
+
const headers = {
|
|
249
|
+
...(await getAuthHeaders()),
|
|
250
|
+
};
|
|
251
|
+
await sdk.store.cart
|
|
252
|
+
.updateLineItem(cartId, lineId, { quantity }, {}, headers)
|
|
253
|
+
.then(async () => {
|
|
254
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
255
|
+
revalidateTag(cartCacheTag);
|
|
256
|
+
const fulfillmentCacheTag = await getCacheTag("fulfillment");
|
|
257
|
+
revalidateTag(fulfillmentCacheTag);
|
|
258
|
+
})
|
|
259
|
+
.catch(medusaError);
|
|
260
|
+
}
|
|
261
|
+
export async function deleteLineItem(lineId) {
|
|
262
|
+
if (!lineId) {
|
|
263
|
+
throw new Error("Missing lineItem ID when deleting line item");
|
|
264
|
+
}
|
|
265
|
+
const cartId = await getCartId();
|
|
266
|
+
if (!cartId) {
|
|
267
|
+
throw new Error("Missing cart ID when deleting line item");
|
|
268
|
+
}
|
|
269
|
+
const headers = {
|
|
270
|
+
...(await getAuthHeaders()),
|
|
271
|
+
};
|
|
272
|
+
await sdk.store.cart
|
|
273
|
+
.deleteLineItem(cartId, lineId, {}, headers)
|
|
274
|
+
.then(async () => {
|
|
275
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
276
|
+
revalidateTag(cartCacheTag);
|
|
277
|
+
const fulfillmentCacheTag = await getCacheTag("fulfillment");
|
|
278
|
+
revalidateTag(fulfillmentCacheTag);
|
|
279
|
+
})
|
|
280
|
+
.catch(medusaError);
|
|
281
|
+
}
|
|
282
|
+
export async function updateLineItemVariant({ lineId, variantId, quantity, countryCode, }) {
|
|
283
|
+
if (!lineId) {
|
|
284
|
+
throw new Error("Missing lineItem ID when updating variant");
|
|
285
|
+
}
|
|
286
|
+
if (!variantId) {
|
|
287
|
+
throw new Error("Missing variant ID when updating variant");
|
|
288
|
+
}
|
|
289
|
+
// Delete the old line item
|
|
290
|
+
await deleteLineItem(lineId);
|
|
291
|
+
// Add new line item with the new variant
|
|
292
|
+
await addToCart({
|
|
293
|
+
variantId,
|
|
294
|
+
quantity,
|
|
295
|
+
countryCode,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
export async function setShippingMethod({ cartId, shippingMethodId, }) {
|
|
299
|
+
const headers = {
|
|
300
|
+
...(await getAuthHeaders()),
|
|
301
|
+
};
|
|
302
|
+
return sdk.store.cart
|
|
303
|
+
.addShippingMethod(cartId, { option_id: shippingMethodId }, {}, headers)
|
|
304
|
+
.then(async () => {
|
|
305
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
306
|
+
revalidateTag(cartCacheTag);
|
|
307
|
+
})
|
|
308
|
+
.catch(medusaError);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Sets a shipping method silently without triggering a full page revalidation.
|
|
312
|
+
*/
|
|
313
|
+
export async function setShippingMethodSilently({ cartId, shippingMethodId, }) {
|
|
314
|
+
const headers = {
|
|
315
|
+
...(await getAuthHeaders()),
|
|
316
|
+
};
|
|
317
|
+
try {
|
|
318
|
+
await sdk.store.cart.addShippingMethod(cartId, { option_id: shippingMethodId }, {}, headers);
|
|
319
|
+
return { success: true };
|
|
320
|
+
}
|
|
321
|
+
catch (e) {
|
|
322
|
+
return { success: false };
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
export async function initiatePaymentSession(cart, data) {
|
|
326
|
+
const headers = {
|
|
327
|
+
...(await getAuthHeaders()),
|
|
328
|
+
};
|
|
329
|
+
// 1. RE-FETCH cart with NO CACHE to ensure we have the latest data from the DB
|
|
330
|
+
const latestCart = await retrieveCart(cart.id);
|
|
331
|
+
if (!latestCart)
|
|
332
|
+
throw new Error("Cart not found");
|
|
333
|
+
// Helper to normalize phone numbers for Razorpay (requires +91 for India if 10 digits)
|
|
334
|
+
const normalize = (p) => {
|
|
335
|
+
if (!p)
|
|
336
|
+
return undefined;
|
|
337
|
+
const cleaned = String(p).replace(/\D/g, "");
|
|
338
|
+
// India specific optimization (+91)
|
|
339
|
+
if (cleaned.length === 10)
|
|
340
|
+
return `+91${cleaned}`;
|
|
341
|
+
if (cleaned.length > 10 && !String(p).startsWith("+"))
|
|
342
|
+
return `+${cleaned}`;
|
|
343
|
+
return p;
|
|
344
|
+
};
|
|
345
|
+
const rawPhone = latestCart.shipping_address?.phone || latestCart.billing_address?.phone;
|
|
346
|
+
const phone = normalize(rawPhone);
|
|
347
|
+
// 2. Critical Fix for Razorpay: Ensure normalized phone is in BOTH shipping and billing addresses
|
|
348
|
+
if (phone) {
|
|
349
|
+
const addressUpdates = {};
|
|
350
|
+
// Clean address helper to ensure we pass a clean object to the update API
|
|
351
|
+
const cleanAddress = (addr) => {
|
|
352
|
+
if (!addr)
|
|
353
|
+
return null;
|
|
354
|
+
return {
|
|
355
|
+
first_name: addr.first_name,
|
|
356
|
+
last_name: addr.last_name,
|
|
357
|
+
address_1: addr.address_1,
|
|
358
|
+
address_2: addr.address_2 || "",
|
|
359
|
+
city: addr.city,
|
|
360
|
+
country_code: addr.country_code,
|
|
361
|
+
postal_code: addr.postal_code,
|
|
362
|
+
province: addr.province,
|
|
363
|
+
company: addr.company,
|
|
364
|
+
phone: phone
|
|
365
|
+
};
|
|
366
|
+
};
|
|
367
|
+
// Update if phone is missing in DB
|
|
368
|
+
if (latestCart.shipping_address && !latestCart.shipping_address.phone) {
|
|
369
|
+
addressUpdates.shipping_address = cleanAddress(latestCart.shipping_address);
|
|
370
|
+
}
|
|
371
|
+
if (latestCart.billing_address && !latestCart.billing_address.phone) {
|
|
372
|
+
addressUpdates.billing_address = cleanAddress(latestCart.billing_address) || cleanAddress(latestCart.shipping_address);
|
|
373
|
+
}
|
|
374
|
+
if (Object.keys(addressUpdates).length > 0) {
|
|
375
|
+
await sdk.store.cart.update(latestCart.id, addressUpdates, {}, headers);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// 3. Final re-fetch (direct SDK call to bypass any lib-level cache)
|
|
379
|
+
const cartResponse = await sdk.store.cart.retrieve(cart.id, { fields: "*shipping_address,*billing_address" }, headers);
|
|
380
|
+
const finalCart = cartResponse.cart;
|
|
381
|
+
// 4. PRE-FLIGHT VALIDATION: Only strictly block for Razorpay, be more lenient for COD
|
|
382
|
+
const isRazorpayProvider = data.provider_id.includes("razorpay");
|
|
383
|
+
const displayPhone = finalCart.shipping_address?.phone || finalCart.billing_address?.phone || phone;
|
|
384
|
+
if (isRazorpayProvider && !displayPhone) {
|
|
385
|
+
throw new Error(`CRITICAL Error: Phone number is missing from your address. Razorpay requires a contact number for verification. Please enter your phone number.`);
|
|
386
|
+
}
|
|
387
|
+
if (isRazorpayProvider && !finalCart.email) {
|
|
388
|
+
throw new Error(`CRITICAL Error: Email is missing from your cart. Razorpay requires an email address. Please enter your email.`);
|
|
389
|
+
}
|
|
390
|
+
// 5. ENHANCED DATA FOR RAZORPAY:
|
|
391
|
+
const enhancedData = {
|
|
392
|
+
...data,
|
|
393
|
+
data: {
|
|
394
|
+
...(data.data || {}),
|
|
395
|
+
billing_address: finalCart.billing_address || finalCart.shipping_address,
|
|
396
|
+
customer: {
|
|
397
|
+
email: finalCart.email,
|
|
398
|
+
phone: displayPhone,
|
|
399
|
+
first_name: finalCart.billing_address?.first_name || finalCart.shipping_address?.first_name,
|
|
400
|
+
last_name: finalCart.billing_address?.last_name || finalCart.shipping_address?.last_name,
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
return sdk.store.payment
|
|
405
|
+
.initiatePaymentSession(finalCart, enhancedData, {}, headers)
|
|
406
|
+
.then(async (resp) => {
|
|
407
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
408
|
+
revalidateTag(cartCacheTag);
|
|
409
|
+
return resp;
|
|
410
|
+
})
|
|
411
|
+
.catch((e) => {
|
|
412
|
+
const errorMsg = e.message || e.response?.data?.message || "Unknown error";
|
|
413
|
+
throw new Error(`Razorpay Initialization Failed: ${errorMsg}`);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
export async function applyPromotions(codes) {
|
|
417
|
+
const cartId = await getCartId();
|
|
418
|
+
if (!cartId) {
|
|
419
|
+
throw new Error("No existing cart found");
|
|
420
|
+
}
|
|
421
|
+
const headers = {
|
|
422
|
+
...(await getAuthHeaders()),
|
|
423
|
+
};
|
|
424
|
+
return sdk.store.cart
|
|
425
|
+
.update(cartId, { promo_codes: codes }, {}, headers)
|
|
426
|
+
.then(async ({ cart }) => {
|
|
427
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
428
|
+
revalidateTag(cartCacheTag);
|
|
429
|
+
const fulfillmentCacheTag = await getCacheTag("fulfillment");
|
|
430
|
+
revalidateTag(fulfillmentCacheTag);
|
|
431
|
+
return cart;
|
|
432
|
+
})
|
|
433
|
+
.catch(medusaError);
|
|
434
|
+
}
|
|
435
|
+
export async function applyGiftCard(code) {
|
|
436
|
+
// const cartId = getCartId()
|
|
437
|
+
// if (!cartId) return "No cartId cookie found"
|
|
438
|
+
// try {
|
|
439
|
+
// await updateCart(cartId, { gift_cards: [{ code }] }).then(() => {
|
|
440
|
+
// revalidateTag("cart")
|
|
441
|
+
// })
|
|
442
|
+
// } catch (error: any) {
|
|
443
|
+
// throw error
|
|
444
|
+
// }
|
|
445
|
+
}
|
|
446
|
+
export async function removeDiscount(code) {
|
|
447
|
+
// const cartId = getCartId()
|
|
448
|
+
// if (!cartId) return "No cartId cookie found"
|
|
449
|
+
// try {
|
|
450
|
+
// await deleteDiscount(cartId, code)
|
|
451
|
+
// revalidateTag("cart")
|
|
452
|
+
// } catch (error: any) {
|
|
453
|
+
// throw error
|
|
454
|
+
// }
|
|
455
|
+
}
|
|
456
|
+
export async function removeGiftCard(codeToRemove, giftCards
|
|
457
|
+
// giftCards: GiftCard[]
|
|
458
|
+
) {
|
|
459
|
+
// const cartId = getCartId()
|
|
460
|
+
// if (!cartId) return "No cartId cookie found"
|
|
461
|
+
// try {
|
|
462
|
+
// await updateCart(cartId, {
|
|
463
|
+
// gift_cards: [...giftCards]
|
|
464
|
+
// .filter((gc) => gc.code !== codeToRemove)
|
|
465
|
+
// .map((gc) => ({ code: gc.code })),
|
|
466
|
+
// }).then(() => {
|
|
467
|
+
// revalidateTag("cart")
|
|
468
|
+
// })
|
|
469
|
+
// } catch (error: any) {
|
|
470
|
+
// throw error
|
|
471
|
+
// }
|
|
472
|
+
}
|
|
473
|
+
export async function submitPromotionForm(currentState, formData) {
|
|
474
|
+
const code = formData.get("code");
|
|
475
|
+
try {
|
|
476
|
+
await applyPromotions([code]);
|
|
477
|
+
}
|
|
478
|
+
catch (e) {
|
|
479
|
+
return e.message;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// TODO: Pass a POJO instead of a form entity here
|
|
483
|
+
export async function setAddresses(currentState, formData) {
|
|
484
|
+
try {
|
|
485
|
+
if (!formData) {
|
|
486
|
+
throw new Error("No form data found when setting addresses");
|
|
487
|
+
}
|
|
488
|
+
const cartId = await getCartId();
|
|
489
|
+
if (!cartId) {
|
|
490
|
+
throw new Error("No existing cart found when setting addresses");
|
|
491
|
+
}
|
|
492
|
+
const shippingCountryCode = formData.get("shipping_address.country_code")?.toLowerCase() || "in";
|
|
493
|
+
const normalize = (p) => {
|
|
494
|
+
if (!p)
|
|
495
|
+
return undefined;
|
|
496
|
+
const cleaned = String(p).replace(/\D/g, "");
|
|
497
|
+
if (cleaned.length === 10)
|
|
498
|
+
return `+91${cleaned}`;
|
|
499
|
+
if (cleaned.length > 10 && !String(p).startsWith("+"))
|
|
500
|
+
return `+${cleaned}`;
|
|
501
|
+
return String(p);
|
|
502
|
+
};
|
|
503
|
+
const rawPhone = formData.get("shipping_address.phone");
|
|
504
|
+
const phone = normalize(rawPhone);
|
|
505
|
+
const shippingAddress = {
|
|
506
|
+
first_name: formData.get("shipping_address.first_name"),
|
|
507
|
+
last_name: formData.get("shipping_address.last_name"),
|
|
508
|
+
address_1: formData.get("shipping_address.address_1"),
|
|
509
|
+
address_2: formData.get("shipping_address.address_2") || "",
|
|
510
|
+
company: formData.get("shipping_address.company"),
|
|
511
|
+
postal_code: formData.get("shipping_address.postal_code"),
|
|
512
|
+
city: formData.get("shipping_address.city"),
|
|
513
|
+
country_code: shippingCountryCode,
|
|
514
|
+
province: formData.get("shipping_address.province"),
|
|
515
|
+
phone: phone,
|
|
516
|
+
};
|
|
517
|
+
const data = {
|
|
518
|
+
shipping_address: shippingAddress,
|
|
519
|
+
email: formData.get("email"),
|
|
520
|
+
};
|
|
521
|
+
// Save address to customer profile if logged in
|
|
522
|
+
const authHeaders = await getAuthHeaders();
|
|
523
|
+
if (authHeaders && "authorization" in authHeaders) {
|
|
524
|
+
try {
|
|
525
|
+
// Fetch current customer to check if address already exists
|
|
526
|
+
const { customer } = await sdk.client.fetch(`/store/customers/me`, {
|
|
527
|
+
method: "GET",
|
|
528
|
+
query: { fields: "*addresses" },
|
|
529
|
+
headers: authHeaders,
|
|
530
|
+
cache: "no-store",
|
|
531
|
+
});
|
|
532
|
+
const addressExists = customer?.addresses?.some((a) => a.address_1 === shippingAddress.address_1 &&
|
|
533
|
+
a.postal_code === shippingAddress.postal_code &&
|
|
534
|
+
a.city === shippingAddress.city &&
|
|
535
|
+
a.first_name === shippingAddress.first_name &&
|
|
536
|
+
a.last_name === shippingAddress.last_name);
|
|
537
|
+
if (!addressExists) {
|
|
538
|
+
await sdk.store.customer.createAddress(shippingAddress, {}, authHeaders);
|
|
539
|
+
const customerCacheTag = await getCacheTag("customers");
|
|
540
|
+
revalidateTag(customerCacheTag);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
catch (e) {
|
|
544
|
+
// Silently fail when saving address to customer profile
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
const sameAsBilling = formData.get("same_as_billing");
|
|
548
|
+
if (sameAsBilling === "on") {
|
|
549
|
+
data.billing_address = { ...data.shipping_address };
|
|
550
|
+
}
|
|
551
|
+
if (sameAsBilling !== "on") {
|
|
552
|
+
const bPhone = normalize(formData.get("billing_address.phone")) || phone;
|
|
553
|
+
data.billing_address = {
|
|
554
|
+
first_name: formData.get("billing_address.first_name"),
|
|
555
|
+
last_name: formData.get("billing_address.last_name"),
|
|
556
|
+
address_1: formData.get("billing_address.address_1"),
|
|
557
|
+
address_2: formData.get("billing_address.address_2") || "",
|
|
558
|
+
company: formData.get("billing_address.company"),
|
|
559
|
+
postal_code: formData.get("billing_address.postal_code"),
|
|
560
|
+
city: formData.get("billing_address.city"),
|
|
561
|
+
country_code: shippingCountryCode,
|
|
562
|
+
province: formData.get("billing_address.province"),
|
|
563
|
+
phone: bPhone,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
await updateCart(data);
|
|
567
|
+
}
|
|
568
|
+
catch (e) {
|
|
569
|
+
return e.message;
|
|
570
|
+
}
|
|
571
|
+
revalidateTag("carts");
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Places an order for a cart. If no cart ID is provided, it will use the cart ID from the cookies.
|
|
575
|
+
* @param cartId - optional - The ID of the cart to place an order for.
|
|
576
|
+
* @returns The cart object if the order was successful, or null if not.
|
|
577
|
+
*/
|
|
578
|
+
export async function placeOrder(cartId) {
|
|
579
|
+
const id = cartId || (await getCartId());
|
|
580
|
+
if (!id) {
|
|
581
|
+
throw new Error("No existing cart found when placing an order");
|
|
582
|
+
}
|
|
583
|
+
const headers = {
|
|
584
|
+
...(await getAuthHeaders()),
|
|
585
|
+
};
|
|
586
|
+
const cartRes = await sdk.store.cart
|
|
587
|
+
.complete(id, {}, headers)
|
|
588
|
+
.then(async (cartRes) => {
|
|
589
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
590
|
+
revalidateTag(cartCacheTag);
|
|
591
|
+
return cartRes;
|
|
592
|
+
})
|
|
593
|
+
.catch(medusaError);
|
|
594
|
+
if (cartRes?.type === "order") {
|
|
595
|
+
const countryCode = cartRes.order.shipping_address?.country_code?.toLowerCase();
|
|
596
|
+
const orderCacheTag = await getCacheTag("orders");
|
|
597
|
+
revalidateTag(orderCacheTag);
|
|
598
|
+
// NEW LOGIC: Check if we just completed a Buy Now cart
|
|
599
|
+
const { cookies: nextCookies } = await import("next/headers");
|
|
600
|
+
const cookiesStore = await nextCookies();
|
|
601
|
+
const buyNowId = cookiesStore.get("buy_now_cart_id")?.value;
|
|
602
|
+
if (buyNowId) {
|
|
603
|
+
// If a Buy Now cart was completed, remove its cookie ONLY.
|
|
604
|
+
// Do NOT touch _medusa_cart_id, so the main cart is preserved!
|
|
605
|
+
await removeBuyNowCartId();
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
// Regular checkout: clear the main cart
|
|
609
|
+
await removeCartId();
|
|
610
|
+
}
|
|
611
|
+
redirect(`/${countryCode}/order/${cartRes?.order.id}/confirmed`);
|
|
612
|
+
}
|
|
613
|
+
return cartRes.cart;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Updates the countrycode param and revalidates the regions cache
|
|
617
|
+
* @param regionId
|
|
618
|
+
* @param countryCode
|
|
619
|
+
*/
|
|
620
|
+
export async function updateRegion(countryCode, currentPath) {
|
|
621
|
+
const cartId = await getCartId();
|
|
622
|
+
const region = await getRegion(countryCode);
|
|
623
|
+
if (!region) {
|
|
624
|
+
throw new Error(`Region not found for country code: ${countryCode}`);
|
|
625
|
+
}
|
|
626
|
+
if (cartId) {
|
|
627
|
+
await updateCart({ region_id: region.id });
|
|
628
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
629
|
+
revalidateTag(cartCacheTag);
|
|
630
|
+
}
|
|
631
|
+
const regionCacheTag = await getCacheTag("regions");
|
|
632
|
+
revalidateTag(regionCacheTag);
|
|
633
|
+
const productsCacheTag = await getCacheTag("products");
|
|
634
|
+
revalidateTag(productsCacheTag);
|
|
635
|
+
redirect(`/${countryCode}${currentPath}`);
|
|
636
|
+
}
|
|
637
|
+
export async function listCartOptions() {
|
|
638
|
+
const cartId = await getCartId();
|
|
639
|
+
const headers = {
|
|
640
|
+
...(await getAuthHeaders()),
|
|
641
|
+
};
|
|
642
|
+
const next = {
|
|
643
|
+
...(await getCacheOptions("shippingOptions")),
|
|
644
|
+
};
|
|
645
|
+
return await sdk.client.fetch("/store/shipping-options", {
|
|
646
|
+
query: { cart_id: cartId },
|
|
647
|
+
next,
|
|
648
|
+
headers,
|
|
649
|
+
cache: "force-cache",
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
export async function addCustomerAddressToCart(currentState, formData) {
|
|
653
|
+
try {
|
|
654
|
+
const headers = {
|
|
655
|
+
...(await getAuthHeaders()),
|
|
656
|
+
};
|
|
657
|
+
const shippingCountryCode = formData.get("shipping_address.country_code")?.toLowerCase() || "in";
|
|
658
|
+
// Parse address data from formData (which uses shipping_address. prefix)
|
|
659
|
+
const addressData = {
|
|
660
|
+
first_name: formData.get("shipping_address.first_name"),
|
|
661
|
+
last_name: formData.get("shipping_address.last_name"),
|
|
662
|
+
company: formData.get("shipping_address.company"),
|
|
663
|
+
address_1: formData.get("shipping_address.address_1"),
|
|
664
|
+
address_2: formData.get("shipping_address.address_2"),
|
|
665
|
+
city: formData.get("shipping_address.city"),
|
|
666
|
+
postal_code: formData.get("shipping_address.postal_code"),
|
|
667
|
+
province: formData.get("shipping_address.province"),
|
|
668
|
+
country_code: shippingCountryCode,
|
|
669
|
+
phone: formData.get("shipping_address.phone"),
|
|
670
|
+
metadata: {
|
|
671
|
+
is_default: formData.get("is_default") === "true",
|
|
672
|
+
address_type: formData.get("address_type"),
|
|
673
|
+
},
|
|
674
|
+
};
|
|
675
|
+
const addressId = formData.get("address_id");
|
|
676
|
+
// 1. Add/Update Customer Address Book
|
|
677
|
+
// We try/catch this separately so if it fails (e.g. user not logged in), we still try to proceed with checkout
|
|
678
|
+
try {
|
|
679
|
+
if (addressId) {
|
|
680
|
+
await sdk.store.customer.updateAddress(addressId, addressData, {}, headers);
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
await sdk.store.customer.createAddress(addressData, {}, headers);
|
|
684
|
+
}
|
|
685
|
+
const customerCacheTag = await getCacheTag("customers");
|
|
686
|
+
revalidateTag(customerCacheTag);
|
|
687
|
+
}
|
|
688
|
+
catch (e) {
|
|
689
|
+
// verify if it is an auth error, maybe we shouldn't fail silently?
|
|
690
|
+
// But for checkout flow, the priority is to proceed.
|
|
691
|
+
}
|
|
692
|
+
// 2. Set as Cart Shipping Address
|
|
693
|
+
const cartId = await getCartId();
|
|
694
|
+
if (!cartId) {
|
|
695
|
+
throw new Error("No existing cart found when setting addresses");
|
|
696
|
+
}
|
|
697
|
+
const cartData = {
|
|
698
|
+
shipping_address: {
|
|
699
|
+
first_name: addressData.first_name,
|
|
700
|
+
last_name: addressData.last_name,
|
|
701
|
+
company: addressData.company,
|
|
702
|
+
address_1: addressData.address_1,
|
|
703
|
+
address_2: addressData.address_2,
|
|
704
|
+
city: addressData.city,
|
|
705
|
+
postal_code: addressData.postal_code,
|
|
706
|
+
province: addressData.province,
|
|
707
|
+
country_code: addressData.country_code,
|
|
708
|
+
phone: addressData.phone,
|
|
709
|
+
},
|
|
710
|
+
email: formData.get("email"),
|
|
711
|
+
};
|
|
712
|
+
const sameAsBilling = formData.get("same_as_billing");
|
|
713
|
+
if (sameAsBilling === "on") {
|
|
714
|
+
cartData.billing_address = cartData.shipping_address;
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
// If not same as billing, we should ideally handle billing address too,
|
|
718
|
+
// but AddAddressModal usually forces same_as_billing="on" (Line 52 in AddAddressModal).
|
|
719
|
+
// If we ever change that, we'll need logic here. For now, AddAddressModal is strictly for "Add New Shippping Address".
|
|
720
|
+
}
|
|
721
|
+
await updateCart(cartData);
|
|
722
|
+
}
|
|
723
|
+
catch (e) {
|
|
724
|
+
return e.message;
|
|
725
|
+
}
|
|
726
|
+
revalidateTag("carts");
|
|
727
|
+
// No redirect if we are already in checkout to prevent disrupting the user
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Updates address in the background without redirecting.
|
|
731
|
+
* Used for real-time shipping calculation when pincode is entered.
|
|
732
|
+
*/
|
|
733
|
+
export async function updateAddressSilently(data) {
|
|
734
|
+
const cartId = await getCartId();
|
|
735
|
+
if (!cartId)
|
|
736
|
+
return;
|
|
737
|
+
try {
|
|
738
|
+
const headers = {
|
|
739
|
+
...(await getAuthHeaders()),
|
|
740
|
+
};
|
|
741
|
+
await sdk.store.cart.update(cartId, data, {}, headers);
|
|
742
|
+
// Revalidate tags to ensure CartTotals gets new shipping info
|
|
743
|
+
const cartCacheTag = await getCacheTag("carts");
|
|
744
|
+
revalidateTag(cartCacheTag);
|
|
745
|
+
const fulfillmentCacheTag = await getCacheTag("fulfillment");
|
|
746
|
+
revalidateTag(fulfillmentCacheTag);
|
|
747
|
+
return { success: true };
|
|
748
|
+
}
|
|
749
|
+
catch (e) {
|
|
750
|
+
return { success: false };
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Updates cart metadata silently without triggering a full page revalidation.
|
|
755
|
+
*/
|
|
756
|
+
export async function updateCartMetadataSilently(metadata) {
|
|
757
|
+
const cartId = await getCartId();
|
|
758
|
+
if (!cartId)
|
|
759
|
+
return;
|
|
760
|
+
try {
|
|
761
|
+
const headers = {
|
|
762
|
+
...(await getAuthHeaders()),
|
|
763
|
+
};
|
|
764
|
+
await sdk.store.cart.update(cartId, { metadata }, {}, headers);
|
|
765
|
+
// We intentionally DO NOT call revalidateTag here to prevent page refresh loops
|
|
766
|
+
return { success: true };
|
|
767
|
+
}
|
|
768
|
+
catch (e) {
|
|
769
|
+
return { success: false };
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
export async function getAbandonedCarts() {
|
|
773
|
+
const headers = {
|
|
774
|
+
...(await getAuthHeaders()),
|
|
775
|
+
};
|
|
776
|
+
// If no authorization, return empty arrays
|
|
777
|
+
if (!headers["authorization"]) {
|
|
778
|
+
return { buyNowCarts: [], reorderCarts: [] };
|
|
779
|
+
}
|
|
780
|
+
try {
|
|
781
|
+
// Just fetch all carts for the customer and filter locally
|
|
782
|
+
const res = await sdk.client.fetch(`/store/carts`, {
|
|
783
|
+
method: "GET",
|
|
784
|
+
query: {
|
|
785
|
+
fields: "*items, *items.product, *items.product.thumbnail, *items.variant, *items.metadata, +total",
|
|
786
|
+
},
|
|
787
|
+
headers,
|
|
788
|
+
cache: "no-store",
|
|
789
|
+
}).catch(() => null);
|
|
790
|
+
let buyNowCarts = [];
|
|
791
|
+
let reorderCarts = [];
|
|
792
|
+
if (res && res.carts) {
|
|
793
|
+
// Filter out the main active cart
|
|
794
|
+
const currentCartId = await getCartId();
|
|
795
|
+
const filteredCarts = res.carts.filter(c => c.id !== currentCartId);
|
|
796
|
+
buyNowCarts = filteredCarts.filter(c => c.metadata?.is_buy_now === true);
|
|
797
|
+
reorderCarts = filteredCarts.filter(c => c.metadata?.is_reorder === true);
|
|
798
|
+
}
|
|
799
|
+
return { buyNowCarts, reorderCarts };
|
|
800
|
+
}
|
|
801
|
+
catch (e) {
|
|
802
|
+
console.error("Failed to fetch abandoned carts:", e);
|
|
803
|
+
return { buyNowCarts: [], reorderCarts: [] };
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
export async function resumeAbandonedCart(cartId, countryCode) {
|
|
807
|
+
// Set as buy now cart so it temporarily overrides the main cart
|
|
808
|
+
// without deleting the main cart cookie (_medusa_cart_id)
|
|
809
|
+
await setBuyNowCartId(cartId);
|
|
810
|
+
redirect(`/${countryCode}/checkout?cart_id=${cartId}`);
|
|
811
|
+
}
|
|
812
|
+
export async function deleteCart(cartId) {
|
|
813
|
+
const headers = await getAuthHeaders();
|
|
814
|
+
try {
|
|
815
|
+
await sdk.client.fetch(`/store/carts/${cartId}`, {
|
|
816
|
+
method: "DELETE",
|
|
817
|
+
headers,
|
|
818
|
+
cache: "no-store",
|
|
819
|
+
});
|
|
820
|
+
revalidateTag("carts");
|
|
821
|
+
return { success: true };
|
|
822
|
+
}
|
|
823
|
+
catch (e) {
|
|
824
|
+
console.error("Failed to delete cart:", e);
|
|
825
|
+
return { success: false, error: e.message };
|
|
826
|
+
}
|
|
827
|
+
}
|