@crediblemark/buayar 0.1.4 → 0.1.6

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 CHANGED
@@ -3,20 +3,23 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@crediblemark/buayar.svg?style=flat-square&color=amber)](https://www.npmjs.com/package/@crediblemark/buayar)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
5
5
 
6
- **`@crediblemark/buayar`** adalah Unified Payment Gateway SDK yang dirancang khusus untuk mempermudah integrasi berbagai gerbang pembayaran (Payment Gateway) pada platform dan situs berbasis **CredBuild**.
6
+ **`@crediblemark/buayar`** adalah Unified Payment Gateway SDK untuk Node.js dan TypeScript yang dirancang untuk mempermudah integrasi berbagai gerbang pembayaran (Payment Gateway) dengan satu struktur API yang seragam.
7
7
 
8
- Dengan SDK ini, Anda cukup menulis kode satu kali menggunakan struktur API yang seragam untuk mengelola pembuatan invoice/transaksi serta verifikasi callback webhook dari berbagai penyedia layanan payment gateway.
8
+ Dengan SDK ini, Anda cukup menulis kode satu kali menggunakan struktur API yang konsisten untuk mengelola pembuatan invoice/transaksi serta verifikasi callback webhook dari berbagai penyedia layanan payment gateway.
9
9
 
10
10
  ---
11
11
 
12
12
  ## 🚀 Fitur Utama
13
-
13
+
14
14
  - 🔄 **Unified API**: Satu antarmuka (interface) terpadu untuk semua provider payment gateway.
15
+ - ⚡ **Direct Payment Support**: Otomatis mendeteksi `paymentMethod` dan beralih ke Direct Inquiry API (v2/inquiry) Duitku untuk mengembalikan `vaNumber`, `qrString` (EMVCo), dan `paymentCode` secara instan tanpa redirect eksternal.
16
+ - 🏷️ **Pre-Categorization (Accordion Ready)**: Menyediakan pengelompokan pembayaran bawaan (`Virtual Account`, `QRIS`, `E-Wallet`, `Retail / Gerai`, `Lainnya`) langsung dari API response untuk memudahkan implementasi UI accordion.
17
+ - 🎨 **Headless SDK Philosophy**: Dirancang murni sebagai core logic & data manager tanpa overhead UI/styling. Memberikan kebebasan penuh bagi pengembang untuk mendesain UI/Tailwind/dark mode di tingkat aplikasi.
15
18
  - 🛡️ **Tipe Data Kuat (TypeScript)**: Dilengkapi dengan deklarasi tipe data lengkap untuk mencegah *runtime error*.
16
19
  - 🇮🇩 **Dukungan Lokal**: Siap digunakan untuk transaksi di Indonesia (seperti Duitku).
17
20
  - ⚙️ **Modular & Dapat Diperluas**: Memungkinkan penambahan provider baru dengan mewarisi kelas base yang disediakan.
18
21
  - 🔒 **Otomatisasi Signature**: Keamanan transaksi terjamin dengan pembuatan *hash* signature (MD5, SHA-256) otomatis secara internal.
19
-
22
+
20
23
  ---
21
24
 
22
25
  ## 📦 Instalasi
@@ -218,11 +221,26 @@ interface InvoiceResponse {
218
221
  success: boolean;
219
222
  paymentUrl?: string;
220
223
  reference?: string;
224
+ vaNumber?: string; // Nomor Virtual Account (untuk metode Bank VA)
225
+ qrString?: string; // EMVCo QRIS payload mentah (untuk scan langsung)
226
+ qrCodeUrl?: string; // Image URL QRIS dari Duitku
227
+ paymentCode?: string; // Kode pembayaran retail (Indomaret/Alfamart)
221
228
  rawResponse: any;
222
229
  error?: string;
223
230
  }
224
231
  ```
225
232
 
233
+ ### `PaymentMethod`
234
+ ```typescript
235
+ interface PaymentMethod {
236
+ paymentMethod: string;
237
+ paymentName: string;
238
+ paymentImage: string;
239
+ totalFee: string;
240
+ category: "Virtual Account" | "QRIS" | "E-Wallet" | "Retail / Gerai" | "Lainnya";
241
+ }
242
+ ```
243
+
226
244
  ### `VerifyCallbackResult`
227
245
  ```typescript
228
246
  interface VerifyCallbackResult {
@@ -236,6 +254,19 @@ interface VerifyCallbackResult {
236
254
 
237
255
  ---
238
256
 
257
+ ## 🛠️ Utilitas Tambahan
258
+
259
+ ### `getPaymentMethodCategory(code, name)`
260
+ Mengkategorikan kode dan nama metode pembayaran Duitku ke kategori terstandarisasi untuk UI Accordion.
261
+ ```typescript
262
+ import { getPaymentMethodCategory } from "@crediblemark/buayar";
263
+
264
+ const cat = getPaymentMethodCategory("BT", "Permata VA");
265
+ // Output: "Virtual Account"
266
+ ```
267
+
268
+ ---
269
+
239
270
  ## 📄 Lisensi
240
271
 
241
272
  Proyek ini dilisensikan di bawah **MIT License**. Hak Cipta © 2026 Rasyiqi Crediblemark.
package/dist/index.d.mts CHANGED
@@ -9,11 +9,21 @@ interface CreateInvoiceParams {
9
9
  };
10
10
  returnUrl: string;
11
11
  callbackUrl: string;
12
+ /** Specific Duitku payment method code (e.g. "BCA", "I1", "OV"). If omitted, all methods are available via Duitku's redirect page. */
13
+ paymentMethod?: string;
12
14
  }
13
15
  interface InvoiceResponse {
14
16
  success: boolean;
15
17
  paymentUrl?: string;
16
18
  reference?: string;
19
+ /** Virtual Account number (for bank VA methods) */
20
+ vaNumber?: string;
21
+ /** QRIS string for display in a QR code */
22
+ qrString?: string;
23
+ /** Duitku-hosted QR code image URL */
24
+ qrCodeUrl?: string;
25
+ /** Generic payment code (for retail/minimarket methods like Indomaret) */
26
+ paymentCode?: string;
17
27
  rawResponse: any;
18
28
  error?: string;
19
29
  }
@@ -29,19 +39,64 @@ interface ProviderConfig {
29
39
  apiKey: string;
30
40
  sandbox: boolean;
31
41
  }
42
+ interface GetPaymentMethodsParams {
43
+ /** Transaction amount (integer, no decimal) */
44
+ amount: number;
45
+ }
46
+ interface PaymentMethod {
47
+ /** Provider-specific method code, e.g. "BT", "I1", "OL" */
48
+ paymentMethod: string;
49
+ /** Human-readable name, e.g. "BCA Virtual Account" */
50
+ paymentName: string;
51
+ /** URL to payment method logo/image */
52
+ paymentImage: string;
53
+ /** Total transaction fee in IDR (string from API) */
54
+ totalFee: string;
55
+ /** Categorized payment channel (Virtual Account, QRIS, E-Wallet, Retail / Gerai, Kartu Kredit, Paylater / Cicilan, Lainnya) */
56
+ category: "Virtual Account" | "QRIS" | "E-Wallet" | "Retail / Gerai" | "Kartu Kredit" | "Paylater / Cicilan" | "Lainnya";
57
+ }
58
+ interface GetPaymentMethodsResult {
59
+ success: boolean;
60
+ methods: PaymentMethod[];
61
+ error?: string;
62
+ rawResponse: any;
63
+ }
64
+ interface CheckTransactionParams {
65
+ /** The merchant order ID used when creating the invoice */
66
+ merchantOrderId: string;
67
+ }
68
+ interface CheckTransactionResult {
69
+ success: boolean;
70
+ orderId: string;
71
+ reference: string;
72
+ amount: number;
73
+ /** "00" = success/paid, "01" = pending, "02" = failed/expired */
74
+ statusCode: string;
75
+ status: "paid" | "pending" | "failed";
76
+ statusMessage: string;
77
+ error?: string;
78
+ rawResponse: any;
79
+ }
32
80
 
33
81
  declare abstract class BasePaymentProvider {
34
82
  abstract readonly name: string;
35
83
  abstract createInvoice(params: CreateInvoiceParams, config: ProviderConfig): Promise<InvoiceResponse>;
36
84
  abstract verifyCallback(body: any, config: ProviderConfig): Promise<VerifyCallbackResult>;
85
+ abstract getPaymentMethods(params: GetPaymentMethodsParams, config: ProviderConfig): Promise<GetPaymentMethodsResult>;
86
+ abstract checkTransaction(params: CheckTransactionParams, config: ProviderConfig): Promise<CheckTransactionResult>;
37
87
  }
38
88
 
39
89
  declare class DuitkuProvider extends BasePaymentProvider {
40
90
  readonly name = "duitku";
91
+ private getBaseUrl;
41
92
  createInvoice(params: CreateInvoiceParams, config: ProviderConfig): Promise<InvoiceResponse>;
42
93
  verifyCallback(body: any, config: ProviderConfig): Promise<VerifyCallbackResult>;
94
+ getPaymentMethods(params: GetPaymentMethodsParams, config: ProviderConfig): Promise<GetPaymentMethodsResult>;
95
+ checkTransaction(params: CheckTransactionParams, config: ProviderConfig): Promise<CheckTransactionResult>;
43
96
  }
44
97
 
98
+ declare function getPaymentMethodCategory(code: string, name?: string): "Virtual Account" | "QRIS" | "E-Wallet" | "Retail / Gerai" | "Kartu Kredit" | "Paylater / Cicilan" | "Lainnya";
99
+
45
100
  declare class PaymentManager {
46
101
  private providers;
47
102
  constructor();
@@ -49,7 +104,9 @@ declare class PaymentManager {
49
104
  getProvider(name: string): BasePaymentProvider;
50
105
  createInvoice(providerName: string, params: CreateInvoiceParams, config: ProviderConfig): Promise<InvoiceResponse>;
51
106
  verifyCallback(providerName: string, body: any, config: ProviderConfig): Promise<VerifyCallbackResult>;
107
+ getPaymentMethods(providerName: string, params: GetPaymentMethodsParams, config: ProviderConfig): Promise<GetPaymentMethodsResult>;
108
+ checkTransaction(providerName: string, params: CheckTransactionParams, config: ProviderConfig): Promise<CheckTransactionResult>;
52
109
  }
53
110
  declare const paymentManager: PaymentManager;
54
111
 
55
- export { BasePaymentProvider, type CreateInvoiceParams, DuitkuProvider, type InvoiceResponse, PaymentManager, type ProviderConfig, type VerifyCallbackResult, paymentManager };
112
+ export { BasePaymentProvider, type CheckTransactionParams, type CheckTransactionResult, type CreateInvoiceParams, DuitkuProvider, type GetPaymentMethodsParams, type GetPaymentMethodsResult, type InvoiceResponse, PaymentManager, type PaymentMethod, type ProviderConfig, type VerifyCallbackResult, getPaymentMethodCategory, paymentManager };
package/dist/index.d.ts CHANGED
@@ -9,11 +9,21 @@ interface CreateInvoiceParams {
9
9
  };
10
10
  returnUrl: string;
11
11
  callbackUrl: string;
12
+ /** Specific Duitku payment method code (e.g. "BCA", "I1", "OV"). If omitted, all methods are available via Duitku's redirect page. */
13
+ paymentMethod?: string;
12
14
  }
13
15
  interface InvoiceResponse {
14
16
  success: boolean;
15
17
  paymentUrl?: string;
16
18
  reference?: string;
19
+ /** Virtual Account number (for bank VA methods) */
20
+ vaNumber?: string;
21
+ /** QRIS string for display in a QR code */
22
+ qrString?: string;
23
+ /** Duitku-hosted QR code image URL */
24
+ qrCodeUrl?: string;
25
+ /** Generic payment code (for retail/minimarket methods like Indomaret) */
26
+ paymentCode?: string;
17
27
  rawResponse: any;
18
28
  error?: string;
19
29
  }
@@ -29,19 +39,64 @@ interface ProviderConfig {
29
39
  apiKey: string;
30
40
  sandbox: boolean;
31
41
  }
42
+ interface GetPaymentMethodsParams {
43
+ /** Transaction amount (integer, no decimal) */
44
+ amount: number;
45
+ }
46
+ interface PaymentMethod {
47
+ /** Provider-specific method code, e.g. "BT", "I1", "OL" */
48
+ paymentMethod: string;
49
+ /** Human-readable name, e.g. "BCA Virtual Account" */
50
+ paymentName: string;
51
+ /** URL to payment method logo/image */
52
+ paymentImage: string;
53
+ /** Total transaction fee in IDR (string from API) */
54
+ totalFee: string;
55
+ /** Categorized payment channel (Virtual Account, QRIS, E-Wallet, Retail / Gerai, Kartu Kredit, Paylater / Cicilan, Lainnya) */
56
+ category: "Virtual Account" | "QRIS" | "E-Wallet" | "Retail / Gerai" | "Kartu Kredit" | "Paylater / Cicilan" | "Lainnya";
57
+ }
58
+ interface GetPaymentMethodsResult {
59
+ success: boolean;
60
+ methods: PaymentMethod[];
61
+ error?: string;
62
+ rawResponse: any;
63
+ }
64
+ interface CheckTransactionParams {
65
+ /** The merchant order ID used when creating the invoice */
66
+ merchantOrderId: string;
67
+ }
68
+ interface CheckTransactionResult {
69
+ success: boolean;
70
+ orderId: string;
71
+ reference: string;
72
+ amount: number;
73
+ /** "00" = success/paid, "01" = pending, "02" = failed/expired */
74
+ statusCode: string;
75
+ status: "paid" | "pending" | "failed";
76
+ statusMessage: string;
77
+ error?: string;
78
+ rawResponse: any;
79
+ }
32
80
 
33
81
  declare abstract class BasePaymentProvider {
34
82
  abstract readonly name: string;
35
83
  abstract createInvoice(params: CreateInvoiceParams, config: ProviderConfig): Promise<InvoiceResponse>;
36
84
  abstract verifyCallback(body: any, config: ProviderConfig): Promise<VerifyCallbackResult>;
85
+ abstract getPaymentMethods(params: GetPaymentMethodsParams, config: ProviderConfig): Promise<GetPaymentMethodsResult>;
86
+ abstract checkTransaction(params: CheckTransactionParams, config: ProviderConfig): Promise<CheckTransactionResult>;
37
87
  }
38
88
 
39
89
  declare class DuitkuProvider extends BasePaymentProvider {
40
90
  readonly name = "duitku";
91
+ private getBaseUrl;
41
92
  createInvoice(params: CreateInvoiceParams, config: ProviderConfig): Promise<InvoiceResponse>;
42
93
  verifyCallback(body: any, config: ProviderConfig): Promise<VerifyCallbackResult>;
94
+ getPaymentMethods(params: GetPaymentMethodsParams, config: ProviderConfig): Promise<GetPaymentMethodsResult>;
95
+ checkTransaction(params: CheckTransactionParams, config: ProviderConfig): Promise<CheckTransactionResult>;
43
96
  }
44
97
 
98
+ declare function getPaymentMethodCategory(code: string, name?: string): "Virtual Account" | "QRIS" | "E-Wallet" | "Retail / Gerai" | "Kartu Kredit" | "Paylater / Cicilan" | "Lainnya";
99
+
45
100
  declare class PaymentManager {
46
101
  private providers;
47
102
  constructor();
@@ -49,7 +104,9 @@ declare class PaymentManager {
49
104
  getProvider(name: string): BasePaymentProvider;
50
105
  createInvoice(providerName: string, params: CreateInvoiceParams, config: ProviderConfig): Promise<InvoiceResponse>;
51
106
  verifyCallback(providerName: string, body: any, config: ProviderConfig): Promise<VerifyCallbackResult>;
107
+ getPaymentMethods(providerName: string, params: GetPaymentMethodsParams, config: ProviderConfig): Promise<GetPaymentMethodsResult>;
108
+ checkTransaction(providerName: string, params: CheckTransactionParams, config: ProviderConfig): Promise<CheckTransactionResult>;
52
109
  }
53
110
  declare const paymentManager: PaymentManager;
54
111
 
55
- export { BasePaymentProvider, type CreateInvoiceParams, DuitkuProvider, type InvoiceResponse, PaymentManager, type ProviderConfig, type VerifyCallbackResult, paymentManager };
112
+ export { BasePaymentProvider, type CheckTransactionParams, type CheckTransactionResult, type CreateInvoiceParams, DuitkuProvider, type GetPaymentMethodsParams, type GetPaymentMethodsResult, type InvoiceResponse, PaymentManager, type PaymentMethod, type ProviderConfig, type VerifyCallbackResult, getPaymentMethodCategory, paymentManager };
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  BasePaymentProvider: () => BasePaymentProvider,
34
34
  DuitkuProvider: () => DuitkuProvider,
35
35
  PaymentManager: () => PaymentManager,
36
+ getPaymentMethodCategory: () => getPaymentMethodCategory,
36
37
  paymentManager: () => paymentManager
37
38
  });
38
39
  module.exports = __toCommonJS(index_exports);
@@ -44,13 +45,30 @@ var import_crypto = __toESM(require("crypto"));
44
45
  var BasePaymentProvider = class {
45
46
  };
46
47
 
48
+ // src/utils.ts
49
+ function getPaymentMethodCategory(code, name = "") {
50
+ const c = code.toUpperCase();
51
+ const n = name.toUpperCase();
52
+ if (c.includes("QR") || n.includes("QRIS")) return "QRIS";
53
+ if (c.includes("VA") || n.includes("VA") || n.includes("VIRTUAL ACCOUNT") || ["I1", "BT", "A1", "M2", "V1", "B1", "AG", "NC", "BR", "S1", "BS", "MY"].some((p) => c.startsWith(p.slice(0, 2)))) return "Virtual Account";
54
+ if (["OV", "GO", "SH", "DA", "LA", "OL", "SL", "GP", "OP", "DN", "JA", "JN", "JP"].some((p) => c.startsWith(p.slice(0, 2))) || n.includes("OVO") || n.includes("DANA") || n.includes("GOPAY") || n.includes("LINKAJA") || n.includes("SHOPEEPAY") || n.includes("JENIUS")) return "E-Wallet";
55
+ if (c.startsWith("FT") || c.startsWith("AL") || n.includes("INDOMARET") || n.includes("ALFAMART") || n.includes("RETAIL")) return "Retail / Gerai";
56
+ if (c.startsWith("VC") || n.includes("CREDIT CARD") || n.includes("KARTU KREDIT")) return "Kartu Kredit";
57
+ if (n.includes("PAYLATER") || n.includes("INDODANA") || n.includes("AKULAKU") || n.includes("KREDIVO")) return "Paylater / Cicilan";
58
+ return "Lainnya";
59
+ }
60
+
47
61
  // src/providers/duitku.ts
48
62
  var DuitkuProvider = class extends BasePaymentProvider {
49
63
  name = "duitku";
64
+ getBaseUrl(sandbox) {
65
+ return sandbox ? "https://api-sandbox.duitku.com" : "https://api-prod.duitku.com";
66
+ }
50
67
  async createInvoice(params, config) {
51
68
  const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
52
69
  const { merchantCode, apiKey, sandbox } = config;
53
- const url = sandbox ? "https://api-sandbox.duitku.com/api/merchant/createInvoice" : "https://api-prod.duitku.com/api/merchant/createInvoice";
70
+ const isDirectInquiry = !!params.paymentMethod;
71
+ const url = isDirectInquiry ? sandbox ? "https://sandbox.duitku.com/webapi/api/merchant/v2/inquiry" : "https://passport.duitku.com/webapi/api/merchant/v2/inquiry" : `${this.getBaseUrl(sandbox)}/api/merchant/createInvoice`;
54
72
  const integerAmount = Math.round(amount);
55
73
  const rawPayloadSignature = merchantCode + orderId + integerAmount.toString() + apiKey;
56
74
  const payloadSignature = import_crypto.default.createHash("md5").update(rawPayloadSignature).digest("hex");
@@ -67,19 +85,23 @@ var DuitkuProvider = class extends BasePaymentProvider {
67
85
  signature: payloadSignature,
68
86
  callbackUrl,
69
87
  returnUrl,
70
- expiryPeriod: 1440
88
+ expiryPeriod: 1440,
71
89
  // 24 hours expiry
90
+ ...params.paymentMethod ? { paymentMethod: params.paymentMethod } : {}
72
91
  };
73
92
  try {
93
+ const headers = {
94
+ "Content-Type": "application/json",
95
+ "Accept": "application/json"
96
+ };
97
+ if (!isDirectInquiry) {
98
+ headers["x-duitku-signature"] = headerSignature;
99
+ headers["x-duitku-timestamp"] = timestamp;
100
+ headers["x-duitku-merchantcode"] = merchantCode;
101
+ }
74
102
  const response = await fetch(url, {
75
103
  method: "POST",
76
- headers: {
77
- "Content-Type": "application/json",
78
- "Accept": "application/json",
79
- "x-duitku-signature": headerSignature,
80
- "x-duitku-timestamp": timestamp,
81
- "x-duitku-merchantcode": merchantCode
82
- },
104
+ headers,
83
105
  body: JSON.stringify(payload)
84
106
  });
85
107
  const text = await response.text();
@@ -100,6 +122,10 @@ var DuitkuProvider = class extends BasePaymentProvider {
100
122
  success: true,
101
123
  paymentUrl: data.paymentUrl,
102
124
  reference: data.reference,
125
+ vaNumber: data.vaNumber,
126
+ qrString: data.qrString,
127
+ qrCodeUrl: data.qrCodeUrl,
128
+ paymentCode: data.paymentCode,
103
129
  rawResponse: data
104
130
  };
105
131
  } else {
@@ -135,6 +161,149 @@ var DuitkuProvider = class extends BasePaymentProvider {
135
161
  rawPayload: body
136
162
  };
137
163
  }
164
+ async getPaymentMethods(params, config) {
165
+ const { amount } = params;
166
+ const { merchantCode, apiKey, sandbox } = config;
167
+ const url = sandbox ? "https://sandbox.duitku.com/webapi/api/merchant/paymentmethod/getpaymentmethod" : "https://passport.duitku.com/webapi/api/merchant/paymentmethod/getpaymentmethod";
168
+ const integerAmount = Math.round(amount);
169
+ const datetime = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
170
+ const stringToSign = merchantCode + integerAmount.toString() + datetime;
171
+ const signature = import_crypto.default.createHmac("sha256", apiKey).update(stringToSign).digest("hex");
172
+ try {
173
+ const response = await fetch(url, {
174
+ method: "POST",
175
+ headers: {
176
+ "Content-Type": "application/json"
177
+ },
178
+ body: JSON.stringify({
179
+ merchantcode: merchantCode,
180
+ amount: integerAmount,
181
+ datetime,
182
+ signature
183
+ })
184
+ });
185
+ const text = await response.text();
186
+ let data = null;
187
+ try {
188
+ data = JSON.parse(text);
189
+ } catch (e) {
190
+ }
191
+ if (!response.ok || !data) {
192
+ return {
193
+ success: false,
194
+ methods: [],
195
+ rawResponse: data,
196
+ error: data?.responseMessage || `HTTP error! Status: ${response.status}`
197
+ };
198
+ }
199
+ if (data.responseCode === "00") {
200
+ const rawMethods = data.paymentFee || [];
201
+ const methods = rawMethods.map((m) => ({
202
+ paymentMethod: m.paymentMethod,
203
+ paymentName: m.paymentName,
204
+ paymentImage: m.paymentImage,
205
+ totalFee: m.totalFee,
206
+ category: getPaymentMethodCategory(m.paymentMethod, m.paymentName)
207
+ }));
208
+ return {
209
+ success: true,
210
+ methods,
211
+ rawResponse: data
212
+ };
213
+ } else {
214
+ return {
215
+ success: false,
216
+ methods: [],
217
+ rawResponse: data,
218
+ error: data.responseMessage || `Duitku Error: ${data.responseCode}`
219
+ };
220
+ }
221
+ } catch (e) {
222
+ return {
223
+ success: false,
224
+ methods: [],
225
+ rawResponse: null,
226
+ error: e.message || "Failed to get payment methods from Duitku"
227
+ };
228
+ }
229
+ }
230
+ async checkTransaction(params, config) {
231
+ const { merchantOrderId } = params;
232
+ const { merchantCode, apiKey, sandbox } = config;
233
+ const url = sandbox ? "https://api-sandbox.duitku.com/api/merchant/transactionStatus" : "https://api-prod.duitku.com/api/merchant/transactionStatus";
234
+ const timestamp = Date.now().toString();
235
+ const rawHeaderSignature = merchantCode + timestamp + apiKey;
236
+ const headerSignature = import_crypto.default.createHash("sha256").update(rawHeaderSignature).digest("hex");
237
+ const rawBodySignature = merchantCode + merchantOrderId + apiKey;
238
+ const bodySignature = import_crypto.default.createHash("md5").update(rawBodySignature).digest("hex");
239
+ try {
240
+ const response = await fetch(url, {
241
+ method: "POST",
242
+ headers: {
243
+ "Content-Type": "application/json",
244
+ "Accept": "application/json",
245
+ "x-duitku-signature": headerSignature,
246
+ "x-duitku-timestamp": timestamp,
247
+ "x-duitku-merchantcode": merchantCode
248
+ },
249
+ body: JSON.stringify({
250
+ merchantCode,
251
+ merchantOrderId,
252
+ signature: bodySignature
253
+ })
254
+ });
255
+ const text = await response.text();
256
+ let data = null;
257
+ try {
258
+ data = JSON.parse(text);
259
+ } catch (e) {
260
+ }
261
+ if (!response.ok || !data) {
262
+ return {
263
+ success: false,
264
+ orderId: merchantOrderId,
265
+ reference: "",
266
+ amount: 0,
267
+ statusCode: "",
268
+ status: "failed",
269
+ statusMessage: `HTTP error! Status: ${response.status}`,
270
+ error: `HTTP ${response.status}`,
271
+ rawResponse: data
272
+ };
273
+ }
274
+ const statusCode = data.statusCode || "";
275
+ let status;
276
+ if (statusCode === "00") {
277
+ status = "paid";
278
+ } else if (statusCode === "01") {
279
+ status = "pending";
280
+ } else {
281
+ status = "failed";
282
+ }
283
+ return {
284
+ success: true,
285
+ orderId: data.merchantOrderId || merchantOrderId,
286
+ reference: data.reference || "",
287
+ amount: data.amount ? Number(data.amount) : 0,
288
+ statusCode,
289
+ status,
290
+ statusMessage: data.statusMessage || "",
291
+ rawResponse: data
292
+ };
293
+ } catch (e) {
294
+ return {
295
+ success: false,
296
+ orderId: merchantOrderId,
297
+ reference: "",
298
+ amount: 0,
299
+ statusCode: "",
300
+ status: "failed",
301
+ statusMessage: "Network error",
302
+ error: e.message || "Failed to check transaction status",
303
+ rawResponse: null
304
+ };
305
+ }
306
+ }
138
307
  };
139
308
 
140
309
  // src/index.ts
@@ -161,6 +330,14 @@ var PaymentManager = class {
161
330
  const provider = this.getProvider(providerName);
162
331
  return provider.verifyCallback(body, config);
163
332
  }
333
+ async getPaymentMethods(providerName, params, config) {
334
+ const provider = this.getProvider(providerName);
335
+ return provider.getPaymentMethods(params, config);
336
+ }
337
+ async checkTransaction(providerName, params, config) {
338
+ const provider = this.getProvider(providerName);
339
+ return provider.checkTransaction(params, config);
340
+ }
164
341
  };
165
342
  var paymentManager = new PaymentManager();
166
343
  // Annotate the CommonJS export names for ESM import in node:
@@ -168,5 +345,6 @@ var paymentManager = new PaymentManager();
168
345
  BasePaymentProvider,
169
346
  DuitkuProvider,
170
347
  PaymentManager,
348
+ getPaymentMethodCategory,
171
349
  paymentManager
172
350
  });
