@crediblemark/buayar 0.4.0 → 0.6.0
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/cli/index.js +643 -0
- package/dist/index.d.mts +131 -1
- package/dist/index.d.ts +131 -1
- package/dist/index.js +514 -30
- package/dist/index.mjs +489 -13
- package/package.json +12 -4
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli/index.ts
|
|
27
|
+
var path2 = __toESM(require("path"));
|
|
28
|
+
var fs2 = __toESM(require("fs"));
|
|
29
|
+
var p2 = __toESM(require("@clack/prompts"));
|
|
30
|
+
|
|
31
|
+
// src/cli/templates.ts
|
|
32
|
+
var DOT_ENV_TEMPLATE = `# \u2500\u2500 @crediblemark/buayar \xB7 Konfigurasi \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
33
|
+
# Provider aktif (salah satu dari 19): midtrans, duitku, ipaymu, xendit, doku,
|
|
34
|
+
# prismalink, faspay, finpay, nicepay, oy, stripe, paypal, adyen, checkoutcom,
|
|
35
|
+
# razorpay, square, payu, braintree, twocheckout
|
|
36
|
+
PROVIDER_PG=midtrans
|
|
37
|
+
|
|
38
|
+
# Kredensial Universal (dipetakan otomatis per provider)
|
|
39
|
+
BUAYAR_API_KEY=your-server-key-atau-secret
|
|
40
|
+
BUAYAR_MERCHANT_CODE=merchant-id-atau-username
|
|
41
|
+
BUAYAR_SANDBOX=true
|
|
42
|
+
|
|
43
|
+
# Callback & Return URL
|
|
44
|
+
BUAYAR_CALLBACK_URL=http://localhost:3000/api/payment/webhook
|
|
45
|
+
BUAYAR_RETURN_URL=http://localhost:3000/payment/success
|
|
46
|
+
|
|
47
|
+
# \u2500\u2500 Kredensial Spesifik Provider (isi sesuai yang aktif saja) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
48
|
+
# MIDTRANS_SERVER_KEY=
|
|
49
|
+
# MIDTRANS_CLIENT_KEY=
|
|
50
|
+
# DUITKU_API_KEY=
|
|
51
|
+
# DUITKU_MERCHANT_CODE=
|
|
52
|
+
# IPAYMU_API_KEY=
|
|
53
|
+
# IPAYMU_VA=
|
|
54
|
+
# XENDIT_SECRET_KEY=
|
|
55
|
+
# XENDIT_WEBHOOK_TOKEN=
|
|
56
|
+
# DOKU_CLIENT_ID=
|
|
57
|
+
# DOKU_SECRET_KEY=
|
|
58
|
+
# PRISMALINK_MERCHANT_ID=
|
|
59
|
+
# PRISMALINK_SECRET_KEY=
|
|
60
|
+
# FASPAY_MERCHANT_ID=
|
|
61
|
+
# FASPAY_USER_ID=
|
|
62
|
+
# FASPAY_PASSWORD=
|
|
63
|
+
# FINPAY_MERCHANT_ID=
|
|
64
|
+
# FINPAY_MERCHANT_KEY=
|
|
65
|
+
# NICEPAY_IMID=
|
|
66
|
+
# NICEPAY_KEY=
|
|
67
|
+
# OY_USERNAME=
|
|
68
|
+
# OY_API_KEY=
|
|
69
|
+
# STRIPE_SECRET_KEY=
|
|
70
|
+
# STRIPE_PUBLIC_KEY=
|
|
71
|
+
# STRIPE_WEBHOOK_SECRET=
|
|
72
|
+
# PAYPAL_CLIENT_ID=
|
|
73
|
+
# PAYPAL_CLIENT_SECRET=
|
|
74
|
+
# PAYPAL_WEBHOOK_ID=
|
|
75
|
+
# ADYEN_API_KEY=
|
|
76
|
+
# ADYEN_MERCHANT_ACCOUNT=
|
|
77
|
+
# ADYEN_HMAC_KEY=
|
|
78
|
+
# CHECKOUTCOM_SECRET_KEY=
|
|
79
|
+
# CHECKOUTCOM_WEBHOOK_SECRET=
|
|
80
|
+
# RAZORPAY_KEY_ID=
|
|
81
|
+
# RAZORPAY_KEY_SECRET=
|
|
82
|
+
# RAZORPAY_WEBHOOK_SECRET=
|
|
83
|
+
# SQUARE_ACCESS_TOKEN=
|
|
84
|
+
# SQUARE_LOCATION_ID=
|
|
85
|
+
# PAYU_POS_ID=
|
|
86
|
+
# PAYU_MD5_KEY=
|
|
87
|
+
# BRAINTREE_MERCHANT_ID=
|
|
88
|
+
# BRAINTREE_PUBLIC_KEY=
|
|
89
|
+
# BRAINTREE_PRIVATE_KEY=
|
|
90
|
+
# TWOCHECKOUT_MERCHANT_CODE=
|
|
91
|
+
# TWOCHECKOUT_SECRET_KEY=
|
|
92
|
+
# TWOCHECKOUT_SECRET_WORD=
|
|
93
|
+
`;
|
|
94
|
+
var CONFIG_TEMPLATE = `// src/payment/buayar.ts
|
|
95
|
+
// Inisialisasi Buayar \u2014 baca otomatis dari process.env (.env)
|
|
96
|
+
import { buayar } from "@crediblemark/buayar";
|
|
97
|
+
|
|
98
|
+
export { buayar };
|
|
99
|
+
|
|
100
|
+
// Bila perlu inisialisasi programatik (menggantikan .env):
|
|
101
|
+
// export const buayar = new Buayar({
|
|
102
|
+
// provider: "midtrans",
|
|
103
|
+
// apiKey: "SB-Mid-server-xxxx",
|
|
104
|
+
// sandbox: true,
|
|
105
|
+
// });
|
|
106
|
+
`;
|
|
107
|
+
var SERVICE_TEMPLATE = `// src/payment/service.ts
|
|
108
|
+
// Service layer \u2014 satu-satunya tempat aplikasi berinteraksi dengan Buayar.
|
|
109
|
+
// Ganti provider cukup dengan ubah .env; kode ini tidak berubah.
|
|
110
|
+
import { buayar } from "./buayar";
|
|
111
|
+
|
|
112
|
+
export interface PaymentService {
|
|
113
|
+
createCheckout(input: {
|
|
114
|
+
orderId: string;
|
|
115
|
+
amount: number;
|
|
116
|
+
productDetails: string;
|
|
117
|
+
customer: { name: string; email: string; phone?: string };
|
|
118
|
+
paymentMethod?: string;
|
|
119
|
+
currency?: string;
|
|
120
|
+
}): Promise<any>;
|
|
121
|
+
getMethods(amount?: number): Promise<any>;
|
|
122
|
+
checkStatus(orderId: string): Promise<any>;
|
|
123
|
+
handleWebhook(payload: any, headers: Record<string, string | string[] | undefined>): Promise<any>;
|
|
124
|
+
refund(transactionId: string, amount?: number, reason?: string): Promise<any>;
|
|
125
|
+
checkBalance(): Promise<any>;
|
|
126
|
+
disburse(payload: {
|
|
127
|
+
externalId: string;
|
|
128
|
+
bankCode: string;
|
|
129
|
+
accountNumber: string;
|
|
130
|
+
amount: number;
|
|
131
|
+
accountHolderName?: string;
|
|
132
|
+
description?: string;
|
|
133
|
+
}): Promise<any>;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const paymentService: PaymentService = {
|
|
137
|
+
async createCheckout({ orderId, amount, productDetails, customer, paymentMethod, currency }) {
|
|
138
|
+
return buayar.createInvoice({
|
|
139
|
+
orderId,
|
|
140
|
+
amount,
|
|
141
|
+
productDetails,
|
|
142
|
+
customer,
|
|
143
|
+
paymentMethod,
|
|
144
|
+
currency,
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
async getMethods(amount = 10000) {
|
|
149
|
+
return buayar.getPaymentMethods({ amount });
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
async checkStatus(orderId) {
|
|
153
|
+
return buayar.checkTransaction({ merchantOrderId: orderId });
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
async handleWebhook(payload, headers) {
|
|
157
|
+
return buayar.verifyWebhook(payload, headers);
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
async refund(transactionId, amount, reason) {
|
|
161
|
+
return buayar.refund({ transactionId, amount, reason });
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
async checkBalance() {
|
|
165
|
+
return buayar.checkBalance();
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
async disburse({ externalId, bankCode, accountNumber, amount, accountHolderName, description }) {
|
|
169
|
+
return buayar.disburse({
|
|
170
|
+
externalId,
|
|
171
|
+
bankCode,
|
|
172
|
+
accountNumber,
|
|
173
|
+
amount,
|
|
174
|
+
accountHolderName,
|
|
175
|
+
description,
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
`;
|
|
180
|
+
var ROUTES_EXPRESS_TEMPLATE = `// src/payment/routes/express.ts
|
|
181
|
+
// Controller + route untuk Express. Mount via:
|
|
182
|
+
// import { paymentRoutes } from "./payment/routes/express";
|
|
183
|
+
// app.use("/api/payment", paymentRoutes);
|
|
184
|
+
import { Router } from "express";
|
|
185
|
+
import { paymentService } from "../service";
|
|
186
|
+
|
|
187
|
+
export const paymentRoutes = Router();
|
|
188
|
+
|
|
189
|
+
// POST /api/payment/checkout \u2014 buat transaksi (redirect / direct)
|
|
190
|
+
paymentRoutes.post("/checkout", async (req, res) => {
|
|
191
|
+
const result = await paymentService.createCheckout(req.body);
|
|
192
|
+
res.status(result.success ? 200 : 400).json(result);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// GET /api/payment/methods \u2014 daftar channel pembayaran
|
|
196
|
+
paymentRoutes.get("/methods", async (req, res) => {
|
|
197
|
+
const amount = Number(req.query.amount) || 10000;
|
|
198
|
+
const result = await paymentService.getMethods(amount);
|
|
199
|
+
res.json(result);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// GET /api/payment/status/:orderId \u2014 cek status transaksi
|
|
203
|
+
paymentRoutes.get("/status/:orderId", async (req, res) => {
|
|
204
|
+
const result = await paymentService.checkStatus(req.params.orderId);
|
|
205
|
+
res.json(result);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// POST /api/payment/webhook \u2014 notifikasi dari payment gateway
|
|
209
|
+
paymentRoutes.post("/webhook", async (req, res) => {
|
|
210
|
+
const verification = await paymentService.handleWebhook(req.body, req.headers as any);
|
|
211
|
+
return verification.isValid
|
|
212
|
+
? res.status(200).json({ status: "OK" })
|
|
213
|
+
: res.status(400).json({ error: "Invalid signature" });
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// POST /api/payment/refund \u2014 refund transaksi
|
|
217
|
+
paymentRoutes.post("/refund", async (req, res) => {
|
|
218
|
+
const { transactionId, amount, reason } = req.body;
|
|
219
|
+
const result = await paymentService.refund(transactionId, amount, reason);
|
|
220
|
+
res.json(result);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// GET /api/payment/balance \u2014 cek saldo merchant
|
|
224
|
+
paymentRoutes.get("/balance", async (_req, res) => {
|
|
225
|
+
res.json(await paymentService.checkBalance());
|
|
226
|
+
});
|
|
227
|
+
`;
|
|
228
|
+
var ROUTES_HONO_TEMPLATE = `// src/payment/routes/hono.ts
|
|
229
|
+
// Controller + route untuk Hono. Mount via:
|
|
230
|
+
// app.route("/api/payment", paymentRoutes);
|
|
231
|
+
import { Hono } from "hono";
|
|
232
|
+
import { paymentService } from "../service";
|
|
233
|
+
|
|
234
|
+
export const paymentRoutes = new Hono();
|
|
235
|
+
|
|
236
|
+
paymentRoutes.post("/checkout", async (c) => {
|
|
237
|
+
const body = await c.req.json();
|
|
238
|
+
const result = await paymentService.createCheckout(body);
|
|
239
|
+
return c.json(result, result.success ? 200 : 400);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
paymentRoutes.get("/methods", async (c) => {
|
|
243
|
+
const amount = Number(c.req.query("amount")) || 10000;
|
|
244
|
+
return c.json(await paymentService.getMethods(amount));
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
paymentRoutes.get("/status/:orderId", async (c) => {
|
|
248
|
+
return c.json(await paymentService.checkStatus(c.req.param("orderId")));
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
paymentRoutes.post("/webhook", async (c) => {
|
|
252
|
+
const payload = await c.req.json();
|
|
253
|
+
const headers = c.req.raw.headers;
|
|
254
|
+
const verification = await paymentService.handleWebhook(payload, Object.fromEntries(headers) as any);
|
|
255
|
+
return verification.isValid
|
|
256
|
+
? c.json({ status: "OK" }, 200)
|
|
257
|
+
: c.json({ error: "Invalid signature" }, 400);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
paymentRoutes.post("/refund", async (c) => {
|
|
261
|
+
const { transactionId, amount, reason } = await c.req.json();
|
|
262
|
+
return c.json(await paymentService.refund(transactionId, amount, reason));
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
paymentRoutes.get("/balance", async (c) => {
|
|
266
|
+
return c.json(await paymentService.checkBalance());
|
|
267
|
+
});
|
|
268
|
+
`;
|
|
269
|
+
var ROUTES_NEXTJS_TEMPLATE = `// src/app/api/payment/webhook/route.ts
|
|
270
|
+
// Route handler untuk Next.js App Router.
|
|
271
|
+
// Jalankan: npm run build && npm run start
|
|
272
|
+
import { NextResponse } from "next/server";
|
|
273
|
+
import { paymentService } from "@/payment/service";
|
|
274
|
+
|
|
275
|
+
export async function POST(request: Request) {
|
|
276
|
+
const payload = await request.json();
|
|
277
|
+
const headers: Record<string, string | string[] | undefined> = {};
|
|
278
|
+
request.headers.forEach((value, key) => {
|
|
279
|
+
headers[key] = value;
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const verification = await paymentService.handleWebhook(payload, headers);
|
|
283
|
+
|
|
284
|
+
return verification.isValid
|
|
285
|
+
? NextResponse.json({ status: "OK" })
|
|
286
|
+
: NextResponse.json({ error: "Invalid signature" }, { status: 400 });
|
|
287
|
+
}
|
|
288
|
+
`;
|
|
289
|
+
var README_PAYMENT_TEMPLATE = (framework, provider) => `# \u{1F4B3} Payment \u2014 @crediblemark/buayar
|
|
290
|
+
|
|
291
|
+
Scaffold dibuat otomatis oleh \`buayar init\`.
|
|
292
|
+
|
|
293
|
+
- **Provider aktif:** \`${provider}\` (ganti di \`.env\` \u2192 \`PROVIDER_PG\`)
|
|
294
|
+
- **Framework route:** \`${framework}\`
|
|
295
|
+
|
|
296
|
+
## Struktur
|
|
297
|
+
\`\`\`
|
|
298
|
+
src/payment/
|
|
299
|
+
\u251C\u2500\u2500 buayar.ts # Inisialisasi Buayar (baca .env)
|
|
300
|
+
\u251C\u2500\u2500 service.ts # Service layer wrapper \u2014 semua interaksi lewat sini
|
|
301
|
+
\u2514\u2500\u2500 routes/ # Controller/route per framework
|
|
302
|
+
\`\`\`
|
|
303
|
+
|
|
304
|
+
## Alur
|
|
305
|
+
1. Salin \`.env.example\` ke \`.env\` dan isi kredensial.
|
|
306
|
+
2. Mount route (lihat template framework di folder \`routes/\`).
|
|
307
|
+
3. Frontend: panggil \`POST /api/payment/checkout\` \u2192 terima \`paymentUrl\` (redirect) atau \`vaNumber\`/\`qrString\` (direct).
|
|
308
|
+
4. Notifikasi masuk ke \`POST /api/payment/webhook\` \u2192 diverifikasi otomatis.
|
|
309
|
+
|
|
310
|
+
## Ganti Provider
|
|
311
|
+
Ubah \`PROVIDER_PG\` + kredensial di \`.env\`. Kode \`service.ts\`/route **tidak berubah**.
|
|
312
|
+
|
|
313
|
+
> Dokumentasi lengkap: https://github.com/crediblemark-official/Buayar#readme
|
|
314
|
+
`;
|
|
315
|
+
var TYPE_DECL_TEMPLATE = `// src/payment/types.ts
|
|
316
|
+
// Tipe-tipe hasil dari Buayar (dapat disesuaikan)
|
|
317
|
+
export interface CheckoutResult {
|
|
318
|
+
success: boolean;
|
|
319
|
+
provider?: string;
|
|
320
|
+
orderId?: string;
|
|
321
|
+
paymentUrl?: string;
|
|
322
|
+
reference?: string;
|
|
323
|
+
vaNumber?: string;
|
|
324
|
+
qrString?: string;
|
|
325
|
+
paymentCode?: string;
|
|
326
|
+
deeplink?: string;
|
|
327
|
+
error?: string;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export interface WebhookVerification {
|
|
331
|
+
isValid: boolean;
|
|
332
|
+
provider?: string;
|
|
333
|
+
orderId: string;
|
|
334
|
+
amount: number;
|
|
335
|
+
isPaid: boolean;
|
|
336
|
+
status: "paid" | "pending" | "failed" | "expired";
|
|
337
|
+
}
|
|
338
|
+
`;
|
|
339
|
+
var PROVIDERS = [
|
|
340
|
+
"midtrans",
|
|
341
|
+
"duitku",
|
|
342
|
+
"ipaymu",
|
|
343
|
+
"xendit",
|
|
344
|
+
"doku",
|
|
345
|
+
"prismalink",
|
|
346
|
+
"faspay",
|
|
347
|
+
"finpay",
|
|
348
|
+
"nicepay",
|
|
349
|
+
"oy",
|
|
350
|
+
"stripe",
|
|
351
|
+
"paypal",
|
|
352
|
+
"adyen",
|
|
353
|
+
"checkoutcom",
|
|
354
|
+
"razorpay",
|
|
355
|
+
"square",
|
|
356
|
+
"payu",
|
|
357
|
+
"braintree",
|
|
358
|
+
"twocheckout"
|
|
359
|
+
];
|
|
360
|
+
var FRAMEWORKS = ["express", "hono", "nextjs"];
|
|
361
|
+
function getRouteTemplate(framework) {
|
|
362
|
+
switch (framework) {
|
|
363
|
+
case "hono":
|
|
364
|
+
return ROUTES_HONO_TEMPLATE;
|
|
365
|
+
case "nextjs":
|
|
366
|
+
return ROUTES_NEXTJS_TEMPLATE;
|
|
367
|
+
case "express":
|
|
368
|
+
default:
|
|
369
|
+
return ROUTES_EXPRESS_TEMPLATE;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function buildScaffold(provider, framework) {
|
|
373
|
+
const routeRel = framework === "nextjs" ? "src/app/api/payment/webhook/route.ts" : "src/payment/routes/index.ts";
|
|
374
|
+
return {
|
|
375
|
+
".env.example": DOT_ENV_TEMPLATE,
|
|
376
|
+
"src/payment/buayar.ts": CONFIG_TEMPLATE,
|
|
377
|
+
"src/payment/service.ts": SERVICE_TEMPLATE,
|
|
378
|
+
"src/payment/types.ts": TYPE_DECL_TEMPLATE,
|
|
379
|
+
[routeRel]: getRouteTemplate(framework),
|
|
380
|
+
"README-PAYMENT.md": README_PAYMENT_TEMPLATE(framework, provider)
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/cli/prompts.ts
|
|
385
|
+
var p = __toESM(require("@clack/prompts"));
|
|
386
|
+
async function selectPrompt(message, choices, initialValue) {
|
|
387
|
+
const result = await p.select({
|
|
388
|
+
message,
|
|
389
|
+
initialValue,
|
|
390
|
+
options: choices.map((c) => {
|
|
391
|
+
const opt = {
|
|
392
|
+
value: c.value,
|
|
393
|
+
label: c.label || c.value
|
|
394
|
+
};
|
|
395
|
+
if (c.hint) opt.hint = c.hint;
|
|
396
|
+
return opt;
|
|
397
|
+
})
|
|
398
|
+
});
|
|
399
|
+
if (p.isCancel(result)) {
|
|
400
|
+
p.cancel("Dibatalkan oleh user.");
|
|
401
|
+
process.exit(0);
|
|
402
|
+
}
|
|
403
|
+
return result;
|
|
404
|
+
}
|
|
405
|
+
async function confirmPrompt(message, initialValue = true) {
|
|
406
|
+
const result = await p.confirm({
|
|
407
|
+
message,
|
|
408
|
+
initialValue
|
|
409
|
+
});
|
|
410
|
+
if (p.isCancel(result)) {
|
|
411
|
+
p.cancel("Dibatalkan oleh user.");
|
|
412
|
+
process.exit(0);
|
|
413
|
+
}
|
|
414
|
+
return result;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// src/cli/scaffold.ts
|
|
418
|
+
var fs = __toESM(require("fs"));
|
|
419
|
+
var path = __toESM(require("path"));
|
|
420
|
+
function scaffold(targetDir, provider, framework, opts = {}) {
|
|
421
|
+
const files = buildScaffold(provider, framework);
|
|
422
|
+
const written = [];
|
|
423
|
+
const skipped = [];
|
|
424
|
+
const copied = [];
|
|
425
|
+
const errors = [];
|
|
426
|
+
if (!fs.existsSync(targetDir)) {
|
|
427
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
428
|
+
}
|
|
429
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
430
|
+
const fullPath = path.join(targetDir, relPath);
|
|
431
|
+
const dir = path.dirname(fullPath);
|
|
432
|
+
try {
|
|
433
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
434
|
+
if (fs.existsSync(fullPath) && !opts.overwrite) {
|
|
435
|
+
skipped.push(relPath);
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
fs.writeFileSync(fullPath, content, "utf8");
|
|
439
|
+
written.push(relPath);
|
|
440
|
+
} catch (e) {
|
|
441
|
+
errors.push(`${relPath}: ${e.message}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const envExample = path.join(targetDir, ".env.example");
|
|
445
|
+
const env = path.join(targetDir, ".env");
|
|
446
|
+
if (fs.existsSync(envExample) && !fs.existsSync(env)) {
|
|
447
|
+
try {
|
|
448
|
+
fs.copyFileSync(envExample, env);
|
|
449
|
+
copied.push(".env");
|
|
450
|
+
} catch (e) {
|
|
451
|
+
errors.push(`.env: ${e.message}`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return { written, skipped, copied, errors };
|
|
455
|
+
}
|
|
456
|
+
function printScaffoldSummary(result) {
|
|
457
|
+
const color = (c, s) => process.stdout.isTTY ? `\x1B[${c}m${s}\x1B[0m` : s;
|
|
458
|
+
if (result.written.length) {
|
|
459
|
+
console.log(color("32", "\n\u2714 File dibuat:"));
|
|
460
|
+
for (const f of result.written) console.log(` ${color("36", f)}`);
|
|
461
|
+
}
|
|
462
|
+
if (result.copied.length) {
|
|
463
|
+
console.log(color("32", "\n\u21AA Disalin dari .env.example (karena .env belum ada):"));
|
|
464
|
+
for (const f of result.copied) console.log(` ${color("36", f)}`);
|
|
465
|
+
}
|
|
466
|
+
if (result.skipped.length) {
|
|
467
|
+
console.log(color("33", "\n\u2022 Dilewati (sudah ada, gunakan --force untuk menimpa):"));
|
|
468
|
+
for (const f of result.skipped) console.log(` ${color("36", f)}`);
|
|
469
|
+
}
|
|
470
|
+
if (result.errors.length) {
|
|
471
|
+
console.log(color("31", "\n\u2716 Error:"));
|
|
472
|
+
for (const e of result.errors) console.log(` ${color("31", e)}`);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/cli/index.ts
|
|
477
|
+
var VERSION = "0.5.0";
|
|
478
|
+
function parseArgs(argv) {
|
|
479
|
+
const flags = [];
|
|
480
|
+
const rest = [];
|
|
481
|
+
let i = 0;
|
|
482
|
+
while (i < argv.length) {
|
|
483
|
+
const a = argv[i];
|
|
484
|
+
if (a.startsWith("--")) {
|
|
485
|
+
const idx = a.indexOf("=");
|
|
486
|
+
if (idx !== -1) {
|
|
487
|
+
flags.push({ flag: a.slice(2, idx), value: a.slice(idx + 1) });
|
|
488
|
+
i++;
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
const name = a.slice(2);
|
|
492
|
+
const next = argv[i + 1];
|
|
493
|
+
if (next !== void 0 && !next.startsWith("-")) {
|
|
494
|
+
flags.push({ flag: name, value: next });
|
|
495
|
+
i += 2;
|
|
496
|
+
} else {
|
|
497
|
+
flags.push({ flag: name, value: void 0 });
|
|
498
|
+
i++;
|
|
499
|
+
}
|
|
500
|
+
} else if (a.startsWith("-") && a.length === 2) {
|
|
501
|
+
flags.push({ flag: a.slice(1), value: void 0 });
|
|
502
|
+
i++;
|
|
503
|
+
} else {
|
|
504
|
+
rest.push(a);
|
|
505
|
+
i++;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
const command = rest.shift() || "init";
|
|
509
|
+
return { command, flags, rest };
|
|
510
|
+
}
|
|
511
|
+
function getFlag(flags, name) {
|
|
512
|
+
const found = flags.find((f) => f.flag === name);
|
|
513
|
+
if (found) {
|
|
514
|
+
if (found.value !== void 0) return found.value;
|
|
515
|
+
return "";
|
|
516
|
+
}
|
|
517
|
+
return void 0;
|
|
518
|
+
}
|
|
519
|
+
function hasFlag(flags, name) {
|
|
520
|
+
return flags.some((f) => f.flag === name);
|
|
521
|
+
}
|
|
522
|
+
function printHelp() {
|
|
523
|
+
console.log(`
|
|
524
|
+
@crediblemark/buayar \u2014 Unified Payment Gateway SDK
|
|
525
|
+
|
|
526
|
+
Usage:
|
|
527
|
+
buayar init Scaffold boilerplate (interaktif, arrow-key)
|
|
528
|
+
buayar init --yes Scaffold non-interaktif (defaults)
|
|
529
|
+
buayar init --out <dir> Direktori target (default: ./)
|
|
530
|
+
buayar init --provider <id> Paksa provider (default: midtrans)
|
|
531
|
+
buayar init --framework <fw> express | hono | nextjs (default: express)
|
|
532
|
+
buayar init --force Timpa file yang sudah ada
|
|
533
|
+
buayar --version | -v Tampilkan versi
|
|
534
|
+
buayar --help | -h Tampilkan bantuan
|
|
535
|
+
|
|
536
|
+
Contoh:
|
|
537
|
+
buayar init --yes --framework hono --provider xendit
|
|
538
|
+
`);
|
|
539
|
+
}
|
|
540
|
+
async function runInit(argv) {
|
|
541
|
+
const { flags } = parseArgs(argv);
|
|
542
|
+
const yes = hasFlag(flags, "yes");
|
|
543
|
+
const force = hasFlag(flags, "force");
|
|
544
|
+
const outRaw = getFlag(flags, "out");
|
|
545
|
+
const targetDir = outRaw ? outRaw === "" ? "./" : outRaw : "./";
|
|
546
|
+
const resolvedDir = path2.resolve(targetDir);
|
|
547
|
+
p2.intro("\u{1F40A} Buayar init \u2014 Unified Payment Gateway scaffold");
|
|
548
|
+
let provider = getFlag(flags, "provider") || void 0;
|
|
549
|
+
if (!provider && !yes) {
|
|
550
|
+
provider = await selectPrompt(
|
|
551
|
+
"Pilih provider aktif",
|
|
552
|
+
PROVIDERS.map((value) => ({
|
|
553
|
+
value,
|
|
554
|
+
hint: value === "midtrans" ? "populer di Indonesia" : void 0
|
|
555
|
+
})),
|
|
556
|
+
"midtrans"
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
provider = provider || "midtrans";
|
|
560
|
+
if (!PROVIDERS.includes(provider)) {
|
|
561
|
+
p2.cancel(`Provider tidak dikenal: "${provider}"`);
|
|
562
|
+
return 1;
|
|
563
|
+
}
|
|
564
|
+
let framework = getFlag(flags, "framework") || void 0;
|
|
565
|
+
if (!framework && !yes) {
|
|
566
|
+
framework = await selectPrompt(
|
|
567
|
+
"Pilih framework route",
|
|
568
|
+
[
|
|
569
|
+
{ value: "express", hint: "Node.js REST API" },
|
|
570
|
+
{ value: "hono", hint: "Ringan, edge-ready" },
|
|
571
|
+
{ value: "nextjs", hint: "App Router" }
|
|
572
|
+
],
|
|
573
|
+
"express"
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
framework = framework || "express";
|
|
577
|
+
if (!FRAMEWORKS.includes(framework)) {
|
|
578
|
+
p2.cancel(`Framework tidak dikenal: "${framework}"`);
|
|
579
|
+
return 1;
|
|
580
|
+
}
|
|
581
|
+
if (!yes && !fs2.existsSync(path2.join(resolvedDir, "package.json"))) {
|
|
582
|
+
const cont = await confirmPrompt(
|
|
583
|
+
`"${resolvedDir}" bukan proyek Node yang terlihat. Lanjutkan?`,
|
|
584
|
+
true
|
|
585
|
+
);
|
|
586
|
+
if (!cont) {
|
|
587
|
+
p2.outro("Dibatalkan.");
|
|
588
|
+
return 0;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (!yes) {
|
|
592
|
+
const ok = await confirmPrompt(
|
|
593
|
+
`Scaffold ${framework} (provider ${provider}) ke "${resolvedDir}"?`,
|
|
594
|
+
true
|
|
595
|
+
);
|
|
596
|
+
if (!ok) {
|
|
597
|
+
p2.outro("Dibatalkan.");
|
|
598
|
+
return 0;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
const result = scaffold(resolvedDir, provider, framework, { overwrite: force });
|
|
602
|
+
printScaffoldSummary(result);
|
|
603
|
+
p2.outro(`
|
|
604
|
+
Selesai! Langkah berikutnya:
|
|
605
|
+
1. Salin .env.example ke .env & isi kredensial (PROVIDER_PG + key provider aktif).
|
|
606
|
+
2. Mount route payment (lihat README-PAYMENT.md).
|
|
607
|
+
3. Ganti provider cukup ubah .env \u2014 kode tidak berubah.
|
|
608
|
+
`);
|
|
609
|
+
return result.errors.length ? 1 : 0;
|
|
610
|
+
}
|
|
611
|
+
async function main() {
|
|
612
|
+
const argv = process.argv.slice(2);
|
|
613
|
+
const { command, flags } = parseArgs(argv);
|
|
614
|
+
if (argv.length === 0 || command === "init") {
|
|
615
|
+
if (hasFlag(flags, "version") || hasFlag(flags, "v")) {
|
|
616
|
+
console.log(VERSION);
|
|
617
|
+
return 0;
|
|
618
|
+
}
|
|
619
|
+
if (hasFlag(flags, "help") || hasFlag(flags, "h")) {
|
|
620
|
+
printHelp();
|
|
621
|
+
return 0;
|
|
622
|
+
}
|
|
623
|
+
return runInit(argv);
|
|
624
|
+
}
|
|
625
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
626
|
+
console.log(VERSION);
|
|
627
|
+
return 0;
|
|
628
|
+
}
|
|
629
|
+
if (command === "--help" || command === "-h" || command === "help") {
|
|
630
|
+
printHelp();
|
|
631
|
+
return 0;
|
|
632
|
+
}
|
|
633
|
+
console.error(`
|
|
634
|
+
\u2716 Perintah tidak dikenal: "${command}"`);
|
|
635
|
+
printHelp();
|
|
636
|
+
return 1;
|
|
637
|
+
}
|
|
638
|
+
main().then((code) => {
|
|
639
|
+
process.exitCode = code;
|
|
640
|
+
}).catch((e) => {
|
|
641
|
+
console.error(e);
|
|
642
|
+
process.exitCode = 1;
|
|
643
|
+
});
|