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,467 @@
|
|
|
1
|
+
"use server"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { cookies } from "next/headers"
|
|
5
|
+
import { sdk } from "../config"
|
|
6
|
+
import medusaError from "../util/medusa-error"
|
|
7
|
+
import { getAuthHeaders, getCacheOptions, getCacheTag, setCartId, setBuyNowCartId, removeBuyNowCartId } from "../cookies"
|
|
8
|
+
import { listReturnReasons } from "./returns"
|
|
9
|
+
import { HttpTypes } from "@medusajs/types"
|
|
10
|
+
import { revalidateTag } from "next/cache"
|
|
11
|
+
|
|
12
|
+
export const retrieveOrder = async (id: string) => {
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const cookieStore = await cookies()
|
|
16
|
+
const token = cookieStore.get("_medusa_jwt")?.value
|
|
17
|
+
// Check for guest token if regular token is missing
|
|
18
|
+
const guestToken = !token
|
|
19
|
+
? (cookieStore.get("_medusa_guest_jwt")?.value || cookieStore.get("_medusa_guest_token")?.value)
|
|
20
|
+
: null
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
// Log token details for debugging (only first 20 chars for security)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
const next = {
|
|
28
|
+
...(await getCacheOptions("orders")),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
// If guest user, use the main orders endpoint with guest token to get full details
|
|
34
|
+
if (guestToken && !token) {
|
|
35
|
+
const publishableKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY
|
|
36
|
+
|
|
37
|
+
const requestHeaders: Record<string, string> = {
|
|
38
|
+
"Authorization": `Bearer ${guestToken}`
|
|
39
|
+
}
|
|
40
|
+
if (publishableKey) requestHeaders["x-publishable-api-key"] = publishableKey
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const response = await sdk.client.fetch<any>(`/store/orders/${id}`, {
|
|
45
|
+
method: "GET",
|
|
46
|
+
headers: requestHeaders,
|
|
47
|
+
query: {
|
|
48
|
+
fields: "id,display_id,customer_id,email,created_at,status,fulfillment_status,payment_status,currency_code,subtotal,shipping_total,tax_total,discount_total,metadata,total,*payment_collections.payments,*items,*items.metadata,*items.variant,*items.variant.images,*items.variant.product,*items.variant.product.thumbnail,*items.variant.product.images,*items.variant.product.variants,*items.product,*items.product.thumbnail,*items.product.images,*items.product.variants,*fulfillments,*fulfillments.items,*fulfillments.location_id,*returns,*returns.items,*cart,*shipping_address,*billing_address,*region,*shipping_methods"
|
|
49
|
+
},
|
|
50
|
+
cache: "no-store"
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
if (!response || !response.order) {
|
|
54
|
+
throw new Error("Order not found or access denied")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
return response.order
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Regular logged-in user flow
|
|
62
|
+
const headers = {
|
|
63
|
+
...(await getAuthHeaders()),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return sdk.client
|
|
67
|
+
.fetch<HttpTypes.StoreOrderResponse>(`/store/orders/${id}`, {
|
|
68
|
+
method: "GET",
|
|
69
|
+
query: {
|
|
70
|
+
fields:
|
|
71
|
+
"id,display_id,customer_id,email,created_at,status,fulfillment_status,payment_status,currency_code,subtotal,shipping_total,tax_total,discount_total,metadata,total,*payment_collections.payments,*items,*items.metadata,*items.variant,*items.variant.images,*items.variant.product,*items.variant.product.variants,*items.product,*items.product.variants,*returns,*shipping_address,*billing_address,*region,*shipping_methods",
|
|
72
|
+
},
|
|
73
|
+
headers,
|
|
74
|
+
next,
|
|
75
|
+
cache: "no-store", // Changed from "force-cache" to "no-store" to always fetch fresh data
|
|
76
|
+
})
|
|
77
|
+
.then(({ order }) => {
|
|
78
|
+
|
|
79
|
+
return order
|
|
80
|
+
})
|
|
81
|
+
.catch((err) => {
|
|
82
|
+
return medusaError(err)
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const listOrders = async (
|
|
87
|
+
limit: number = 10,
|
|
88
|
+
offset: number = 0,
|
|
89
|
+
filters?: Record<string, any>
|
|
90
|
+
) => {
|
|
91
|
+
const headers = {
|
|
92
|
+
...(await getAuthHeaders()),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const next = {
|
|
96
|
+
...(await getCacheOptions("orders")),
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return sdk.client
|
|
100
|
+
.fetch<HttpTypes.StoreOrderListResponse>(`/store/orders`, {
|
|
101
|
+
method: "GET",
|
|
102
|
+
query: {
|
|
103
|
+
limit,
|
|
104
|
+
offset,
|
|
105
|
+
order: "-created_at",
|
|
106
|
+
fields: "*items,+items.metadata,*items.variant,*items.variant.images,*items.product,*returns",
|
|
107
|
+
...filters,
|
|
108
|
+
},
|
|
109
|
+
headers,
|
|
110
|
+
next,
|
|
111
|
+
cache: "no-store",
|
|
112
|
+
})
|
|
113
|
+
.then(({ orders }) => orders)
|
|
114
|
+
.catch((err) => medusaError(err))
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const createTransferRequest = async (
|
|
118
|
+
state: {
|
|
119
|
+
success: boolean
|
|
120
|
+
error: string | null
|
|
121
|
+
order: HttpTypes.StoreOrder | null
|
|
122
|
+
},
|
|
123
|
+
formData: FormData
|
|
124
|
+
): Promise<{
|
|
125
|
+
success: boolean
|
|
126
|
+
error: string | null
|
|
127
|
+
order: HttpTypes.StoreOrder | null
|
|
128
|
+
}> => {
|
|
129
|
+
const id = formData.get("order_id") as string
|
|
130
|
+
|
|
131
|
+
if (!id) {
|
|
132
|
+
return { success: false, error: "Order ID is required", order: null }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const headers = await getAuthHeaders()
|
|
136
|
+
|
|
137
|
+
return await sdk.store.order
|
|
138
|
+
.requestTransfer(
|
|
139
|
+
id,
|
|
140
|
+
{},
|
|
141
|
+
{
|
|
142
|
+
fields: "id, email",
|
|
143
|
+
},
|
|
144
|
+
headers
|
|
145
|
+
)
|
|
146
|
+
.then(({ order }) => ({ success: true, error: null, order }))
|
|
147
|
+
.catch((err) => ({ success: false, error: err.message, order: null }))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const acceptTransferRequest = async (id: string, token: string) => {
|
|
151
|
+
const headers = await getAuthHeaders()
|
|
152
|
+
|
|
153
|
+
return await sdk.store.order
|
|
154
|
+
.acceptTransfer(id, { token }, {}, headers)
|
|
155
|
+
.then(({ order }) => ({ success: true, error: null, order }))
|
|
156
|
+
.catch((err) => ({ success: false, error: err.message, order: null }))
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const declineTransferRequest = async (id: string, token: string) => {
|
|
160
|
+
const headers = await getAuthHeaders()
|
|
161
|
+
|
|
162
|
+
return await sdk.store.order
|
|
163
|
+
.declineTransfer(id, { token }, {}, headers)
|
|
164
|
+
.then(({ order }) => ({ success: true, error: null, order }))
|
|
165
|
+
.catch((err) => ({ success: false, error: err.message, order: null }))
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const downloadInvoice = async (orderId: string) => {
|
|
169
|
+
try {
|
|
170
|
+
const cookieStore = await cookies()
|
|
171
|
+
const token = cookieStore.get("_medusa_jwt")?.value
|
|
172
|
+
// Check for guest token if regular token is missing
|
|
173
|
+
const guestToken = !token
|
|
174
|
+
? (cookieStore.get("_medusa_guest_jwt")?.value || cookieStore.get("_medusa_guest_token")?.value)
|
|
175
|
+
: null
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
const headers: HeadersInit = {
|
|
180
|
+
"x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY!,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
let url = `/store/invoice/download/${orderId}`
|
|
184
|
+
|
|
185
|
+
if (token) {
|
|
186
|
+
headers["Cookie"] = `_medusa_jwt=${token}`
|
|
187
|
+
headers["Authorization"] = `Bearer ${token}`
|
|
188
|
+
} else if (guestToken) {
|
|
189
|
+
// Use guest endpoint and token
|
|
190
|
+
url = `/store/guest/invoice/download/${orderId}`
|
|
191
|
+
headers["Cookie"] = `_medusa_guest_jwt=${guestToken}`
|
|
192
|
+
headers["Authorization"] = `Bearer ${guestToken}`
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const backendUrl = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL || "http://localhost:9000"
|
|
196
|
+
const response = await fetch(`${backendUrl}${url}`, {
|
|
197
|
+
method: "GET",
|
|
198
|
+
headers,
|
|
199
|
+
cache: "no-store",
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
throw new Error(`Failed to download invoice: ${response.status} ${response.statusText}`)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const contentType = response.headers.get("content-type")
|
|
207
|
+
if (!contentType || !contentType.includes("application/pdf")) {
|
|
208
|
+
throw new Error("Server returned non-PDF content")
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const arrayBuffer = await response.arrayBuffer()
|
|
212
|
+
|
|
213
|
+
if (arrayBuffer.byteLength === 0) {
|
|
214
|
+
throw new Error("Generated PDF is empty")
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const base64 = Buffer.from(arrayBuffer).toString("base64")
|
|
218
|
+
|
|
219
|
+
return { success: true, data: base64 }
|
|
220
|
+
} catch (error: any) {
|
|
221
|
+
return { success: false, error: error.message }
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Cancel an order
|
|
227
|
+
*/
|
|
228
|
+
export const cancelOrder = async (orderId: string, reasonId?: string) => {
|
|
229
|
+
try {
|
|
230
|
+
const authHeaders = await getAuthHeaders()
|
|
231
|
+
const cookiesStore = await cookies()
|
|
232
|
+
const guestToken = cookiesStore.get("_medusa_guest_jwt")?.value || cookiesStore.get("_medusa_guest_token")?.value
|
|
233
|
+
const publishableKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY
|
|
234
|
+
const backendUrl = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL || "http://localhost:9000"
|
|
235
|
+
|
|
236
|
+
// 1. Regular logged-in user flow ALWAYS takes priority
|
|
237
|
+
const token = cookiesStore.get("_medusa_jwt")?.value
|
|
238
|
+
|
|
239
|
+
if (token) {
|
|
240
|
+
let finalReasonId = reasonId
|
|
241
|
+
if (!finalReasonId) {
|
|
242
|
+
const reasons = await listReturnReasons()
|
|
243
|
+
finalReasonId = reasons.find(r => r.value === "other")?.id || reasons[0]?.id || "other"
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const headers: Record<string, string> = {
|
|
247
|
+
"Content-Type": "application/json",
|
|
248
|
+
"x-publishable-api-key": publishableKey!,
|
|
249
|
+
"Authorization": `Bearer ${token}`
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Try the primary endpoint
|
|
253
|
+
let response = await fetch(`${backendUrl}/store/orders/cancel/${orderId}`, {
|
|
254
|
+
method: "POST",
|
|
255
|
+
headers,
|
|
256
|
+
body: JSON.stringify({ reason_id: finalReasonId }),
|
|
257
|
+
cache: "no-store",
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
// If the first endpoint fails with 404 or a resolution error, try the alternative format
|
|
261
|
+
if (!response.ok) {
|
|
262
|
+
const errorData = await response.json().catch(() => ({}))
|
|
263
|
+
|
|
264
|
+
if (response.status === 404 || (errorData.message && errorData.message.includes("resolve"))) {
|
|
265
|
+
const altResponse = await fetch(`${backendUrl}/store/orders/${orderId}/cancel`, {
|
|
266
|
+
method: "POST",
|
|
267
|
+
headers,
|
|
268
|
+
body: JSON.stringify({ reason_id: finalReasonId }),
|
|
269
|
+
cache: "no-store",
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
if (altResponse.ok) return { success: true }
|
|
273
|
+
|
|
274
|
+
// If both fail, return the error from the second attempt
|
|
275
|
+
const altError = await altResponse.json().catch(() => ({ message: "Failed to cancel order" }))
|
|
276
|
+
return { success: false, error: altError.message || "Failed to cancel order" }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return { success: false, error: errorData.message || "Failed to cancel order" }
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return { success: true }
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// 2. Only if NOT logged in, check for guest user token
|
|
286
|
+
if (guestToken) {
|
|
287
|
+
let finalReasonId = reasonId
|
|
288
|
+
if (!finalReasonId) {
|
|
289
|
+
const reasons = await listReturnReasons()
|
|
290
|
+
finalReasonId = reasons.find(r => r.value === "other")?.id || reasons[0]?.id || "other"
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Try guest endpoint
|
|
294
|
+
const response = await fetch(`${backendUrl}/store/guest-orders/${orderId}/cancel`, {
|
|
295
|
+
method: "POST",
|
|
296
|
+
headers: {
|
|
297
|
+
"Content-Type": "application/json",
|
|
298
|
+
"x-publishable-api-key": publishableKey!,
|
|
299
|
+
"Authorization": `Bearer ${guestToken}`
|
|
300
|
+
},
|
|
301
|
+
body: JSON.stringify({ reason_id: finalReasonId }),
|
|
302
|
+
cache: "no-store",
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
if (!response.ok) {
|
|
306
|
+
// Fallback for guest as well
|
|
307
|
+
const altResponse = await fetch(`${backendUrl}/store/guest/orders/${orderId}/cancel`, {
|
|
308
|
+
method: "POST",
|
|
309
|
+
headers: {
|
|
310
|
+
"Content-Type": "application/json",
|
|
311
|
+
"x-publishable-api-key": publishableKey!,
|
|
312
|
+
"Authorization": `Bearer ${guestToken}`
|
|
313
|
+
},
|
|
314
|
+
body: JSON.stringify({ reason_id: finalReasonId }),
|
|
315
|
+
cache: "no-store",
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
if (altResponse.ok) return { success: true }
|
|
319
|
+
return { success: false, error: "Failed to cancel order" }
|
|
320
|
+
}
|
|
321
|
+
return { success: true }
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// 3. Neither token found
|
|
325
|
+
return { success: false, error: "Unauthorized: No valid session found" }
|
|
326
|
+
} catch (error: any) {
|
|
327
|
+
return { success: false, error: error.message }
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Reorder an order (supports both logged-in and guest users)
|
|
333
|
+
*/
|
|
334
|
+
export const reorderOrder = async (orderId: string, skipVariantIds?: string[], forceReorder: boolean = false) => {
|
|
335
|
+
try {
|
|
336
|
+
const cookieStore = await cookies()
|
|
337
|
+
const token = cookieStore.get("_medusa_jwt")?.value
|
|
338
|
+
// Check for guest token if regular token is missing
|
|
339
|
+
const guestToken = !token
|
|
340
|
+
? (cookieStore.get("_medusa_guest_jwt")?.value || cookieStore.get("_medusa_guest_token")?.value)
|
|
341
|
+
: null
|
|
342
|
+
|
|
343
|
+
const publishableKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY
|
|
344
|
+
const backendUrl = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL || "http://localhost:9000"
|
|
345
|
+
|
|
346
|
+
const headers: HeadersInit = {
|
|
347
|
+
"Content-Type": "application/json",
|
|
348
|
+
"x-publishable-api-key": publishableKey!,
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Determine the correct endpoint based on user type
|
|
352
|
+
let url = `${backendUrl}/store/orders/reorder/${orderId}`
|
|
353
|
+
|
|
354
|
+
if (token) {
|
|
355
|
+
// IMPORTANT: The Order Management plugin requires an explicit Bearer token
|
|
356
|
+
// in the Authorization header to identify actor_id. Using getAuthHeaders()
|
|
357
|
+
// alone (which is cookie-based) is not enough for this custom endpoint.
|
|
358
|
+
headers["Authorization"] = `Bearer ${token}`
|
|
359
|
+
} else if (guestToken) {
|
|
360
|
+
// Use guest reorder endpoint
|
|
361
|
+
url = `${backendUrl}/store/guest-orders/${orderId}/reorder`
|
|
362
|
+
headers["Authorization"] = `Bearer ${guestToken}`
|
|
363
|
+
} else {
|
|
364
|
+
return { success: false, error: "Unauthorized: Please log in to reorder." }
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Add force_reorder query param if requested
|
|
368
|
+
if (forceReorder) {
|
|
369
|
+
url += `?force_reorder=true`
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Call the plugin's reorder endpoint (adds all items)
|
|
373
|
+
const response = await fetch(url, {
|
|
374
|
+
method: "POST",
|
|
375
|
+
headers,
|
|
376
|
+
cache: "no-store",
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
if (!response.ok) {
|
|
380
|
+
const errorBody = await response.json().catch(() => null)
|
|
381
|
+
|
|
382
|
+
// Handle 409 Conflict - Inventory Issues
|
|
383
|
+
if (response.status === 409 && errorBody?.inventory_issues) {
|
|
384
|
+
return {
|
|
385
|
+
success: false,
|
|
386
|
+
error: "Inventory issues found",
|
|
387
|
+
inventory_issues: errorBody.inventory_issues
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const errorMsg = errorBody?.message || "Failed to reorder"
|
|
392
|
+
return { success: false, error: errorMsg }
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const data = await response.json()
|
|
396
|
+
|
|
397
|
+
// Support multiple response formats: data.cart.id, data.id, or data.cart_id
|
|
398
|
+
const newCartId = data.cart?.id || data.id || data.cart_id || (data.reorder?.cart_id)
|
|
399
|
+
|
|
400
|
+
// Set the cart ID in cookies and revalidate
|
|
401
|
+
if (newCartId) {
|
|
402
|
+
// 1. Tag the cart as Buy Now to prevent merging and allow auto-restore,
|
|
403
|
+
// and add is_reorder flag to distinguish it
|
|
404
|
+
try {
|
|
405
|
+
await sdk.store.cart.update(newCartId, {
|
|
406
|
+
metadata: {
|
|
407
|
+
is_reorder: true
|
|
408
|
+
}
|
|
409
|
+
}, {}, {
|
|
410
|
+
...headers,
|
|
411
|
+
cache: "no-store",
|
|
412
|
+
} as any)
|
|
413
|
+
|
|
414
|
+
// 2. Ensure this new cart is not merged with existing ones (CRITICAL for Buy Now flow)
|
|
415
|
+
await sdk.client.fetch(`/store/carts/merge/skip`, {
|
|
416
|
+
method: "POST",
|
|
417
|
+
body: { cart_id: newCartId },
|
|
418
|
+
headers: headers as Record<string, string>,
|
|
419
|
+
})
|
|
420
|
+
} catch (updateErr) {
|
|
421
|
+
console.error("[Reorder] Error updating cart metadata or skipping merge:", updateErr)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// FALLBACK: If skipVariantIds were provided but the plugin added everything,
|
|
425
|
+
// we manually remove the unwanted items from the new cart.
|
|
426
|
+
if (skipVariantIds && skipVariantIds.length > 0) {
|
|
427
|
+
try {
|
|
428
|
+
// Fetch the new cart to find line item IDs
|
|
429
|
+
const { cart } = await sdk.store.cart.retrieve(newCartId, {}, {
|
|
430
|
+
...headers,
|
|
431
|
+
} as any)
|
|
432
|
+
|
|
433
|
+
if (cart && cart.items) {
|
|
434
|
+
const itemsToRemove = cart.items.filter(item =>
|
|
435
|
+
item.variant_id && skipVariantIds.includes(item.variant_id)
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
// Delete unwanted items one by one
|
|
439
|
+
for (const item of itemsToRemove) {
|
|
440
|
+
await sdk.store.cart.deleteLineItem(newCartId, item.id, {}, {
|
|
441
|
+
...headers,
|
|
442
|
+
} as any)
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
} catch (removeErr) {
|
|
446
|
+
console.error("[Reorder] Error removing skipped items:", removeErr)
|
|
447
|
+
// We continue anyway since the cart is created
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Use buy_now_cart_id instead of overwriting main cart
|
|
452
|
+
await removeBuyNowCartId()
|
|
453
|
+
await setBuyNowCartId(newCartId)
|
|
454
|
+
|
|
455
|
+
// Revalidate standard carts tag
|
|
456
|
+
revalidateTag("carts")
|
|
457
|
+
|
|
458
|
+
// Also revalidate specific cache tag if available
|
|
459
|
+
const cartCacheTag = await getCacheTag("carts")
|
|
460
|
+
if (cartCacheTag) revalidateTag(cartCacheTag)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return { success: true, data }
|
|
464
|
+
} catch (error: any) {
|
|
465
|
+
return { success: false, error: error.message }
|
|
466
|
+
}
|
|
467
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use server"
|
|
2
|
+
|
|
3
|
+
import { sdk } from "../config"
|
|
4
|
+
import { getAuthHeaders, getCacheOptions } from "../cookies"
|
|
5
|
+
|
|
6
|
+
export const listPaymentDetails = async () => {
|
|
7
|
+
const authHeaders = await getAuthHeaders()
|
|
8
|
+
|
|
9
|
+
// If no auth headers (guest user), don't even try to fetch
|
|
10
|
+
if (!authHeaders || Object.keys(authHeaders).length === 0) {
|
|
11
|
+
return []
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const headers = {
|
|
15
|
+
...authHeaders,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const next = {
|
|
19
|
+
...(await getCacheOptions("payment_details")),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return sdk.client
|
|
23
|
+
.fetch<any>(`/store/payment-details`, {
|
|
24
|
+
method: "GET",
|
|
25
|
+
headers,
|
|
26
|
+
next,
|
|
27
|
+
cache: "no-store",
|
|
28
|
+
})
|
|
29
|
+
.then((res: any) => res.payment_details || [])
|
|
30
|
+
.catch(() => [])
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const createPaymentDetail = async (
|
|
34
|
+
type: "upi" | "bank" | "card",
|
|
35
|
+
detail_json: Record<string, string>
|
|
36
|
+
) => {
|
|
37
|
+
const headers = {
|
|
38
|
+
...(await getAuthHeaders()),
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return sdk.client.fetch<any>(`/store/payment-details`, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers,
|
|
44
|
+
body: { type, detail_json },
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const makeDefaultPaymentDetail = async (id: string) => {
|
|
49
|
+
const headers = {
|
|
50
|
+
...(await getAuthHeaders()),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return sdk.client.fetch<any>(`/store/payment-details/${id}/make-default`, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers,
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const deletePaymentDetail = async (id: string) => {
|
|
60
|
+
const headers = {
|
|
61
|
+
...(await getAuthHeaders()),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return sdk.client.fetch<any>(`/store/payment-details/${id}`, {
|
|
65
|
+
method: "DELETE",
|
|
66
|
+
headers,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use server"
|
|
2
|
+
|
|
3
|
+
import { sdk } from "../config"
|
|
4
|
+
import { getAuthHeaders, getCacheOptions } from "../cookies"
|
|
5
|
+
import { HttpTypes } from "@medusajs/types"
|
|
6
|
+
|
|
7
|
+
export const listCartPaymentMethods = async (regionId: string) => {
|
|
8
|
+
const headers = {
|
|
9
|
+
...(await getAuthHeaders()),
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const next = {
|
|
13
|
+
...(await getCacheOptions("payment_providers")),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return sdk.client
|
|
17
|
+
.fetch<HttpTypes.StorePaymentProviderListResponse>(
|
|
18
|
+
`/store/payment-providers`,
|
|
19
|
+
{
|
|
20
|
+
method: "GET",
|
|
21
|
+
query: { region_id: regionId },
|
|
22
|
+
headers,
|
|
23
|
+
next,
|
|
24
|
+
cache: "no-store",
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
.then(({ payment_providers }) =>
|
|
28
|
+
payment_providers.sort((a, b) => {
|
|
29
|
+
return a.id > b.id ? 1 : -1
|
|
30
|
+
})
|
|
31
|
+
)
|
|
32
|
+
.catch(() => {
|
|
33
|
+
return null
|
|
34
|
+
})
|
|
35
|
+
}
|