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