food402 1.0.4-beta.1 → 1.1.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/README.md +80 -7
- package/dist/src/postinstall.js +1 -1
- package/package.json +3 -3
- package/dist/api.d.ts +0 -417
- package/dist/api.js +0 -1164
- package/dist/auth.d.ts +0 -16
- package/dist/auth.js +0 -117
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -455
- package/dist/postinstall.d.ts +0 -2
- package/dist/postinstall.js +0 -29
package/dist/api.js
DELETED
|
@@ -1,1164 +0,0 @@
|
|
|
1
|
-
// src/api.ts - TGO Yemek API Functions
|
|
2
|
-
import { getToken } from "./auth.js";
|
|
3
|
-
import { randomUUID } from "crypto";
|
|
4
|
-
const USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36";
|
|
5
|
-
const API_BASE = "https://api.tgoapis.com";
|
|
6
|
-
const PAYMENT_API_BASE = "https://payment.tgoapps.com";
|
|
7
|
-
export async function getAddresses() {
|
|
8
|
-
const token = await getToken();
|
|
9
|
-
const response = await fetch(`${API_BASE}/web-user-apimemberaddress-santral/addresses`, {
|
|
10
|
-
method: "GET",
|
|
11
|
-
headers: {
|
|
12
|
-
"Accept": "application/json, text/plain, */*",
|
|
13
|
-
"Authorization": `Bearer ${token}`,
|
|
14
|
-
"User-Agent": USER_AGENT,
|
|
15
|
-
"Origin": "https://tgoyemek.com",
|
|
16
|
-
"x-correlationid": randomUUID(),
|
|
17
|
-
"pid": randomUUID(),
|
|
18
|
-
"sid": randomUUID()
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
if (!response.ok) {
|
|
22
|
-
throw new Error(`Failed to fetch addresses: ${response.status} ${response.statusText}`);
|
|
23
|
-
}
|
|
24
|
-
return response.json();
|
|
25
|
-
}
|
|
26
|
-
export async function getRestaurants(latitude, longitude, page = 1) {
|
|
27
|
-
const token = await getToken();
|
|
28
|
-
const pageSize = 50;
|
|
29
|
-
const params = new URLSearchParams({
|
|
30
|
-
sortType: "RESTAURANT_SCORE",
|
|
31
|
-
minBasketPrice: "400",
|
|
32
|
-
openRestaurants: "true",
|
|
33
|
-
latitude,
|
|
34
|
-
longitude,
|
|
35
|
-
pageSize: pageSize.toString(),
|
|
36
|
-
page: page.toString()
|
|
37
|
-
});
|
|
38
|
-
const response = await fetch(`${API_BASE}/web-discovery-apidiscovery-santral/restaurants/filters?${params}`, {
|
|
39
|
-
method: "GET",
|
|
40
|
-
headers: {
|
|
41
|
-
"Accept": "application/json, text/plain, */*",
|
|
42
|
-
"Authorization": `Bearer ${token}`,
|
|
43
|
-
"User-Agent": USER_AGENT,
|
|
44
|
-
"Origin": "https://tgoyemek.com",
|
|
45
|
-
"x-correlationid": randomUUID(),
|
|
46
|
-
"pid": randomUUID(),
|
|
47
|
-
"sid": randomUUID()
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
if (!response.ok) {
|
|
51
|
-
throw new Error(`Failed to fetch restaurants: ${response.status} ${response.statusText}`);
|
|
52
|
-
}
|
|
53
|
-
const data = await response.json();
|
|
54
|
-
// Transform to simplified format for AI context efficiency
|
|
55
|
-
const restaurants = data.restaurants.map((r) => ({
|
|
56
|
-
id: r.id,
|
|
57
|
-
name: r.name,
|
|
58
|
-
kitchen: r.kitchen,
|
|
59
|
-
rating: r.rating,
|
|
60
|
-
ratingText: r.ratingText,
|
|
61
|
-
minBasketPrice: r.minBasketPrice,
|
|
62
|
-
averageDeliveryInterval: r.averageDeliveryInterval,
|
|
63
|
-
distance: r.location?.distance ?? 0,
|
|
64
|
-
neighborhoodName: r.location?.neighborhoodName ?? "",
|
|
65
|
-
isClosed: r.isClosed,
|
|
66
|
-
campaignText: r.campaignText
|
|
67
|
-
}));
|
|
68
|
-
return {
|
|
69
|
-
restaurants,
|
|
70
|
-
totalCount: data.restaurantCount,
|
|
71
|
-
currentPage: page,
|
|
72
|
-
pageSize,
|
|
73
|
-
hasNextPage: !!data.links?.next?.href
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
export async function getRestaurantMenu(restaurantId, latitude, longitude) {
|
|
77
|
-
const token = await getToken();
|
|
78
|
-
const params = new URLSearchParams({ latitude, longitude });
|
|
79
|
-
const response = await fetch(`${API_BASE}/web-restaurant-apirestaurant-santral/restaurants/${restaurantId}?${params}`, {
|
|
80
|
-
method: "GET",
|
|
81
|
-
headers: {
|
|
82
|
-
"Accept": "application/json, text/plain, */*",
|
|
83
|
-
"Authorization": `Bearer ${token}`,
|
|
84
|
-
"User-Agent": USER_AGENT,
|
|
85
|
-
"Origin": "https://tgoyemek.com",
|
|
86
|
-
"x-correlationid": randomUUID(),
|
|
87
|
-
"pid": randomUUID(),
|
|
88
|
-
"sid": randomUUID()
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
throw new Error(`Failed to fetch restaurant menu: ${response.status} ${response.statusText}`);
|
|
93
|
-
}
|
|
94
|
-
const data = await response.json();
|
|
95
|
-
const restaurant = data.restaurant;
|
|
96
|
-
// Transform restaurant info
|
|
97
|
-
const info = {
|
|
98
|
-
id: restaurant.info.id,
|
|
99
|
-
name: restaurant.info.name,
|
|
100
|
-
status: restaurant.info.status,
|
|
101
|
-
rating: restaurant.info.score?.overall ?? 0,
|
|
102
|
-
ratingText: restaurant.info.score?.ratingText ?? "",
|
|
103
|
-
workingHours: restaurant.info.workingHours,
|
|
104
|
-
deliveryTime: restaurant.info.deliveryInfo?.eta ?? "",
|
|
105
|
-
minOrderPrice: restaurant.info.deliveryInfo?.minPrice ?? 0
|
|
106
|
-
};
|
|
107
|
-
// Transform categories and products
|
|
108
|
-
let totalItems = 0;
|
|
109
|
-
const categories = restaurant.sections.map((section) => {
|
|
110
|
-
const items = section.products.map((product) => ({
|
|
111
|
-
id: product.id,
|
|
112
|
-
name: product.name,
|
|
113
|
-
description: product.description ?? "",
|
|
114
|
-
price: product.price?.salePrice ?? 0,
|
|
115
|
-
likePercentage: product.productScore?.likePercentageInfo
|
|
116
|
-
}));
|
|
117
|
-
totalItems += items.length;
|
|
118
|
-
return {
|
|
119
|
-
name: section.name,
|
|
120
|
-
slug: section.slug,
|
|
121
|
-
items
|
|
122
|
-
};
|
|
123
|
-
});
|
|
124
|
-
return {
|
|
125
|
-
info,
|
|
126
|
-
categories,
|
|
127
|
-
totalItems
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
export async function getProductRecommendations(restaurantId, productIds) {
|
|
131
|
-
const token = await getToken();
|
|
132
|
-
const response = await fetch(`${API_BASE}/web-discovery-apidiscovery-santral/recommendation/product`, {
|
|
133
|
-
method: "POST",
|
|
134
|
-
headers: {
|
|
135
|
-
"Accept": "application/json, text/plain, */*",
|
|
136
|
-
"Authorization": `Bearer ${token}`,
|
|
137
|
-
"Content-Type": "application/json",
|
|
138
|
-
"User-Agent": USER_AGENT,
|
|
139
|
-
"Origin": "https://tgoyemek.com",
|
|
140
|
-
"x-correlationid": randomUUID(),
|
|
141
|
-
"pid": randomUUID(),
|
|
142
|
-
"sid": randomUUID()
|
|
143
|
-
},
|
|
144
|
-
body: JSON.stringify({
|
|
145
|
-
restaurantId: restaurantId.toString(),
|
|
146
|
-
productIds: productIds.map(id => id.toString()),
|
|
147
|
-
page: "PDP"
|
|
148
|
-
})
|
|
149
|
-
});
|
|
150
|
-
if (!response.ok) {
|
|
151
|
-
throw new Error(`Failed to fetch product recommendations: ${response.status} ${response.statusText}`);
|
|
152
|
-
}
|
|
153
|
-
const data = await response.json();
|
|
154
|
-
// Transform to simplified format
|
|
155
|
-
let totalItems = 0;
|
|
156
|
-
const collections = (data.collections || []).map((collection) => {
|
|
157
|
-
const items = (collection.items || []).map((item) => ({
|
|
158
|
-
id: item.id,
|
|
159
|
-
name: item.name,
|
|
160
|
-
description: item.description,
|
|
161
|
-
price: item.sellingPrice ?? 0,
|
|
162
|
-
imageUrl: item.imageUrl ?? ""
|
|
163
|
-
}));
|
|
164
|
-
totalItems += items.length;
|
|
165
|
-
return {
|
|
166
|
-
name: collection.name,
|
|
167
|
-
items
|
|
168
|
-
};
|
|
169
|
-
});
|
|
170
|
-
return {
|
|
171
|
-
collections,
|
|
172
|
-
totalItems
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
export async function getProductDetails(restaurantId, productId, latitude, longitude) {
|
|
176
|
-
const token = await getToken();
|
|
177
|
-
const params = new URLSearchParams({ latitude, longitude });
|
|
178
|
-
const response = await fetch(`${API_BASE}/web-restaurant-apirestaurant-santral/restaurants/${restaurantId}/products/${productId}?${params}`, {
|
|
179
|
-
method: "POST",
|
|
180
|
-
headers: {
|
|
181
|
-
"Accept": "application/json, text/plain, */*",
|
|
182
|
-
"Authorization": `Bearer ${token}`,
|
|
183
|
-
"Content-Type": "application/json",
|
|
184
|
-
"User-Agent": USER_AGENT,
|
|
185
|
-
"Origin": "https://tgoyemek.com",
|
|
186
|
-
"x-correlationid": randomUUID(),
|
|
187
|
-
"pid": randomUUID(),
|
|
188
|
-
"sid": randomUUID()
|
|
189
|
-
},
|
|
190
|
-
body: JSON.stringify({})
|
|
191
|
-
});
|
|
192
|
-
if (!response.ok) {
|
|
193
|
-
throw new Error(`Failed to fetch product details: ${response.status} ${response.statusText}`);
|
|
194
|
-
}
|
|
195
|
-
const data = await response.json();
|
|
196
|
-
// Transform components to simplified format
|
|
197
|
-
const components = (data.components || []).map((comp) => ({
|
|
198
|
-
type: comp.type,
|
|
199
|
-
title: comp.title,
|
|
200
|
-
description: comp.description,
|
|
201
|
-
modifierGroupId: comp.modifierGroupId, // Include for MODIFIER_GROUP types
|
|
202
|
-
options: (comp.options || []).map((opt) => ({
|
|
203
|
-
id: opt.optionId,
|
|
204
|
-
name: opt.title,
|
|
205
|
-
price: opt.price?.salePrice ?? 0,
|
|
206
|
-
selected: opt.selected ?? false,
|
|
207
|
-
isPopular: opt.badges?.some((b) => b.type === "POPULAR_OPTION") ?? false
|
|
208
|
-
})),
|
|
209
|
-
isSingleChoice: comp.isSingleChoice ?? false,
|
|
210
|
-
minSelections: comp.min ?? 0,
|
|
211
|
-
maxSelections: comp.max ?? 0
|
|
212
|
-
}));
|
|
213
|
-
return {
|
|
214
|
-
restaurantId: data.restaurantId,
|
|
215
|
-
restaurantName: data.restaurantName,
|
|
216
|
-
productId: data.productId,
|
|
217
|
-
productName: data.productName,
|
|
218
|
-
description: data.productDescription ?? "",
|
|
219
|
-
imageUrl: data.productImage ?? "",
|
|
220
|
-
price: data.price?.salePrice ?? 0,
|
|
221
|
-
maxQuantity: data.maxQuantity ?? 50,
|
|
222
|
-
components
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
export async function setShippingAddress(request) {
|
|
226
|
-
const token = await getToken();
|
|
227
|
-
const response = await fetch(`${API_BASE}/web-checkout-apicheckout-santral/shipping`, {
|
|
228
|
-
method: "POST",
|
|
229
|
-
headers: {
|
|
230
|
-
"Accept": "application/json, text/plain, */*",
|
|
231
|
-
"Authorization": `Bearer ${token}`,
|
|
232
|
-
"Content-Type": "application/json",
|
|
233
|
-
"User-Agent": USER_AGENT,
|
|
234
|
-
"Origin": "https://tgoyemek.com",
|
|
235
|
-
"x-correlationid": randomUUID(),
|
|
236
|
-
"pid": randomUUID(),
|
|
237
|
-
"sid": randomUUID()
|
|
238
|
-
},
|
|
239
|
-
body: JSON.stringify(request)
|
|
240
|
-
});
|
|
241
|
-
if (!response.ok) {
|
|
242
|
-
throw new Error(`Failed to set shipping address: ${response.status} ${response.statusText}`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
export async function addToBasket(request) {
|
|
246
|
-
const token = await getToken();
|
|
247
|
-
const response = await fetch(`${API_BASE}/web-checkout-apicheckout-santral/carts/items`, {
|
|
248
|
-
method: "POST",
|
|
249
|
-
headers: {
|
|
250
|
-
"Accept": "application/json, text/plain, */*",
|
|
251
|
-
"Authorization": `Bearer ${token}`,
|
|
252
|
-
"Content-Type": "application/json",
|
|
253
|
-
"User-Agent": USER_AGENT,
|
|
254
|
-
"Origin": "https://tgoyemek.com",
|
|
255
|
-
"x-correlationid": randomUUID(),
|
|
256
|
-
"pid": randomUUID(),
|
|
257
|
-
"sid": randomUUID()
|
|
258
|
-
},
|
|
259
|
-
body: JSON.stringify({
|
|
260
|
-
storeId: request.storeId,
|
|
261
|
-
items: request.items,
|
|
262
|
-
isFlashSale: false, // Always false
|
|
263
|
-
storePickup: false, // Always false (delivery mode)
|
|
264
|
-
latitude: request.latitude,
|
|
265
|
-
longitude: request.longitude
|
|
266
|
-
})
|
|
267
|
-
});
|
|
268
|
-
if (!response.ok) {
|
|
269
|
-
throw new Error(`Failed to add to basket: ${response.status} ${response.statusText}`);
|
|
270
|
-
}
|
|
271
|
-
const data = await response.json();
|
|
272
|
-
// Extract store info from first grouped product
|
|
273
|
-
const storeData = data.groupedProducts?.[0]?.store;
|
|
274
|
-
const store = {
|
|
275
|
-
id: storeData?.id ?? request.storeId,
|
|
276
|
-
name: storeData?.name ?? "",
|
|
277
|
-
imageUrl: storeData?.imageUrl ?? "",
|
|
278
|
-
rating: storeData?.rating ?? 0,
|
|
279
|
-
averageDeliveryInterval: storeData?.averageDeliveryInterval ?? "",
|
|
280
|
-
minAmount: storeData?.minAmount ?? 0
|
|
281
|
-
};
|
|
282
|
-
// Extract products from grouped products
|
|
283
|
-
const products = (data.groupedProducts?.[0]?.products || []).map((p) => ({
|
|
284
|
-
productId: p.productId,
|
|
285
|
-
itemId: p.itemId,
|
|
286
|
-
name: p.name,
|
|
287
|
-
quantity: p.quantity,
|
|
288
|
-
salePrice: p.salePrice,
|
|
289
|
-
description: p.description ?? ""
|
|
290
|
-
}));
|
|
291
|
-
// Transform summary
|
|
292
|
-
const summary = (data.summary || []).map((s) => ({
|
|
293
|
-
title: s.title,
|
|
294
|
-
amount: s.amount,
|
|
295
|
-
isPromotion: s.isPromotion ?? false
|
|
296
|
-
}));
|
|
297
|
-
return {
|
|
298
|
-
store,
|
|
299
|
-
products,
|
|
300
|
-
summary,
|
|
301
|
-
totalProductCount: data.totalProductCount ?? 0,
|
|
302
|
-
totalProductPrice: data.totalProductPrice ?? 0,
|
|
303
|
-
totalProductPriceDiscounted: data.totalProductPriceDiscounted ?? 0,
|
|
304
|
-
totalPrice: data.totalPrice ?? 0,
|
|
305
|
-
deliveryPrice: data.deliveryPrice ?? 0
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
export async function getBasket() {
|
|
309
|
-
const token = await getToken();
|
|
310
|
-
const response = await fetch(`${API_BASE}/web-checkout-apicheckout-santral/carts`, {
|
|
311
|
-
method: "GET",
|
|
312
|
-
headers: {
|
|
313
|
-
"Accept": "application/json, text/plain, */*",
|
|
314
|
-
"Authorization": `Bearer ${token}`,
|
|
315
|
-
"User-Agent": USER_AGENT,
|
|
316
|
-
"Origin": "https://tgoyemek.com",
|
|
317
|
-
"x-correlationid": randomUUID(),
|
|
318
|
-
"pid": randomUUID(),
|
|
319
|
-
"sid": randomUUID()
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
if (!response.ok) {
|
|
323
|
-
throw new Error(`Failed to get basket: ${response.status} ${response.statusText}`);
|
|
324
|
-
}
|
|
325
|
-
const data = await response.json();
|
|
326
|
-
// Transform grouped products to store groups
|
|
327
|
-
const storeGroups = (data.groupedProducts || []).map((group) => ({
|
|
328
|
-
store: {
|
|
329
|
-
id: group.store?.id ?? 0,
|
|
330
|
-
name: group.store?.name ?? "",
|
|
331
|
-
imageUrl: group.store?.imageUrl ?? "",
|
|
332
|
-
rating: group.store?.rating ?? 0,
|
|
333
|
-
averageDeliveryInterval: group.store?.averageDeliveryInterval ?? "",
|
|
334
|
-
minAmount: group.store?.minAmount ?? 0
|
|
335
|
-
},
|
|
336
|
-
products: (group.products || []).map((p) => ({
|
|
337
|
-
productId: p.productId,
|
|
338
|
-
itemId: p.itemId,
|
|
339
|
-
name: p.name,
|
|
340
|
-
quantity: p.quantity,
|
|
341
|
-
salePrice: p.salePrice,
|
|
342
|
-
description: p.description ?? "",
|
|
343
|
-
marketPrice: p.marketPrice ?? 0,
|
|
344
|
-
modifierProducts: (p.modifierProducts || []).map((m) => ({
|
|
345
|
-
productId: m.productId,
|
|
346
|
-
modifierGroupId: m.modifierGroupId,
|
|
347
|
-
name: m.name,
|
|
348
|
-
price: m.price
|
|
349
|
-
})),
|
|
350
|
-
ingredientExcludes: (p.ingredientOption?.excludes || []).map((e) => ({
|
|
351
|
-
id: e.id,
|
|
352
|
-
name: e.name
|
|
353
|
-
}))
|
|
354
|
-
}))
|
|
355
|
-
}));
|
|
356
|
-
// Transform summary
|
|
357
|
-
const summary = (data.summary || []).map((s) => ({
|
|
358
|
-
title: s.title,
|
|
359
|
-
amount: s.amount,
|
|
360
|
-
isPromotion: s.isPromotion ?? false
|
|
361
|
-
}));
|
|
362
|
-
return {
|
|
363
|
-
storeGroups,
|
|
364
|
-
summary,
|
|
365
|
-
totalProductCount: data.totalProductCount ?? 0,
|
|
366
|
-
totalProductPrice: data.totalProductPrice ?? 0,
|
|
367
|
-
totalProductPriceDiscounted: data.totalProductPriceDiscounted ?? 0,
|
|
368
|
-
totalPrice: data.totalPrice ?? 0,
|
|
369
|
-
deliveryPrice: data.deliveryPrice ?? 0,
|
|
370
|
-
isEmpty: (data.totalProductCount ?? 0) === 0
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
export async function removeFromBasket(itemId) {
|
|
374
|
-
const token = await getToken();
|
|
375
|
-
const response = await fetch(`${API_BASE}/web-checkout-apicheckout-santral/carts/items/${itemId}`, {
|
|
376
|
-
method: "DELETE",
|
|
377
|
-
headers: {
|
|
378
|
-
"Accept": "application/json, text/plain, */*",
|
|
379
|
-
"Authorization": `Bearer ${token}`,
|
|
380
|
-
"User-Agent": USER_AGENT,
|
|
381
|
-
"Origin": "https://tgoyemek.com",
|
|
382
|
-
"x-correlationid": randomUUID(),
|
|
383
|
-
"pid": randomUUID(),
|
|
384
|
-
"sid": randomUUID()
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
if (!response.ok) {
|
|
388
|
-
throw new Error(`Failed to remove from basket: ${response.status} ${response.statusText}`);
|
|
389
|
-
}
|
|
390
|
-
const data = await response.json();
|
|
391
|
-
// Transform using same logic as getBasket
|
|
392
|
-
const storeGroups = (data.groupedProducts || []).map((group) => ({
|
|
393
|
-
store: {
|
|
394
|
-
id: group.store?.id ?? 0,
|
|
395
|
-
name: group.store?.name ?? "",
|
|
396
|
-
imageUrl: group.store?.imageUrl ?? "",
|
|
397
|
-
rating: group.store?.rating ?? 0,
|
|
398
|
-
averageDeliveryInterval: group.store?.averageDeliveryInterval ?? "",
|
|
399
|
-
minAmount: group.store?.minAmount ?? 0
|
|
400
|
-
},
|
|
401
|
-
products: (group.products || []).map((p) => ({
|
|
402
|
-
productId: p.productId,
|
|
403
|
-
itemId: p.itemId,
|
|
404
|
-
name: p.name,
|
|
405
|
-
quantity: p.quantity,
|
|
406
|
-
salePrice: p.salePrice,
|
|
407
|
-
description: p.description ?? "",
|
|
408
|
-
marketPrice: p.marketPrice ?? 0,
|
|
409
|
-
modifierProducts: (p.modifierProducts || []).map((m) => ({
|
|
410
|
-
productId: m.productId,
|
|
411
|
-
modifierGroupId: m.modifierGroupId,
|
|
412
|
-
name: m.name,
|
|
413
|
-
price: m.price
|
|
414
|
-
})),
|
|
415
|
-
ingredientExcludes: (p.ingredientOption?.excludes || []).map((e) => ({
|
|
416
|
-
id: e.id,
|
|
417
|
-
name: e.name
|
|
418
|
-
}))
|
|
419
|
-
}))
|
|
420
|
-
}));
|
|
421
|
-
const summary = (data.summary || []).map((s) => ({
|
|
422
|
-
title: s.title,
|
|
423
|
-
amount: s.amount,
|
|
424
|
-
isPromotion: s.isPromotion ?? false
|
|
425
|
-
}));
|
|
426
|
-
return {
|
|
427
|
-
storeGroups,
|
|
428
|
-
summary,
|
|
429
|
-
totalProductCount: data.totalProductCount ?? 0,
|
|
430
|
-
totalProductPrice: data.totalProductPrice ?? 0,
|
|
431
|
-
totalProductPriceDiscounted: data.totalProductPriceDiscounted ?? 0,
|
|
432
|
-
totalPrice: data.totalPrice ?? 0,
|
|
433
|
-
deliveryPrice: data.deliveryPrice ?? 0,
|
|
434
|
-
isEmpty: (data.totalProductCount ?? 0) === 0
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
export async function clearBasket() {
|
|
438
|
-
const token = await getToken();
|
|
439
|
-
const response = await fetch(`${API_BASE}/web-checkout-apicheckout-santral/carts`, {
|
|
440
|
-
method: "DELETE",
|
|
441
|
-
headers: {
|
|
442
|
-
"Accept": "application/json, text/plain, */*",
|
|
443
|
-
"Authorization": `Bearer ${token}`,
|
|
444
|
-
"User-Agent": USER_AGENT,
|
|
445
|
-
"Origin": "https://tgoyemek.com",
|
|
446
|
-
"x-correlationid": randomUUID(),
|
|
447
|
-
"pid": randomUUID(),
|
|
448
|
-
"sid": randomUUID()
|
|
449
|
-
}
|
|
450
|
-
});
|
|
451
|
-
if (!response.ok) {
|
|
452
|
-
throw new Error(`Failed to clear basket: ${response.status} ${response.statusText}`);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
export async function getCities() {
|
|
456
|
-
const token = await getToken();
|
|
457
|
-
const response = await fetch(`${API_BASE}/web-user-apimemberaddress-santral/cities`, {
|
|
458
|
-
method: "GET",
|
|
459
|
-
headers: {
|
|
460
|
-
"Accept": "application/json, text/plain, */*",
|
|
461
|
-
"Authorization": `Bearer ${token}`,
|
|
462
|
-
"User-Agent": USER_AGENT,
|
|
463
|
-
"Origin": "https://tgoyemek.com",
|
|
464
|
-
"x-correlationid": randomUUID(),
|
|
465
|
-
"pid": randomUUID(),
|
|
466
|
-
"sid": randomUUID()
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
if (!response.ok) {
|
|
470
|
-
throw new Error(`Failed to fetch cities: ${response.status} ${response.statusText}`);
|
|
471
|
-
}
|
|
472
|
-
const data = await response.json();
|
|
473
|
-
const cities = (data.cities || []).map((c) => ({
|
|
474
|
-
id: c.id,
|
|
475
|
-
code: c.code,
|
|
476
|
-
name: c.name
|
|
477
|
-
}));
|
|
478
|
-
return {
|
|
479
|
-
cities,
|
|
480
|
-
count: cities.length
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
export async function getDistricts(cityId) {
|
|
484
|
-
const token = await getToken();
|
|
485
|
-
const response = await fetch(`${API_BASE}/web-user-apimemberaddress-santral/cities/${cityId}/districts`, {
|
|
486
|
-
method: "GET",
|
|
487
|
-
headers: {
|
|
488
|
-
"Accept": "application/json, text/plain, */*",
|
|
489
|
-
"Authorization": `Bearer ${token}`,
|
|
490
|
-
"User-Agent": USER_AGENT,
|
|
491
|
-
"Origin": "https://tgoyemek.com",
|
|
492
|
-
"x-correlationid": randomUUID(),
|
|
493
|
-
"pid": randomUUID(),
|
|
494
|
-
"sid": randomUUID()
|
|
495
|
-
}
|
|
496
|
-
});
|
|
497
|
-
if (!response.ok) {
|
|
498
|
-
throw new Error(`Failed to fetch districts: ${response.status} ${response.statusText}`);
|
|
499
|
-
}
|
|
500
|
-
const data = await response.json();
|
|
501
|
-
const districts = (data.districts || []).map((d) => ({
|
|
502
|
-
id: d.id,
|
|
503
|
-
name: d.name
|
|
504
|
-
}));
|
|
505
|
-
return {
|
|
506
|
-
districts,
|
|
507
|
-
count: districts.length,
|
|
508
|
-
cityId
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
export async function getNeighborhoods(districtId) {
|
|
512
|
-
const token = await getToken();
|
|
513
|
-
const response = await fetch(`${API_BASE}/web-user-apimemberaddress-santral/districts/${districtId}/neighborhoods`, {
|
|
514
|
-
method: "GET",
|
|
515
|
-
headers: {
|
|
516
|
-
"Accept": "application/json, text/plain, */*",
|
|
517
|
-
"Authorization": `Bearer ${token}`,
|
|
518
|
-
"User-Agent": USER_AGENT,
|
|
519
|
-
"Origin": "https://tgoyemek.com",
|
|
520
|
-
"x-correlationid": randomUUID(),
|
|
521
|
-
"pid": randomUUID(),
|
|
522
|
-
"sid": randomUUID()
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
if (!response.ok) {
|
|
526
|
-
throw new Error(`Failed to fetch neighborhoods: ${response.status} ${response.statusText}`);
|
|
527
|
-
}
|
|
528
|
-
const data = await response.json();
|
|
529
|
-
const neighborhoods = (data.neighborhoods || []).map((n) => ({
|
|
530
|
-
id: n.id,
|
|
531
|
-
name: n.name
|
|
532
|
-
}));
|
|
533
|
-
return {
|
|
534
|
-
neighborhoods,
|
|
535
|
-
count: neighborhoods.length,
|
|
536
|
-
districtId
|
|
537
|
-
};
|
|
538
|
-
}
|
|
539
|
-
export async function addAddress(request) {
|
|
540
|
-
const token = await getToken();
|
|
541
|
-
const payload = {
|
|
542
|
-
name: request.name,
|
|
543
|
-
surname: request.surname,
|
|
544
|
-
phone: request.phone,
|
|
545
|
-
apartmentNumber: request.apartmentNumber ?? "",
|
|
546
|
-
floor: request.floor ?? "",
|
|
547
|
-
doorNumber: request.doorNumber ?? "",
|
|
548
|
-
addressName: request.addressName,
|
|
549
|
-
addressDescription: request.addressDescription ?? "",
|
|
550
|
-
addressLine: request.addressLine,
|
|
551
|
-
cityId: request.cityId,
|
|
552
|
-
districtId: request.districtId,
|
|
553
|
-
neighborhoodId: request.neighborhoodId,
|
|
554
|
-
latitude: request.latitude,
|
|
555
|
-
longitude: request.longitude,
|
|
556
|
-
countryCode: request.countryCode ?? "TR",
|
|
557
|
-
elevatorAvailable: request.elevatorAvailable ?? false
|
|
558
|
-
};
|
|
559
|
-
const response = await fetch(`${API_BASE}/web-user-apimemberaddress-santral/addresses`, {
|
|
560
|
-
method: "POST",
|
|
561
|
-
headers: {
|
|
562
|
-
"Accept": "application/json, text/plain, */*",
|
|
563
|
-
"Authorization": `Bearer ${token}`,
|
|
564
|
-
"Content-Type": "application/json",
|
|
565
|
-
"User-Agent": USER_AGENT,
|
|
566
|
-
"Origin": "https://tgoyemek.com",
|
|
567
|
-
"x-correlationid": randomUUID(),
|
|
568
|
-
"pid": randomUUID(),
|
|
569
|
-
"sid": randomUUID()
|
|
570
|
-
},
|
|
571
|
-
body: JSON.stringify(payload)
|
|
572
|
-
});
|
|
573
|
-
// Handle OTP required case (429 Too Many Requests)
|
|
574
|
-
if (response.status === 429) {
|
|
575
|
-
return {
|
|
576
|
-
success: false,
|
|
577
|
-
requiresOtp: true,
|
|
578
|
-
message: "OTP verification required. Please add this address through the TGO Yemek website."
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
if (!response.ok) {
|
|
582
|
-
throw new Error(`Failed to add address: ${response.status} ${response.statusText}`);
|
|
583
|
-
}
|
|
584
|
-
const data = await response.json();
|
|
585
|
-
// Transform the returned address
|
|
586
|
-
const address = {
|
|
587
|
-
id: data.id,
|
|
588
|
-
name: data.name,
|
|
589
|
-
surname: data.surname,
|
|
590
|
-
phone: data.phone,
|
|
591
|
-
countryPhoneCode: data.countryPhoneCode ?? "+90",
|
|
592
|
-
addressLine: data.addressLine,
|
|
593
|
-
addressName: data.addressName,
|
|
594
|
-
postalCode: data.postalCode ?? "",
|
|
595
|
-
cityId: data.cityId,
|
|
596
|
-
cityName: data.cityName ?? "",
|
|
597
|
-
districtId: data.districtId,
|
|
598
|
-
districtName: data.districtName ?? "",
|
|
599
|
-
neighborhoodId: data.neighborhoodId,
|
|
600
|
-
neighborhoodName: data.neighborhoodName ?? "",
|
|
601
|
-
latitude: data.latitude,
|
|
602
|
-
longitude: data.longitude,
|
|
603
|
-
addressDescription: data.addressDescription ?? "",
|
|
604
|
-
apartmentNumber: data.apartmentNumber ?? "",
|
|
605
|
-
floor: data.floor ?? "",
|
|
606
|
-
doorNumber: data.doorNumber ?? "",
|
|
607
|
-
addressType: data.addressType ?? "HOME",
|
|
608
|
-
elevatorAvailable: data.elevatorAvailable ?? false
|
|
609
|
-
};
|
|
610
|
-
return {
|
|
611
|
-
success: true,
|
|
612
|
-
address,
|
|
613
|
-
message: "Address added successfully"
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
export async function updateCustomerNote(request) {
|
|
617
|
-
const token = await getToken();
|
|
618
|
-
const response = await fetch(`${API_BASE}/web-checkout-apicheckout-santral/carts/customerNote`, {
|
|
619
|
-
method: "PUT",
|
|
620
|
-
headers: {
|
|
621
|
-
"Accept": "application/json, text/plain, */*",
|
|
622
|
-
"Authorization": `Bearer ${token}`,
|
|
623
|
-
"Content-Type": "application/json",
|
|
624
|
-
"User-Agent": USER_AGENT,
|
|
625
|
-
"Origin": "https://tgoyemek.com",
|
|
626
|
-
"x-correlationid": randomUUID(),
|
|
627
|
-
"pid": randomUUID(),
|
|
628
|
-
"sid": randomUUID()
|
|
629
|
-
},
|
|
630
|
-
body: JSON.stringify({
|
|
631
|
-
customerNote: request.customerNote,
|
|
632
|
-
noServiceWare: request.noServiceWare,
|
|
633
|
-
contactlessDelivery: request.contactlessDelivery,
|
|
634
|
-
dontRingBell: request.dontRingBell
|
|
635
|
-
})
|
|
636
|
-
});
|
|
637
|
-
if (!response.ok) {
|
|
638
|
-
throw new Error(`Failed to update customer note: ${response.status} ${response.statusText}`);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
export async function getSavedCards() {
|
|
642
|
-
const token = await getToken();
|
|
643
|
-
const response = await fetch(`${PAYMENT_API_BASE}/v2/cards/`, {
|
|
644
|
-
method: "GET",
|
|
645
|
-
headers: {
|
|
646
|
-
"Accept": "application/json, text/plain, */*",
|
|
647
|
-
"Authorization": `Bearer ${token}`,
|
|
648
|
-
"User-Agent": USER_AGENT,
|
|
649
|
-
"Origin": "https://tgoyemek.com",
|
|
650
|
-
"app-name": "TrendyolGo",
|
|
651
|
-
"x-applicationid": "1",
|
|
652
|
-
"x-channelid": "4",
|
|
653
|
-
"x-storefrontid": "1",
|
|
654
|
-
"x-features": "OPTIONAL_REBATE;MEAL_CART_ENABLED",
|
|
655
|
-
"x-supported-payment-options": "MULTINET;SODEXO;EDENRED;ON_DELIVERY;SETCARD",
|
|
656
|
-
"x-correlationid": randomUUID(),
|
|
657
|
-
"pid": randomUUID(),
|
|
658
|
-
"sid": randomUUID()
|
|
659
|
-
}
|
|
660
|
-
});
|
|
661
|
-
if (!response.ok) {
|
|
662
|
-
throw new Error(`Failed to fetch saved cards: ${response.status} ${response.statusText}`);
|
|
663
|
-
}
|
|
664
|
-
const data = await response.json();
|
|
665
|
-
const cardsData = data.json?.cards || data.cards || [];
|
|
666
|
-
const cards = cardsData.map((c) => ({
|
|
667
|
-
cardId: c.cardId,
|
|
668
|
-
name: c.name ?? "",
|
|
669
|
-
maskedCardNumber: c.maskedCardNumber ?? "",
|
|
670
|
-
cardTypeName: c.cardTypeName ?? "",
|
|
671
|
-
bankName: c.bankName ?? "",
|
|
672
|
-
isDebitCard: c.isDebitCard ?? false,
|
|
673
|
-
cvvRequired: c.cvvRequired ?? false,
|
|
674
|
-
cardNetwork: c.cardNetwork ?? ""
|
|
675
|
-
}));
|
|
676
|
-
if (cards.length === 0) {
|
|
677
|
-
return {
|
|
678
|
-
cards: [],
|
|
679
|
-
hasCards: false,
|
|
680
|
-
message: "No saved cards. Please add a payment method at tgoyemek.com"
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
return {
|
|
684
|
-
cards,
|
|
685
|
-
hasCards: true
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
|
-
export async function getCheckoutReady() {
|
|
689
|
-
const token = await getToken();
|
|
690
|
-
const response = await fetch(`${API_BASE}/web-checkout-apicheckout-santral/carts?cartContext=payment&limitPromoMbs=false`, {
|
|
691
|
-
method: "GET",
|
|
692
|
-
headers: {
|
|
693
|
-
"Accept": "application/json, text/plain, */*",
|
|
694
|
-
"Authorization": `Bearer ${token}`,
|
|
695
|
-
"User-Agent": USER_AGENT,
|
|
696
|
-
"Origin": "https://tgoyemek.com",
|
|
697
|
-
"x-correlationid": randomUUID(),
|
|
698
|
-
"pid": randomUUID(),
|
|
699
|
-
"sid": randomUUID()
|
|
700
|
-
}
|
|
701
|
-
});
|
|
702
|
-
// Handle 400 error (typically means empty cart)
|
|
703
|
-
if (response.status === 400) {
|
|
704
|
-
return {
|
|
705
|
-
ready: false,
|
|
706
|
-
store: {
|
|
707
|
-
id: 0,
|
|
708
|
-
name: "",
|
|
709
|
-
imageUrl: "",
|
|
710
|
-
rating: 0,
|
|
711
|
-
averageDeliveryInterval: "",
|
|
712
|
-
minAmount: 0
|
|
713
|
-
},
|
|
714
|
-
products: [],
|
|
715
|
-
summary: [],
|
|
716
|
-
totalPrice: 0,
|
|
717
|
-
deliveryPrice: 0,
|
|
718
|
-
warnings: ["Cart is empty. Add items before checkout."]
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
if (!response.ok) {
|
|
722
|
-
throw new Error(`Failed to get checkout ready: ${response.status} ${response.statusText}`);
|
|
723
|
-
}
|
|
724
|
-
const data = await response.json();
|
|
725
|
-
// Extract warnings from response
|
|
726
|
-
const warnings = [];
|
|
727
|
-
if (data.warnings) {
|
|
728
|
-
warnings.push(...data.warnings.map((w) => w.message || String(w)));
|
|
729
|
-
}
|
|
730
|
-
// Check if cart is empty
|
|
731
|
-
if ((data.totalProductCount ?? 0) === 0) {
|
|
732
|
-
return {
|
|
733
|
-
ready: false,
|
|
734
|
-
store: {
|
|
735
|
-
id: 0,
|
|
736
|
-
name: "",
|
|
737
|
-
imageUrl: "",
|
|
738
|
-
rating: 0,
|
|
739
|
-
averageDeliveryInterval: "",
|
|
740
|
-
minAmount: 0
|
|
741
|
-
},
|
|
742
|
-
products: [],
|
|
743
|
-
summary: [],
|
|
744
|
-
totalPrice: 0,
|
|
745
|
-
deliveryPrice: 0,
|
|
746
|
-
warnings: ["Cart is empty. Add items before checkout."]
|
|
747
|
-
};
|
|
748
|
-
}
|
|
749
|
-
// Extract store and products from first group
|
|
750
|
-
const group = data.groupedProducts?.[0];
|
|
751
|
-
const store = {
|
|
752
|
-
id: group?.store?.id ?? 0,
|
|
753
|
-
name: group?.store?.name ?? "",
|
|
754
|
-
imageUrl: group?.store?.imageUrl ?? "",
|
|
755
|
-
rating: group?.store?.rating ?? 0,
|
|
756
|
-
averageDeliveryInterval: group?.store?.averageDeliveryInterval ?? "",
|
|
757
|
-
minAmount: group?.store?.minAmount ?? 0
|
|
758
|
-
};
|
|
759
|
-
const products = (group?.products || []).map((p) => ({
|
|
760
|
-
productId: p.productId,
|
|
761
|
-
itemId: p.itemId,
|
|
762
|
-
name: p.name,
|
|
763
|
-
quantity: p.quantity,
|
|
764
|
-
salePrice: p.salePrice,
|
|
765
|
-
description: p.description ?? "",
|
|
766
|
-
marketPrice: p.marketPrice ?? 0,
|
|
767
|
-
modifierProducts: (p.modifierProducts || []).map((m) => ({
|
|
768
|
-
productId: m.productId,
|
|
769
|
-
modifierGroupId: m.modifierGroupId,
|
|
770
|
-
name: m.name,
|
|
771
|
-
price: m.price
|
|
772
|
-
})),
|
|
773
|
-
ingredientExcludes: (p.ingredientOption?.excludes || []).map((e) => ({
|
|
774
|
-
id: e.id,
|
|
775
|
-
name: e.name
|
|
776
|
-
}))
|
|
777
|
-
}));
|
|
778
|
-
const summary = (data.summary || []).map((s) => ({
|
|
779
|
-
title: s.title,
|
|
780
|
-
amount: s.amount,
|
|
781
|
-
isPromotion: s.isPromotion ?? false
|
|
782
|
-
}));
|
|
783
|
-
// Check minimum order amount
|
|
784
|
-
const minAmount = store.minAmount || 0;
|
|
785
|
-
const totalPrice = data.totalPrice ?? 0;
|
|
786
|
-
if (minAmount > 0 && totalPrice < minAmount) {
|
|
787
|
-
warnings.push(`Minimum order amount is ${minAmount} TL. Current total: ${totalPrice} TL`);
|
|
788
|
-
}
|
|
789
|
-
return {
|
|
790
|
-
ready: warnings.length === 0,
|
|
791
|
-
store,
|
|
792
|
-
products,
|
|
793
|
-
summary,
|
|
794
|
-
totalPrice: data.totalPrice ?? 0,
|
|
795
|
-
deliveryPrice: data.deliveryPrice ?? 0,
|
|
796
|
-
warnings
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
async function selectPaymentMethod(cardId, binCode) {
|
|
800
|
-
const token = await getToken();
|
|
801
|
-
const response = await fetch(`${PAYMENT_API_BASE}/v3/payment/options`, {
|
|
802
|
-
method: "POST",
|
|
803
|
-
headers: {
|
|
804
|
-
"Accept": "application/json, text/plain, */*",
|
|
805
|
-
"Authorization": `Bearer ${token}`,
|
|
806
|
-
"Content-Type": "application/json",
|
|
807
|
-
"User-Agent": USER_AGENT,
|
|
808
|
-
"Origin": "https://tgoyemek.com",
|
|
809
|
-
"app-name": "TrendyolGo",
|
|
810
|
-
"x-applicationid": "1",
|
|
811
|
-
"x-channelid": "4",
|
|
812
|
-
"x-storefrontid": "1",
|
|
813
|
-
"x-features": "OPTIONAL_REBATE;MEAL_CART_ENABLED",
|
|
814
|
-
"x-supported-payment-options": "MULTINET;SODEXO;EDENRED;ON_DELIVERY;SETCARD",
|
|
815
|
-
"x-correlationid": randomUUID(),
|
|
816
|
-
"pid": randomUUID(),
|
|
817
|
-
"sid": randomUUID()
|
|
818
|
-
},
|
|
819
|
-
body: JSON.stringify({
|
|
820
|
-
paymentType: "payWithCard",
|
|
821
|
-
data: {
|
|
822
|
-
savedCardId: cardId,
|
|
823
|
-
binCode: binCode,
|
|
824
|
-
installmentId: 0,
|
|
825
|
-
reward: null,
|
|
826
|
-
installmentPostponingSelected: false
|
|
827
|
-
}
|
|
828
|
-
})
|
|
829
|
-
});
|
|
830
|
-
if (!response.ok) {
|
|
831
|
-
throw new Error(`Failed to select payment method: ${response.status} ${response.statusText}`);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
export async function placeOrder(cardId) {
|
|
835
|
-
const token = await getToken();
|
|
836
|
-
// First, get the saved cards to find the bin code for this card
|
|
837
|
-
const cardsResponse = await getSavedCards();
|
|
838
|
-
const card = cardsResponse.cards.find(c => c.cardId === cardId);
|
|
839
|
-
if (!card) {
|
|
840
|
-
return {
|
|
841
|
-
success: false,
|
|
842
|
-
message: `Card with ID ${cardId} not found. Use get_saved_cards to see available cards.`
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
// Extract bin code from masked card number (first 6 digits + **)
|
|
846
|
-
const binCode = card.maskedCardNumber.substring(0, 6) + "**";
|
|
847
|
-
// IMPORTANT: Use the same session IDs across all payment-related calls
|
|
848
|
-
// This is required for the payment system to track the transaction properly
|
|
849
|
-
const correlationId = randomUUID();
|
|
850
|
-
const pid = randomUUID();
|
|
851
|
-
const sid = randomUUID();
|
|
852
|
-
const paymentHeaders = {
|
|
853
|
-
"Accept": "application/json, text/plain, */*",
|
|
854
|
-
"Authorization": `Bearer ${token}`,
|
|
855
|
-
"Content-Type": "application/json",
|
|
856
|
-
"User-Agent": USER_AGENT,
|
|
857
|
-
"Origin": "https://tgoyemek.com",
|
|
858
|
-
"app-name": "TrendyolGo",
|
|
859
|
-
"x-applicationid": "1",
|
|
860
|
-
"x-channelid": "4",
|
|
861
|
-
"x-storefrontid": "1",
|
|
862
|
-
"x-features": "OPTIONAL_REBATE;MEAL_CART_ENABLED",
|
|
863
|
-
"x-supported-payment-options": "MULTINET;SODEXO;EDENRED;ON_DELIVERY;SETCARD",
|
|
864
|
-
"x-correlationid": correlationId,
|
|
865
|
-
"pid": pid,
|
|
866
|
-
"sid": sid
|
|
867
|
-
};
|
|
868
|
-
// Step 1: Initialize cart state in payment system
|
|
869
|
-
const checkoutResponse = await fetch(`${API_BASE}/web-checkout-apicheckout-santral/carts?cartContext=payment&limitPromoMbs=false`, { method: "GET", headers: paymentHeaders });
|
|
870
|
-
if (!checkoutResponse.ok) {
|
|
871
|
-
return {
|
|
872
|
-
success: false,
|
|
873
|
-
message: `Failed to initialize checkout: ${checkoutResponse.status} ${checkoutResponse.statusText}`
|
|
874
|
-
};
|
|
875
|
-
}
|
|
876
|
-
// Step 2: Select payment method
|
|
877
|
-
const optionsResponse = await fetch(`${PAYMENT_API_BASE}/v3/payment/options`, {
|
|
878
|
-
method: "POST",
|
|
879
|
-
headers: paymentHeaders,
|
|
880
|
-
body: JSON.stringify({
|
|
881
|
-
paymentType: "payWithCard",
|
|
882
|
-
data: {
|
|
883
|
-
savedCardId: cardId,
|
|
884
|
-
binCode: binCode,
|
|
885
|
-
installmentId: 0,
|
|
886
|
-
reward: null,
|
|
887
|
-
installmentPostponingSelected: false
|
|
888
|
-
}
|
|
889
|
-
})
|
|
890
|
-
});
|
|
891
|
-
if (!optionsResponse.ok) {
|
|
892
|
-
return {
|
|
893
|
-
success: false,
|
|
894
|
-
message: `Failed to select payment method: ${optionsResponse.status} ${optionsResponse.statusText}`
|
|
895
|
-
};
|
|
896
|
-
}
|
|
897
|
-
// Step 3: Place the order with 3D Secure
|
|
898
|
-
const response = await fetch(`${PAYMENT_API_BASE}/v2/payment/pay`, {
|
|
899
|
-
method: "POST",
|
|
900
|
-
headers: paymentHeaders,
|
|
901
|
-
body: JSON.stringify({
|
|
902
|
-
customerSelectedThreeD: false,
|
|
903
|
-
paymentOptions: [
|
|
904
|
-
{
|
|
905
|
-
name: "payWithCard",
|
|
906
|
-
cardNo: "",
|
|
907
|
-
customerSelectedThreeD: false
|
|
908
|
-
}
|
|
909
|
-
],
|
|
910
|
-
callbackUrl: "https://tgoyemek.com/odeme"
|
|
911
|
-
})
|
|
912
|
-
});
|
|
913
|
-
if (!response.ok) {
|
|
914
|
-
const errorText = await response.text();
|
|
915
|
-
// Check for 3D Secure requirement in error response
|
|
916
|
-
if (response.status === 400 || response.status === 403) {
|
|
917
|
-
try {
|
|
918
|
-
const errorData = JSON.parse(errorText);
|
|
919
|
-
if (errorData.redirectUrl || errorData.requires3DSecure || errorData.threeDSecureUrl || errorData.htmlContent || errorData.json?.content) {
|
|
920
|
-
return {
|
|
921
|
-
success: false,
|
|
922
|
-
requires3DSecure: true,
|
|
923
|
-
redirectUrl: errorData.redirectUrl || errorData.threeDSecureUrl,
|
|
924
|
-
htmlContent: errorData.htmlContent || errorData.json?.content,
|
|
925
|
-
message: "3D Secure verification required. Complete payment in browser."
|
|
926
|
-
};
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
catch {
|
|
930
|
-
// Not JSON, continue with generic error
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
throw new Error(`Failed to place order: ${response.status} ${response.statusText}`);
|
|
934
|
-
}
|
|
935
|
-
const data = await response.json();
|
|
936
|
-
// Check if 3D Secure HTML content is returned (successful 3D Secure initiation)
|
|
937
|
-
if (data.json?.content) {
|
|
938
|
-
// Extract redirect URL from HTML form if present
|
|
939
|
-
const formMatch = data.json.content.match(/action="([^"]+)"/);
|
|
940
|
-
const redirectUrl = formMatch ? formMatch[1] : undefined;
|
|
941
|
-
return {
|
|
942
|
-
success: false,
|
|
943
|
-
requires3DSecure: true,
|
|
944
|
-
redirectUrl,
|
|
945
|
-
htmlContent: data.json.content,
|
|
946
|
-
message: "3D Secure verification required. Complete payment in browser."
|
|
947
|
-
};
|
|
948
|
-
}
|
|
949
|
-
// Check other 3D Secure indicators
|
|
950
|
-
if (data.requires3DSecure || data.redirectUrl || data.threeDSecureUrl || data.htmlContent) {
|
|
951
|
-
return {
|
|
952
|
-
success: false,
|
|
953
|
-
requires3DSecure: true,
|
|
954
|
-
redirectUrl: data.redirectUrl || data.threeDSecureUrl,
|
|
955
|
-
htmlContent: data.htmlContent,
|
|
956
|
-
message: "3D Secure verification required. Complete payment in browser."
|
|
957
|
-
};
|
|
958
|
-
}
|
|
959
|
-
return {
|
|
960
|
-
success: true,
|
|
961
|
-
orderId: data.orderId || data.orderNumber || data.id,
|
|
962
|
-
message: "Order placed successfully!"
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
export async function getOrders(page = 1) {
|
|
966
|
-
const token = await getToken();
|
|
967
|
-
const pageSize = 50;
|
|
968
|
-
const params = new URLSearchParams({
|
|
969
|
-
page: page.toString(),
|
|
970
|
-
pageSize: pageSize.toString()
|
|
971
|
-
});
|
|
972
|
-
const response = await fetch(`${API_BASE}/web-checkout-apicheckout-santral/orders?${params}`, {
|
|
973
|
-
method: "GET",
|
|
974
|
-
headers: {
|
|
975
|
-
"Accept": "application/json, text/plain, */*",
|
|
976
|
-
"Authorization": `Bearer ${token}`,
|
|
977
|
-
"User-Agent": USER_AGENT,
|
|
978
|
-
"Origin": "https://tgoyemek.com",
|
|
979
|
-
"x-correlationid": randomUUID(),
|
|
980
|
-
"pid": randomUUID(),
|
|
981
|
-
"sid": randomUUID()
|
|
982
|
-
}
|
|
983
|
-
});
|
|
984
|
-
if (!response.ok) {
|
|
985
|
-
throw new Error(`Failed to fetch orders: ${response.status} ${response.statusText}`);
|
|
986
|
-
}
|
|
987
|
-
const data = await response.json();
|
|
988
|
-
// Transform orders to simplified format
|
|
989
|
-
const orders = (data.orders || []).map((o) => ({
|
|
990
|
-
id: o.id,
|
|
991
|
-
orderDate: o.orderDate ?? "",
|
|
992
|
-
store: {
|
|
993
|
-
id: o.store?.id ?? 0,
|
|
994
|
-
name: o.store?.name ?? ""
|
|
995
|
-
},
|
|
996
|
-
status: {
|
|
997
|
-
status: o.status?.status ?? "",
|
|
998
|
-
statusText: o.status?.statusText ?? "",
|
|
999
|
-
statusColor: o.status?.statusColor ?? ""
|
|
1000
|
-
},
|
|
1001
|
-
price: {
|
|
1002
|
-
totalPrice: o.price?.totalPrice ?? 0,
|
|
1003
|
-
totalPriceText: o.price?.totalPriceText ?? "",
|
|
1004
|
-
refundedPrice: o.price?.refundedPrice ?? 0,
|
|
1005
|
-
cancelledPrice: o.price?.cancelledPrice ?? 0,
|
|
1006
|
-
totalDeliveryPrice: o.price?.totalDeliveryPrice ?? 0,
|
|
1007
|
-
totalServicePrice: o.price?.totalServicePrice ?? 0
|
|
1008
|
-
},
|
|
1009
|
-
productSummary: o.product?.name ?? "",
|
|
1010
|
-
products: (o.productList || []).map((p) => ({
|
|
1011
|
-
productId: p.productId,
|
|
1012
|
-
name: p.name,
|
|
1013
|
-
imageUrl: p.imageUrl ?? ""
|
|
1014
|
-
})),
|
|
1015
|
-
isReady: o.isReady ?? false
|
|
1016
|
-
}));
|
|
1017
|
-
return {
|
|
1018
|
-
orders,
|
|
1019
|
-
pagination: {
|
|
1020
|
-
currentPage: data.pagination?.currentPage ?? page,
|
|
1021
|
-
pageSize: data.pagination?.pageSize ?? pageSize,
|
|
1022
|
-
totalCount: data.pagination?.totalCount ?? 0,
|
|
1023
|
-
hasNext: data.pagination?.hasNext ?? false
|
|
1024
|
-
}
|
|
1025
|
-
};
|
|
1026
|
-
}
|
|
1027
|
-
export async function getOrderDetail(orderId) {
|
|
1028
|
-
const token = await getToken();
|
|
1029
|
-
const params = new URLSearchParams({
|
|
1030
|
-
orderId
|
|
1031
|
-
});
|
|
1032
|
-
const response = await fetch(`${API_BASE}/web-checkout-apicheckout-santral/orders/detail?${params}`, {
|
|
1033
|
-
method: "GET",
|
|
1034
|
-
headers: {
|
|
1035
|
-
"Accept": "application/json, text/plain, */*",
|
|
1036
|
-
"Authorization": `Bearer ${token}`,
|
|
1037
|
-
"User-Agent": USER_AGENT,
|
|
1038
|
-
"Origin": "https://tgoyemek.com",
|
|
1039
|
-
"x-correlationid": randomUUID(),
|
|
1040
|
-
"pid": randomUUID(),
|
|
1041
|
-
"sid": randomUUID()
|
|
1042
|
-
}
|
|
1043
|
-
});
|
|
1044
|
-
if (!response.ok) {
|
|
1045
|
-
throw new Error(`Failed to fetch order detail: ${response.status} ${response.statusText}`);
|
|
1046
|
-
}
|
|
1047
|
-
const data = await response.json();
|
|
1048
|
-
// Extract shipment info
|
|
1049
|
-
const shipment = data.shipment;
|
|
1050
|
-
const shipmentSummary = shipment?.summary;
|
|
1051
|
-
const shipmentItem = shipment?.items?.[0];
|
|
1052
|
-
// Extract status steps from shipment item state
|
|
1053
|
-
const statusSteps = (shipmentItem?.state?.statuses || []).map((s) => ({
|
|
1054
|
-
status: s.status ?? "",
|
|
1055
|
-
statusText: s.statusText ?? ""
|
|
1056
|
-
}));
|
|
1057
|
-
// Extract products from shipment item
|
|
1058
|
-
const products = (shipmentItem?.products || []).map((p) => ({
|
|
1059
|
-
name: p.name ?? "",
|
|
1060
|
-
imageUrl: p.imageUrl ?? "",
|
|
1061
|
-
salePrice: p.salePrice ?? 0,
|
|
1062
|
-
salePriceText: p.salePriceText ?? "",
|
|
1063
|
-
quantity: p.quantity ?? 1,
|
|
1064
|
-
description: p.description ?? ""
|
|
1065
|
-
}));
|
|
1066
|
-
// Extract delivery address
|
|
1067
|
-
const addr = data.deliveryAddress;
|
|
1068
|
-
// Extract price from summary
|
|
1069
|
-
const summaryPrice = data.summary?.price;
|
|
1070
|
-
return {
|
|
1071
|
-
orderId: data.summary?.orderId ?? orderId,
|
|
1072
|
-
orderNumber: data.summary?.orderNumber ?? "",
|
|
1073
|
-
orderDate: data.summary?.orderDate ?? "",
|
|
1074
|
-
customerNote: data.summary?.customerNote ?? "",
|
|
1075
|
-
store: {
|
|
1076
|
-
id: parseInt(shipmentSummary?.store?.id, 10) || 0,
|
|
1077
|
-
name: shipmentSummary?.store?.name ?? ""
|
|
1078
|
-
},
|
|
1079
|
-
eta: shipmentSummary?.eta ?? "",
|
|
1080
|
-
deliveredDate: shipmentSummary?.deliveredDate ?? "",
|
|
1081
|
-
status: {
|
|
1082
|
-
status: shipmentItem?.status?.status ?? "",
|
|
1083
|
-
statusText: shipmentItem?.status?.statusText ?? "",
|
|
1084
|
-
statusColor: shipmentItem?.status?.statusColor ?? ""
|
|
1085
|
-
},
|
|
1086
|
-
statusSteps,
|
|
1087
|
-
products,
|
|
1088
|
-
price: {
|
|
1089
|
-
totalPrice: summaryPrice?.totalPrice ?? 0,
|
|
1090
|
-
totalPriceText: summaryPrice?.totalPriceText ?? "",
|
|
1091
|
-
refundedPrice: summaryPrice?.refundedPrice ?? 0,
|
|
1092
|
-
cancelledPrice: summaryPrice?.cancelledPrice ?? 0,
|
|
1093
|
-
totalDeliveryPrice: summaryPrice?.totalDeliveryPrice ?? 0,
|
|
1094
|
-
totalServicePrice: summaryPrice?.totalServicePrice ?? 0
|
|
1095
|
-
},
|
|
1096
|
-
paymentDescription: data.paymentInfo?.paymentDescription ?? "",
|
|
1097
|
-
deliveryAddress: {
|
|
1098
|
-
name: addr?.name ?? "",
|
|
1099
|
-
address: addr?.address ?? "",
|
|
1100
|
-
districtCity: addr?.districtCity ?? "",
|
|
1101
|
-
phoneNumber: addr?.phoneNumber ?? ""
|
|
1102
|
-
}
|
|
1103
|
-
};
|
|
1104
|
-
}
|
|
1105
|
-
export async function searchRestaurants(searchQuery, latitude, longitude, page = 1) {
|
|
1106
|
-
const token = await getToken();
|
|
1107
|
-
const pageSize = 50;
|
|
1108
|
-
const params = new URLSearchParams({
|
|
1109
|
-
searchQuery,
|
|
1110
|
-
latitude,
|
|
1111
|
-
longitude,
|
|
1112
|
-
pageSize: pageSize.toString(),
|
|
1113
|
-
page: page.toString()
|
|
1114
|
-
});
|
|
1115
|
-
const response = await fetch(`${API_BASE}/web-restaurant-apirestaurant-santral/restaurants/in/search?${params}`, {
|
|
1116
|
-
method: "GET",
|
|
1117
|
-
headers: {
|
|
1118
|
-
"Accept": "application/json, text/plain, */*",
|
|
1119
|
-
"Authorization": `Bearer ${token}`,
|
|
1120
|
-
"User-Agent": USER_AGENT,
|
|
1121
|
-
"Origin": "https://tgoyemek.com",
|
|
1122
|
-
"x-correlationid": randomUUID(),
|
|
1123
|
-
"pid": randomUUID(),
|
|
1124
|
-
"sid": randomUUID()
|
|
1125
|
-
}
|
|
1126
|
-
});
|
|
1127
|
-
if (!response.ok) {
|
|
1128
|
-
throw new Error(`Failed to search restaurants: ${response.status} ${response.statusText}`);
|
|
1129
|
-
}
|
|
1130
|
-
const data = await response.json();
|
|
1131
|
-
// Transform to simplified format for AI context efficiency
|
|
1132
|
-
const restaurants = (data.restaurants || []).map((r) => {
|
|
1133
|
-
const isClosed = r.isClosed ?? false;
|
|
1134
|
-
return {
|
|
1135
|
-
id: r.id,
|
|
1136
|
-
name: r.name,
|
|
1137
|
-
kitchen: r.kitchen ?? "",
|
|
1138
|
-
rating: r.rating ?? 0,
|
|
1139
|
-
ratingText: r.ratingText ?? "",
|
|
1140
|
-
minBasketPrice: r.minBasketPrice ?? 0,
|
|
1141
|
-
averageDeliveryInterval: r.averageDeliveryInterval ?? "",
|
|
1142
|
-
distance: r.location?.distance ?? 0,
|
|
1143
|
-
neighborhoodName: r.location?.neighborhoodName ?? "",
|
|
1144
|
-
isClosed,
|
|
1145
|
-
campaignText: r.campaignText,
|
|
1146
|
-
products: (r.products || []).map((p) => ({
|
|
1147
|
-
id: p.id,
|
|
1148
|
-
name: p.name,
|
|
1149
|
-
description: p.description,
|
|
1150
|
-
price: p.price?.salePrice ?? p.price ?? 0,
|
|
1151
|
-
imageUrl: p.imageUrl
|
|
1152
|
-
})),
|
|
1153
|
-
...(isClosed && { warning: "This restaurant is currently closed. Do not proceed with ordering from this restaurant." })
|
|
1154
|
-
};
|
|
1155
|
-
});
|
|
1156
|
-
return {
|
|
1157
|
-
restaurants,
|
|
1158
|
-
totalCount: data.restaurantCount ?? 0,
|
|
1159
|
-
currentPage: page,
|
|
1160
|
-
pageSize,
|
|
1161
|
-
hasNextPage: !!data.links?.next?.href,
|
|
1162
|
-
searchQuery: data.searchQuery ?? searchQuery
|
|
1163
|
-
};
|
|
1164
|
-
}
|