@crediblemark/buayar 0.8.5 → 0.8.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.
@@ -0,0 +1,359 @@
1
+ # 💳 Panduan Integrasi SumoPod di Buayar
2
+
3
+ Dokumentasi lengkap mengenai integrasi payment gateway **SumoPod** pada SDK Buayar ([`@crediblemark/buayar`](https://github.com/crediblemark-official/Buayar)), mencakup pembuatan link pembayaran (Payment Links), kanal QRIS, pengecekan status, dan verifikasi webhook ganda (**Svix HMAC-SHA256** dan **X-Webhook-Token**).
4
+
5
+ ---
6
+
7
+ ## 📑 Daftar Isi
8
+
9
+ 1. [Fitur & Kemampuan](#-fitur--kemampuan)
10
+ 2. [Konfigurasi Lingkungan (.env)](#-konfigurasi-lingkungan-env)
11
+ 3. [Inisialisasi SDK](#-inisialisasi-sdk)
12
+ 4. [Membuat Transaksi Pembayaran](#-membuat-transaksi-pembayaran)
13
+ 5. [Mendapatkan Metode Pembayaran (QRIS)](#-mendapatkan-metode-pembayaran-qris)
14
+ 6. [Mengecek Status Transaksi](#-mengecek-status-transaksi)
15
+ 7. [Penanganan Webhook Universal](#-penanganan-webhook-universal)
16
+ 8. [Menggunakan Direct Client (`SumopodClient`)](#-menggunakan-direct-client-sumopodclient)
17
+ 9. [Contoh Implementasi Webhook (Express, Hono, Next.js)](#-contoh-implementasi-webhook)
18
+
19
+ ---
20
+
21
+ ## 🚀 Fitur & Kemampuan
22
+
23
+ - ⚡ **Host API**:
24
+ - **Live / Production**: `https://api-pay.sumopod.com`
25
+ - **Sandbox / Testing**: `https://api-pay-sandbox.sumopod.com`
26
+ - 📱 **Metode Pembayaran**: **QRIS** (Settlement 2 hari, Biaya Transaksi: `0.7% + Rp 300`)
27
+ - 🏦 **Virtual Account / Payment Code**: Otomatis mendeteksi kode bayar/VA (misal `BRI.VA`, `ACCOUNT_NUMBER`) pada response transaksi.
28
+ - 🔗 **Mode Pembayaran**: Hosted Payment Link / QRIS Checkout Page
29
+ - 🪝 **Dukungan Webhook Ganda**:
30
+ - **Svix Signature** (`svix-id`, `svix-timestamp`, `svix-signature` HMAC-SHA256) dengan proteksi replay attack (5 menit) dan toleransi rotasi secret.
31
+ - **X-Webhook-Token** (`x-webhook-token`) dengan perbandingan konstan waktu (*timing-safe*).
32
+ - 🔄 **Zero-Code PG Switcher**: Ganti provider ke/dari SumoPod cukup dengan mengubah variabel `.env`.
33
+
34
+ ---
35
+
36
+ ## ⚙️ Konfigurasi Lingkungan (.env)
37
+
38
+ ### 1. Variabel Universal (`BUAYAR_*`) — **Direkomendasikan**
39
+
40
+ ```env
41
+ # Aktifkan SumoPod sebagai provider
42
+ BUAYAR_PROVIDER=sumopod
43
+
44
+ # Mode Sandbox:
45
+ # true -> Mengarah ke https://api-pay-sandbox.sumopod.com
46
+ # false -> Mengarah ke https://api-pay.sumopod.com (Production)
47
+ BUAYAR_SANDBOX=true
48
+
49
+ # API Key SumoPod dari dashboard Anda
50
+ BUAYAR_API_KEY=your_sumopod_api_key_here
51
+
52
+ # Signing secret webhook Svix (dimulai dengan whsec_...)
53
+ BUAYAR_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
54
+
55
+ # ATAU Token webhook (dimulai dengan whtok_...)
56
+ BUAYAR_WEBHOOK_TOKEN=whtok_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
57
+
58
+ # URL callback dan redirect
59
+ BUAYAR_CALLBACK_URL=https://domain-anda.com/api/payment/webhook
60
+ BUAYAR_RETURN_URL=https://domain-anda.com/payment/success
61
+ ```
62
+
63
+ ### 2. Variabel Spesifik SumoPod (Opsional)
64
+
65
+ Jika Anda ingin menggunakan format env khusus SumoPod:
66
+
67
+ ```env
68
+ SUMOPOD_API_KEY=your_sumopod_api_key_here
69
+ SUMOPOD_SANDBOX=true
70
+ SUMOPOD_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
71
+ SUMOPOD_WEBHOOK_TOKEN=whtok_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
72
+ ```
73
+
74
+ ---
75
+
76
+ ## 🚀 Inisialisasi SDK
77
+
78
+ ### Menggunakan Singleton Global (Membaca `.env` Otomatis)
79
+
80
+ ```typescript
81
+ import { buayar } from "@crediblemark/buayar";
82
+
83
+ // Siap langsung digunakan
84
+ ```
85
+
86
+ ### Inisialisasi Instance Manual
87
+
88
+ ```typescript
89
+ import { Buayar } from "@crediblemark/buayar";
90
+
91
+ const buayar = new Buayar({
92
+ provider: "sumopod",
93
+ apiKey: "your_sumopod_api_key_here",
94
+ extra: {
95
+ webhookSecret: "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
96
+ webhookToken: "whtok_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
97
+ },
98
+ });
99
+ ```
100
+
101
+ ---
102
+
103
+ ## 🛒 Membuat Transaksi Pembayaran
104
+
105
+ Panggil `createInvoice()` untuk membuat payment link SumoPod:
106
+
107
+ ```typescript
108
+ const invoice = await buayar.createInvoice({
109
+ orderId: "INV-2026-001",
110
+ amount: 50000,
111
+ productDetails: "Langganan Premium 1 Bulan",
112
+ customer: {
113
+ name: "Budi Pratama",
114
+ email: "budi@example.com",
115
+ phone: "081234567890",
116
+ },
117
+ // Opsional: override URL per transaksi
118
+ returnUrl: "https://domain-anda.com/payment/success",
119
+ // Opsional: kustomisasi durasi kedaluwarsa (jam, 1-24)
120
+ providerParams: {
121
+ expires_in_hours: 24,
122
+ cancel_return_url: "https://domain-anda.com/payment/cancel",
123
+ },
124
+ });
125
+
126
+ if (invoice.success) {
127
+ console.log("Payment URL:", invoice.paymentUrl);
128
+ // https://pay.sumopod.com/pay/uuid-xxxx
129
+ console.log("Payment ID:", invoice.reference);
130
+ console.log("Kode Bayar / VA:", invoice.paymentCode || invoice.vaNumber);
131
+ console.log("Bank VA:", invoice.vaBank); // misal: "bri" jika mode VA
132
+ console.log("Mode:", invoice.mode); // "va" | "qris" | "checkout"
133
+ console.log("Kedaluwarsa pada:", invoice.expiresAt);
134
+
135
+ // Redirect pelanggan ke invoice.paymentUrl atau tampilkan nomor VA langsung
136
+ } else {
137
+ console.error("Gagal membuat invoice:", invoice.error);
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 📱 Mendapatkan Metode Pembayaran (QRIS)
144
+
145
+ SumoPod menyediakan kanal pembayaran QRIS nasional:
146
+
147
+ ```typescript
148
+ const methods = await buayar.getPaymentMethods({ amount: 50000 });
149
+
150
+ if (methods.success) {
151
+ console.log(methods.methods);
152
+ /* Output:
153
+ [
154
+ {
155
+ paymentMethod: "qris",
156
+ code: "QRIS",
157
+ paymentName: "QRIS",
158
+ paymentImage: "https://upload.wikimedia.org/wikipedia/commons/a/a2/Logo_QRIS.svg",
159
+ totalFee: "0.7% + Rp 300",
160
+ category: "QRIS",
161
+ feeDetail: {
162
+ percent: 0.7,
163
+ flat: 300,
164
+ totalFee: 650
165
+ }
166
+ }
167
+ ]
168
+ */
169
+ }
170
+ ```
171
+
172
+ ---
173
+
174
+ ## 🔍 Mengecek Status Transaksi
175
+
176
+ Untuk mengecek status transaksi yang sudah dibuat:
177
+
178
+ ```typescript
179
+ const status = await buayar.checkTransaction({
180
+ merchantOrderId: "INV-2026-001", // atau payment_id
181
+ });
182
+
183
+ if (status.success) {
184
+ console.log("Status:", status.status); // "paid" | "pending" | "failed" | "expired"
185
+ console.log("Apakah sudah terbayar?", status.isPaid);
186
+ console.log("Nominal:", status.amount);
187
+ }
188
+ ```
189
+
190
+ ---
191
+
192
+ ## 🪝 Penanganan Webhook Universal
193
+
194
+ SumoPod mengirimkan HTTP `POST` webhook untuk setiap perubahan status pembayaran.
195
+
196
+ ### Struktur Payload Masuk dari SumoPod
197
+
198
+ ```json
199
+ {
200
+ "event_type": "payment.completed",
201
+ "data": {
202
+ "payment_id": "uuid-xxxx",
203
+ "order_id": "INV-2026-001",
204
+ "amount": 50000,
205
+ "fee": 750,
206
+ "net_amount": 49250,
207
+ "status": "completed",
208
+ "payment_method": "qris",
209
+ "completed_at": "2026-06-18T12:00:00Z"
210
+ }
211
+ }
212
+ ```
213
+
214
+ ### Event yang Didukung:
215
+ | Event | Status Buayar | Deskripsi |
216
+ |---|---|---|
217
+ | `payment.completed` | `paid` (`isPaid: true`) | Pembayaran berhasil |
218
+ | `payment.failed` | `failed` (`isFailed: true`) | Pembayaran gagal |
219
+ | `payment.expired` | `expired` (`isExpired: true`) | Tagihan kedaluwarsa |
220
+ | `payment.test` | `pending` (`isPending: true`) | Event pengujian dari Dashboard |
221
+
222
+ ---
223
+
224
+ ## 🌐 Contoh Implementasi Webhook
225
+
226
+ ### 1. Express.js
227
+
228
+ > **PENTING untuk Verifikasi Svix:**
229
+ > Header Svix membutuhkan string raw body asli tanpa modifikasi spasi. Gunakan `express.raw({ type: "application/json" })` atau `express.json({ verify: ... })`.
230
+
231
+ ```typescript
232
+ import express from "express";
233
+ import { buayar } from "@crediblemark/buayar";
234
+
235
+ const app = express();
236
+
237
+ app.post(
238
+ "/api/payment/webhook",
239
+ express.raw({ type: "application/json" }),
240
+ async (req, res) => {
241
+ const rawBody = req.body.toString("utf8");
242
+ const payload = JSON.parse(rawBody);
243
+
244
+ // Kirim payload dan headers (svix-id, svix-timestamp, svix-signature, atau x-webhook-token)
245
+ const result = await buayar.verifyWebhook(payload, req.headers, {
246
+ extra: { rawBody },
247
+ });
248
+
249
+ if (!result.isValid) {
250
+ return res.status(401).send("Invalid signature or token");
251
+ }
252
+
253
+ if (result.isPaid) {
254
+ console.log(`Order ${result.orderId} senilai Rp ${result.amount} sukses terbayar!`);
255
+ // Update status pesanan di database Anda
256
+ } else if (result.isExpired) {
257
+ console.log(`Order ${result.orderId} kedaluwarsa.`);
258
+ }
259
+
260
+ return res.status(200).send("OK");
261
+ }
262
+ );
263
+ ```
264
+
265
+ ### 2. Hono
266
+
267
+ ```typescript
268
+ import { Hono } from "hono";
269
+ import { buayar } from "@crediblemark/buayar";
270
+
271
+ const app = new Hono();
272
+
273
+ app.post("/api/payment/webhook", async (c) => {
274
+ const rawBody = await c.req.text();
275
+ const payload = JSON.parse(rawBody);
276
+ const headers = c.req.header();
277
+
278
+ const result = await buayar.verifyWebhook(payload, headers, {
279
+ extra: { rawBody },
280
+ });
281
+
282
+ if (!result.isValid) {
283
+ return c.text("Invalid webhook signature", 401);
284
+ }
285
+
286
+ if (result.isPaid) {
287
+ // Proses pembayaran sukses
288
+ }
289
+
290
+ return c.text("OK", 200);
291
+ });
292
+ ```
293
+
294
+ ### 3. Next.js App Router (`src/app/api/payment/webhook/route.ts`)
295
+
296
+ ```typescript
297
+ import { NextResponse } from "next/server";
298
+ import { buayar } from "@crediblemark/buayar";
299
+
300
+ export async function POST(request: Request) {
301
+ const rawBody = await request.text();
302
+ const payload = JSON.parse(rawBody);
303
+
304
+ const headers: Record<string, string> = {};
305
+ request.headers.forEach((val, key) => {
306
+ headers[key] = val;
307
+ });
308
+
309
+ const result = await buayar.verifyWebhook(payload, headers, {
310
+ extra: { rawBody },
311
+ });
312
+
313
+ if (!result.isValid) {
314
+ return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
315
+ }
316
+
317
+ if (result.isPaid) {
318
+ // Update order status di database
319
+ }
320
+
321
+ return NextResponse.json({ status: "OK" });
322
+ }
323
+ ```
324
+
325
+ ---
326
+
327
+ ## 🛠️ Menggunakan Direct Client (`SumopodClient`)
328
+
329
+ Jika Anda memerlukan akses langsung tanpa melalui abstraksi universal:
330
+
331
+ ```typescript
332
+ import { SumopodClient } from "@crediblemark/buayar";
333
+
334
+ const client = new SumopodClient({
335
+ apiKey: process.env.SUMOPOD_API_KEY,
336
+ });
337
+
338
+ // 1. Buat transaksi
339
+ const payment = await client.createPayment({
340
+ order_id: "ORD-999",
341
+ amount: 25000,
342
+ currency: "IDR",
343
+ expires_in_hours: 12,
344
+ payment_method_type_code: "QRIS",
345
+ });
346
+ console.log("Payment URL:", payment.payment_link_url);
347
+
348
+ // 2. Ambil detail pembayaran
349
+ const detail = await client.getPayment(payment.payment_id);
350
+ console.log("Status terkini:", detail.status);
351
+ ```
352
+
353
+ ---
354
+
355
+ ## 🔒 Catatan Keamanan
356
+
357
+ 1. **Replay Attack Protection**: Verifikasi Svix otomatis menolak webhook jika timestamp berbeda lebih dari 5 menit (300 detik) dari waktu server.
358
+ 2. **Timing-Safe Comparison**: Perbandingan signature HMAC dan `x-webhook-token` dilakukan menggunakan `safeCompare` berbasis `crypto.timingSafeEqual` untuk mencegah kerentanan timing attack.
359
+ 3. **Secret Rotation**: Saat Anda melakukan rotasi webhook secret di dashboard SumoPod, header `svix-signature` dapat memuat beberapa signature sekaligus. SDK Buayar secara otomatis memverifikasi seluruh kandidat signature.
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@crediblemark/buayar",
3
- "version": "0.8.5",
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",
3
+ "version": "0.8.7",
4
+ "description": "Unified Payment Gateway SDK for Node.js & TypeScript — 20 providers (Midtrans, Xendit, Duitku, Stripe, PayPal, Adyen, Razorpay, Square, Checkout.com, PayU, Braintree, 2Checkout, DOKU, iPaymu, PrismaLink, Faspay, Finpay, Nicepay, OY!, SumoPod) 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
8
  "bin": {
9
- "buayar": "./dist/cli/index.js"
9
+ "buayar": "dist/cli/index.js"
10
10
  },
11
11
  "exports": {
12
12
  ".": {
@@ -40,6 +40,7 @@
40
40
  "finpay",
41
41
  "nicepay",
42
42
  "oy-indonesia",
43
+ "sumopod",
43
44
  "virtual-account",
44
45
  "qris",
45
46
  "ewallet",
@@ -64,7 +65,7 @@
64
65
  },
65
66
  "repository": {
66
67
  "type": "git",
67
- "url": "https://github.com/crediblemark-official/Buayar.git"
68
+ "url": "git+https://github.com/crediblemark-official/Buayar.git"
68
69
  },
69
70
  "publishConfig": {
70
71
  "access": "public"