package/dist/index.mjs CHANGED
@@ -5,13 +5,30 @@ import crypto from "crypto";
5
5
  var BasePaymentProvider = class {
6
6
  };
7
7
 
8
+ // src/utils.ts
9
+ function getPaymentMethodCategory(code, name = "") {
10
+ const c = code.toUpperCase();
11
+ const n = name.toUpperCase();
12
+ if (c.includes("QR") || n.includes("QRIS")) return "QRIS";
13
+ if (c.includes("VA") || n.includes("VA") || n.includes("VIRTUAL ACCOUNT") || ["I1", "BT", "A1", "M2", "V1", "B1", "AG", "NC", "BR", "S1", "BS", "MY"].some((p) => c.startsWith(p.slice(0, 2)))) return "Virtual Account";
14
+ if (["OV", "GO", "SH", "DA", "LA", "OL", "SL", "GP", "OP", "DN", "JA", "JN", "JP"].some((p) => c.startsWith(p.slice(0, 2))) || n.includes("OVO") || n.includes("DANA") || n.includes("GOPAY") || n.includes("LINKAJA") || n.includes("SHOPEEPAY") || n.includes("JENIUS")) return "E-Wallet";
15
+ if (c.startsWith("FT") || c.startsWith("AL") || n.includes("INDOMARET") || n.includes("ALFAMART") || n.includes("RETAIL")) return "Retail / Gerai";
16
+ if (c.startsWith("VC") || n.includes("CREDIT CARD") || n.includes("KARTU KREDIT")) return "Kartu Kredit";
17
+ if (n.includes("PAYLATER") || n.includes("INDODANA") || n.includes("AKULAKU") || n.includes("KREDIVO")) return "Paylater / Cicilan";
18
+ return "Lainnya";
19
+ }
20
+
8
21
  // src/providers/duitku.ts
