@crediblemark/buayar 0.1.6 → 0.1.7

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/index.mjs CHANGED
@@ -264,6 +264,793 @@ var DuitkuProvider = class extends BasePaymentProvider {
264
264
  };
265
265
  }
266
266
  }
267
+ async probePaymentMethods(config) {
268
+ const res = await this.getPaymentMethods({ amount: 1e4 }, config);
269
+ if (res.success) {
270
+ return { success: true, enabled: res.methods.map((m) => m.paymentMethod) };
271
+ }
272
+ return { success: false, enabled: [], error: res.error };
273
+ }
274
+ };
275
+
276
+ // src/providers/midtrans.ts
277
+ import crypto2 from "crypto";
278
+
279
+ // src/clients/midtrans.ts
280
+ var MidtransClient = class {
281
+ apiKey;
282
+ sandbox;
283
+ constructor(config) {
284
+ this.apiKey = config.apiKey;
285
+ this.sandbox = config.sandbox;
286
+ }
287
+ getApiBaseUrl() {
288
+ return this.sandbox ? "https://api.sandbox.midtrans.com/v2" : "https://api.midtrans.com/v2";
289
+ }
290
+ async request(method, path, body) {
291
+ let url = path.startsWith("http") ? path : `${this.getApiBaseUrl()}${path}`;
292
+ if (!path.startsWith("http") && (path.startsWith("/v1/") || path.startsWith("/v2/"))) {
293
+ url = `${this.sandbox ? "https://api.sandbox.midtrans.com" : "https://api.midtrans.com"}${path}`;
294
+ }
295
+ const authHeader = `Basic ${Buffer.from(this.apiKey + ":").toString("base64")}`;
296
+ const headers = {
297
+ "Content-Type": "application/json",
298
+ "Accept": "application/json",
299
+ "Authorization": authHeader
300
+ };
301
+ const fetchOptions = {
302
+ method,
303
+ headers
304
+ };
305
+ if (body) {
306
+ fetchOptions.body = JSON.stringify(body);
307
+ }
308
+ const response = await fetch(url, fetchOptions);
309
+ const text = await response.text();
310
+ let data = null;
311
+ try {
312
+ data = JSON.parse(text);
313
+ } catch (e) {
314
+ }
315
+ if (!response.ok) {
316
+ throw new Error(data?.message || data?.status_message || `HTTP error! Status: ${response.status} - ${text}`);
317
+ }
318
+ return data || text;
319
+ }
320
+ // ─── TRANSACTION ACTIONS ─────────────────────────────────────────────────────
321
+ async cancelTransaction(orderId) {
322
+ return this.request("POST", `/${orderId}/cancel`, null);
323
+ }
324
+ async refundTransaction(orderId, payload) {
325
+ return this.request("POST", `/${orderId}/refund`, payload);
326
+ }
327
+ async expireTransaction(orderId) {
328
+ return this.request("POST", `/${orderId}/expire`, null);
329
+ }
330
+ async approveTransaction(orderId) {
331
+ return this.request("POST", `/${orderId}/approve`, null);
332
+ }
333
+ async denyTransaction(orderId) {
334
+ return this.request("POST", `/${orderId}/deny`, null);
335
+ }
336
+ async captureTransaction(transactionId, amount) {
337
+ const payload = amount ? { transaction_id: transactionId, gross_amount: Math.round(amount) } : { transaction_id: transactionId };
338
+ return this.request("POST", "/capture", payload);
339
+ }
340
+ // ─── GOPAY TOKENIZATION API ──────────────────────────────────────────────────
341
+ async linkPayAccount(payload) {
342
+ return this.request("POST", "/v2/pay/account", payload);
343
+ }
344
+ async getPayAccount(accountId) {
345
+ return this.request("GET", `/v2/pay/account/${accountId}`, null);
346
+ }
347
+ async unbindPayAccount(accountId) {
348
+ return this.request("POST", `/v2/pay/account/${accountId}/unbind`, null);
349
+ }
350
+ async getGoPayPromo(accountId, grossAmount, currency = "IDR") {
351
+ return this.request("GET", `/v2/gopay/promo/${accountId}?gross_amount=${grossAmount}&currency=${currency}`, null);
352
+ }
353
+ // ─── SUBSCRIPTION API ────────────────────────────────────────────────────────
354
+ async createSubscription(payload) {
355
+ return this.request("POST", "/v1/subscriptions", payload);
356
+ }
357
+ async getSubscription(subscriptionId) {
358
+ return this.request("GET", `/v1/subscriptions/${subscriptionId}`, null);
359
+ }
360
+ async updateSubscription(subscriptionId, payload) {
361
+ return this.request("PATCH", `/v1/subscriptions/${subscriptionId}`, payload);
362
+ }
363
+ async disableSubscription(subscriptionId) {
364
+ return this.request("POST", `/v1/subscriptions/${subscriptionId}/disable`, null);
365
+ }
366
+ async enableSubscription(subscriptionId) {
367
+ return this.request("POST", `/v1/subscriptions/${subscriptionId}/enable`, null);
368
+ }
369
+ // ─── PAYMENT LINK API ────────────────────────────────────────────────────────
370
+ async createPaymentLink(payload) {
371
+ return this.request("POST", "/v1/payment-links", payload);
372
+ }
373
+ async getPaymentLink(paymentLinkId) {
374
+ return this.request("GET", `/v1/payment-links/${paymentLinkId}`, null);
375
+ }
376
+ async deletePaymentLink(paymentLinkId) {
377
+ return this.request("DELETE", `/v1/payment-links/${paymentLinkId}`, null);
378
+ }
379
+ // ─── BALANCE API ─────────────────────────────────────────────────────────────
380
+ async getBalance() {
381
+ try {
382
+ return await this.request("GET", "/v1/balance", null);
383
+ } catch (e) {
384
+ try {
385
+ const irisUrl = `${this.sandbox ? "https://api.sandbox.midtrans.com" : "https://api.midtrans.com"}/iris/api/v1/balance`;
386
+ const authHeader = `Basic ${Buffer.from(this.apiKey + ":").toString("base64")}`;
387
+ const response = await fetch(irisUrl, {
388
+ method: "GET",
389
+ headers: {
390
+ "Accept": "application/json",
391
+ "Authorization": authHeader
392
+ }
393
+ });
394
+ const text = await response.text();
395
+ let data = null;
396
+ try {
397
+ data = JSON.parse(text);
398
+ } catch (err) {
399
+ }
400
+ if (response.ok) return data || text;
401
+ } catch (irisErr) {
402
+ }
403
+ throw e;
404
+ }
405
+ }
406
+ // ─── INVOICING API ───────────────────────────────────────────────────────────
407
+ async createBillingInvoice(payload) {
408
+ return this.request("POST", "/v1/invoices", payload);
409
+ }
410
+ async getBillingInvoice(invoiceId) {
411
+ return this.request("GET", `/v1/invoices/${invoiceId}`, null);
412
+ }
413
+ async voidBillingInvoice(invoiceId) {
414
+ return this.request("PATCH", `/v1/invoices/${invoiceId}/void`, null);
415
+ }
416
+ async convertBillingInvoice(invoiceId) {
417
+ return this.request("PATCH", `/v1/invoices/${invoiceId}/convert`, null);
418
+ }
419
+ };
420
+
421
+ // src/providers/midtrans.ts
422
+ var MidtransProvider = class extends BasePaymentProvider {
423
+ name = "midtrans";
424
+ getSnapBaseUrl(sandbox) {
425
+ return sandbox ? "https://app.sandbox.midtrans.com/snap/v1/transactions" : "https://app.midtrans.com/snap/v1/transactions";
426
+ }
427
+ getApiBaseUrl(sandbox) {
428
+ return sandbox ? "https://api.sandbox.midtrans.com/v2" : "https://api.midtrans.com/v2";
429
+ }
430
+ async createInvoice(params, config) {
431
+ const { orderId, amount, productDetails, customer, returnUrl } = params;
432
+ const { apiKey, sandbox } = config;
433
+ const integerAmount = Math.round(amount);
434
+ const method = params.paymentMethod?.toLowerCase() || "";
435
+ const coreApiMethods = [
436
+ "bca_va",
437
+ "bni_va",
438
+ "bri_va",
439
+ "permata_va",
440
+ "cimb_va",
441
+ "danamon_va",
442
+ "bsi_va",
443
+ "seabank_va",
444
+ "mandiri_va",
445
+ "qris",
446
+ "gopay",
447
+ "shopeepay",
448
+ "ovo",
449
+ "dana",
450
+ "linkaja",
451
+ "alfamart",
452
+ "indomaret",
453
+ "credit_card",
454
+ "googlepay",
455
+ "kredivo",
456
+ "akulaku"
457
+ ];
458
+ if (method && coreApiMethods.includes(method)) {
459
+ const url2 = `${this.getApiBaseUrl(sandbox)}/charge`;
460
+ let payload2 = {
461
+ transaction_details: {
462
+ order_id: orderId,
463
+ gross_amount: integerAmount
464
+ },
465
+ customer_details: {
466
+ first_name: customer.name,
467
+ email: customer.email,
468
+ phone: customer.phone || ""
469
+ },
470
+ item_details: [
471
+ {
472
+ id: orderId,
473
+ price: integerAmount,
474
+ quantity: 1,
475
+ name: productDetails.length > 50 ? productDetails.substring(0, 47) + "..." : productDetails
476
+ }
477
+ ],
478
+ ...params.providerParams
479
+ };
480
+ if (["bca_va", "bni_va", "bri_va", "cimb_va", "permata_va", "danamon_va", "bsi_va", "seabank_va"].includes(method)) {
481
+ const bankName = method.split("_")[0];
482
+ payload2.payment_type = "bank_transfer";
483
+ payload2.bank_transfer = {
484
+ bank: bankName
485
+ };
486
+ } else if (method === "mandiri_va") {
487
+ payload2.payment_type = "echannel";
488
+ payload2.echannel = {
489
+ bill_info1: "Payment for",
490
+ bill_info2: productDetails.length > 30 ? productDetails.substring(0, 27) + "..." : productDetails
491
+ };
492
+ } else if (method === "qris") {
493
+ payload2.payment_type = "qris";
494
+ } else if (method === "gopay") {
495
+ payload2.payment_type = "gopay";
496
+ payload2.gopay = {
497
+ enable_callback: true,
498
+ callback_url: returnUrl
499
+ };
500
+ } else if (method === "shopeepay") {
501
+ payload2.payment_type = "shopeepay";
502
+ payload2.shopeepay = {
503
+ callback_url: returnUrl
504
+ };
505
+ } else if (method === "ovo") {
506
+ if (!customer.phone) {
507
+ return {
508
+ success: false,
509
+ rawResponse: null,
510
+ error: "OVO payment method requires a customer phone number in customer.phone"
511
+ };
512
+ }
513
+ payload2.payment_type = "ovo";
514
+ payload2.ovo = {
515
+ phone: customer.phone
516
+ };
517
+ } else if (method === "dana") {
518
+ payload2.payment_type = "dana";
519
+ } else if (method === "linkaja") {
520
+ payload2.payment_type = "linkaja";
521
+ } else if (method === "credit_card") {
522
+ const token = params.providerParams?.credit_card?.token_id || params.providerParams?.tokenId;
523
+ if (!token) {
524
+ return {
525
+ success: false,
526
+ rawResponse: null,
527
+ error: "Credit Card payment method requires token_id (passed via providerParams.credit_card.token_id or providerParams.tokenId)"
528
+ };
529
+ }
530
+ payload2.payment_type = "credit_card";
531
+ payload2.credit_card = {
532
+ token_id: token,
533
+ authentication: params.providerParams?.credit_card?.authentication ?? true,
534
+ save_card: params.providerParams?.credit_card?.save_card,
535
+ bank: params.providerParams?.credit_card?.bank,
536
+ installment_term: params.providerParams?.credit_card?.installment_term,
537
+ bins: params.providerParams?.credit_card?.bins,
538
+ type: params.providerParams?.credit_card?.type
539
+ };
540
+ } else if (method === "googlepay") {
541
+ const token = params.providerParams?.googlepay?.token_id || params.providerParams?.tokenId;
542
+ if (!token) {
543
+ return {
544
+ success: false,
545
+ rawResponse: null,
546
+ error: "Google Pay payment method requires token_id (passed via providerParams.googlepay.token_id or providerParams.tokenId)"
547
+ };
548
+ }
549
+ payload2.payment_type = "googlepay";
550
+ payload2.googlepay = {
551
+ token_id: token
552
+ };
553
+ } else if (method === "kredivo") {
554
+ payload2.payment_type = "kredivo";
555
+ payload2.kredivo = {
556
+ address: params.providerParams?.kredivo?.address,
557
+ first_name: params.providerParams?.kredivo?.first_name || customer.name.split(" ")[0],
558
+ last_name: params.providerParams?.kredivo?.last_name || customer.name.split(" ").slice(1).join(" "),
559
+ email: params.providerParams?.kredivo?.email || customer.email,
560
+ phone: params.providerParams?.kredivo?.phone || customer.phone || ""
561
+ };
562
+ } else if (method === "akulaku") {
563
+ payload2.payment_type = "akulaku";
564
+ } else if (["alfamart", "indomaret"].includes(method)) {
565
+ payload2.payment_type = "cstore";
566
+ payload2.cstore = {
567
+ store: method,
568
+ message: productDetails.length > 30 ? productDetails.substring(0, 27) + "..." : productDetails
569
+ };
570
+ }
571
+ try {
572
+ const authHeader = `Basic ${Buffer.from(apiKey + ":").toString("base64")}`;
573
+ const response = await fetch(url2, {
574
+ method: "POST",
575
+ headers: {
576
+ "Content-Type": "application/json",
577
+ "Accept": "application/json",
578
+ "Authorization": authHeader
579
+ },
580
+ body: JSON.stringify(payload2)
581
+ });
582
+ const text = await response.text();
583
+ let data = null;
584
+ try {
585
+ data = JSON.parse(text);
586
+ } catch (e) {
587
+ }
588
+ if (!response.ok || !data) {
589
+ return {
590
+ success: false,
591
+ rawResponse: data || text,
592
+ error: data?.status_message || `HTTP error! Status: ${response.status} - ${text}`
593
+ };
594
+ }
595
+ if (data.status_code === "201" || data.status_code === "200") {
596
+ const res = {
597
+ success: true,
598
+ reference: data.transaction_id || data.order_id,
599
+ rawResponse: data
600
+ };
601
+ if (["bca_va", "bni_va", "bri_va", "cimb_va", "danamon_va", "bsi_va", "seabank_va"].includes(method)) {
602
+ res.vaNumber = data.va_numbers?.[0]?.va_number;
603
+ } else if (method === "permata_va") {
604
+ res.vaNumber = data.permata_va_number;
605
+ } else if (method === "mandiri_va") {
606
+ res.vaNumber = `${data.biller_code}-${data.bill_key}`;
607
+ } else if (method === "qris") {
608
+ res.qrString = data.qr_string;
609
+ res.qrCodeUrl = data.actions?.find((a) => a.name === "generate-qr-code")?.url;
610
+ } else if (["gopay", "shopeepay", "dana", "linkaja", "kredivo", "akulaku", "googlepay"].includes(method)) {
611
+ res.paymentUrl = data.actions?.find((a) => a.name === "deeplink-redirect")?.url || data.actions?.find((a) => a.name === "web-redirect")?.url || data.actions?.find((a) => a.name === "generate-qr-code")?.url || data.redirect_url;
612
+ res.qrCodeUrl = data.actions?.find((a) => a.name === "generate-qr-code")?.url;
613
+ } else if (method === "credit_card") {
614
+ res.paymentUrl = data.redirect_url || data.actions?.find((a) => a.name === "redirect")?.url;
615
+ } else if (method === "ovo") {
616
+ res.paymentUrl = "";
617
+ } else if (["alfamart", "indomaret"].includes(method)) {
618
+ res.paymentCode = data.payment_code;
619
+ }
620
+ return res;
621
+ } else {
622
+ return {
623
+ success: false,
624
+ rawResponse: data,
625
+ error: data.status_message || `Midtrans Core Error: ${data.status_code}`
626
+ };
627
+ }
628
+ } catch (e) {
629
+ return {
630
+ success: false,
631
+ rawResponse: null,
632
+ error: e.message || "Failed to make request to Midtrans Core API"
633
+ };
634
+ }
635
+ }
636
+ const url = this.getSnapBaseUrl(sandbox);
637
+ const payload = {
638
+ transaction_details: {
639
+ order_id: orderId,
640
+ gross_amount: integerAmount
641
+ },
642
+ customer_details: {
643
+ first_name: customer.name,
644
+ email: customer.email,
645
+ phone: customer.phone || ""
646
+ },
647
+ item_details: [
648
+ {
649
+ id: orderId,
650
+ price: integerAmount,
651
+ quantity: 1,
652
+ name: productDetails.length > 50 ? productDetails.substring(0, 47) + "..." : productDetails
653
+ }
654
+ ],
655
+ callbacks: {
656
+ finish: returnUrl
657
+ },
658
+ ...params.paymentMethod ? { enabled_payments: [params.paymentMethod] } : {},
659
+ ...params.providerParams
660
+ };
661
+ try {
662
+ const authHeader = `Basic ${Buffer.from(apiKey + ":").toString("base64")}`;
663
+ const response = await fetch(url, {
664
+ method: "POST",
665
+ headers: {
666
+ "Content-Type": "application/json",
667
+ "Accept": "application/json",
668
+ "Authorization": authHeader
669
+ },
670
+ body: JSON.stringify(payload)
671
+ });
672
+ const text = await response.text();
673
+ let data = null;
674
+ try {
675
+ data = JSON.parse(text);
676
+ } catch (e) {
677
+ }
678
+ if (!response.ok || !data) {
679
+ return {
680
+ success: false,
681
+ rawResponse: data || text,
682
+ error: data?.error_messages?.[0] || `HTTP error! Status: ${response.status} - ${text}`
683
+ };
684
+ }
685
+ if (data.token) {
686
+ return {
687
+ success: true,
688
+ paymentUrl: data.redirect_url,
689
+ reference: data.token,
690
+ rawResponse: data
691
+ };
692
+ } else {
693
+ return {
694
+ success: false,
695
+ rawResponse: data,
696
+ error: data.error_messages?.[0] || "Failed to create Midtrans Snap transaction"
697
+ };
698
+ }
699
+ } catch (e) {
700
+ return {
701
+ success: false,
702
+ rawResponse: null,
703
+ error: e.message || "Failed to make request to Midtrans Snap API"
704
+ };
705
+ }
706
+ }
707
+ async verifyCallback(body, config) {
708
+ const { apiKey } = config;
709
+ const orderId = body.order_id || "";
710
+ const statusCode = body.status_code || "";
711
+ const grossAmount = body.gross_amount || "";
712
+ const signatureKey = body.signature_key || "";
713
+ const rawSignature = orderId + statusCode + grossAmount + apiKey;
714
+ const computedSignature = crypto2.createHash("sha512").update(rawSignature).digest("hex");
715
+ const isValid = signatureKey.toLowerCase() === computedSignature.toLowerCase();
716
+ const transactionStatus = body.transaction_status || "";
717
+ const fraudStatus = body.fraud_status || "";
718
+ let status = "pending";
719
+ if (transactionStatus === "capture" || transactionStatus === "settlement") {
720
+ if (fraudStatus === "accept" || !fraudStatus) {
721
+ status = "paid";
722
+ } else if (fraudStatus === "challenge") {
723
+ status = "pending";
724
+ } else {
725
+ status = "failed";
726
+ }
727
+ } else if (transactionStatus === "pending") {
728
+ status = "pending";
729
+ } else if (["deny", "cancel", "expire"].includes(transactionStatus)) {
730
+ status = "failed";
731
+ }
732
+ return {
733
+ isValid,
734
+ orderId,
735
+ amount: grossAmount ? Number(grossAmount) : 0,
736
+ status: isValid ? status : "failed",
737
+ rawPayload: body
738
+ };
739
+ }
740
+ async getPaymentMethods(params, config) {
741
+ const staticMethods = [
742
+ {
743
+ paymentMethod: "credit_card",
744
+ paymentName: "Credit / Debit Card",
745
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/credit_card.png",
746
+ totalFee: "2.9% + IDR 2,000",
747
+ category: "Kartu Kredit"
748
+ },
749
+ {
750
+ paymentMethod: "googlepay",
751
+ paymentName: "Google Pay\u2122",
752
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/googlepay.png",
753
+ totalFee: "2.9% + IDR 2,000",
754
+ category: "Kartu Kredit"
755
+ },
756
+ {
757
+ paymentMethod: "bca_va",
758
+ paymentName: "BCA Virtual Account",
759
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/bca_va.png",
760
+ totalFee: "IDR 4,000",
761
+ category: "Virtual Account"
762
+ },
763
+ {
764
+ paymentMethod: "bni_va",
765
+ paymentName: "BNI Virtual Account",
766
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/bni_va.png",
767
+ totalFee: "IDR 4,000",
768
+ category: "Virtual Account"
769
+ },
770
+ {
771
+ paymentMethod: "bri_va",
772
+ paymentName: "BRI Virtual Account",
773
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/bri_va.png",
774
+ totalFee: "IDR 4,000",
775
+ category: "Virtual Account"
776
+ },
777
+ {
778
+ paymentMethod: "mandiri_va",
779
+ paymentName: "Mandiri Bill Payment / VA",
780
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/mandiri_va.png",
781
+ totalFee: "IDR 4,000",
782
+ category: "Virtual Account"
783
+ },
784
+ {
785
+ paymentMethod: "permata_va",
786
+ paymentName: "Permata Virtual Account",
787
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/permata_va.png",
788
+ totalFee: "IDR 4,000",
789
+ category: "Virtual Account"
790
+ },
791
+ {
792
+ paymentMethod: "cimb_va",
793
+ paymentName: "CIMB Virtual Account",
794
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/cimb_va.png",
795
+ totalFee: "IDR 4,000",
796
+ category: "Virtual Account"
797
+ },
798
+ {
799
+ paymentMethod: "danamon_va",
800
+ paymentName: "Danamon Virtual Account",
801
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/danamon_va.png",
802
+ totalFee: "IDR 4,000",
803
+ category: "Virtual Account"
804
+ },
805
+ {
806
+ paymentMethod: "bsi_va",
807
+ paymentName: "BSI Virtual Account",
808
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/bsi_va.png",
809
+ totalFee: "IDR 4,000",
810
+ category: "Virtual Account"
811
+ },
812
+ {
813
+ paymentMethod: "seabank_va",
814
+ paymentName: "SeaBank Virtual Account",
815
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/seabank_va.png",
816
+ totalFee: "IDR 4,000",
817
+ category: "Virtual Account"
818
+ },
819
+ {
820
+ paymentMethod: "other_va",
821
+ paymentName: "Other Banks (ATM Bersama, Prima, Alto)",
822
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/other_va.png",
823
+ totalFee: "IDR 4,000",
824
+ category: "Virtual Account"
825
+ },
826
+ {
827
+ paymentMethod: "qris",
828
+ paymentName: "QRIS (GoPay, ShopeePay, Dana, LinkAja)",
829
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/qris.png",
830
+ totalFee: "0.7%",
831
+ category: "QRIS"
832
+ },
833
+ {
834
+ paymentMethod: "other_qris",
835
+ paymentName: "Other QRIS",
836
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/other_qris.png",
837
+ totalFee: "0.7%",
838
+ category: "QRIS"
839
+ },
840
+ {
841
+ paymentMethod: "gopay",
842
+ paymentName: "GoPay E-Wallet",
843
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/gopay.png",
844
+ totalFee: "2.0%",
845
+ category: "E-Wallet"
846
+ },
847
+ {
848
+ paymentMethod: "shopeepay",
849
+ paymentName: "ShopeePay E-Wallet",
850
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/shopeepay.png",
851
+ totalFee: "2.0%",
852
+ category: "E-Wallet"
853
+ },
854
+ {
855
+ paymentMethod: "ovo",
856
+ paymentName: "OVO E-Wallet",
857
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/ovo.png",
858
+ totalFee: "1.5%",
859
+ category: "E-Wallet"
860
+ },
861
+ {
862
+ paymentMethod: "dana",
863
+ paymentName: "DANA E-Wallet",
864
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/dana.png",
865
+ totalFee: "1.7%",
866
+ category: "E-Wallet"
867
+ },
868
+ {
869
+ paymentMethod: "linkaja",
870
+ paymentName: "LinkAja E-Wallet",
871
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/linkaja.png",
872
+ totalFee: "1.7%",
873
+ category: "E-Wallet"
874
+ },
875
+ {
876
+ paymentMethod: "indomaret",
877
+ paymentName: "Indomaret",
878
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/indomaret.png",
879
+ totalFee: "IDR 5,000",
880
+ category: "Retail / Gerai"
881
+ },
882
+ {
883
+ paymentMethod: "alfamart",
884
+ paymentName: "Alfamart / Alfamidi",
885
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/alfamart.png",
886
+ totalFee: "IDR 5,000",
887
+ category: "Retail / Gerai"
888
+ },
889
+ {
890
+ paymentMethod: "kredivo",
891
+ paymentName: "Kredivo Paylater",
892
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/kredivo.png",
893
+ totalFee: "2.3%",
894
+ category: "Paylater / Cicilan"
895
+ },
896
+ {
897
+ paymentMethod: "akulaku",
898
+ paymentName: "Akulaku Paylater",
899
+ paymentImage: "https://docs.midtrans.com/asset/payment_methods/akulaku.png",
900
+ totalFee: "1.7%",
901
+ category: "Paylater / Cicilan"
902
+ }
903
+ ];
904
+ return {
905
+ success: true,
906
+ methods: staticMethods,
907
+ rawResponse: staticMethods
908
+ };
909
+ }
910
+ async checkTransaction(params, config) {
911
+ const { merchantOrderId } = params;
912
+ const { apiKey, sandbox } = config;
913
+ const url = `${this.getApiBaseUrl(sandbox)}/${merchantOrderId}/status`;
914
+ try {
915
+ const authHeader = `Basic ${Buffer.from(apiKey + ":").toString("base64")}`;
916
+ const response = await fetch(url, {
917
+ method: "GET",
918
+ headers: {
919
+ "Content-Type": "application/json",
920
+ "Accept": "application/json",
921
+ "Authorization": authHeader
922
+ }
923
+ });
924
+ const text = await response.text();
925
+ let data = null;
926
+ try {
927
+ data = JSON.parse(text);
928
+ } catch (e) {
929
+ }
930
+ if (!response.ok || !data) {
931
+ return {
932
+ success: false,
933
+ orderId: merchantOrderId,
934
+ reference: "",
935
+ amount: 0,
936
+ statusCode: "",
937
+ status: "failed",
938
+ statusMessage: `HTTP error! Status: ${response.status}`,
939
+ error: `HTTP ${response.status} - ${text}`,
940
+ rawResponse: data || text
941
+ };
942
+ }
943
+ const transactionStatus = data.transaction_status || "";
944
+ const fraudStatus = data.fraud_status || "";
945
+ let status = "pending";
946
+ if (transactionStatus === "capture" || transactionStatus === "settlement") {
947
+ if (fraudStatus === "accept" || !fraudStatus) {
948
+ status = "paid";
949
+ } else if (fraudStatus === "challenge") {
950
+ status = "pending";
951
+ } else {
952
+ status = "failed";
953
+ }
954
+ } else if (transactionStatus === "pending") {
955
+ status = "pending";
956
+ } else if (["deny", "cancel", "expire"].includes(transactionStatus)) {
957
+ status = "failed";
958
+ }
959
+ return {
960
+ success: true,
961
+ orderId: data.order_id || merchantOrderId,
962
+ reference: data.transaction_id || "",
963
+ amount: data.gross_amount ? Number(data.gross_amount) : 0,
964
+ statusCode: data.status_code || "",
965
+ status,
966
+ statusMessage: data.status_message || "",
967
+ rawResponse: data
968
+ };
969
+ } catch (e) {
970
+ return {
971
+ success: false,
972
+ orderId: merchantOrderId,
973
+ reference: "",
974
+ amount: 0,
975
+ statusCode: "",
976
+ status: "failed",
977
+ statusMessage: "Network error",
978
+ error: e.message || "Failed to check transaction status with Midtrans",
979
+ rawResponse: null
980
+ };
981
+ }
982
+ }
983
+ /**
984
+ * Get an instance of MidtransClient to perform transaction actions, subscriptions, invoicing, etc.
985
+ */
986
+ getClient(config) {
987
+ return new MidtransClient(config);
988
+ }
989
+ async probePaymentMethods(config) {
990
+ const { apiKey, sandbox } = config;
991
+ const baseUrl = sandbox ? "https://api.sandbox.midtrans.com/v2" : "https://api.midtrans.com/v2";
992
+ const auth = Buffer.from(apiKey + ":").toString("base64");
993
+ const probePayloads = {
994
+ qris: { payment_type: "qris", qris: { acquirer: "gopay" } },
995
+ gopay: { payment_type: "gopay", gopay: { enable_callback: true, callback_url: "https://example.com" } },
996
+ shopeepay: { payment_type: "shopeepay", shopeepay: { callback_url: "https://example.com" } },
997
+ bca: { payment_type: "bank_transfer", bank_transfer: { bank: "bca" } },
998
+ bni: { payment_type: "bank_transfer", bank_transfer: { bank: "bni" } },
999
+ bri: { payment_type: "bank_transfer", bank_transfer: { bank: "bri" } },
1000
+ cimb: { payment_type: "bank_transfer", bank_transfer: { bank: "cimb" } },
1001
+ mandiri: { payment_type: "echannel", echannel: { bill_info1: "Payment", bill_info2: "Probe" } },
1002
+ permata: { payment_type: "permata" },
1003
+ alfamart: { payment_type: "cstore", cstore: { store: "alfamart", message: "Probe" } },
1004
+ indomaret: { payment_type: "cstore", cstore: { store: "indomaret", message: "Probe" } },
1005
+ akulaku: { payment_type: "akulaku" },
1006
+ kredivo: {
1007
+ payment_type: "kredivo",
1008
+ seller_details: { address: { city: "Jakarta" } }
1009
+ }
1010
+ };
1011
+ const enabled = [];
1012
+ for (const [methodId, specificPayload] of Object.entries(probePayloads)) {
1013
+ try {
1014
+ const probeOrderId = `PROBE-${methodId}-${Date.now()}`;
1015
+ const probeBody = {
1016
+ ...specificPayload,
1017
+ transaction_details: { order_id: probeOrderId, gross_amount: 15e3 },
1018
+ item_details: [{ id: probeOrderId, name: "Probe", price: 15e3, quantity: 1 }],
1019
+ customer_details: { first_name: "Probe", email: "probe@test.com" }
1020
+ };
1021
+ const res = await fetch(`${baseUrl}/charge`, {
1022
+ method: "POST",
1023
+ headers: {
1024
+ "Content-Type": "application/json",
1025
+ "Accept": "application/json",
1026
+ "Authorization": `Basic ${auth}`
1027
+ },
1028
+ body: JSON.stringify(probeBody)
1029
+ });
1030
+ const text = await res.text();
1031
+ let result = null;
1032
+ try {
1033
+ result = JSON.parse(text);
1034
+ } catch (e) {
1035
+ }
1036
+ if (result && ["200", "201", "202"].includes(result.status_code)) {
1037
+ enabled.push(methodId);
1038
+ try {
1039
+ await fetch(`${baseUrl}/${probeOrderId}/cancel`, {
1040
+ method: "POST",
1041
+ headers: {
1042
+ "Accept": "application/json",
1043
+ "Authorization": `Basic ${auth}`
1044
+ }
1045
+ });
1046
+ } catch (e) {
1047
+ }
1048
+ }
1049
+ } catch (e) {
1050
+ }
1051
+ }
1052
+ return { success: true, enabled };
1053
+ }
267
1054
  };
