@crediblemark/buayar 0.3.2 → 0.5.1

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
@@ -1980,11 +1980,11 @@ var XenditProvider = class extends BasePaymentProvider {
1980
1980
  amount: integerAmount,
1981
1981
  reference_id: orderId,
1982
1982
  description: productDetails,
1983
- customer: {
1984
- given_names: customer.name,
1985
- email: customer.email,
1986
- mobile_number: customer.phone || ""
1987
- },
1983
+ // Xendit Payment Requests API rejects the inline `customer` object
1984
+ // with API_VALIDATION_ERROR. Attribution is only supported via a
1985
+ // pre-created `customer_id` (Customer API), so we forward that when
1986
+ // provided through providerParams and otherwise omit customer data.
1987
+ ...params.providerParams?.customer_id ? { customer_id: params.providerParams.customer_id } : {},
1988
1988
  payment_method: paymentMethodPayload,
1989
1989
  ...params.providerParams
1990
1990
  };
@@ -8764,6 +8764,184 @@ var PaymentManager = class {
8764
8764
  }
8765
8765
  return { success: false, enabled: [], error: `Provider '${providerName}' does not support payment methods probing` };
8766
8766
  }
8767
+ // ─── Unified Advanced Operations (Refund / Balance / Disburse) ─────────────
8768
+ // "Mata tertutup": satu API untuk semua provider yang mendukung fitur.
8769
+ // Provider yang tidak mendukung mengembalikan `supported: false` (bukan throw).
8770
+ unsupported(supported, provider, operation, rawResponse = null) {
8771
+ return { success: false, supported, provider, rawResponse, error: `Provider '${provider}' does not support ${operation}` };
8772
+ }
8773
+ async refund(providerName, params, config) {
8774
+ const name = providerName.toLowerCase();
8775
+ try {
8776
+ switch (name) {
8777
+ case "midtrans": {
8778
+ const data = await this.getMidtransClient(config).refundTransaction(params.transactionId, {
8779
+ amount: params.amount,
8780
+ reason: params.reason
8781
+ });
8782
+ return { success: true, supported: true, provider: "midtrans", reference: data.transaction_id || data.order_id, status: data.status_message, rawResponse: data };
8783
+ }
8784
+ case "stripe": {
8785
+ const data = await this.getStripeClient(config).createRefund(params.transactionId, params.amount);
8786
+ return { success: true, supported: true, provider: "stripe", reference: data.id, status: data.status, rawResponse: data };
8787
+ }
8788
+ case "paypal": {
8789
+ const data = await this.getPaypalClient(config).refundCapture(params.transactionId, params.amount, params.currency);
8790
+ return { success: true, supported: true, provider: "paypal", reference: data.id, status: data.status, rawResponse: data };
8791
+ }
8792
+ case "adyen": {
8793
+ const client = this.getAdyenClient(config);
8794
+ const merchantAccount = config.extra?.merchantAccount || config.merchantCode || config.merchantId || "";
8795
+ const data = await client.refundPayment(params.transactionId, params.amount || 0, params.currency || "IDR", merchantAccount);
8796
+ return { success: true, supported: true, provider: "adyen", reference: data.pspReference, status: data.response, rawResponse: data };
8797
+ }
8798
+ case "checkoutcom": {
8799
+ const data = await this.getCheckoutComClient(config).refundPayment(params.transactionId, params.amount);
8800
+ return { success: true, supported: true, provider: "checkoutcom", reference: data.reference, status: data.status, rawResponse: data };
8801
+ }
8802
+ case "razorpay": {
8803
+ const data = await this.getRazorpayClient(config).createRefund(params.transactionId, params.amount);
8804
+ return { success: true, supported: true, provider: "razorpay", reference: data.id, status: data.status, rawResponse: data };
8805
+ }
8806
+ case "square": {
8807
+ const client = this.getSquareClient(config);
8808
+ const currency = (params.currency || "IDR").toUpperCase();
8809
+ const idempotencyKey = config.extra?.idempotencyKey || `refund-${params.transactionId}-${Date.now()}`;
8810
+ const data = await client.refundPayment(params.transactionId, Math.round(params.amount || 0), currency, idempotencyKey, params.reason);
8811
+ const refund = data.refund;
8812
+ return { success: !data.errors, supported: true, provider: "square", reference: refund?.id, status: refund?.status, rawResponse: data };
8813
+ }
8814
+ case "payu": {
8815
+ const data = await this.getPayuClient(config).refundOrder(params.transactionId, params.amount, params.reason);
8816
+ return { success: true, supported: true, provider: "payu", reference: data.orderId, status: data.status, rawResponse: data };
8817
+ }
8818
+ case "braintree": {
8819
+ const data = await this.getBraintreeClient(config).refundTransaction(params.transactionId, params.amount);
8820
+ return { success: true, supported: true, provider: "braintree", reference: data?.transaction?.id, status: data?.transaction?.status, rawResponse: data };
8821
+ }
8822
+ case "twocheckout": {
8823
+ const data = await this.getTwoCheckoutClient(config).refundOrder(params.transactionId, params.amount || 0, params.reason);
8824
+ return { success: true, supported: true, provider: "twocheckout", reference: data.refno, status: data.response_code, rawResponse: data };
8825
+ }
8826
+ default:
8827
+ return this.unsupported(false, name, "refund");
8828
+ }
8829
+ } catch (e) {
8830
+ return { success: false, supported: true, provider: name, rawResponse: null, error: e.message || "Refund failed" };
8831
+ }
8832
+ }
8833
+ async checkBalance(providerName, config) {
8834
+ const name = providerName.toLowerCase();
8835
+ try {
8836
+ let balance;
8837
+ let currency;
8838
+ let raw;
8839
+ switch (name) {
8840
+ case "midtrans": {
8841
+ raw = await this.getMidtransClient(config).getBalance();
8842
+ balance = raw?.balance ?? raw?.balance_amount ?? raw?.amount;
8843
+ break;
8844
+ }
8845
+ case "duitku": {
8846
+ const result = await this.getDuitkuClient(config).checkBalance();
8847
+ return { success: result.success, supported: true, provider: "duitku", balance: result.balance, rawResponse: result.rawResponse, error: result.error };
8848
+ }
8849
+ case "ipaymu": {
8850
+ const result = await this.getIpaymuClient(config).checkBalance();
8851
+ return { success: result.success, supported: true, provider: "ipaymu", balance: result.balance, rawResponse: result.rawResponse, error: result.error };
8852
+ }
8853
+ case "xendit": {
8854
+ const result = await this.getXenditClient(config).checkBalance("CASH");
8855
+ return { success: result.success, supported: true, provider: "xendit", balance: result.balance, rawResponse: result.rawResponse, error: result.error };
8856
+ }
8857
+ case "oy": {
8858
+ raw = await this.getOyClient(config).checkBalance();
8859
+ balance = raw?.balance ?? raw?.data?.balance;
8860
+ break;
8861
+ }
8862
+ case "stripe": {
8863
+ raw = await this.getStripeClient(config).checkBalance();
8864
+ const available = raw?.available?.[0];
8865
+ balance = available?.amount;
8866
+ currency = available?.currency;
8867
+ break;
8868
+ }
8869
+ case "paypal": {
8870
+ raw = await this.getPaypalClient(config).checkBalance();
8871
+ const first = raw?.balances?.[0];
8872
+ balance = first?.total_balance?.value !== void 0 ? Number(first.total_balance.value) * 100 : void 0;
8873
+ currency = first?.currency_code;
8874
+ break;
8875
+ }
8876
+ case "checkoutcom": {
8877
+ raw = await this.getCheckoutComClient(config).checkBalance();
8878
+ const first = raw?.data?.[0]?.available;
8879
+ balance = first?.[0]?.value;
8880
+ currency = first?.[0]?.currency;
8881
+ break;
8882
+ }
8883
+ case "razorpay": {
8884
+ raw = await this.getRazorpayClient(config).checkBalance();
8885
+ balance = raw?.balance ?? raw?.amount;
8886
+ currency = raw?.currency;
8887
+ break;
8888
+ }
8889
+ case "square": {
8890
+ raw = await this.getSquareClient(config).retrieveBalance();
8891
+ balance = raw?.balance_money?.amount;
8892
+ currency = raw?.balance_money?.currency;
8893
+ break;
8894
+ }
8895
+ default:
8896
+ return this.unsupported(false, name, "checkBalance");
8897
+ }
8898
+ return { success: true, supported: true, provider: name, balance, currency, rawResponse: raw };
8899
+ } catch (e) {
8900
+ return { success: false, supported: true, provider: name, rawResponse: null, error: e.message || "Balance check failed" };
8901
+ }
8902
+ }
8903
+ async disburse(providerName, params, config) {
8904
+ const name = providerName.toLowerCase();
8905
+ try {
8906
+ switch (name) {
8907
+ case "duitku": {
8908
+ const data = await this.getDuitkuClient(config).disburse({
8909
+ bankCode: params.bankCode,
8910
+ bankAccount: params.accountNumber,
8911
+ amount: params.amount,
8912
+ purpose: params.description || "Disbursement",
8913
+ merchantOrderId: params.externalId
8914
+ });
8915
+ return { success: data?.statusCode === "00", supported: true, provider: "duitku", reference: params.externalId, status: data?.statusMessage, rawResponse: data };
8916
+ }
8917
+ case "xendit": {
8918
+ const data = await this.getXenditClient(config).createDisbursement({
8919
+ externalId: params.externalId,
8920
+ bankCode: params.bankCode,
8921
+ accountHolderName: params.accountHolderName || "",
8922
+ accountNumber: params.accountNumber,
8923
+ description: params.description || "Disbursement",
8924
+ amount: params.amount
8925
+ });
8926
+ return { success: true, supported: true, provider: "xendit", reference: data.id, status: data.status, rawResponse: data };
8927
+ }
8928
+ case "oy": {
8929
+ const data = await this.getOyClient(config).remit({
8930
+ recipientBank: params.bankCode,
8931
+ recipientAccount: params.accountNumber,
8932
+ amount: params.amount,
8933
+ note: params.description,
8934
+ partnerTrxId: params.externalId
8935
+ });
8936
+ return { success: true, supported: true, provider: "oy", reference: params.externalId, status: data?.status, rawResponse: data };
8937
+ }
8938
+ default:
8939
+ return this.unsupported(false, name, "disburse");
8940
+ }
8941
+ } catch (e) {
8942
+ return { success: false, supported: true, provider: name, rawResponse: null, error: e.message || "Disbursement failed" };
8943
+ }
8944
+ }
8767
8945
  };