9
22
  var DuitkuProvider = class extends BasePaymentProvider {
10
23
  name = "duitku";
24
+ getBaseUrl(sandbox) {
25
+ return sandbox ? "https://api-sandbox.duitku.com" : "https://api-prod.duitku.com";
26
+ }
11
27
  async createInvoice(params, config) {
12
28
  const { orderId, amount, productDetails, customer, returnUrl, callbackUrl } = params;
13
29
  const { merchantCode, apiKey, sandbox } = config;
14
- const url = sandbox ? "https://api-sandbox.duitku.com/api/merchant/createInvoice" : "https://api-prod.duitku.com/api/merchant/createInvoice";
30
+ const isDirectInquiry = !!params.paymentMethod;
31
+ const url = isDirectInquiry ? sandbox ? "https://sandbox.duitku.com/webapi/api/merchant/v2/inquiry" : "https://passport.duitku.com/webapi/api/merchant/v2/inquiry" : `${this.getBaseUrl(sandbox)}/api/merchant/createInvoice`;
15
32
  const integerAmount = Math.round(amount);
16
33
  const rawPayloadSignature = merchantCode + orderId + integerAmount.toString() + apiKey;
17
34
  const payloadSignature = crypto.createHash("md5").update(rawPayloadSignature).digest("hex");
@@ -28,19 +45,23 @@ var DuitkuProvider = class extends BasePaymentProvider {
28
45
  signature: payloadSignature,
29
46
  callbackUrl,
30
47
  returnUrl,
31
- expiryPeriod: 1440
48
+ expiryPeriod: 1440,
32
49
  // 24 hours expiry
50
+ ...params.paymentMethod ? { paymentMethod: params.paymentMethod } : {}
33
51
  };
34
52
  try {
53
+ const headers = {
54
+ "Content-Type": "application/json",
55
+ "Accept": "application/json"
56
+ };
57
+ if (!isDirectInquiry) {
58
+ headers["x-duitku-signature"] = headerSignature;
59
+ headers["x-duitku-timestamp"] = timestamp;
60
+ headers["x-duitku-merchantcode"] = merchantCode;
61
+ }
35
62
  const response = await fetch(url, {
36
63
  method: "POST",
37
- headers: {
38
- "Content-Type": "application/json",
39
- "Accept": "application/json",
40
- "x-duitku-signature": headerSignature,
41
- "x-duitku-timestamp": timestamp,
42
- "x-duitku-merchantcode": merchantCode
43
- },
64
+ headers,
44
65
  body: JSON.stringify(payload)
45
66
  });
46
67
  const text = await response.text();
@@ -61,6 +82,10 @@ var DuitkuProvider = class extends BasePaymentProvider {
61
82
  success: true,
62
83
  paymentUrl: data.paymentUrl,
63
84
  reference: data.reference,
85
+ vaNumber: data.vaNumber,
86
+ qrString: data.qrString,
87
+ qrCodeUrl: data.qrCodeUrl,
88
+ paymentCode: data.paymentCode,
64
89
  rawResponse: data
65
90
  };
66
91
  } else {
@@ -96,6 +121,149 @@ var DuitkuProvider = class extends BasePaymentProvider {
96
121
  rawPayload: body
97
122
  };
98
123
  }
124
+ async getPaymentMethods(params, config) {
125
+ const { amount } = params;
126
+ const { merchantCode, apiKey, sandbox } = config;
127
+ const url = sandbox ? "https://sandbox.duitku.com/webapi/api/merchant/paymentmethod/getpaymentmethod" : "https://passport.duitku.com/webapi/api/merchant/paymentmethod/getpaymentmethod";
128
+ const integerAmount = Math.round(amount);
129
+ const datetime = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
130
+ const stringToSign = merchantCode + integerAmount.toString() + datetime;
131
+ const signature = crypto.createHmac("sha256", apiKey).update(stringToSign).digest("hex");
132
+ try {
133
+ const response = await fetch(url, {
134
+ method: "POST",
135
+ headers: {
136
+ "Content-Type": "application/json"
137
+ },
138
+ body: JSON.stringify({
139
+ merchantcode: merchantCode,
140
+ amount: integerAmount,
141
+ datetime,
142
+ signature
143
+ })
144
+ });
145
+ const text = await response.text();
146
+ let data = null;
147
+ try {
148
+ data = JSON.parse(text);
149
+ } catch (e) {
150
+ }
151
+ if (!response.ok || !data) {
152
+ return {
153
+ success: false,
154
+ methods: [],
155
+ rawResponse: data,
156
+ error: data?.responseMessage || `HTTP error! Status: ${response.status}`
157
+ };
158
+ }
159
+ if (data.responseCode === "00") {
160
+ const rawMethods = data.paymentFee || [];
161
+ const methods = rawMethods.map((m) => ({
162
+ paymentMethod: m.paymentMethod,
163
+ paymentName: m.paymentName,
164
+ paymentImage: m.paymentImage,
165
+ totalFee: m.totalFee,
166
+ category: getPaymentMethodCategory(m.paymentMethod, m.paymentName)
167
+ }));
168
+ return {
169
+ success: true,
170
+ methods,
171
+ rawResponse: data
172
+ };
173
+ } else {
174
+ return {
175
+ success: false,
176
+ methods: [],
177
+ rawResponse: data,
178
+ error: data.responseMessage || `Duitku Error: ${data.responseCode}`
179
+ };
180
+ }
181
+ } catch (e) {
182
+ return {
183
+ success: false,
184
+ methods: [],
185
+ rawResponse: null,
186
+ error: e.message || "Failed to get payment methods from Duitku"
187
+ };
188
+ }
189
+ }
190
+ async checkTransaction(params, config) {
191
+ const { merchantOrderId } = params;
192
+ const { merchantCode, apiKey, sandbox } = config;
193
+ const url = sandbox ? "https://api-sandbox.duitku.com/api/merchant/transactionStatus" : "https://api-prod.duitku.com/api/merchant/transactionStatus";
194
+ const timestamp = Date.now().toString();
195
+ const rawHeaderSignature = merchantCode + timestamp + apiKey;
196
+ const headerSignature = crypto.createHash("sha256").update(rawHeaderSignature).digest("hex");
197
+ const rawBodySignature = merchantCode + merchantOrderId + apiKey;
198
+ const bodySignature = crypto.createHash("md5").update(rawBodySignature).digest("hex");
199
+ try {
200
+ const response = await fetch(url, {
201
+ method: "POST",
202
+ headers: {
203
+ "Content-Type": "application/json",
204
+ "Accept": "application/json",
205
+ "x-duitku-signature": headerSignature,
206
+ "x-duitku-timestamp": timestamp,
207
+ "x-duitku-merchantcode": merchantCode
208
+ },
209
+ body: JSON.stringify({
210
+ merchantCode,
211
+ merchantOrderId,
212
+ signature: bodySignature
213
+ })
214
+ });
215
+ const text = await response.text();
216
+ let data = null;
217
+ try {
218
+ data = JSON.parse(text);
219
+ } catch (e) {
220
+ }
221
+ if (!response.ok || !data) {
222
+ return {
223
+ success: false,
224
+ orderId: merchantOrderId,
225
+ reference: "",
226
+ amount: 0,
227
+ statusCode: "",
228
+ status: "failed",
229
+ statusMessage: `HTTP error! Status: ${response.status}`,
230
+ error: `HTTP ${response.status}`,
231
+ rawResponse: data
232
+ };
233
+ }
234
+ const statusCode = data.statusCode || "";
235
+ let status;
236
+ if (statusCode === "00") {
237
+ status = "paid";
238
+ } else if (statusCode === "01") {
239
+ status = "pending";
240
+ } else {
241
+ status = "failed";
242
+ }
243
+ return {
244
+ success: true,
245
+ orderId: data.merchantOrderId || merchantOrderId,
246
+ reference: data.reference || "",
247
+ amount: data.amount ? Number(data.amount) : 0,
248
+ statusCode,
249
+ status,
250
+ statusMessage: data.statusMessage || "",
251
+ rawResponse: data
252
+ };
253
+ } catch (e) {
254
+ return {
255
+ success: false,
256
+ orderId: merchantOrderId,
257
+ reference: "",
258
+ amount: 0,
259
+ statusCode: "",
260
+ status: "failed",
261
+ statusMessage: "Network error",
262
+ error: e.message || "Failed to check transaction status",
263
+ rawResponse: null
264
+ };
265
+ }
266
+ }
99
267
  };
100
268
 
101
269
  // src/index.ts
@@ -122,11 +290,20 @@ var PaymentManager = class {
122
290
  const provider = this.getProvider(providerName);
123
291
  return provider.verifyCallback(body, config);
124
292
  }
293
+ async getPaymentMethods(providerName, params, config) {
294
+ const provider = this.getProvider(providerName);
295
+ return provider.getPaymentMethods(params, config);
296
+ }
297
+ async checkTransaction(providerName, params, config) {
298
+ const provider = this.getProvider(providerName);
299
+ return provider.checkTransaction(params, config);
300
+ }
125
301
  };
126
302
  var paymentManager = new PaymentManager();
127
303
  export {
128
304
  BasePaymentProvider,
129
305
  DuitkuProvider,
130
306
  PaymentManager,
307
+ getPaymentMethodCategory,
131
308
  paymentManager
132
309
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@crediblemark/buayar",
3
- "version": "0.1.4",
4
- "description": "Unified Payment Gateway SDK for CredBuild platforms and sites",
3
+ "version": "0.1.6",
4
+ "description": "Unified Payment Gateway SDK for Node.js and TypeScript supporting multiple providers",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",