268
1055
 
269
1056
  // src/index.ts
@@ -271,6 +1058,7 @@ var PaymentManager = class {
271
1058
  providers = /* @__PURE__ */ new Map();
272
1059
  constructor() {
273
1060
  this.registerProvider(new DuitkuProvider());
1061
+ this.registerProvider(new MidtransProvider());
274
1062
  }
275
1063
  registerProvider(provider) {
276
1064
  this.providers.set(provider.name.toLowerCase(), provider);
@@ -282,6 +1070,12 @@ var PaymentManager = class {
282
1070
  }
283
1071
  return provider;
284
1072
  }
1073
+ getMidtransProvider() {
1074
+ return this.getProvider("midtrans");
1075
+ }
1076
+ getMidtransClient(config) {
1077
+ return new MidtransClient(config);
1078
+ }
285
1079
  async createInvoice(providerName, params, config) {
286
1080
  const provider = this.getProvider(providerName);
287
1081
  return provider.createInvoice(params, config);
@@ -298,11 +1092,20 @@ var PaymentManager = class {
298
1092
  const provider = this.getProvider(providerName);
299
1093
  return provider.checkTransaction(params, config);
300
1094
  }
1095
+ async probePaymentMethods(providerName, config) {
1096
+ const provider = this.getProvider(providerName);
1097
+ if (provider.probePaymentMethods) {
1098
+ return provider.probePaymentMethods(config);
1099
+ }
1100
+ return { success: false, enabled: [], error: `Provider '${providerName}' does not support payment methods probing` };
1101
+ }
301
1102
  };
302
1103
  var paymentManager = new PaymentManager();
303
1104
  export {
304
1105
  BasePaymentProvider,
305
1106
  DuitkuProvider,
1107
+ MidtransClient,
1108
+ MidtransProvider,
306
1109
  PaymentManager,
307
1110
  getPaymentMethodCategory,
308
1111
  paymentManager