8768
8946
  var paymentManager = new PaymentManager();
8769
8947
 
@@ -9127,6 +9305,33 @@ var Buayar = class {
9127
9305
  async handleWebhook(payload, headers, configOverride) {
9128
9306
  return this.verifyWebhook(payload, headers, configOverride);
9129
9307
  }
9308
+ /**
9309
+ * Unified Refund — berlaku untuk semua provider yang mendukung refund.
9310
+ * Provider tanpa fitur refund mengembalikan `{ supported: false }`, bukan error.
9311
+ */
9312
+ async refund(params, configOverride) {
9313
+ const mergedConfig = { ...this.config, ...configOverride };
9314
+ const providerName = configOverride?.provider || this.provider;
9315
+ return this.manager.refund(providerName, params, mergedConfig);
9316
+ }
9317
+ /**
9318
+ * Unified Check Balance — ambil saldo merchant dari provider aktif.
9319
+ * Provider tanpa fitur balance mengembalikan `{ supported: false }`.
9320
+ */
9321
+ async checkBalance(configOverride) {
9322
+ const mergedConfig = { ...this.config, ...configOverride };
9323
+ const providerName = configOverride?.provider || this.provider;
9324
+ return this.manager.checkBalance(providerName, mergedConfig);
9325
+ }
9326
+ /**
9327
+ * Unified Disburse / Payout — transfer dana ke rekening bank tujuan.
9328
+ * Provider tanpa fitur disbursement mengembalikan `{ supported: false }`.
9329
+ */
9330
+ async disburse(params, configOverride) {
9331
+ const mergedConfig = { ...this.config, ...configOverride };
9332
+ const providerName = configOverride?.provider || this.provider;
9333
+ return this.manager.disburse(providerName, params, mergedConfig);
9334
+ }
9130
9335
  // ─── Indonesian Provider Client Getters ───────────────────────────────────
9131
9336
  getMidtransClient(configOverride) {
9132
9337
  return new MidtransClient({ ...this.config, ...configOverride });
package/docs/guide.md ADDED
@@ -0,0 +1,356 @@
1
+ # 💳 Panduan Unified `@crediblemark/buayar`
2
+
3
+ Panduan ini adalah **satu-satunya** panduan yang Anda butuhkan untuk mengintegrasikan **semua** payment gateway yang didukung Buayar (19 provider: 10 Indonesia + 9 Internasional). Anda **tidak perlu** membaca dokumentasi masing-masing PG — kode yang Anda tulis **identik** untuk semua provider.
4
+
5
+ > 🎯 **Prinsip "mata tertutup":** Anda 100% tidak tahu (dan tidak perlu tahu) provider mana yang sedang aktif. Yang Anda tahu hanya: "Buaya mendukung PG A, PG B, PG C". Cukup ubah kredensial di `.env`, semuanya jalan.
6
+
7
+ ---
8
+
9
+ ## 📑 Daftar Isi
10
+
11
+ 1. [Filosofi Unified](#-filosofi-unified)
12
+ 2. [Inisialisasi & Konfigurasi](#-inisialisasi--konfigurasi)
13
+ 3. [Membuat Transaksi (Semi & Full)](#-membuat-transaksi)
14
+ 4. [Ambil Metode Pembayaran (Accordion-Ready)](#-ambil-metode-pembayaran)
15
+ 5. [Cek Status Transaksi](#-cek-status-transaksi)
16
+ 6. [Webhook Universal](#-webhook-universal)
17
+ 7. [Refund / Saldo / Payout Unified](#-refund--saldo--payout-unified)
18
+ 8. [Zero-Code PG Switch](#-zero-code-pg-switch)
19
+ 9. [Fitur Khusus Provider (`<X>Client`)](#-fitur-khusus-provider-xclient)
20
+ 10. [Kamus Variabel `.env` per Provider](#-kamus-variabel-env-per-provider)
21
+
22
+ ---
23
+
24
+ ## 🧠 Filosofi Unified
25
+
26
+ Seluruh provider mengimplementasikan **satu kontrak API** yang sama. Artinya:
27
+
28
+ | Kebutuhan Anda | API yang Anda gunakan |
29
+ | :--- | :--- |
30
+ | Checkout (redirect / direct) | `buayar.createInvoice()` |
31
+ | Daftar channel pembayaran | `buayar.getPaymentMethods()` |
32
+ | Cek status transaksi | `buayar.checkTransaction()` |
33
+ | Verifikasi callback | `buayar.verifyWebhook()` |
34
+ | Refund | `buayar.refund()` |
35
+ | Cek saldo merchant | `buayar.checkBalance()` |
36
+ | Transfer dana / payout | `buayar.disburse()` |
37
+ | Fitur benar-benar eksklusif per PG | `buayar.get<X>Client()` |
38
+
39
+ Semua metode pembayaran memakai **kode canonical universal** (`bca_va`, `qris`, `gopay`, `alfamart`, dst.) yang dipetakan otomatis ke format internal provider aktif. Anda tidak pernah menyentuh format internal siapa pun.
40
+
41
+ ---
42
+
43
+ ## ⚙️ Inisialisasi & Konfigurasi
44
+
45
+ ### 1. Zero-Config (baca dari `.env`) — **Direkomendasikan**
46
+
47
+ ```env
48
+ # Pilih provider aktif: midtrans, duitku, ipaymu, xendit, doku, prismalink,
49
+ # faspay, finpay, nicepay, oy, stripe, paypal, adyen, checkoutcom,
50
+ # razorpay, square, payu, braintree, twocheckout
51
+ PROVIDER_PG=midtrans
52
+
53
+ # Kredensial Universal (dipetakan otomatis per provider)
54
+ BUAYAR_API_KEY=your-server-key-atau-secret
55
+ BUAYAR_MERCHANT_CODE=merchant-id-atau-username
56
+ BUAYAR_SANDBOX=true
57
+
58
+ # Callback & Return URL
59
+ BUAYAR_CALLBACK_URL=https://myapp.com/api/payment/webhook
60
+ BUAYAR_RETURN_URL=https://myapp.com/payment/finish
61
+ ```
62
+
63
+ ```typescript
64
+ import { buayar } from "@crediblemark/buayar";
65
+ // Konfigurasi ter-baca otomatis dari process.env. Selesai.
66
+ ```
67
+
68
+ ### 2. Inisialisasi Manual (programatik)
69
+
70
+ ```typescript
71
+ import { Buayar } from "@crediblemark/buayar";
72
+
73
+ const buayar = new Buayar({
74
+ provider: "xendit",
75
+ apiKey: "xnd_development_xxxx",
76
+ merchantCode: "", // jika dibutuhkan (mis. Midtrans boleh kosong)
77
+ sandbox: true,
78
+ extra: { webhookSecret: "whsec_..." }, // untuk provider yang butuh secret tambahan
79
+ });
80
+ ```
81
+
82
+ > 💡 Banyak `get<X>Client()` juga bisa dipakai dengan meneruskan `provider` di `configOverride` agar menargetkan provider tertentu dari satu instance `buayar`.
83
+
84
+ ---
85
+
86
+ ## 🛠️ Membuat Transaksi
87
+
88
+ ### Semi Integrasi (Redirect / Hosted Checkout)
89
+
90
+ Tanpa `paymentMethod`, SDK mengembalikan `paymentUrl` untuk mengarahkan pelanggan ke halaman checkout PG.
91
+
92
+ ```typescript
93
+ import { buayar } from "@crediblemark/buayar";
94
+
95
+ const invoice = await buayar.createInvoice({
96
+ orderId: "ORDER-1001",
97
+ amount: 250000,
98
+ currency: "IDR", // wajib untuk PG internasional (USD, EUR, dll)
99
+ productDetails: "Pembelian Lisensi Software Premium",
100
+ customer: { name: "Budi", email: "budi@example.com", phone: "081234567890" },
101
+ returnUrl: "https://myapp.com/payment/success",
102
+ });
103
+
104
+ if (invoice.success) {
105
+ redirect(invoice.paymentUrl!); // redirect pelanggan ke sini
106
+ }
107
+ ```
108
+
109
+ ### Full Integrasi (Custom Native UI)
110
+
111
+ Sertakan `paymentMethod` dengan **kode canonical**. SDK mengembalikan data mentah (`vaNumber`, `qrString` EMVCo, `paymentCode`, `deeplink`) untuk dirender di UI Anda sendiri.
112
+
113
+ ```typescript
114
+ // Virtual Account
115
+ const va = await buayar.createInvoice({
116
+ orderId: "ORDER-1002",
117
+ amount: 150000,
118
+ paymentMethod: "bca_va", // canonical — berlaku di semua provider
119
+ productDetails: "Top Up Saldo",
120
+ customer: { name: "Budi", email: "budi@example.com" },
121
+ });
122
+ console.log("Nomor VA:", va.vaNumber); // "123456789012"
123
+ console.log("Bank:", va.vaBank); // "bca"
124
+
125
+ // QRIS
126
+ const qris = await buayar.createInvoice({
127
+ orderId: "ORDER-1003",
128
+ amount: 50000,
129
+ paymentMethod: "qris",
130
+ productDetails: "Kopi",
131
+ customer: { name: "Budi", email: "budi@example.com" },
132
+ });
133
+ console.log("Raw QRIS (EMVCo):", qris.qrString); // untuk dirender
134
+ console.log("QR Image URL:", qris.qrCodeUrl);
135
+ ```
136
+
137
+ ### Menargetkan Provider Tertentu (opsional)
138
+
139
+ Jika satu instance `buayar` dipakai untuk beberapa provider sekaligus, lewatkan `provider` pada `configOverride`:
140
+
141
+ ```typescript
142
+ const stripeInvoice = await buayar.createInvoice(
143
+ { orderId: "ORDER-2001", amount: 50000, paymentMethod: "credit_card" },
144
+ { provider: "stripe", apiKey: "sk_test_..." } // override provider & kredensial
145
+ );
146
+ ```
147
+
148
+ ---
149
+
150
+ ## 📋 Ambil Metode Pembayaran
151
+
152
+ Dapatkan daftar channel aktif yang sudah dikelompokkan per kategori (accordion-ready), lengkap dengan fee dan icon URL.
153
+
154
+ ```typescript
155
+ const { categories } = await buayar.getPaymentMethods({ amount: 150000 });
156
+ // categories: { "Virtual Account": [...], "QRIS": [...], "E-Wallet": [...], ... }
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 🔍 Cek Status Transaksi
162
+
163
+ ```typescript
164
+ const result = await buayar.checkTransaction({ merchantOrderId: "ORDER-1001" });
165
+
166
+ if (result.success) {
167
+ console.log("Status:", result.status); // "paid" | "pending" | "failed"
168
+ console.log("Pesan:", result.statusMessage);
169
+ }
170
+ ```
171
+
172
+ ---
173
+
174
+ ## 🪝 Webhook Universal
175
+
176
+ Satu endpoint untuk semua provider. Provider **terdeteksi otomatis** dari struktur payload — tidak ada routing manual.
177
+
178
+ ```typescript
179
+ import { buayar } from "@crediblemark/buayar";
180
+
181
+ // Bekerja dengan Express, Elysia, Hono, Next.js App Router, dll.
182
+ app.post("/api/payment/webhook", async (req, res) => {
183
+ const result = await buayar.verifyWebhook(req.body, req.headers);
184
+ // header signature otomatis diekstrak sesuai provider (Stripe-Signature,
185
+ // Cko-Signature, X-Razorpay-Signature, dll.)
186
+
187
+ if (!result.isValid) return res.status(400).json({ error: "Invalid signature" });
188
+
189
+ if (result.isPaid) {
190
+ console.log(`✅ Order ${result.orderId} senilai ${result.amount} LUNAS!`);
191
+ // Aktifkan langganan / kirim produk
192
+ }
193
+
194
+ return res.status(200).json({ status: "OK" });
195
+ });
196
+ ```
197
+
198
+ > `buayar.handleWebhook(payload, headers)` adalah alias dari `verifyWebhook`.
199
+
200
+ ---
201
+
202
+ ## 💸 Refund / Saldo / Payout Unified
203
+
204
+ Selain alur transaksi inti, Buayar menyediakan **operasi lanjutan unified** untuk hal-hal yang paling sering dipakai: **refund**, **cek saldo**, dan **payout** (disbursement).
205
+
206
+ Sama seperti operasi lainnya — kode Anda **tidak bergantung provider**. Setiap operasi otomatis diarahkan ke provider aktif.
207
+
208
+ > 🧊 **Provider tanpa fitur** mengembalikan `{ supported: false }` — bukan error. Anda bisa cek `result.supported` untuk menangani fallback.
209
+
210
+ ### 1. Refund
211
+
212
+ ```typescript
213
+ const refund = await buayar.refund({
214
+ transactionId: "pi_3MtwBwLkdIwHu7ix28a3tqPa", // ID transaksi/capture/payment
215
+ amount: 50000, // opsional — default full refund
216
+ reason: "Produk cacat", // opsional, bila didukung provider
217
+ currency: "IDR", // opsional, untuk provider internasional
218
+ });
219
+
220
+ if (refund.success) {
221
+ console.log("Refund diproses:", refund.reference);
222
+ } else if (!refund.supported) {
223
+ console.log("Provider aktif tidak mendukung refund!");
224
+ }
225
+ ```
226
+
227
+ Mendukung: **Midtrans, Stripe, PayPal, Adyen, Checkout.com, Razorpay, Square, PayU, Braintree, 2Checkout**.
228
+
229
+ ### 2. Cek Saldo Merchant
230
+
231
+ ```typescript
232
+ const balance = await buayar.checkBalance();
233
+
234
+ if (balance.success) {
235
+ console.log("Saldo:", balance.balance, balance.currency);
236
+ }
237
+ ```
238
+
239
+ Mendukung: **Midtrans, Duitku, iPaymu, Xendit, OY!, Stripe, PayPal, Checkout.com, Razorpay, Square**.
240
+
241
+ ### 3. Payout / Transfer Dana (Disbursement)
242
+
243
+ ```typescript
244
+ const payout = await buayar.disburse({
245
+ externalId: "DISB-001", // ID unik merchant untuk transfer ini
246
+ bankCode: "BCA", // kode bank tujuan (ikuti aturan provider aktif)
247
+ accountHolderName: "Budi Santoso", // wajib untuk beberapa provider
248
+ accountNumber: "1234567890",
249
+ amount: 500000,
250
+ description: "Penarikan saldo mitra",
251
+ });
252
+
253
+ if (payout.success) {
254
+ console.log("Transfer diproses:", payout.reference);
255
+ }
256
+ ```
257
+
258
+ Mendukung: **Duitku, Xendit, OY!**.
259
+
260
+ ---
261
+
262
+ ## 🔄 Zero-Code PG Switch
263
+
264
+ Beralih provider **tanpa mengubah satu baris pun** di controller/service Anda — cukup ubah kredensial di `.env`:
265
+
266
+ ```env
267
+ # Sebelum: Midtrans
268
+ PROVIDER_PG=midtrans
269
+ MIDTRANS_SERVER_KEY=SB-Mid-server-xxxx
270
+
271
+ # Sesudah: Stripe — kode aplikasi TIDAK berubah
272
+ PROVIDER_PG=stripe
273
+ STRIPE_SECRET_KEY=sk_test_51...
274
+ STRIPE_WEBHOOK_SECRET=whsec_...
275
+ ```
276
+
277
+ ---
278
+
279
+ ## 🏦 Fitur Khusus Provider (`<X>Client`)
280
+
281
+ Refund, saldo, dan payout sudah tersedia secara **unified** di atas. Namun untuk **fitur yang benar-benar eksklusif** tiap PG yang tidak masuk interface umum (subscription, payment link, tokenisasi GoPay, billing invoice, dsb.), pakai getter client per provider:
282
+
283
+ ```typescript
284
+ const xendit = buayar.getXenditClient();
285
+ const balance = await xendit.checkBalance("CASH");
286
+ ```
287
+
288
+ Rangkuman kemampuan ekstra tiap provider:
289
+
290
+ | Provider | Getter | Kemampuan ekstra |
291
+ | :--- | :--- | :--- |
292
+ | Midtrans | `getMidtransClient()` | cancel/refund/expire/approve/deny/capture, GoPay tokenization, Subscription, Payment Link, IRIS balance |
293
+ | Duitku | `getDuitkuClient()` | balance, listBanks, inquiryBankAccount, disburse, checkDisbursementStatus |
294
+ | iPaymu | `getIpaymuClient()` | balance, checkTransaction |
295
+ | Xendit | `getXenditClient()` | balance, expireInvoice, createDisbursement |
296
+ | DOKU | `getDokuClient()` | checkTransaction |
297
+ | PrismaLink | `getPrismalinkClient()` | checkTransaction |
298
+ | Faspay | `getFaspayClient()` | cancelTransaction, checkTransaction |
299
+ | Finpay | `getFinpayClient()` | checkTransaction |
300
+ | Nicepay | `getNicepayClient()` | cancelTransaction, checkTransaction |
301
+ | OY! Bisnis | `getOyClient()` | checkTransaction, balance, remit (transfer dana) |
302
+ | Stripe | `getStripeClient()` | balance, createRefund, retrieveCheckoutSession, retrievePaymentIntent |
303
+ | PayPal | `getPaypalClient()` | captureOrder, getOrder, refundCapture, checkBalance, verifyWebhookSignature |
304
+ | Adyen | `getAdyenClient()` | capturePayment, cancelPayment, refundPayment, getPaymentDetails, getAvailablePaymentMethods |
305
+ | Checkout.com | `getCheckoutComClient()` | balance, refundPayment, voidPayment, getPaymentDetails, listPaymentLinks |
306
+ | Razorpay | `getRazorpayClient()` | capturePayment, createRefund, checkBalance, fetchPayment, listPayments |
307
+ | Square | `getSquareClient()` | retrieveBalance, refundPayment, cancelPayment, getPayment, listLocations |
308
+ | PayU | `getPayuClient()` | cancelOrder, getOrder, refundOrder |
309
+ | Braintree | `getBraintreeClient()` | getClientToken, findTransaction, refundTransaction, voidTransaction |
310
+ | 2Checkout | `getTwoCheckoutClient()` | getOrder, listOrders, getSubscription, refundOrder |
311
+
312
+ ---
313
+
314
+ ## 📚 Kamus Variabel `.env` per Provider
315
+
316
+ Anda hanya perlu mengisi kredensial provider yang **sedang aktif**. Gunakan variabel universal (`BUAYAR_*`) bila ingin kode benar-benar provider-agnostic, atau variabel spesifik di bawah.
317
+
318
+ | Provider | Variabel `.env` |
319
+ | :--- | :--- |
320
+ | Midtrans | `MIDTRANS_SERVER_KEY`, `MIDTRANS_CLIENT_KEY` |
321
+ | Duitku | `DUITKU_API_KEY`, `DUITKU_MERCHANT_CODE` |
322
+ | iPaymu | `IPAYMU_API_KEY`, `IPAYMU_VA` |
323
+ | Xendit | `XENDIT_SECRET_KEY`, `XENDIT_WEBHOOK_TOKEN` |
324
+ | DOKU | `DOKU_CLIENT_ID`, `DOKU_SECRET_KEY` |
325
+ | PrismaLink | `PRISMALINK_MERCHANT_ID`, `PRISMALINK_SECRET_KEY` |
326
+ | Faspay | `FASPAY_MERCHANT_ID`, `FASPAY_USER_ID`, `FASPAY_PASSWORD`, `FASPAY_MERCHANT_NAME` |
327
+ | Finpay | `FINPAY_MERCHANT_ID`, `FINPAY_MERCHANT_KEY` |
328
+ | Nicepay | `NICEPAY_IMID`, `NICEPAY_KEY` |
329
+ | OY! Bisnis | `OY_USERNAME`, `OY_API_KEY` |
330
+ | Stripe | `STRIPE_SECRET_KEY`, `STRIPE_PUBLIC_KEY`, `STRIPE_WEBHOOK_SECRET` |
331
+ | PayPal | `PAYPAL_CLIENT_ID`, `PAYPAL_CLIENT_SECRET`, `PAYPAL_WEBHOOK_ID` |
332
+ | Adyen | `ADYEN_API_KEY`, `ADYEN_MERCHANT_ACCOUNT`, `ADYEN_CLIENT_KEY`, `ADYEN_HMAC_KEY`, `ADYEN_LIVE_URL_PREFIX` |
333
+ | Checkout.com | `CHECKOUTCOM_SECRET_KEY`, `CHECKOUTCOM_PUBLIC_KEY`, `CHECKOUTCOM_WEBHOOK_SECRET` |
334
+ | Razorpay | `RAZORPAY_KEY_ID`, `RAZORPAY_KEY_SECRET`, `RAZORPAY_WEBHOOK_SECRET` |
335
+ | Square | `SQUARE_ACCESS_TOKEN`, `SQUARE_APPLICATION_ID`, `SQUARE_LOCATION_ID`, `SQUARE_WEBHOOK_SIGNATURE_KEY` |
336
+ | PayU | `PAYU_POS_ID`, `PAYU_MD5_KEY`, `PAYU_OAUTH_CLIENT_ID`, `PAYU_OAUTH_CLIENT_SECRET` |
337
+ | Braintree | `BRAINTREE_MERCHANT_ID`, `BRAINTREE_PUBLIC_KEY`, `BRAINTREE_PRIVATE_KEY` |
338
+ | 2Checkout | `TWOCHECKOUT_MERCHANT_CODE`, `TWOCHECKOUT_SECRET_KEY`, `TWOCHECKOUT_SECRET_WORD` |
339
+
340
+ Sandbox: otomatis terdeteksi dari `BUAYAR_SANDBOX`/`NODE_ENV`, atau set per-provider (mis. `STRIPE_SANDBOX=true`, `MIDTRANS_SANDBOX=true`).
341
+
342
+ ---
343
+
344
+ ## 🏷️ Daftar Canonical Payment Methods
345
+
346
+ | Kategori | Canonical Code |
347
+ | :--- | :--- |
348
+ | **Virtual Account** | `bca_va`, `mandiri_va`, `bni_va`, `bri_va`, `permata_va`, `cimb_va`, `danamon_va`, `bsi_va`, `seabank_va` |
349
+ | **QRIS** | `qris`, `gopay_qris`, `shopeepay_qris`, `nobu_qris` |
350
+ | **E-Wallet** | `gopay`, `shopeepay`, `ovo`, `dana`, `linkaja`, `jenius` |
351
+ | **Retail** | `alfamart`, `indomaret`, `pos` |
352
+ | **Kartu Kredit** | `credit_card` |
353
+ | **Paylater** | `kredivo`, `akulaku`, `indodana` |
354
+ | **International** | `apple_pay`, `google_pay`, `paypal`, `klarna`, `sepa` |
355
+
356
+ > Tidak semua canonical code tersedia di semua provider. Selalu gunakan `getPaymentMethods()` untuk memfilter channel yang benar-benar aktif pada provider Anda.
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@crediblemark/buayar",
3
- "version": "0.3.2",
3
+ "version": "0.5.1",
4
4
  "description": "Unified Payment Gateway SDK for Node.js & TypeScript — 19 providers (Midtrans, Xendit, Duitku, Stripe, PayPal, Adyen, Razorpay, Square, Checkout.com, PayU, Braintree, 2Checkout, DOKU, iPaymu, PrismaLink, Faspay, Finpay, Nicepay, OY!) with zero-code switching via .env",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "buayar": "./dist/cli/index.js"
10
+ },
8
11
  "exports": {
9
12
  ".": {
10
13
  "types": "./dist/index.d.ts",
@@ -68,14 +71,19 @@
68
71
  },
69
72
  "license": "MIT",
70
73
  "scripts": {
71
- "build": "tsup src/index.ts --format cjs,esm --dts",
72
- "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
74
+ "build": "tsup src/index.ts --format cjs,esm --dts && tsup src/cli/index.ts --format cjs --platform node --out-dir dist/cli && node -e \"const fs=require('fs');const f='dist/cli/index.js';const s=fs.readFileSync(f,'utf8');if(!s.startsWith('#!'))fs.writeFileSync(f,'#!/usr/bin/env node\\n'+s);fs.chmodSync(f,0o755);\"",
75
+ "dev": "tsup src/index.ts src/cli/index.ts --format cjs,esm --dts --out-dir dist --watch",
76
+ "init": "node ./dist/cli/index.js init",
77
+ "postinstall": "echo \"🐊 Buayar terpasang! Jalankan \\\"npx buayar init\\\" untuk membuat boilerplate payment yang siap pakai.\"",
78
+ "test": "bun test"
73
79
  },
74
80
  "devDependencies": {
75
81
  "@types/bun": "^1.4.0",
76
82
  "@types/node": "^24.12.2",
77
83
  "tsup": "^8.5.1",
78
84
  "typescript": "^5.9.3"
85
+ },
86
+ "dependencies": {
87
+ "@clack/prompts": "^1.7.0"
79
88
  }
80
89
  }
81
-
package/docs/adyen.md DELETED
@@ -1,60 +0,0 @@
1
- # 💳 Integrasi Adyen (`adyen`)
2
-
3
- Panduan lengkap integrasi Payment Gateway **Adyen Checkout Sessions API v71** dengan `@crediblemark/buayar`.
4
-
5
- ---
6
-
7
- ## ⚙️ Konfigurasi Kredensial
8
-
9
- Kredensial Adyen dapat diperoleh melalui [ca-test.adyen.com](https://ca-test.adyen.com):
10
-
11
- ```env
12
- # Menggunakan .env (Direkomendasikan)
13
- PROVIDER_PG=adyen
14
- ADYEN_API_KEY=AQE...
15
- ADYEN_MERCHANT_ACCOUNT=YourMerchantAccount
16
- ADYEN_CLIENT_KEY=test_...
17
- ADYEN_HMAC_KEY=4A6...
18
- ADYEN_SANDBOX=true
19
- ```
20
-
21
- Atau inisialisasi manual:
22
-
23
- ```typescript
24
- import { Buayar } from "@crediblemark/buayar";
25
-
26
- const buayarAdyen = new Buayar({
27
- provider: "adyen",
28
- apiKey: "AQE...",
29
- merchantCode: "YourMerchantAccount",
30
- extra: {
31
- hmacKey: "4A6...",
32
- },
33
- sandbox: true,
34
- });
35
- ```
36
-
37
- ---
38
-
39
- ## 🛠️ Membuat Transaksi
40
-
41
- ```typescript
42
- import { buayar } from "@crediblemark/buayar";
43
-
44
- const invoice = await buayar.createInvoice({
45
- orderId: "ORDER-ADYEN-001",
46
- amount: 150000,
47
- currency: "IDR",
48
- productDetails: "Annual Cloud Hosting",
49
- customer: {
50
- name: "Alex",
51
- email: "alex@example.com",
52
- },
53
- returnUrl: "https://myapp.com/payment/return",
54
- });
55
-
56
- if (invoice.success) {
57
- console.log("Checkout URL:", invoice.paymentUrl);
58
- console.log("Session ID:", invoice.reference);
59
- }
60
- ```