playttm 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib.d.ts +3 -0
- package/dist/lib.js +828 -292
- package/package.json +1 -1
- package/src/lib.ts +905 -381
package/src/lib.ts
CHANGED
|
@@ -261,30 +261,154 @@ export class ThaiTicketMajor {
|
|
|
261
261
|
/**
|
|
262
262
|
* Setup declarativeNetRequest session rules in Chrome Extension to set Referer header for API requests only.
|
|
263
263
|
*/
|
|
264
|
+
/**
|
|
265
|
+
* Setup declarativeNetRequest session rules in Chrome Extension to set Referer headers accurately for each endpoint.
|
|
266
|
+
*/
|
|
264
267
|
public static async setupExtensionRules(): Promise<void> {
|
|
265
268
|
if (typeof chrome === "undefined" || !chrome?.declarativeNetRequest?.updateSessionRules) {
|
|
266
269
|
return;
|
|
267
270
|
}
|
|
268
271
|
|
|
269
272
|
try {
|
|
273
|
+
const ruleIdsToRemove = [1001, 1002, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019];
|
|
270
274
|
await chrome.declarativeNetRequest.updateSessionRules({
|
|
271
|
-
removeRuleIds:
|
|
275
|
+
removeRuleIds: ruleIdsToRemove,
|
|
272
276
|
addRules: [
|
|
273
277
|
{
|
|
274
|
-
id:
|
|
275
|
-
priority:
|
|
278
|
+
id: 1010,
|
|
279
|
+
priority: 20,
|
|
276
280
|
action: {
|
|
277
281
|
type: chrome.declarativeNetRequest.RuleActionType?.MODIFY_HEADERS || "modifyHeaders",
|
|
278
282
|
requestHeaders: [
|
|
279
283
|
{
|
|
280
284
|
header: "Referer",
|
|
281
285
|
operation: chrome.declarativeNetRequest.HeaderOperation?.SET || "set",
|
|
282
|
-
value: "https://booking.thaiticketmajor.com",
|
|
286
|
+
value: "https://booking.thaiticketmajor.com/booking/3m/orderenc_kbankqr.php",
|
|
283
287
|
},
|
|
284
288
|
],
|
|
285
289
|
},
|
|
286
290
|
condition: {
|
|
287
|
-
urlFilter: "
|
|
291
|
+
urlFilter: "getkbankqr.php",
|
|
292
|
+
resourceTypes: [
|
|
293
|
+
chrome.declarativeNetRequest.ResourceType?.XMLHTTPREQUEST || "xmlhttprequest",
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
id: 1011,
|
|
299
|
+
priority: 20,
|
|
300
|
+
action: {
|
|
301
|
+
type: chrome.declarativeNetRequest.RuleActionType?.MODIFY_HEADERS || "modifyHeaders",
|
|
302
|
+
requestHeaders: [
|
|
303
|
+
{
|
|
304
|
+
header: "Referer",
|
|
305
|
+
operation: chrome.declarativeNetRequest.HeaderOperation?.SET || "set",
|
|
306
|
+
value: "https://booking.thaiticketmajor.com/booking/3m/orderenc_kbankqr.php",
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
},
|
|
310
|
+
condition: {
|
|
311
|
+
urlFilter: "payment_kbankqr.php",
|
|
312
|
+
resourceTypes: [
|
|
313
|
+
chrome.declarativeNetRequest.ResourceType?.XMLHTTPREQUEST || "xmlhttprequest",
|
|
314
|
+
],
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
id: 1012,
|
|
319
|
+
priority: 20,
|
|
320
|
+
action: {
|
|
321
|
+
type: chrome.declarativeNetRequest.RuleActionType?.MODIFY_HEADERS || "modifyHeaders",
|
|
322
|
+
requestHeaders: [
|
|
323
|
+
{
|
|
324
|
+
header: "Referer",
|
|
325
|
+
operation: chrome.declarativeNetRequest.HeaderOperation?.SET || "set",
|
|
326
|
+
value: "https://booking.thaiticketmajor.com/booking/3m/paycfmall.php",
|
|
327
|
+
},
|
|
328
|
+
],
|
|
329
|
+
},
|
|
330
|
+
condition: {
|
|
331
|
+
urlFilter: "orderenc_kbankqr.php",
|
|
332
|
+
resourceTypes: [
|
|
333
|
+
chrome.declarativeNetRequest.ResourceType?.XMLHTTPREQUEST || "xmlhttprequest",
|
|
334
|
+
],
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
id: 1013,
|
|
339
|
+
priority: 20,
|
|
340
|
+
action: {
|
|
341
|
+
type: chrome.declarativeNetRequest.RuleActionType?.MODIFY_HEADERS || "modifyHeaders",
|
|
342
|
+
requestHeaders: [
|
|
343
|
+
{
|
|
344
|
+
header: "Referer",
|
|
345
|
+
operation: chrome.declarativeNetRequest.HeaderOperation?.SET || "set",
|
|
346
|
+
value: "https://booking.thaiticketmajor.com/booking/3m/paymentall.php",
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
},
|
|
350
|
+
condition: {
|
|
351
|
+
urlFilter: "paycfmall.php",
|
|
352
|
+
resourceTypes: [
|
|
353
|
+
chrome.declarativeNetRequest.ResourceType?.XMLHTTPREQUEST || "xmlhttprequest",
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
id: 1014,
|
|
359
|
+
priority: 20,
|
|
360
|
+
action: {
|
|
361
|
+
type: chrome.declarativeNetRequest.RuleActionType?.MODIFY_HEADERS || "modifyHeaders",
|
|
362
|
+
requestHeaders: [
|
|
363
|
+
{
|
|
364
|
+
header: "Referer",
|
|
365
|
+
operation: chrome.declarativeNetRequest.HeaderOperation?.SET || "set",
|
|
366
|
+
value: "https://booking.thaiticketmajor.com/booking/3m/fixed.php",
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
},
|
|
370
|
+
condition: {
|
|
371
|
+
urlFilter: "paymentall.php",
|
|
372
|
+
resourceTypes: [
|
|
373
|
+
chrome.declarativeNetRequest.ResourceType?.XMLHTTPREQUEST || "xmlhttprequest",
|
|
374
|
+
],
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
id: 1015,
|
|
379
|
+
priority: 20,
|
|
380
|
+
action: {
|
|
381
|
+
type: chrome.declarativeNetRequest.RuleActionType?.MODIFY_HEADERS || "modifyHeaders",
|
|
382
|
+
requestHeaders: [
|
|
383
|
+
{
|
|
384
|
+
header: "Referer",
|
|
385
|
+
operation: chrome.declarativeNetRequest.HeaderOperation?.SET || "set",
|
|
386
|
+
value: "https://booking.thaiticketmajor.com/booking/3m/fixed.php",
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
condition: {
|
|
391
|
+
urlFilter: "enroll",
|
|
392
|
+
resourceTypes: [
|
|
393
|
+
chrome.declarativeNetRequest.ResourceType?.XMLHTTPREQUEST || "xmlhttprequest",
|
|
394
|
+
],
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
id: 1018,
|
|
399
|
+
priority: 20,
|
|
400
|
+
action: {
|
|
401
|
+
type: chrome.declarativeNetRequest.RuleActionType?.MODIFY_HEADERS || "modifyHeaders",
|
|
402
|
+
requestHeaders: [
|
|
403
|
+
{
|
|
404
|
+
header: "Referer",
|
|
405
|
+
operation: chrome.declarativeNetRequest.HeaderOperation?.SET || "set",
|
|
406
|
+
value: "https://booking.thaiticketmajor.com/booking/3m/zones.php",
|
|
407
|
+
},
|
|
408
|
+
],
|
|
409
|
+
},
|
|
410
|
+
condition: {
|
|
411
|
+
urlFilter: "zonesavail.php",
|
|
288
412
|
resourceTypes: [
|
|
289
413
|
chrome.declarativeNetRequest.ResourceType?.XMLHTTPREQUEST || "xmlhttprequest",
|
|
290
414
|
],
|
|
@@ -292,7 +416,7 @@ export class ThaiTicketMajor {
|
|
|
292
416
|
},
|
|
293
417
|
{
|
|
294
418
|
id: 1002,
|
|
295
|
-
priority:
|
|
419
|
+
priority: 20,
|
|
296
420
|
action: {
|
|
297
421
|
type: chrome.declarativeNetRequest.RuleActionType?.MODIFY_HEADERS || "modifyHeaders",
|
|
298
422
|
requestHeaders: [
|
|
@@ -310,6 +434,26 @@ export class ThaiTicketMajor {
|
|
|
310
434
|
],
|
|
311
435
|
},
|
|
312
436
|
},
|
|
437
|
+
{
|
|
438
|
+
id: 1001,
|
|
439
|
+
priority: 1,
|
|
440
|
+
action: {
|
|
441
|
+
type: chrome.declarativeNetRequest.RuleActionType?.MODIFY_HEADERS || "modifyHeaders",
|
|
442
|
+
requestHeaders: [
|
|
443
|
+
{
|
|
444
|
+
header: "Referer",
|
|
445
|
+
operation: chrome.declarativeNetRequest.HeaderOperation?.SET || "set",
|
|
446
|
+
value: "https://booking.thaiticketmajor.com/booking/3m/",
|
|
447
|
+
},
|
|
448
|
+
],
|
|
449
|
+
},
|
|
450
|
+
condition: {
|
|
451
|
+
urlFilter: "thaiticketmajor.com",
|
|
452
|
+
resourceTypes: [
|
|
453
|
+
chrome.declarativeNetRequest.ResourceType?.XMLHTTPREQUEST || "xmlhttprequest",
|
|
454
|
+
],
|
|
455
|
+
},
|
|
456
|
+
},
|
|
313
457
|
],
|
|
314
458
|
});
|
|
315
459
|
} catch (err) {
|
|
@@ -337,44 +481,64 @@ export class ThaiTicketMajor {
|
|
|
337
481
|
* Step 1: ดึงรอบการแสดงทั้งหมดจากหน้าหลักคอนเสิร์ต
|
|
338
482
|
*/
|
|
339
483
|
public async getRounds(eventUrl: string): Promise<RoundInfo[]> {
|
|
340
|
-
this.emitProgress("GET_ROUNDS", `Fetching rounds from ${eventUrl}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
headers: {
|
|
344
|
-
...this.defaultHeaders,
|
|
345
|
-
referer: "https://www.thaiticketmajor.com/all-event/",
|
|
346
|
-
},
|
|
484
|
+
this.emitProgress("GET_ROUNDS", `Fetching rounds from ${eventUrl}`, {
|
|
485
|
+
url: eventUrl,
|
|
486
|
+
requestPayload: null,
|
|
347
487
|
});
|
|
348
488
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
time
|
|
371
|
-
|
|
372
|
-
query
|
|
373
|
-
|
|
489
|
+
try {
|
|
490
|
+
const response = await this.client.get(eventUrl, {
|
|
491
|
+
headers: {
|
|
492
|
+
...this.defaultHeaders,
|
|
493
|
+
referer: "https://www.thaiticketmajor.com/all-event/",
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const $ = cheerio.load(response.data);
|
|
498
|
+
const rounds: RoundInfo[] = [];
|
|
499
|
+
|
|
500
|
+
$("a[data-button]").each((_, el) => {
|
|
501
|
+
const link = $(el);
|
|
502
|
+
const href = link.attr("href") ?? "";
|
|
503
|
+
const onclick = link.attr("onclick") ?? "";
|
|
504
|
+
const dataUrl = link.attr("data-url") ?? "";
|
|
505
|
+
const dataHref = link.attr("data-href") ?? "";
|
|
506
|
+
const allLinkAttrs = `${href} ${onclick} ${dataUrl} ${dataHref}`;
|
|
507
|
+
|
|
508
|
+
const queryMatch = allLinkAttrs.match(/query=([0-9a-zA-Z_-]+)/);
|
|
509
|
+
const query = queryMatch ? queryMatch[1] : "";
|
|
510
|
+
const time = link.find(".item-show").text().trim();
|
|
511
|
+
const date = link.closest(".row").find(".date").text().trim();
|
|
512
|
+
const isDisabled = link.is(":disabled") || link.attr("disabled") !== undefined || !query;
|
|
513
|
+
|
|
514
|
+
rounds.push({
|
|
515
|
+
id: query || link.attr("data-button") || "",
|
|
516
|
+
label: `${date} ${time}`.trim(),
|
|
517
|
+
date,
|
|
518
|
+
time,
|
|
519
|
+
url: href,
|
|
520
|
+
query,
|
|
521
|
+
isDisabled,
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
this.emitProgress("GET_ROUNDS_RES", `Found ${rounds.length} rounds`, {
|
|
526
|
+
url: eventUrl,
|
|
527
|
+
requestPayload: null,
|
|
528
|
+
responseStatus: response.status,
|
|
529
|
+
responseData: rounds,
|
|
374
530
|
});
|
|
375
|
-
});
|
|
376
531
|
|
|
377
|
-
|
|
532
|
+
return rounds;
|
|
533
|
+
} catch (err: any) {
|
|
534
|
+
this.emitProgress("GET_ROUNDS_ERR", `Failed to fetch rounds: ${err.message}`, {
|
|
535
|
+
url: eventUrl,
|
|
536
|
+
requestPayload: null,
|
|
537
|
+
responseStatus: err.response?.status,
|
|
538
|
+
responseData: err.response?.data || err.message,
|
|
539
|
+
});
|
|
540
|
+
throw err;
|
|
541
|
+
}
|
|
378
542
|
}
|
|
379
543
|
|
|
380
544
|
/**
|
|
@@ -385,113 +549,180 @@ export class ThaiTicketMajor {
|
|
|
385
549
|
throw new Error("Query ID is missing. The selected round might be disabled or not open for sale yet.");
|
|
386
550
|
}
|
|
387
551
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
);
|
|
552
|
+
const url = `${this.endpoint}/verify_checkcondition.php`;
|
|
553
|
+
const payload = {
|
|
554
|
+
rdagree: "1",
|
|
555
|
+
rdId: "",
|
|
556
|
+
autopopup: "",
|
|
557
|
+
query: query,
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
this.emitProgress("VERIFY_CONDITION", `Verifying condition for query ${query}`, {
|
|
561
|
+
url,
|
|
562
|
+
requestPayload: payload,
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
try {
|
|
566
|
+
const response = await this.client.post(
|
|
567
|
+
url,
|
|
568
|
+
new URLSearchParams(payload),
|
|
569
|
+
{ headers: this.defaultHeaders }
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
this.emitProgress("VERIFY_CONDITION_RES", `Condition check: ${response.data?.result ? "PASS" : "FAIL"}`, {
|
|
573
|
+
url,
|
|
574
|
+
requestPayload: payload,
|
|
575
|
+
responseStatus: response.status,
|
|
576
|
+
responseData: response.data,
|
|
577
|
+
});
|
|
400
578
|
|
|
401
|
-
|
|
579
|
+
return response.data;
|
|
580
|
+
} catch (err: any) {
|
|
581
|
+
this.emitProgress("VERIFY_CONDITION_ERR", `Condition check error: ${err.message}`, {
|
|
582
|
+
url,
|
|
583
|
+
requestPayload: payload,
|
|
584
|
+
responseStatus: err.response?.status,
|
|
585
|
+
responseData: err.response?.data || err.message,
|
|
586
|
+
});
|
|
587
|
+
throw err;
|
|
588
|
+
}
|
|
402
589
|
}
|
|
403
590
|
|
|
404
591
|
/**
|
|
405
592
|
* Step 3: ดึงข้อมูลโซน, Token (k, tk) และรอบการแสดง
|
|
406
593
|
*/
|
|
407
594
|
public async getZones(query: string): Promise<{ k: string; tk: string; query: string; rounds: { id: string; label: string }[]; rawInputs: Record<string, string> }> {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
595
|
+
const url = `${this.endpoint}/zones.php?query=${query}`;
|
|
596
|
+
this.emitProgress("GET_ZONES", `Fetching zone data and tokens for query ${query}`, {
|
|
597
|
+
url,
|
|
598
|
+
requestPayload: { query },
|
|
412
599
|
});
|
|
413
600
|
|
|
414
|
-
|
|
415
|
-
|
|
601
|
+
try {
|
|
602
|
+
const response = await this.client.get(url, {
|
|
603
|
+
headers: this.defaultHeaders,
|
|
604
|
+
});
|
|
416
605
|
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
const value = $(el).attr("value") ?? "";
|
|
420
|
-
if (name) {
|
|
421
|
-
rawInputs[name] = value;
|
|
422
|
-
}
|
|
423
|
-
});
|
|
606
|
+
const $ = cheerio.load(response.data);
|
|
607
|
+
const rawInputs: Record<string, string> = {};
|
|
424
608
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
};
|
|
433
|
-
})
|
|
434
|
-
.get();
|
|
609
|
+
$("#frm input[type='hidden']").each((_, el) => {
|
|
610
|
+
const name = $(el).attr("name");
|
|
611
|
+
const value = $(el).attr("value") ?? "";
|
|
612
|
+
if (name) {
|
|
613
|
+
rawInputs[name] = value;
|
|
614
|
+
}
|
|
615
|
+
});
|
|
435
616
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
617
|
+
const rounds = $("#rdId option")
|
|
618
|
+
.filter((_, el) => !!$(el).attr("value"))
|
|
619
|
+
.map((_, el) => {
|
|
620
|
+
const opt = $(el);
|
|
621
|
+
return {
|
|
622
|
+
id: opt.attr("value") ?? "",
|
|
623
|
+
label: opt.text().trim(),
|
|
624
|
+
};
|
|
625
|
+
})
|
|
626
|
+
.get();
|
|
627
|
+
|
|
628
|
+
const result = {
|
|
629
|
+
k: rawInputs["k"] || "",
|
|
630
|
+
tk: rawInputs["tk"] || "",
|
|
631
|
+
query,
|
|
632
|
+
rounds,
|
|
633
|
+
rawInputs,
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
this.emitProgress("GET_ZONES_RES", `Extracted tokens k=${result.k ? result.k.slice(0, 10) + '...' : 'none'} and ${rounds.length} rounds`, {
|
|
637
|
+
url,
|
|
638
|
+
requestPayload: { query },
|
|
639
|
+
responseStatus: response.status,
|
|
640
|
+
responseData: result,
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
return result;
|
|
644
|
+
} catch (err: any) {
|
|
645
|
+
this.emitProgress("GET_ZONES_ERR", `Failed to get zones: ${err.message}`, {
|
|
646
|
+
url,
|
|
647
|
+
requestPayload: { query },
|
|
648
|
+
responseStatus: err.response?.status,
|
|
649
|
+
responseData: err.response?.data || err.message,
|
|
650
|
+
});
|
|
651
|
+
throw err;
|
|
652
|
+
}
|
|
443
653
|
}
|
|
444
654
|
|
|
445
655
|
/**
|
|
446
656
|
* Step 4: ตรวจสอบสถานะที่นั่งว่างของแต่ละโซน
|
|
447
657
|
*/
|
|
448
658
|
public async getZoneAvailability(roundId: string, tk: string, k: string, query: string): Promise<ZoneItem[]> {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
...this.defaultHeaders,
|
|
454
|
-
referer: `${this.endpoint}/zones.php?query=${query}`,
|
|
455
|
-
},
|
|
659
|
+
const url = `${this.endpoint}/zonesavail.php?round=${roundId}&tk=${tk}`;
|
|
660
|
+
this.emitProgress("GET_ZONE_AVAIL", `Checking zone availability for round ${roundId}`, {
|
|
661
|
+
url,
|
|
662
|
+
requestPayload: { roundId, tk, query },
|
|
456
663
|
});
|
|
457
664
|
|
|
458
|
-
|
|
459
|
-
|
|
665
|
+
try {
|
|
666
|
+
const response = await this.client.get(url, {
|
|
667
|
+
headers: {
|
|
668
|
+
...this.defaultHeaders,
|
|
669
|
+
referer: `${this.endpoint}/zones.php?query=${query}`,
|
|
670
|
+
},
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
const $ = cheerio.load(response.data);
|
|
674
|
+
const zones: ZoneItem[] = [];
|
|
460
675
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
676
|
+
$(".table tbody tr").each((_, el) => {
|
|
677
|
+
const row = $(el);
|
|
678
|
+
const zoneId = row.find("td:nth-child(1) a").attr("id") ?? "";
|
|
679
|
+
const onclick = row.attr("onclick") ?? "";
|
|
680
|
+
const match = onclick.match(/gonextstep\('([^']+)','([^']+)'/);
|
|
466
681
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
682
|
+
const page = (match?.[1] ?? "") as "fixed.php" | "festival.php" | string;
|
|
683
|
+
const zoneValue = match?.[2] ?? "";
|
|
684
|
+
const statusText = row.find("td:nth-child(2) a").text().trim();
|
|
470
685
|
|
|
471
|
-
|
|
472
|
-
|
|
686
|
+
let available = 0;
|
|
687
|
+
let isAvailable = false;
|
|
473
688
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
isAvailable = true;
|
|
477
|
-
} else {
|
|
478
|
-
const num = Number(statusText);
|
|
479
|
-
if (!Number.isNaN(num) && num > 0) {
|
|
480
|
-
available = num;
|
|
689
|
+
if (statusText.toLowerCase() === "available") {
|
|
690
|
+
available = 1;
|
|
481
691
|
isAvailable = true;
|
|
692
|
+
} else {
|
|
693
|
+
const num = Number(statusText);
|
|
694
|
+
if (!Number.isNaN(num) && num > 0) {
|
|
695
|
+
available = num;
|
|
696
|
+
isAvailable = true;
|
|
697
|
+
}
|
|
482
698
|
}
|
|
483
|
-
}
|
|
484
699
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
700
|
+
zones.push({
|
|
701
|
+
id: zoneId,
|
|
702
|
+
value: zoneValue,
|
|
703
|
+
available,
|
|
704
|
+
page,
|
|
705
|
+
isAvailable,
|
|
706
|
+
});
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
this.emitProgress("GET_ZONE_AVAIL_RES", `Found ${zones.length} zones (${zones.filter(z => z.isAvailable).length} available)`, {
|
|
710
|
+
url,
|
|
711
|
+
requestPayload: { roundId, tk, query },
|
|
712
|
+
responseStatus: response.status,
|
|
713
|
+
responseData: zones,
|
|
491
714
|
});
|
|
492
|
-
});
|
|
493
715
|
|
|
494
|
-
|
|
716
|
+
return zones;
|
|
717
|
+
} catch (err: any) {
|
|
718
|
+
this.emitProgress("GET_ZONE_AVAIL_ERR", `Failed to check zone availability: ${err.message}`, {
|
|
719
|
+
url,
|
|
720
|
+
requestPayload: { roundId, tk, query },
|
|
721
|
+
responseStatus: err.response?.status,
|
|
722
|
+
responseData: err.response?.data || err.message,
|
|
723
|
+
});
|
|
724
|
+
throw err;
|
|
725
|
+
}
|
|
495
726
|
}
|
|
496
727
|
|
|
497
728
|
// --- Flow บัตรนั่ง (Fixed Seats) ---
|
|
@@ -500,33 +731,58 @@ export class ThaiTicketMajor {
|
|
|
500
731
|
* Step 5 (Fixed): ดึงผังที่นั่งว่างและ form payload
|
|
501
732
|
*/
|
|
502
733
|
public async getFixedSeats(k: string, zone: string, roundId: string): Promise<{ seats: SeatItem[]; form: Record<string, string> }> {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
734
|
+
const url = `${this.endpoint}/fixed.php?k=${k}&zone=${zone}&round=${roundId}`;
|
|
735
|
+
this.emitProgress("GET_FIXED_SEATS", `Fetching seat layout for zone ${zone}`, {
|
|
736
|
+
url,
|
|
737
|
+
requestPayload: { k, zone, roundId },
|
|
507
738
|
});
|
|
508
739
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
|
|
740
|
+
try {
|
|
741
|
+
const response = await this.client.get(url, {
|
|
742
|
+
headers: this.defaultHeaders,
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
const $ = cheerio.load(response.data);
|
|
746
|
+
const seats: SeatItem[] = $("#tableseats .seatuncheck")
|
|
747
|
+
.map((_, el) => {
|
|
748
|
+
const seat = $(el);
|
|
749
|
+
return {
|
|
750
|
+
id: seat.attr("id") ?? "",
|
|
751
|
+
seat: seat.attr("data-seat") ?? "",
|
|
752
|
+
seatk: seat.attr("data-seatk") ?? "",
|
|
753
|
+
};
|
|
754
|
+
})
|
|
755
|
+
.get();
|
|
756
|
+
|
|
757
|
+
const form: Record<string, string> = {};
|
|
758
|
+
$("#frmPayment input").each((_, el) => {
|
|
759
|
+
const name = $(el).attr("name");
|
|
760
|
+
if (name) {
|
|
761
|
+
form[name] = $(el).attr("value") ?? "";
|
|
762
|
+
}
|
|
763
|
+
});
|
|
528
764
|
|
|
529
|
-
|
|
765
|
+
this.emitProgress("GET_FIXED_SEATS_RES", `Found ${seats.length} available seats in zone ${zone}`, {
|
|
766
|
+
url,
|
|
767
|
+
requestPayload: { k, zone, roundId },
|
|
768
|
+
responseStatus: response.status,
|
|
769
|
+
responseData: {
|
|
770
|
+
seatsCount: seats.length,
|
|
771
|
+
seatsSample: seats.slice(0, 10),
|
|
772
|
+
form,
|
|
773
|
+
},
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
return { seats, form };
|
|
777
|
+
} catch (err: any) {
|
|
778
|
+
this.emitProgress("GET_FIXED_SEATS_ERR", `Failed to get fixed seats: ${err.message}`, {
|
|
779
|
+
url,
|
|
780
|
+
requestPayload: { k, zone, roundId },
|
|
781
|
+
responseStatus: err.response?.status,
|
|
782
|
+
responseData: err.response?.data || err.message,
|
|
783
|
+
});
|
|
784
|
+
throw err;
|
|
785
|
+
}
|
|
530
786
|
}
|
|
531
787
|
|
|
532
788
|
/**
|
|
@@ -588,22 +844,42 @@ export class ThaiTicketMajor {
|
|
|
588
844
|
formData.append("seat", seatNo);
|
|
589
845
|
formData.append("book_type", bookType);
|
|
590
846
|
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
847
|
+
const url = `${this.endpoint}/validateseat.php?k=${k}&zw=${zone}`;
|
|
848
|
+
const reqPayloadObj = Object.fromEntries(formData.entries());
|
|
849
|
+
|
|
850
|
+
try {
|
|
851
|
+
const response = await this.client.post(
|
|
852
|
+
url,
|
|
853
|
+
formData.toString(),
|
|
854
|
+
{
|
|
855
|
+
headers: {
|
|
856
|
+
...this.defaultHeaders,
|
|
857
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
858
|
+
"x-requested-with": "XMLHttpRequest",
|
|
859
|
+
referer: `${this.endpoint}/fixed.php?k=${k}&zone=${payload.zone}&round=${payload.rdId}`,
|
|
860
|
+
},
|
|
861
|
+
}
|
|
862
|
+
);
|
|
863
|
+
|
|
864
|
+
lastResponse = response.data;
|
|
865
|
+
this.emitProgress("VALIDATE_FIXED_SEATS_RES", `Seat validated (${currentSeat.seat}): ${lastResponse?.result ? "SUCCESS" : "FAIL"}`, {
|
|
866
|
+
url,
|
|
867
|
+
requestPayload: reqPayloadObj,
|
|
868
|
+
responseStatus: response.status,
|
|
869
|
+
responseData: lastResponse,
|
|
870
|
+
});
|
|
603
871
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
872
|
+
if (!lastResponse.result) {
|
|
873
|
+
throw new Error(`Validate seat failed (${currentSeat.seat}): ${lastResponse.message || "Unknown error"}`);
|
|
874
|
+
}
|
|
875
|
+
} catch (err: any) {
|
|
876
|
+
this.emitProgress("VALIDATE_FIXED_SEATS_ERR", `Validate seat failed (${currentSeat.seat}): ${err.message}`, {
|
|
877
|
+
url,
|
|
878
|
+
requestPayload: reqPayloadObj,
|
|
879
|
+
responseStatus: err.response?.status,
|
|
880
|
+
responseData: err.response?.data || err.message,
|
|
881
|
+
});
|
|
882
|
+
throw err;
|
|
607
883
|
}
|
|
608
884
|
}
|
|
609
885
|
|
|
@@ -656,25 +932,45 @@ export class ThaiTicketMajor {
|
|
|
656
932
|
bodyParams.set("inclvat", "");
|
|
657
933
|
bodyParams.set("seatklist", seatklistvalue);
|
|
658
934
|
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
bodyParams.toString(),
|
|
662
|
-
{
|
|
663
|
-
headers: {
|
|
664
|
-
...this.defaultHeaders,
|
|
665
|
-
accept: "application/json, text/javascript, */*; q=0.01",
|
|
666
|
-
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
667
|
-
"x-requested-with": "XMLHttpRequest",
|
|
668
|
-
referer: `${this.endpoint}/fixed.php?k=${k}&zone=${payload.zone}&round=${payload.rdId}`,
|
|
669
|
-
origin: "https://booking.thaiticketmajor.com",
|
|
670
|
-
},
|
|
671
|
-
}
|
|
672
|
-
);
|
|
935
|
+
const url = `${this.endpoint}/bookingseats.php?k=${k}`;
|
|
936
|
+
const reqPayloadObj = Object.fromEntries(bodyParams.entries());
|
|
673
937
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
938
|
+
try {
|
|
939
|
+
const response = await this.client.post(
|
|
940
|
+
url,
|
|
941
|
+
bodyParams.toString(),
|
|
942
|
+
{
|
|
943
|
+
headers: {
|
|
944
|
+
...this.defaultHeaders,
|
|
945
|
+
accept: "application/json, text/javascript, */*; q=0.01",
|
|
946
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
947
|
+
"x-requested-with": "XMLHttpRequest",
|
|
948
|
+
referer: `${this.endpoint}/fixed.php?k=${k}&zone=${payload.zone}&round=${payload.rdId}`,
|
|
949
|
+
origin: "https://booking.thaiticketmajor.com",
|
|
950
|
+
},
|
|
951
|
+
}
|
|
952
|
+
);
|
|
953
|
+
|
|
954
|
+
this.emitProgress("BOOK_FIXED_SEATS_RES", `Book fixed seats completed`, {
|
|
955
|
+
url,
|
|
956
|
+
requestPayload: reqPayloadObj,
|
|
957
|
+
responseStatus: response.status,
|
|
958
|
+
responseData: response.data,
|
|
959
|
+
});
|
|
960
|
+
|
|
961
|
+
return {
|
|
962
|
+
data: response.data,
|
|
963
|
+
nextPayload: bodyParams,
|
|
964
|
+
};
|
|
965
|
+
} catch (err: any) {
|
|
966
|
+
this.emitProgress("BOOK_FIXED_SEATS_ERR", `Booking seats error: ${err.message}`, {
|
|
967
|
+
url,
|
|
968
|
+
requestPayload: reqPayloadObj,
|
|
969
|
+
responseStatus: err.response?.status,
|
|
970
|
+
responseData: err.response?.data || err.message,
|
|
971
|
+
});
|
|
972
|
+
throw err;
|
|
973
|
+
}
|
|
678
974
|
}
|
|
679
975
|
|
|
680
976
|
// --- Flow บัตรยืน (Festival / Standing) ---
|
|
@@ -683,26 +979,47 @@ export class ThaiTicketMajor {
|
|
|
683
979
|
* Step 5 (Festival): ดึงข้อมูลหน้าบัตรยืน
|
|
684
980
|
*/
|
|
685
981
|
public async getFestival(k: string, tk: string, query: string, zone: string, roundId: string): Promise<Record<string, string>> {
|
|
686
|
-
this.
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
...this.defaultHeaders,
|
|
691
|
-
referer: `${this.endpoint}/zones.php?rdId=${roundId}&k=${k}&tk=${tk}&query=${query}`,
|
|
692
|
-
},
|
|
982
|
+
const url = `${this.endpoint}/festival.php?k=${k}&zone=${zone}&round=${roundId}`;
|
|
983
|
+
this.emitProgress("GET_FESTIVAL", `Fetching festival zone ${zone}`, {
|
|
984
|
+
url,
|
|
985
|
+
requestPayload: { k, tk, query, zone, roundId },
|
|
693
986
|
});
|
|
694
987
|
|
|
695
|
-
|
|
696
|
-
|
|
988
|
+
try {
|
|
989
|
+
const response = await this.client.get(url, {
|
|
990
|
+
headers: {
|
|
991
|
+
...this.defaultHeaders,
|
|
992
|
+
referer: `${this.endpoint}/zones.php?rdId=${roundId}&k=${k}&tk=${tk}&query=${query}`,
|
|
993
|
+
},
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
const $ = cheerio.load(response.data);
|
|
997
|
+
const formData: Record<string, string> = {};
|
|
697
998
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
999
|
+
$("#frm input").each((_, el) => {
|
|
1000
|
+
const name = $(el).attr("name");
|
|
1001
|
+
if (name) {
|
|
1002
|
+
formData[name] = $(el).attr("value") ?? "";
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
this.emitProgress("GET_FESTIVAL_RES", `Loaded festival form (${Object.keys(formData).length} fields)`, {
|
|
1007
|
+
url,
|
|
1008
|
+
requestPayload: { k, tk, query, zone, roundId },
|
|
1009
|
+
responseStatus: response.status,
|
|
1010
|
+
responseData: formData,
|
|
1011
|
+
});
|
|
704
1012
|
|
|
705
|
-
|
|
1013
|
+
return formData;
|
|
1014
|
+
} catch (err: any) {
|
|
1015
|
+
this.emitProgress("GET_FESTIVAL_ERR", `Failed to get festival page: ${err.message}`, {
|
|
1016
|
+
url,
|
|
1017
|
+
requestPayload: { k, tk, query, zone, roundId },
|
|
1018
|
+
responseStatus: err.response?.status,
|
|
1019
|
+
responseData: err.response?.data || err.message,
|
|
1020
|
+
});
|
|
1021
|
+
throw err;
|
|
1022
|
+
}
|
|
706
1023
|
}
|
|
707
1024
|
|
|
708
1025
|
/**
|
|
@@ -718,21 +1035,39 @@ export class ThaiTicketMajor {
|
|
|
718
1035
|
};
|
|
719
1036
|
|
|
720
1037
|
const formData = new URLSearchParams(preparePayload);
|
|
1038
|
+
const url = `${this.endpoint}/validateseat.php?k=${k}&zw=${payload.zone}`;
|
|
721
1039
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
1040
|
+
try {
|
|
1041
|
+
const response = await this.client.post(
|
|
1042
|
+
url,
|
|
1043
|
+
formData.toString(),
|
|
1044
|
+
{
|
|
1045
|
+
headers: {
|
|
1046
|
+
...this.defaultHeaders,
|
|
1047
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
1048
|
+
"x-requested-with": "XMLHttpRequest",
|
|
1049
|
+
referer: `${this.endpoint}/festival.php?k=${k}&zone=${payload.zone}&round=${payload.rdId}`,
|
|
1050
|
+
},
|
|
1051
|
+
}
|
|
1052
|
+
);
|
|
1053
|
+
|
|
1054
|
+
this.emitProgress("VALIDATE_FESTIVAL_RES", `Validate festival tickets: ${response.data?.result ? "SUCCESS" : "FAIL"}`, {
|
|
1055
|
+
url,
|
|
1056
|
+
requestPayload: preparePayload,
|
|
1057
|
+
responseStatus: response.status,
|
|
1058
|
+
responseData: response.data,
|
|
1059
|
+
});
|
|
734
1060
|
|
|
735
|
-
|
|
1061
|
+
return response.data;
|
|
1062
|
+
} catch (err: any) {
|
|
1063
|
+
this.emitProgress("VALIDATE_FESTIVAL_ERR", `Validate festival error: ${err.message}`, {
|
|
1064
|
+
url,
|
|
1065
|
+
requestPayload: preparePayload,
|
|
1066
|
+
responseStatus: err.response?.status,
|
|
1067
|
+
responseData: err.response?.data || err.message,
|
|
1068
|
+
});
|
|
1069
|
+
throw err;
|
|
1070
|
+
}
|
|
736
1071
|
}
|
|
737
1072
|
|
|
738
1073
|
/**
|
|
@@ -747,21 +1082,39 @@ export class ThaiTicketMajor {
|
|
|
747
1082
|
};
|
|
748
1083
|
|
|
749
1084
|
const formData = new URLSearchParams(preparePayload);
|
|
1085
|
+
const url = `${this.endpoint}/bookingfestival.php?k=${k}`;
|
|
750
1086
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
1087
|
+
try {
|
|
1088
|
+
const response = await this.client.post(
|
|
1089
|
+
url,
|
|
1090
|
+
formData.toString(),
|
|
1091
|
+
{
|
|
1092
|
+
headers: {
|
|
1093
|
+
...this.defaultHeaders,
|
|
1094
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
1095
|
+
"x-requested-with": "XMLHttpRequest",
|
|
1096
|
+
referer: `${this.endpoint}/festival.php?k=${k}&zone=${payload.zone}&round=${payload.rdId}`,
|
|
1097
|
+
},
|
|
1098
|
+
}
|
|
1099
|
+
);
|
|
763
1100
|
|
|
764
|
-
|
|
1101
|
+
this.emitProgress("BOOK_FESTIVAL_RES", `Book festival tickets completed`, {
|
|
1102
|
+
url,
|
|
1103
|
+
requestPayload: preparePayload,
|
|
1104
|
+
responseStatus: response.status,
|
|
1105
|
+
responseData: response.data,
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
return response.data;
|
|
1109
|
+
} catch (err: any) {
|
|
1110
|
+
this.emitProgress("BOOK_FESTIVAL_ERR", `Book festival error: ${err.message}`, {
|
|
1111
|
+
url,
|
|
1112
|
+
requestPayload: preparePayload,
|
|
1113
|
+
responseStatus: err.response?.status,
|
|
1114
|
+
responseData: err.response?.data || err.message,
|
|
1115
|
+
});
|
|
1116
|
+
throw err;
|
|
1117
|
+
}
|
|
765
1118
|
}
|
|
766
1119
|
|
|
767
1120
|
// --- Flow การลงทะเบียนผู้เข้าชม (Enrollment) ---
|
|
@@ -770,30 +1123,51 @@ export class ThaiTicketMajor {
|
|
|
770
1123
|
* Step 8 (Enroll): ดึง Form ลงทะเบียนผู้เข้าชม
|
|
771
1124
|
*/
|
|
772
1125
|
public async enroll(k: string, zone: string, roundId: string, payload: URLSearchParams | string): Promise<Record<string, string>> {
|
|
773
|
-
|
|
1126
|
+
const url = `${this.endpoint}/enroll.php?k=${k}&zone=${zone}&round=${roundId}`;
|
|
1127
|
+
this.emitProgress("GET_ENROLL", `Fetching enrollment form`, {
|
|
1128
|
+
url,
|
|
1129
|
+
requestPayload: payload.toString(),
|
|
1130
|
+
});
|
|
774
1131
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
1132
|
+
try {
|
|
1133
|
+
const response = await this.client.post(
|
|
1134
|
+
url,
|
|
1135
|
+
payload.toString(),
|
|
1136
|
+
{
|
|
1137
|
+
headers: {
|
|
1138
|
+
...this.defaultHeaders,
|
|
1139
|
+
referer: `${this.endpoint}/fixed.php?k=${k}&zone=${zone}&round=${roundId}`,
|
|
1140
|
+
},
|
|
1141
|
+
}
|
|
1142
|
+
);
|
|
785
1143
|
|
|
786
|
-
|
|
787
|
-
|
|
1144
|
+
const $ = cheerio.load(response.data);
|
|
1145
|
+
const formData: Record<string, string> = {};
|
|
788
1146
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
1147
|
+
$("#form input, #form select").each((_, el) => {
|
|
1148
|
+
const name = $(el).attr("name");
|
|
1149
|
+
if (name) {
|
|
1150
|
+
formData[name] = $(el).attr("value") ?? $(el).find("option:selected").attr("value") ?? "";
|
|
1151
|
+
}
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1154
|
+
this.emitProgress("GET_ENROLL_RES", `Loaded enrollment form (${Object.keys(formData).length} fields)`, {
|
|
1155
|
+
url,
|
|
1156
|
+
requestPayload: payload.toString(),
|
|
1157
|
+
responseStatus: response.status,
|
|
1158
|
+
responseData: formData,
|
|
1159
|
+
});
|
|
795
1160
|
|
|
796
|
-
|
|
1161
|
+
return formData;
|
|
1162
|
+
} catch (err: any) {
|
|
1163
|
+
this.emitProgress("GET_ENROLL_ERR", `Enroll form error: ${err.message}`, {
|
|
1164
|
+
url,
|
|
1165
|
+
requestPayload: payload.toString(),
|
|
1166
|
+
responseStatus: err.response?.status,
|
|
1167
|
+
responseData: err.response?.data || err.message,
|
|
1168
|
+
});
|
|
1169
|
+
throw err;
|
|
1170
|
+
}
|
|
797
1171
|
}
|
|
798
1172
|
|
|
799
1173
|
/**
|
|
@@ -804,7 +1178,11 @@ export class ThaiTicketMajor {
|
|
|
804
1178
|
payload: Record<string, any>,
|
|
805
1179
|
attendees: Attendee[]
|
|
806
1180
|
): Promise<{ data: any; nextPayload: URLSearchParams }> {
|
|
807
|
-
|
|
1181
|
+
const url = `${this.endpoint}/enroll_process.php?k=${k}`;
|
|
1182
|
+
this.emitProgress("ENROLL_PROCESS", `Submitting info for ${attendees.length} attendees`, {
|
|
1183
|
+
url,
|
|
1184
|
+
requestPayload: { attendeesCount: attendees.length, attendees },
|
|
1185
|
+
});
|
|
808
1186
|
|
|
809
1187
|
const formData = new URLSearchParams();
|
|
810
1188
|
|
|
@@ -834,23 +1212,42 @@ export class ThaiTicketMajor {
|
|
|
834
1212
|
}
|
|
835
1213
|
}
|
|
836
1214
|
|
|
837
|
-
const
|
|
838
|
-
`${this.endpoint}/enroll_process.php?k=${k}`,
|
|
839
|
-
formData.toString(),
|
|
840
|
-
{
|
|
841
|
-
headers: {
|
|
842
|
-
...this.defaultHeaders,
|
|
843
|
-
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
844
|
-
"x-requested-with": "XMLHttpRequest",
|
|
845
|
-
referer: `${this.endpoint}/enroll.php?k=${k}&zone=${payload.zone}&round=${payload.rdId}`,
|
|
846
|
-
},
|
|
847
|
-
}
|
|
848
|
-
);
|
|
1215
|
+
const reqPayloadObj = Object.fromEntries(formData.entries());
|
|
849
1216
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
1217
|
+
try {
|
|
1218
|
+
const response = await this.client.post(
|
|
1219
|
+
url,
|
|
1220
|
+
formData.toString(),
|
|
1221
|
+
{
|
|
1222
|
+
headers: {
|
|
1223
|
+
...this.defaultHeaders,
|
|
1224
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
1225
|
+
"x-requested-with": "XMLHttpRequest",
|
|
1226
|
+
referer: `${this.endpoint}/enroll.php?k=${k}&zone=${payload.zone}&round=${payload.rdId}`,
|
|
1227
|
+
},
|
|
1228
|
+
}
|
|
1229
|
+
);
|
|
1230
|
+
|
|
1231
|
+
this.emitProgress("ENROLL_PROCESS_RES", `Enrollment processed successfully`, {
|
|
1232
|
+
url,
|
|
1233
|
+
requestPayload: reqPayloadObj,
|
|
1234
|
+
responseStatus: response.status,
|
|
1235
|
+
responseData: response.data,
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
return {
|
|
1239
|
+
data: response.data,
|
|
1240
|
+
nextPayload: formData,
|
|
1241
|
+
};
|
|
1242
|
+
} catch (err: any) {
|
|
1243
|
+
this.emitProgress("ENROLL_PROCESS_ERR", `Enrollment error: ${err.message}`, {
|
|
1244
|
+
url,
|
|
1245
|
+
requestPayload: reqPayloadObj,
|
|
1246
|
+
responseStatus: err.response?.status,
|
|
1247
|
+
responseData: err.response?.data || err.message,
|
|
1248
|
+
});
|
|
1249
|
+
throw err;
|
|
1250
|
+
}
|
|
854
1251
|
}
|
|
855
1252
|
|
|
856
1253
|
// --- Flow การชำระเงิน (Payment) ---
|
|
@@ -865,61 +1262,83 @@ export class ThaiTicketMajor {
|
|
|
865
1262
|
roundId: string,
|
|
866
1263
|
delayMs: number = 1000
|
|
867
1264
|
): Promise<Record<string, string>> {
|
|
868
|
-
|
|
869
|
-
|
|
1265
|
+
const url = `${this.endpoint}/paymentall.php?k=${k}`;
|
|
870
1266
|
const body = formData instanceof URLSearchParams ? formData.toString() : new URLSearchParams(formData).toString();
|
|
1267
|
+
const reqPayloadObj = formData instanceof URLSearchParams ? Object.fromEntries(formData.entries()) : formData;
|
|
871
1268
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
876
|
-
"x-requested-with": "XMLHttpRequest",
|
|
877
|
-
referer: `${this.endpoint}/enroll.php?k=${k}&zone=${zone}&round=${roundId}`,
|
|
878
|
-
},
|
|
1269
|
+
this.emitProgress("GET_PAYMENT_DETAILS", `Loading payment confirmation page`, {
|
|
1270
|
+
url,
|
|
1271
|
+
requestPayload: reqPayloadObj,
|
|
879
1272
|
});
|
|
880
1273
|
|
|
881
|
-
|
|
882
|
-
|
|
1274
|
+
try {
|
|
1275
|
+
const response = await this.client.post(url, body, {
|
|
1276
|
+
headers: {
|
|
1277
|
+
...this.defaultHeaders,
|
|
1278
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
1279
|
+
"x-requested-with": "XMLHttpRequest",
|
|
1280
|
+
referer: `${this.endpoint}/enroll.php?k=${k}&zone=${zone}&round=${roundId}`,
|
|
1281
|
+
},
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
const $ = cheerio.load(response.data);
|
|
1285
|
+
const hiddenInputs: Record<string, string> = {};
|
|
1286
|
+
|
|
1287
|
+
$("#frm-confirm input").each((_, el) => {
|
|
1288
|
+
const key = $(el).attr("name") ?? $(el).attr("id");
|
|
1289
|
+
if (key) {
|
|
1290
|
+
hiddenInputs[key] = $(el).val()?.toString() ?? "";
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
883
1293
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1294
|
+
const addressForm = $("#frm-address");
|
|
1295
|
+
if (addressForm.length > 0) {
|
|
1296
|
+
const fname = addressForm.find("input[name='c_fname_m']").val()?.toString() ?? "";
|
|
1297
|
+
const lname = addressForm.find("input[name='c_lname_m']").val()?.toString() ?? "";
|
|
1298
|
+
const address = addressForm.find("textarea[name='c_address_m']").val()?.toString() ?? "";
|
|
1299
|
+
const ctId = addressForm.find("select[name='c_ctCode'] option:selected").val()?.toString() ?? "";
|
|
1300
|
+
const pvId = addressForm.find("select[name='c_pvId'] option:selected").val()?.toString() ?? "";
|
|
1301
|
+
const amId = addressForm.find("select[name='c_amId'] option:selected").val()?.toString() ?? "";
|
|
1302
|
+
const zipcode = addressForm.find("input[name='c_PostCode']").val()?.toString() ?? "";
|
|
1303
|
+
|
|
1304
|
+
let rawPhone = addressForm.find("input[name='c_ContactRecipient']").val()?.toString() ?? "";
|
|
1305
|
+
if (rawPhone.startsWith("66")) {
|
|
1306
|
+
rawPhone = rawPhone.slice(2);
|
|
1307
|
+
}
|
|
1308
|
+
const phoneArea = addressForm.find("input[name='telephoneArea']").val()?.toString() ?? "66";
|
|
1309
|
+
|
|
1310
|
+
hiddenInputs["adr_fname"] = fname;
|
|
1311
|
+
hiddenInputs["adr_lname"] = lname;
|
|
1312
|
+
hiddenInputs["adr_address"] = address;
|
|
1313
|
+
hiddenInputs["adr_ctId"] = ctId;
|
|
1314
|
+
hiddenInputs["adr_pvId"] = pvId;
|
|
1315
|
+
hiddenInputs["adr_amId"] = amId;
|
|
1316
|
+
hiddenInputs["adr_zipcode"] = zipcode;
|
|
1317
|
+
hiddenInputs["adr_mobile"] = rawPhone.replace(/(\d{2})(\d{3})(\d{4})/, "$1 $2 $3");
|
|
1318
|
+
hiddenInputs["adr_mobilearea"] = phoneArea;
|
|
888
1319
|
}
|
|
889
|
-
});
|
|
890
1320
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
let rawPhone = addressForm.find("input[name='c_ContactRecipient']").val()?.toString() ?? "";
|
|
902
|
-
if (rawPhone.startsWith("66")) {
|
|
903
|
-
rawPhone = rawPhone.slice(2);
|
|
1321
|
+
this.emitProgress("GET_PAYMENT_DETAILS_RES", `Loaded payment confirmation details (${Object.keys(hiddenInputs).length} fields)`, {
|
|
1322
|
+
url,
|
|
1323
|
+
requestPayload: reqPayloadObj,
|
|
1324
|
+
responseStatus: response.status,
|
|
1325
|
+
responseData: hiddenInputs,
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
if (delayMs > 0) {
|
|
1329
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
904
1330
|
}
|
|
905
|
-
const phoneArea = addressForm.find("input[name='telephoneArea']").val()?.toString() ?? "66";
|
|
906
|
-
|
|
907
|
-
hiddenInputs["adr_fname"] = fname;
|
|
908
|
-
hiddenInputs["adr_lname"] = lname;
|
|
909
|
-
hiddenInputs["adr_address"] = address;
|
|
910
|
-
hiddenInputs["adr_ctId"] = ctId;
|
|
911
|
-
hiddenInputs["adr_pvId"] = pvId;
|
|
912
|
-
hiddenInputs["adr_amId"] = amId;
|
|
913
|
-
hiddenInputs["adr_zipcode"] = zipcode;
|
|
914
|
-
hiddenInputs["adr_mobile"] = rawPhone.replace(/(\d{2})(\d{3})(\d{4})/, "$1 $2 $3");
|
|
915
|
-
hiddenInputs["adr_mobilearea"] = phoneArea;
|
|
916
|
-
}
|
|
917
1331
|
|
|
918
|
-
|
|
919
|
-
|
|
1332
|
+
return hiddenInputs;
|
|
1333
|
+
} catch (err: any) {
|
|
1334
|
+
this.emitProgress("GET_PAYMENT_DETAILS_ERR", `Payment details error: ${err.message}`, {
|
|
1335
|
+
url,
|
|
1336
|
+
requestPayload: reqPayloadObj,
|
|
1337
|
+
responseStatus: err.response?.status,
|
|
1338
|
+
responseData: err.response?.data || err.message,
|
|
1339
|
+
});
|
|
1340
|
+
throw err;
|
|
920
1341
|
}
|
|
921
|
-
|
|
922
|
-
return hiddenInputs;
|
|
923
1342
|
}
|
|
924
1343
|
|
|
925
1344
|
/**
|
|
@@ -931,8 +1350,7 @@ export class ThaiTicketMajor {
|
|
|
931
1350
|
deliver: string = "1",
|
|
932
1351
|
payType: string = "KBQR"
|
|
933
1352
|
): Promise<Record<string, string>> {
|
|
934
|
-
|
|
935
|
-
|
|
1353
|
+
const url = `${this.endpoint}/paycfmall.php?k=${k}`;
|
|
936
1354
|
const cntTicket = parseInt(payload.cal_cntticket || "0", 10);
|
|
937
1355
|
const amountCode = parseFloat(payload.amountcode || "0");
|
|
938
1356
|
const deliverFee = parseFloat(payload.cal_deliverfee || payload.val_deliverfee || "80");
|
|
@@ -951,6 +1369,11 @@ export class ThaiTicketMajor {
|
|
|
951
1369
|
cal_totalamount: Math.round(totalAmount).toString(),
|
|
952
1370
|
};
|
|
953
1371
|
|
|
1372
|
+
this.emitProgress("CONFIRM_PAYMENT", `Calculating total and confirming payment (${payType})`, {
|
|
1373
|
+
url,
|
|
1374
|
+
requestPayload: computedPayload,
|
|
1375
|
+
});
|
|
1376
|
+
|
|
954
1377
|
const formData = new URLSearchParams();
|
|
955
1378
|
for (const [key, value] of Object.entries(computedPayload)) {
|
|
956
1379
|
if (key !== "check-protect") {
|
|
@@ -958,78 +1381,137 @@ export class ThaiTicketMajor {
|
|
|
958
1381
|
}
|
|
959
1382
|
}
|
|
960
1383
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1384
|
+
try {
|
|
1385
|
+
const response = await this.client.post(url, formData.toString(), {
|
|
1386
|
+
headers: {
|
|
1387
|
+
...this.defaultHeaders,
|
|
1388
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
1389
|
+
"x-requested-with": "XMLHttpRequest",
|
|
1390
|
+
referer: `${this.endpoint}/paymentall.php?k=${k}`,
|
|
1391
|
+
},
|
|
1392
|
+
});
|
|
969
1393
|
|
|
970
|
-
|
|
971
|
-
|
|
1394
|
+
const $ = cheerio.load(response.data);
|
|
1395
|
+
const nextJsonData: Record<string, string> = {};
|
|
972
1396
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1397
|
+
$("#payallfrm input[type='hidden']").each((_, el) => {
|
|
1398
|
+
const name = $(el).attr("name");
|
|
1399
|
+
const value = $(el).attr("value") ?? "";
|
|
1400
|
+
if (name) {
|
|
1401
|
+
nextJsonData[name] = value;
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
980
1404
|
|
|
981
|
-
|
|
1405
|
+
this.emitProgress("CONFIRM_PAYMENT_RES", `Confirmed payment (Total: ฿${computedPayload.cal_totalamount})`, {
|
|
1406
|
+
url,
|
|
1407
|
+
requestPayload: computedPayload,
|
|
1408
|
+
responseStatus: response.status,
|
|
1409
|
+
responseData: nextJsonData,
|
|
1410
|
+
});
|
|
1411
|
+
|
|
1412
|
+
return nextJsonData;
|
|
1413
|
+
} catch (err: any) {
|
|
1414
|
+
this.emitProgress("CONFIRM_PAYMENT_ERR", `Payment confirmation error: ${err.message}`, {
|
|
1415
|
+
url,
|
|
1416
|
+
requestPayload: computedPayload,
|
|
1417
|
+
responseStatus: err.response?.status,
|
|
1418
|
+
responseData: err.response?.data || err.message,
|
|
1419
|
+
});
|
|
1420
|
+
throw err;
|
|
1421
|
+
}
|
|
982
1422
|
}
|
|
983
1423
|
|
|
984
1424
|
/**
|
|
985
1425
|
* Step 12: ดึง payload เข้ารหัสสำหรับ KBank
|
|
986
1426
|
*/
|
|
987
1427
|
public async orderEncKBankQR(k: string, payCfmData: Record<string, string>): Promise<Record<string, string>> {
|
|
988
|
-
|
|
1428
|
+
const url = `${this.endpoint}/orderenc_kbankqr.php`;
|
|
1429
|
+
this.emitProgress("ORDER_ENC_KBANK", `Encrypting order payload for KBank`, {
|
|
1430
|
+
url,
|
|
1431
|
+
requestPayload: payCfmData,
|
|
1432
|
+
});
|
|
989
1433
|
|
|
990
1434
|
const formData = new URLSearchParams(payCfmData);
|
|
991
1435
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1436
|
+
try {
|
|
1437
|
+
const response = await this.client.post(url, formData.toString(), {
|
|
1438
|
+
headers: {
|
|
1439
|
+
...this.defaultHeaders,
|
|
1440
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
1441
|
+
"x-requested-with": "XMLHttpRequest",
|
|
1442
|
+
referer: `${this.endpoint}/paycfmall.php?k=${k}`,
|
|
1443
|
+
},
|
|
1444
|
+
});
|
|
1000
1445
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1446
|
+
const $ = cheerio.load(response.data);
|
|
1447
|
+
const kbankqrData: Record<string, string> = {};
|
|
1003
1448
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1449
|
+
$("#kbankqr input[type='hidden']").each((_, el) => {
|
|
1450
|
+
const name = $(el).attr("name");
|
|
1451
|
+
const value = $(el).attr("value") ?? "";
|
|
1452
|
+
if (name) {
|
|
1453
|
+
kbankqrData[name] = value;
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
this.emitProgress("ORDER_ENC_KBANK_RES", `Received orderenc_kbankqr.php response`, {
|
|
1458
|
+
url,
|
|
1459
|
+
requestPayload: payCfmData,
|
|
1460
|
+
responseStatus: response.status,
|
|
1461
|
+
responseData: Object.keys(kbankqrData).length > 0 ? kbankqrData : response.data,
|
|
1462
|
+
});
|
|
1011
1463
|
|
|
1012
|
-
|
|
1464
|
+
return kbankqrData;
|
|
1465
|
+
} catch (err: any) {
|
|
1466
|
+
this.emitProgress("ORDER_ENC_KBANK_ERR", `Order encrypt error: ${err.message}`, {
|
|
1467
|
+
url,
|
|
1468
|
+
requestPayload: payCfmData,
|
|
1469
|
+
responseStatus: err.response?.status,
|
|
1470
|
+
responseData: err.response?.data || err.message,
|
|
1471
|
+
});
|
|
1472
|
+
throw err;
|
|
1473
|
+
}
|
|
1013
1474
|
}
|
|
1014
1475
|
|
|
1015
1476
|
/**
|
|
1016
1477
|
* Step 13: เรียก getkbankqr.php
|
|
1017
1478
|
*/
|
|
1018
1479
|
public async getKBankQR(orderEncData: Record<string, string>): Promise<any> {
|
|
1019
|
-
|
|
1480
|
+
const url = `${this.endpoint}/getkbankqr.php`;
|
|
1481
|
+
this.emitProgress("GET_KBANK_QR", `Calling KBank Gateway info`, {
|
|
1482
|
+
url,
|
|
1483
|
+
requestPayload: orderEncData,
|
|
1484
|
+
});
|
|
1020
1485
|
|
|
1021
1486
|
const formData = new URLSearchParams(orderEncData);
|
|
1022
1487
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1488
|
+
try {
|
|
1489
|
+
const response = await this.client.post(url, formData.toString(), {
|
|
1490
|
+
headers: {
|
|
1491
|
+
...this.defaultHeaders,
|
|
1492
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
1493
|
+
"x-requested-with": "XMLHttpRequest",
|
|
1494
|
+
referer: `${this.endpoint}/orderenc_kbankqr.php`,
|
|
1495
|
+
},
|
|
1496
|
+
});
|
|
1497
|
+
|
|
1498
|
+
this.emitProgress("GET_KBANK_QR_RES", `Received getkbankqr.php response`, {
|
|
1499
|
+
url,
|
|
1500
|
+
requestPayload: orderEncData,
|
|
1501
|
+
responseStatus: response.status,
|
|
1502
|
+
responseData: response.data,
|
|
1503
|
+
});
|
|
1031
1504
|
|
|
1032
|
-
|
|
1505
|
+
return response.data;
|
|
1506
|
+
} catch (err: any) {
|
|
1507
|
+
this.emitProgress("GET_KBANK_QR_ERR", `Get KBank QR error: ${err.message}`, {
|
|
1508
|
+
url,
|
|
1509
|
+
requestPayload: orderEncData,
|
|
1510
|
+
responseStatus: err.response?.status,
|
|
1511
|
+
responseData: err.response?.data || err.message,
|
|
1512
|
+
});
|
|
1513
|
+
throw err;
|
|
1514
|
+
}
|
|
1033
1515
|
}
|
|
1034
1516
|
|
|
1035
1517
|
/**
|
|
@@ -1039,48 +1521,68 @@ export class ThaiTicketMajor {
|
|
|
1039
1521
|
orderEncData: Record<string, string>,
|
|
1040
1522
|
kbankPayload: Record<string, any>
|
|
1041
1523
|
): Promise<{ orderId: string; apiKey: string }> {
|
|
1042
|
-
this.emitProgress("PAYMENT_KBANK_QR", `Extracting KBank OrderID and API Key`);
|
|
1043
|
-
|
|
1044
1524
|
const preparePayload = {
|
|
1045
1525
|
...orderEncData,
|
|
1046
|
-
reasoncode: kbankPayload
|
|
1047
|
-
eventname: kbankPayload
|
|
1048
|
-
rounddetail: kbankPayload
|
|
1049
|
-
totalamount: kbankPayload
|
|
1050
|
-
orderstatus: kbankPayload
|
|
1051
|
-
kbankqr_txExpiry: kbankPayload
|
|
1526
|
+
reasoncode: kbankPayload?.reasoncode,
|
|
1527
|
+
eventname: kbankPayload?.eventname,
|
|
1528
|
+
rounddetail: kbankPayload?.rounddetail,
|
|
1529
|
+
totalamount: kbankPayload?.totalamount,
|
|
1530
|
+
orderstatus: kbankPayload?.orderstatus,
|
|
1531
|
+
kbankqr_txExpiry: kbankPayload?.kbankqr_txExpiry,
|
|
1052
1532
|
};
|
|
1053
1533
|
|
|
1534
|
+
const url = `${this.endpoint}/payment_kbankqr.php`;
|
|
1535
|
+
this.emitProgress("PAYMENT_KBANK_QR", `Extracting KBank OrderID and API Key`, {
|
|
1536
|
+
url,
|
|
1537
|
+
requestPayload: preparePayload,
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1054
1540
|
const formData = new URLSearchParams(preparePayload);
|
|
1055
1541
|
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1542
|
+
try {
|
|
1543
|
+
const response = await this.client.post(url, formData.toString(), {
|
|
1544
|
+
headers: {
|
|
1545
|
+
...this.defaultHeaders,
|
|
1546
|
+
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
|
1547
|
+
"x-requested-with": "XMLHttpRequest",
|
|
1548
|
+
referer: `${this.endpoint}/orderenc_kbankqr.php`,
|
|
1549
|
+
},
|
|
1550
|
+
});
|
|
1064
1551
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1552
|
+
const $ = cheerio.load(response.data);
|
|
1553
|
+
const script = $("#kbankpost script[data-order-id]");
|
|
1067
1554
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1555
|
+
const orderId = script.attr("data-order-id") ?? "";
|
|
1556
|
+
const apiKey = script.attr("data-apikey") ?? "";
|
|
1070
1557
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1558
|
+
this.emitProgress("PAYMENT_KBANK_QR_RES", `Received payment_kbankqr.php response (OrderID: ${orderId || "N/A"})`, {
|
|
1559
|
+
url,
|
|
1560
|
+
requestPayload: preparePayload,
|
|
1561
|
+
responseStatus: response.status,
|
|
1562
|
+
responseData: orderId && apiKey ? { orderId, apiKey, htmlSnippet: $("#kbankpost").html() } : response.data,
|
|
1563
|
+
});
|
|
1564
|
+
|
|
1565
|
+
if (!orderId || !apiKey) {
|
|
1566
|
+
throw new Error("Unable to extract KBank orderId or apiKey from response");
|
|
1567
|
+
}
|
|
1074
1568
|
|
|
1075
|
-
|
|
1569
|
+
return { orderId, apiKey };
|
|
1570
|
+
} catch (err: any) {
|
|
1571
|
+
this.emitProgress("PAYMENT_KBANK_QR_ERR", `Payment KBank QR error: ${err.message}`, {
|
|
1572
|
+
url,
|
|
1573
|
+
requestPayload: preparePayload,
|
|
1574
|
+
responseStatus: err.response?.status,
|
|
1575
|
+
responseData: err.response?.data || err.message,
|
|
1576
|
+
});
|
|
1577
|
+
throw err;
|
|
1578
|
+
}
|
|
1076
1579
|
}
|
|
1077
1580
|
|
|
1078
1581
|
/**
|
|
1079
1582
|
* Step 15: ยิงตรง Kasikorn Bank Gateway เพื่อสร้าง ThaiQR Code
|
|
1080
1583
|
*/
|
|
1081
1584
|
public async generateThaiQR(orderId: string, apiKey: string, amount: string | number): Promise<any> {
|
|
1082
|
-
|
|
1083
|
-
|
|
1585
|
+
const url = "https://kpaymentgateway-services.kasikornbank.com/qr/v2/qr";
|
|
1084
1586
|
const payload = {
|
|
1085
1587
|
amount: amount,
|
|
1086
1588
|
currency: "THB",
|
|
@@ -1089,15 +1591,37 @@ export class ThaiTicketMajor {
|
|
|
1089
1591
|
sof: "ThaiQR",
|
|
1090
1592
|
};
|
|
1091
1593
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
referer: "https://kpaymentgateway.kasikornbank.com/",
|
|
1096
|
-
"x-api-key": apiKey,
|
|
1097
|
-
},
|
|
1594
|
+
this.emitProgress("GENERATE_THAI_QR", `Requesting ThaiQR code from Kasikorn Bank`, {
|
|
1595
|
+
url,
|
|
1596
|
+
requestPayload: payload,
|
|
1098
1597
|
});
|
|
1099
1598
|
|
|
1100
|
-
|
|
1599
|
+
try {
|
|
1600
|
+
const response = await this.client.post(url, payload, {
|
|
1601
|
+
headers: {
|
|
1602
|
+
...this.defaultHeaders,
|
|
1603
|
+
referer: "https://kpaymentgateway.kasikornbank.com/",
|
|
1604
|
+
"x-api-key": apiKey,
|
|
1605
|
+
},
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
this.emitProgress("GENERATE_THAI_QR_RES", `Received ThaiQR from Kasikorn Bank`, {
|
|
1609
|
+
url,
|
|
1610
|
+
requestPayload: payload,
|
|
1611
|
+
responseStatus: response.status,
|
|
1612
|
+
responseData: response.data,
|
|
1613
|
+
});
|
|
1614
|
+
|
|
1615
|
+
return response.data;
|
|
1616
|
+
} catch (err: any) {
|
|
1617
|
+
this.emitProgress("GENERATE_THAI_QR_ERR", `Kasikorn ThaiQR error: ${err.message}`, {
|
|
1618
|
+
url,
|
|
1619
|
+
requestPayload: payload,
|
|
1620
|
+
responseStatus: err.response?.status,
|
|
1621
|
+
responseData: err.response?.data || err.message,
|
|
1622
|
+
});
|
|
1623
|
+
throw err;
|
|
1624
|
+
}
|
|
1101
1625
|
}
|
|
1102
1626
|
|
|
1103
1627
|
// ==========================================
|