@garuhq/cli 0.7.0 → 0.9.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/CHANGELOG.md +56 -0
- package/README.md +93 -19
- package/dist/index.cjs +595 -185
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,62 @@
|
|
|
3
3
|
All notable changes to `@garuhq/cli` are documented in this file. Format:
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.9.0] — 2026-08-21
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **`garu installment-plans` — boleto parcelado (carnê).** Sell a product as
|
|
11
|
+
2–12 monthly bank slips: `create`, `list`, `get`, `reissue`, `postpone`,
|
|
12
|
+
`mark-paid`, `cancel`, `request-refund`.
|
|
13
|
+
- **`garu refund-requests` — refunds Garu cannot make for you.** A boleto
|
|
14
|
+
cannot be reversed and Celcoin exposes no Pix devolução, so this records the
|
|
15
|
+
request and waits for you to confirm the transfer: `list`, `get`, `confirm`,
|
|
16
|
+
`reject`.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **`@garuhq/node` bumped to `1.1.0`.**
|
|
21
|
+
|
|
22
|
+
### Breaking
|
|
23
|
+
|
|
24
|
+
- **`charges` commands now use the versioned `/api/v1/charges` API**, via
|
|
25
|
+
`@garuhq/node@1.0.0`+.
|
|
26
|
+
- `garu charges get <id>` and `garu charges refund <id>` now take the
|
|
27
|
+
charge's **uuid**, not the old numeric id.
|
|
28
|
+
- `garu charges refund --amount` is now **decimal BRL / reais** (e.g.
|
|
29
|
+
`10.00`), not centavos. The `--idempotency-key` flag on `refund` is
|
|
30
|
+
removed — the v1 refund route doesn't take one.
|
|
31
|
+
- `garu charges create --type credit_card` keeps its flag spelling; the
|
|
32
|
+
underlying SDK call now sends `creditCard` and a `card` object (was
|
|
33
|
+
`cardInfo`) — no CLI-facing change beyond the `--card-*` flags already in
|
|
34
|
+
use.
|
|
35
|
+
- `garu charges list --payment-method` now takes `credit_card` (was
|
|
36
|
+
`creditcard`, undocumented and unvalidated); `--status` is validated
|
|
37
|
+
against the new friendly status set (`pending`, `authorized`, `paid`,
|
|
38
|
+
`failed`, `expired`, `canceled`, `refund_pending`, `refunded`,
|
|
39
|
+
`chargeback`).
|
|
40
|
+
- `garu charges get`/`list` output: `charge.uuid` (was `.id`),
|
|
41
|
+
`charge.paymentMethod` (was `.paymentMethodId`), `charge.createdAt` /
|
|
42
|
+
`.expiresAt` (was `.date` / `.deadline`), and a new `charge.chargedTotal`
|
|
43
|
+
field (what was actually charged, vs. `.amount`, the product's base
|
|
44
|
+
price). `charges list`'s response shape is now `{ data, count, totalCount,
|
|
45
|
+
totalPages }` (was `{ data, meta }`).
|
|
46
|
+
|
|
47
|
+
## [0.8.0] — 2026-07-18
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- **Products now use the versioned public API.** Via `@garuhq/node@0.16.0`,
|
|
52
|
+
`garu products create` / `update` hit `/api/v1/products` (uuid-keyed). The
|
|
53
|
+
numeric product id is no longer returned; product output shows the `uuid`.
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
- **`--value` is Reais, not centavos.** The flag was documented and parsed as
|
|
58
|
+
centavos; following the help (`--value 4990`) created a product priced 100×
|
|
59
|
+
(R$ 4.990,00 instead of R$ 49,90). `--value` now takes a decimal amount in
|
|
60
|
+
Reais (e.g. `49.90`). `charges` / refund `--amount` remain centavos.
|
|
61
|
+
|
|
6
62
|
## [0.7.0] — 2026-05-31
|
|
7
63
|
|
|
8
64
|
### Added
|
package/README.md
CHANGED
|
@@ -156,13 +156,13 @@ garu charges list --status paid --limit 50
|
|
|
156
156
|
garu charges list --search "Maria" --payment-method pix
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
-
| Flag | Description
|
|
160
|
-
| --------------------------- |
|
|
161
|
-
| `--page <n>` | Page number (1-based)
|
|
162
|
-
| `--limit <n>` | Items per page (1-100)
|
|
163
|
-
| `--status <status>` | Filter by status
|
|
164
|
-
| `--search <query>` | Search by customer name, email, or document
|
|
165
|
-
| `--payment-method <method>` | Filter: `pix`, `
|
|
159
|
+
| Flag | Description |
|
|
160
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
161
|
+
| `--page <n>` | Page number (1-based) |
|
|
162
|
+
| `--limit <n>` | Items per page (1-100) |
|
|
163
|
+
| `--status <status>` | Filter by status: `pending`, `authorized`, `paid`, `failed`, `expired`, `canceled`, `refund_pending`, `refunded`, `chargeback` |
|
|
164
|
+
| `--search <query>` | Search by customer name, email, or document |
|
|
165
|
+
| `--payment-method <method>` | Filter: `pix`, `credit_card`, `boleto` |
|
|
166
166
|
|
|
167
167
|
---
|
|
168
168
|
|
|
@@ -211,32 +211,31 @@ garu charges create --type credit_card --product-id prod-uuid \
|
|
|
211
211
|
|
|
212
212
|
### `garu charges get`
|
|
213
213
|
|
|
214
|
-
Fetch a single charge by
|
|
214
|
+
Fetch a single charge by its uuid.
|
|
215
215
|
|
|
216
216
|
```bash
|
|
217
|
-
garu charges get
|
|
218
|
-
garu charges get
|
|
217
|
+
garu charges get 6f1c9b2e-4a7d-4f0b-9a3e-1d2c3b4a5e6f
|
|
218
|
+
garu charges get 6f1c9b2e-4a7d-4f0b-9a3e-1d2c3b4a5e6f --json | jq '.status'
|
|
219
219
|
```
|
|
220
220
|
|
|
221
221
|
---
|
|
222
222
|
|
|
223
223
|
### `garu charges refund`
|
|
224
224
|
|
|
225
|
-
Refund a charge (full or partial).
|
|
225
|
+
Refund a charge by its uuid (full or partial).
|
|
226
226
|
|
|
227
227
|
```bash
|
|
228
228
|
# Full refund
|
|
229
|
-
garu charges refund
|
|
229
|
+
garu charges refund 6f1c9b2e-4a7d-4f0b-9a3e-1d2c3b4a5e6f
|
|
230
230
|
|
|
231
|
-
# Partial refund
|
|
232
|
-
garu charges refund
|
|
231
|
+
# Partial refund, in decimal BRL / reais
|
|
232
|
+
garu charges refund 6f1c9b2e-4a7d-4f0b-9a3e-1d2c3b4a5e6f --amount 10.00 --reason "customer_request"
|
|
233
233
|
```
|
|
234
234
|
|
|
235
|
-
| Flag
|
|
236
|
-
|
|
|
237
|
-
| `--amount <
|
|
238
|
-
| `--reason <text>`
|
|
239
|
-
| `--idempotency-key <key>` | Idempotency key (auto-generated if omitted) |
|
|
235
|
+
| Flag | Description |
|
|
236
|
+
| ------------------ | ------------------------------------------------------------------ |
|
|
237
|
+
| `--amount <reais>` | Partial refund amount in decimal BRL, e.g. `10.00` (omit for full) |
|
|
238
|
+
| `--reason <text>` | Optional refund reason |
|
|
240
239
|
|
|
241
240
|
---
|
|
242
241
|
|
|
@@ -328,6 +327,81 @@ The process **exits non-zero** when:
|
|
|
328
327
|
|
|
329
328
|
---
|
|
330
329
|
|
|
330
|
+
### `garu installment-plans create`
|
|
331
|
+
|
|
332
|
+
Sell a product as **boleto parcelado (carnê)** — a purchase split into 2–12
|
|
333
|
+
monthly bank slips. This is seller-financed consumer credit: nobody guarantees
|
|
334
|
+
a boleto, so Garu never advances funds and carries none of the default risk.
|
|
335
|
+
Only the first slip is registered at creation; the rest are emitted month by
|
|
336
|
+
month once parcela 1 compensates.
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
garu installment-plans create \
|
|
340
|
+
--product-id 40381e8e-6ee7-4b8e-9393-766a6e2109d2 \
|
|
341
|
+
--customer-id 4821 --installments 12
|
|
342
|
+
|
|
343
|
+
# Attribute the sale to an affiliate (fixed for the whole carnê) and pick
|
|
344
|
+
# the first due date
|
|
345
|
+
garu installment-plans create \
|
|
346
|
+
--product-id 40381e8e-6ee7-4b8e-9393-766a6e2109d2 \
|
|
347
|
+
--customer-id 4821 --installments 6 \
|
|
348
|
+
--first-due-date 2026-10-05 --affiliate-id 5
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
| Flag | Description |
|
|
352
|
+
| ------------------------- | --------------------------------------------------------------- |
|
|
353
|
+
| `--product-id <uuid>` | Product uuid (must have boleto parcelado enabled) — required |
|
|
354
|
+
| `--customer-id <n>` | Customer id — required |
|
|
355
|
+
| `--installments <n>` | 2–12 installments — required |
|
|
356
|
+
| `--first-due-date <date>` | First installment due date (default: today) |
|
|
357
|
+
| `--affiliate-id <n>` | Attribute the sale to this affiliate (fixed for the whole plan) |
|
|
358
|
+
| `--idempotency-key <key>` | Idempotency key (auto-generated if omitted) |
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
### `garu installment-plans` — other subcommands
|
|
363
|
+
|
|
364
|
+
| Command | Description |
|
|
365
|
+
| --------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
366
|
+
| `list [--page --limit --customer-id --product-id --status ...]` | List carnês (`--status` repeatable; `--due-from/--due-to`) |
|
|
367
|
+
| `get <uuid>` | Fetch a carnê with every installment: due date, status, barcode and PDF |
|
|
368
|
+
| `reissue <uuid> <number>` | Issue a segunda via for one installment once its slip has expired |
|
|
369
|
+
| `postpone <uuid> <number> --new-due-date <date>` | Move one installment to a later date (its siblings keep theirs) |
|
|
370
|
+
| `mark-paid <uuid> <number>` | Record an installment as paid when the webhook never arrived |
|
|
371
|
+
| `cancel <uuid> [--note]` | Cancel the carnê — stops emission/reminders, cancels open provider slips |
|
|
372
|
+
| `request-refund <uuid> [--amount --reason]` | Ask for the carnê to be refunded (see `garu refund-requests` below) |
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
### `garu refund-requests`
|
|
377
|
+
|
|
378
|
+
Garu cannot reverse a boleto, and Celcoin exposes no Pix devolução, so a
|
|
379
|
+
refund on either rail is a request-and-notify flow: you transfer the money
|
|
380
|
+
back yourself, then tell Garu it happened. Card charges keep their automated
|
|
381
|
+
reversal via `garu charges refund`.
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
# See everything you still owe a buyer
|
|
385
|
+
garu refund-requests list --status pending
|
|
386
|
+
|
|
387
|
+
# After you've transferred the money back
|
|
388
|
+
garu refund-requests confirm a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
|
|
389
|
+
--note "Pix devolvido em 14/08, e2e E12345678"
|
|
390
|
+
|
|
391
|
+
# Decline instead — the carnê or charge is left untouched
|
|
392
|
+
garu refund-requests reject a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
|
|
393
|
+
--note "Produto entregue e retirado na loja"
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
| Command | Description |
|
|
397
|
+
| ------------------------------------------------------ | ------------------------------------------------------------ |
|
|
398
|
+
| `list [--page --limit --status --plan-id --charge-id]` | List refund requests (`--status` repeatable) |
|
|
399
|
+
| `get <uuid>` | Fetch a single refund request |
|
|
400
|
+
| `confirm <uuid> [--note]` | Record that you returned the money — call AFTER the transfer |
|
|
401
|
+
| `reject <uuid> [--note]` | Decline the request — the carnê or charge is untouched |
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
331
405
|
### `garu doctor`
|
|
332
406
|
|
|
333
407
|
Run environment diagnostics. Verifies your CLI version, API connectivity, credentials, and detects AI agent integrations.
|
package/dist/index.cjs
CHANGED
|
@@ -15,7 +15,7 @@ var pc__default = /*#__PURE__*/_interopDefault(pc);
|
|
|
15
15
|
// src/index.ts
|
|
16
16
|
|
|
17
17
|
// src/version.ts
|
|
18
|
-
var CLI_VERSION = "0.
|
|
18
|
+
var CLI_VERSION = "0.9.0";
|
|
19
19
|
var CliError = class extends Error {
|
|
20
20
|
code;
|
|
21
21
|
exitCode;
|
|
@@ -233,6 +233,185 @@ function createGaruClient(opts) {
|
|
|
233
233
|
});
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
// src/lib/parse.ts
|
|
237
|
+
function parsePositiveIntId(raw, label) {
|
|
238
|
+
const id = Number.parseInt(raw, 10);
|
|
239
|
+
if (!Number.isFinite(id) || id <= 0) {
|
|
240
|
+
throw new CliError("invalid_input", `${label} must be a positive integer (got '${raw}')`);
|
|
241
|
+
}
|
|
242
|
+
return id;
|
|
243
|
+
}
|
|
244
|
+
function parsePaymentMethod(raw) {
|
|
245
|
+
if (raw === "pix" || raw === "credit_card" || raw === "boleto") return raw;
|
|
246
|
+
throw new CliError(
|
|
247
|
+
"invalid_input",
|
|
248
|
+
`--type must be 'pix', 'credit_card', or 'boleto' (got '${raw}')`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
function toChargePaymentMethod(type) {
|
|
252
|
+
return type === "credit_card" ? "creditCard" : type;
|
|
253
|
+
}
|
|
254
|
+
function parseChargePaymentMethodFilter(raw) {
|
|
255
|
+
return toChargePaymentMethod(parsePaymentMethod(raw));
|
|
256
|
+
}
|
|
257
|
+
var CHARGE_STATUSES = [
|
|
258
|
+
"pending",
|
|
259
|
+
"authorized",
|
|
260
|
+
"paid",
|
|
261
|
+
"failed",
|
|
262
|
+
"expired",
|
|
263
|
+
"canceled",
|
|
264
|
+
"refund_pending",
|
|
265
|
+
"refunded",
|
|
266
|
+
"chargeback"
|
|
267
|
+
];
|
|
268
|
+
function parseChargeStatus(raw) {
|
|
269
|
+
if (CHARGE_STATUSES.includes(raw)) return raw;
|
|
270
|
+
throw new CliError(
|
|
271
|
+
"invalid_input",
|
|
272
|
+
`--status must be one of ${CHARGE_STATUSES.join(", ")} (got '${raw}')`
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
var INSTALLMENT_PLAN_STATUSES = [
|
|
276
|
+
"pending_activation",
|
|
277
|
+
"active",
|
|
278
|
+
"completed",
|
|
279
|
+
"defaulted",
|
|
280
|
+
"canceled",
|
|
281
|
+
"refunded"
|
|
282
|
+
];
|
|
283
|
+
function parseInstallmentPlanStatus(raw) {
|
|
284
|
+
if (INSTALLMENT_PLAN_STATUSES.includes(raw)) return raw;
|
|
285
|
+
throw new CliError(
|
|
286
|
+
"invalid_input",
|
|
287
|
+
`--status must be one of ${INSTALLMENT_PLAN_STATUSES.join(", ")} (got '${raw}')`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
var REFUND_REQUEST_STATUSES = ["pending", "confirmed", "rejected"];
|
|
291
|
+
function parseRefundRequestStatus(raw) {
|
|
292
|
+
if (REFUND_REQUEST_STATUSES.includes(raw)) return raw;
|
|
293
|
+
throw new CliError(
|
|
294
|
+
"invalid_input",
|
|
295
|
+
`--status must be one of ${REFUND_REQUEST_STATUSES.join(", ")} (got '${raw}')`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
function parseWebhookEventStatus(raw) {
|
|
299
|
+
if (raw === "pending" || raw === "success" || raw === "failed") return raw;
|
|
300
|
+
throw new CliError(
|
|
301
|
+
"invalid_input",
|
|
302
|
+
`--status must be 'pending', 'success', or 'failed' (got '${raw}')`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
function parseScheduledChargeType(raw) {
|
|
306
|
+
if (raw === "one_time" || raw === "recurring") return raw;
|
|
307
|
+
throw new CliError("invalid_input", `--type must be 'one_time' or 'recurring' (got '${raw}')`);
|
|
308
|
+
}
|
|
309
|
+
var SCHEDULED_PAYMENT_METHODS = [
|
|
310
|
+
"pix",
|
|
311
|
+
"boleto",
|
|
312
|
+
"card",
|
|
313
|
+
"pix_automatic"
|
|
314
|
+
];
|
|
315
|
+
function parseCsvList(raw) {
|
|
316
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
317
|
+
}
|
|
318
|
+
function parseScheduledPaymentMethods(raw) {
|
|
319
|
+
const parts = parseCsvList(raw);
|
|
320
|
+
if (parts.length === 0) {
|
|
321
|
+
throw new CliError(
|
|
322
|
+
"invalid_input",
|
|
323
|
+
`--methods must list at least one of: ${SCHEDULED_PAYMENT_METHODS.join(", ")}`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
for (const p of parts) {
|
|
327
|
+
if (!SCHEDULED_PAYMENT_METHODS.includes(p)) {
|
|
328
|
+
throw new CliError(
|
|
329
|
+
"invalid_input",
|
|
330
|
+
`--methods entries must be one of ${SCHEDULED_PAYMENT_METHODS.join(", ")} (got '${p}')`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return parts;
|
|
335
|
+
}
|
|
336
|
+
var RECURRENCE_INTERVALS = [
|
|
337
|
+
"weekly",
|
|
338
|
+
"biweekly",
|
|
339
|
+
"monthly",
|
|
340
|
+
"bimonthly",
|
|
341
|
+
"quarterly",
|
|
342
|
+
"biannual",
|
|
343
|
+
"yearly"
|
|
344
|
+
];
|
|
345
|
+
function parseRecurrenceInterval(raw) {
|
|
346
|
+
if (RECURRENCE_INTERVALS.includes(raw)) return raw;
|
|
347
|
+
throw new CliError(
|
|
348
|
+
"invalid_input",
|
|
349
|
+
`--recurrence-interval must be one of ${RECURRENCE_INTERVALS.join(", ")} (got '${raw}')`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
var SCHEDULED_CHARGE_STATUSES = [
|
|
353
|
+
"scheduled",
|
|
354
|
+
"due_today",
|
|
355
|
+
"overdue",
|
|
356
|
+
"paid",
|
|
357
|
+
"paused",
|
|
358
|
+
"canceled",
|
|
359
|
+
"trial",
|
|
360
|
+
"pending_tokenization",
|
|
361
|
+
"recurrence_canceled"
|
|
362
|
+
];
|
|
363
|
+
function parseScheduledChargeStatus(raw) {
|
|
364
|
+
if (SCHEDULED_CHARGE_STATUSES.includes(raw)) return raw;
|
|
365
|
+
throw new CliError(
|
|
366
|
+
"invalid_input",
|
|
367
|
+
`--status must be one of ${SCHEDULED_CHARGE_STATUSES.join(", ")} (got '${raw}')`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
function parseIntInRange(raw, label, min, max) {
|
|
371
|
+
const trimmed = raw.trim();
|
|
372
|
+
const n = Number.parseInt(trimmed, 10);
|
|
373
|
+
if (!Number.isFinite(n) || String(n) !== trimmed || n < min || n > max) {
|
|
374
|
+
throw new CliError(
|
|
375
|
+
"invalid_input",
|
|
376
|
+
`${label} must be an integer between ${min} and ${max} (got '${raw}')`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
return n;
|
|
380
|
+
}
|
|
381
|
+
function parseNonNegativeBrl(raw, label) {
|
|
382
|
+
const trimmed = raw.trim();
|
|
383
|
+
const n = Number(trimmed);
|
|
384
|
+
if (trimmed === "" || !Number.isFinite(n) || n < 0) {
|
|
385
|
+
throw new CliError(
|
|
386
|
+
"invalid_input",
|
|
387
|
+
`${label} must be a non-negative amount in BRL/reais, e.g. 49.90 (got '${raw}')`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
return n;
|
|
391
|
+
}
|
|
392
|
+
function parseAmountBrl(raw) {
|
|
393
|
+
const n = Number(raw);
|
|
394
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
395
|
+
throw new CliError(
|
|
396
|
+
"invalid_input",
|
|
397
|
+
`--amount must be a positive decimal in BRL, e.g. 297.50 (got '${raw}')`
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
return n;
|
|
401
|
+
}
|
|
402
|
+
function parseMetadata(raw) {
|
|
403
|
+
let parsed;
|
|
404
|
+
try {
|
|
405
|
+
parsed = JSON.parse(raw);
|
|
406
|
+
} catch {
|
|
407
|
+
throw new CliError("invalid_input", `--metadata must be valid JSON (got '${raw}')`);
|
|
408
|
+
}
|
|
409
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
410
|
+
throw new CliError("invalid_input", `--metadata must be a JSON object`);
|
|
411
|
+
}
|
|
412
|
+
return parsed;
|
|
413
|
+
}
|
|
414
|
+
|
|
236
415
|
// src/commands/charges.ts
|
|
237
416
|
async function getClient(opts) {
|
|
238
417
|
if (opts.garu) return opts.garu;
|
|
@@ -275,8 +454,9 @@ async function chargesCreateCommand(opts) {
|
|
|
275
454
|
assertCardFieldsPresent(opts);
|
|
276
455
|
charge = await garu.charges.create({
|
|
277
456
|
...base,
|
|
278
|
-
|
|
279
|
-
|
|
457
|
+
paymentMethod: toChargePaymentMethod(opts.type),
|
|
458
|
+
card: {
|
|
459
|
+
number: opts.cardNumber,
|
|
280
460
|
cvv: opts.cardCvv,
|
|
281
461
|
expirationDate: opts.cardExpiration,
|
|
282
462
|
holderName: opts.cardHolder,
|
|
@@ -284,14 +464,17 @@ async function chargesCreateCommand(opts) {
|
|
|
284
464
|
}
|
|
285
465
|
});
|
|
286
466
|
} else {
|
|
287
|
-
charge = await garu.charges.create(
|
|
467
|
+
charge = await garu.charges.create({
|
|
468
|
+
...base,
|
|
469
|
+
paymentMethod: toChargePaymentMethod(opts.type)
|
|
470
|
+
});
|
|
288
471
|
}
|
|
289
472
|
printResult(charge, { ...opts, prettyPrint: prettyCharge });
|
|
290
473
|
return charge;
|
|
291
474
|
}
|
|
292
475
|
async function chargesGetCommand(opts) {
|
|
293
476
|
const garu = await getClient(opts);
|
|
294
|
-
const charge = await garu.charges.
|
|
477
|
+
const charge = await garu.charges.retrieve(opts.id);
|
|
295
478
|
printResult(charge, { ...opts, prettyPrint: prettyCharge });
|
|
296
479
|
return charge;
|
|
297
480
|
}
|
|
@@ -300,7 +483,6 @@ async function chargesRefundCommand(opts) {
|
|
|
300
483
|
const params = {};
|
|
301
484
|
if (opts.amount !== void 0) params.amount = opts.amount;
|
|
302
485
|
if (opts.reason !== void 0) params.reason = opts.reason;
|
|
303
|
-
if (opts.idempotencyKey !== void 0) params.idempotencyKey = opts.idempotencyKey;
|
|
304
486
|
const charge = await garu.charges.refund(opts.id, params);
|
|
305
487
|
printResult(charge, { ...opts, prettyPrint: prettyCharge });
|
|
306
488
|
return charge;
|
|
@@ -319,23 +501,26 @@ async function chargesListCommand(opts) {
|
|
|
319
501
|
}
|
|
320
502
|
function prettyChargeList(list) {
|
|
321
503
|
if (list.data.length === 0) {
|
|
322
|
-
return
|
|
504
|
+
return "No charges found";
|
|
323
505
|
}
|
|
324
|
-
const header = `Charges (
|
|
506
|
+
const header = `Charges (${list.count} of ${list.totalCount} total, ${list.totalPages} page(s))`;
|
|
325
507
|
const rows = list.data.map(
|
|
326
|
-
(c) => ` ${
|
|
508
|
+
(c) => ` ${c.uuid} ${String(c.status).padEnd(14)} ${c.paymentMethod.padEnd(10)} ${c.createdAt}`
|
|
327
509
|
);
|
|
328
510
|
return [header, ...rows].join("\n");
|
|
329
511
|
}
|
|
330
512
|
function prettyCharge(charge) {
|
|
331
513
|
const lines = [
|
|
332
|
-
`Charge ${charge.
|
|
333
|
-
` status:
|
|
334
|
-
` amount:
|
|
335
|
-
`
|
|
336
|
-
`
|
|
514
|
+
`Charge ${charge.uuid}`,
|
|
515
|
+
` status: ${charge.status}`,
|
|
516
|
+
` amount: ${charge.amount}`,
|
|
517
|
+
` chargedTotal: ${charge.chargedTotal}`,
|
|
518
|
+
` method: ${charge.paymentMethod}`,
|
|
519
|
+
` createdAt: ${charge.createdAt}`
|
|
337
520
|
];
|
|
338
|
-
if (charge.
|
|
521
|
+
if (charge.expiresAt) lines.push(` expiresAt: ${charge.expiresAt}`);
|
|
522
|
+
if (charge.refund)
|
|
523
|
+
lines.push(` refund: ${charge.refund.amount} (${charge.refund.reason ?? "no reason"})`);
|
|
339
524
|
return lines.join("\n");
|
|
340
525
|
}
|
|
341
526
|
async function doctorCommand(opts = {}) {
|
|
@@ -430,6 +615,152 @@ function prettyDoctor(r) {
|
|
|
430
615
|
].filter((l) => l !== void 0);
|
|
431
616
|
return lines.join("\n");
|
|
432
617
|
}
|
|
618
|
+
|
|
619
|
+
// src/commands/installment-plans.ts
|
|
620
|
+
async function getClient2(opts) {
|
|
621
|
+
if (opts.garu) return opts.garu;
|
|
622
|
+
const auth = await resolveAuth({
|
|
623
|
+
...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
|
|
624
|
+
...opts.profile !== void 0 ? { profile: opts.profile } : {}
|
|
625
|
+
});
|
|
626
|
+
return createGaruClient({
|
|
627
|
+
auth,
|
|
628
|
+
...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {}
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
async function installmentPlansCreateCommand(opts) {
|
|
632
|
+
const garu = await getClient2(opts);
|
|
633
|
+
const params = {
|
|
634
|
+
productId: opts.productId,
|
|
635
|
+
customerId: opts.customerId,
|
|
636
|
+
installments: opts.installments
|
|
637
|
+
};
|
|
638
|
+
if (opts.firstDueDate !== void 0) params.firstDueDate = opts.firstDueDate;
|
|
639
|
+
if (opts.affiliateId !== void 0) params.affiliateId = opts.affiliateId;
|
|
640
|
+
if (opts.idempotencyKey !== void 0) params.idempotencyKey = opts.idempotencyKey;
|
|
641
|
+
const plan = await garu.installmentPlans.create(params);
|
|
642
|
+
printResult(plan, { ...opts, prettyPrint: prettyInstallmentPlan });
|
|
643
|
+
return plan;
|
|
644
|
+
}
|
|
645
|
+
async function installmentPlansListCommand(opts) {
|
|
646
|
+
const garu = await getClient2(opts);
|
|
647
|
+
const params = {};
|
|
648
|
+
if (opts.page !== void 0) params.page = opts.page;
|
|
649
|
+
if (opts.limit !== void 0) params.limit = opts.limit;
|
|
650
|
+
if (opts.customerId !== void 0) params.customerId = opts.customerId;
|
|
651
|
+
if (opts.productId !== void 0) params.productId = opts.productId;
|
|
652
|
+
if (opts.dueFrom !== void 0) params.dueFrom = opts.dueFrom;
|
|
653
|
+
if (opts.dueTo !== void 0) params.dueTo = opts.dueTo;
|
|
654
|
+
if (opts.status !== void 0 && opts.status.length > 0) {
|
|
655
|
+
params.status = opts.status.length === 1 ? opts.status[0] : opts.status;
|
|
656
|
+
}
|
|
657
|
+
const result = await garu.installmentPlans.list(params);
|
|
658
|
+
printResult(result, { ...opts, prettyPrint: prettyInstallmentPlanList });
|
|
659
|
+
return result;
|
|
660
|
+
}
|
|
661
|
+
async function installmentPlansGetCommand(opts) {
|
|
662
|
+
const garu = await getClient2(opts);
|
|
663
|
+
const plan = await garu.installmentPlans.get(opts.uuid);
|
|
664
|
+
printResult(plan, { ...opts, prettyPrint: prettyInstallmentPlanDetail });
|
|
665
|
+
return plan;
|
|
666
|
+
}
|
|
667
|
+
async function installmentPlansReissueCommand(opts) {
|
|
668
|
+
const garu = await getClient2(opts);
|
|
669
|
+
const result = await garu.installmentPlans.reissueInstallment(opts.uuid, opts.number);
|
|
670
|
+
printResult(result, { ...opts, prettyPrint: prettyReissueResult });
|
|
671
|
+
return result;
|
|
672
|
+
}
|
|
673
|
+
async function installmentPlansPostponeCommand(opts) {
|
|
674
|
+
const garu = await getClient2(opts);
|
|
675
|
+
const params = { newDueDate: opts.newDueDate };
|
|
676
|
+
const installment = await garu.installmentPlans.postponeInstallment(
|
|
677
|
+
opts.uuid,
|
|
678
|
+
opts.number,
|
|
679
|
+
params
|
|
680
|
+
);
|
|
681
|
+
printResult(installment, { ...opts, prettyPrint: prettyInstallment });
|
|
682
|
+
return installment;
|
|
683
|
+
}
|
|
684
|
+
async function installmentPlansMarkPaidCommand(opts) {
|
|
685
|
+
const garu = await getClient2(opts);
|
|
686
|
+
const installment = await garu.installmentPlans.markInstallmentPaid(opts.uuid, opts.number);
|
|
687
|
+
printResult(installment, { ...opts, prettyPrint: prettyInstallment });
|
|
688
|
+
return installment;
|
|
689
|
+
}
|
|
690
|
+
async function installmentPlansCancelCommand(opts) {
|
|
691
|
+
const garu = await getClient2(opts);
|
|
692
|
+
const params = {};
|
|
693
|
+
if (opts.note !== void 0) params.note = opts.note;
|
|
694
|
+
const plan = await garu.installmentPlans.cancel(opts.uuid, params);
|
|
695
|
+
printResult(plan, { ...opts, prettyPrint: prettyInstallmentPlan });
|
|
696
|
+
return plan;
|
|
697
|
+
}
|
|
698
|
+
async function installmentPlansRequestRefundCommand(opts) {
|
|
699
|
+
const garu = await getClient2(opts);
|
|
700
|
+
const params = {};
|
|
701
|
+
if (opts.amount !== void 0) params.amount = opts.amount;
|
|
702
|
+
if (opts.reason !== void 0) params.reason = opts.reason;
|
|
703
|
+
const request = await garu.installmentPlans.requestRefund(opts.uuid, params);
|
|
704
|
+
printResult(request, { ...opts, prettyPrint: prettyRefundRequest });
|
|
705
|
+
return request;
|
|
706
|
+
}
|
|
707
|
+
function prettyInstallmentPlan(p) {
|
|
708
|
+
const lines = [
|
|
709
|
+
`Installment plan ${p.uuid}`,
|
|
710
|
+
` status: ${p.status}`,
|
|
711
|
+
` installments: ${p.installmentsPaid}/${p.installments} paid`,
|
|
712
|
+
` baseValue: ${p.baseValue}`,
|
|
713
|
+
` installmentAmount: ${p.installmentAmount}`,
|
|
714
|
+
` totalScheduled: ${p.totalScheduled}`,
|
|
715
|
+
` totalCollected: ${p.totalCollected}`,
|
|
716
|
+
` firstDueDate: ${p.firstDueDate}`
|
|
717
|
+
];
|
|
718
|
+
if (p.product) lines.push(` product: ${p.product.name} (${p.product.uuid})`);
|
|
719
|
+
if (p.customer) lines.push(` customer: ${p.customer.name} <${p.customer.email}>`);
|
|
720
|
+
return lines.join("\n");
|
|
721
|
+
}
|
|
722
|
+
function prettyInstallmentPlanDetail(p) {
|
|
723
|
+
const lines = [prettyInstallmentPlan(p)];
|
|
724
|
+
if (p.installmentsDetail && p.installmentsDetail.length > 0) {
|
|
725
|
+
lines.push(" installments:");
|
|
726
|
+
for (const i of p.installmentsDetail) {
|
|
727
|
+
lines.push(
|
|
728
|
+
` #${i.number} ${i.status.padEnd(10)} ${String(i.amount).padStart(10)} due ${i.dueDate}${i.paidAt ? ` paid ${i.paidAt}` : ""}`
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return lines.join("\n");
|
|
733
|
+
}
|
|
734
|
+
function prettyInstallmentPlanList(list) {
|
|
735
|
+
if (list.data.length === 0) return "No installment plans found";
|
|
736
|
+
const header = `Installment plans (${list.count} of ${list.totalCount} total, ${list.totalPages} page(s))`;
|
|
737
|
+
const rows = list.data.map(
|
|
738
|
+
(p) => ` ${p.uuid} ${p.status.padEnd(18)} ${p.installmentsPaid}/${p.installments} paid ${p.firstDueDate}`
|
|
739
|
+
);
|
|
740
|
+
return [header, ...rows].join("\n");
|
|
741
|
+
}
|
|
742
|
+
function prettyInstallment(i) {
|
|
743
|
+
const lines = [
|
|
744
|
+
`Installment #${i.number}`,
|
|
745
|
+
` status: ${i.status}`,
|
|
746
|
+
` amount: ${i.amount}`,
|
|
747
|
+
` dueDate: ${i.dueDate}`
|
|
748
|
+
];
|
|
749
|
+
if (i.paidAt) lines.push(` paidAt: ${i.paidAt}`);
|
|
750
|
+
if (i.boleto) lines.push(` boleto: ${i.boleto.barcodeLine}`);
|
|
751
|
+
return lines.join("\n");
|
|
752
|
+
}
|
|
753
|
+
function prettyReissueResult(result) {
|
|
754
|
+
const lines = [`Reissue: ${result.status}`];
|
|
755
|
+
if (result.reason) lines.push(` reason: ${result.reason}`);
|
|
756
|
+
if (result.installment) lines.push(prettyInstallment(result.installment));
|
|
757
|
+
return lines.join("\n");
|
|
758
|
+
}
|
|
759
|
+
function prettyRefundRequest(r) {
|
|
760
|
+
const lines = [`Refund request ${r.uuid}`, ` status: ${r.status}`, ` amount: ${r.amount}`];
|
|
761
|
+
if (r.reason) lines.push(` reason: ${r.reason}`);
|
|
762
|
+
return lines.join("\n");
|
|
763
|
+
}
|
|
433
764
|
async function loginCommand(opts = {}) {
|
|
434
765
|
const profile = opts.profile ?? "default";
|
|
435
766
|
const apiKey = opts.apiKey ?? await import('@inquirer/prompts').then(
|
|
@@ -477,7 +808,7 @@ async function logoutCommand(opts = {}) {
|
|
|
477
808
|
}
|
|
478
809
|
|
|
479
810
|
// src/commands/products.ts
|
|
480
|
-
async function
|
|
811
|
+
async function getClient3(opts) {
|
|
481
812
|
if (opts.garu) return opts.garu;
|
|
482
813
|
const auth = await resolveAuth({
|
|
483
814
|
...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
|
|
@@ -508,7 +839,7 @@ function buildProductBody(opts) {
|
|
|
508
839
|
return body;
|
|
509
840
|
}
|
|
510
841
|
async function productsCreateCommand(opts) {
|
|
511
|
-
const garu = await
|
|
842
|
+
const garu = await getClient3(opts);
|
|
512
843
|
const params = { ...buildProductBody(opts), name: opts.name };
|
|
513
844
|
const product = await garu.products.create(params);
|
|
514
845
|
printSuccess(`Created product ${product.uuid ?? product.id}`, opts);
|
|
@@ -520,7 +851,7 @@ async function productsUpdateCommand(opts) {
|
|
|
520
851
|
if (Object.keys(body).length === 0) {
|
|
521
852
|
throw new CliError("invalid_input", "Nothing to update \u2014 pass at least one field to change.");
|
|
522
853
|
}
|
|
523
|
-
const garu = await
|
|
854
|
+
const garu = await getClient3(opts);
|
|
524
855
|
const product = await garu.products.update(opts.id, body);
|
|
525
856
|
printSuccess(`Updated product ${product.uuid ?? product.id}`, opts);
|
|
526
857
|
printResult(product, { ...opts, prettyPrint: prettyProduct });
|
|
@@ -536,15 +867,81 @@ function prettyProduct(p) {
|
|
|
536
867
|
const lines = [
|
|
537
868
|
`Product ${p.uuid ?? p.id}`,
|
|
538
869
|
` name: ${p.name}`,
|
|
539
|
-
` value: ${p.value} (
|
|
870
|
+
` value: ${p.value} (reais)`,
|
|
540
871
|
` methods: ${methods || "(none)"}`
|
|
541
872
|
];
|
|
542
873
|
if (p.description) lines.push(` description: ${p.description}`);
|
|
543
874
|
return lines.join("\n");
|
|
544
875
|
}
|
|
545
876
|
|
|
877
|
+
// src/commands/refund-requests.ts
|
|
878
|
+
async function getClient4(opts) {
|
|
879
|
+
if (opts.garu) return opts.garu;
|
|
880
|
+
const auth = await resolveAuth({
|
|
881
|
+
...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
|
|
882
|
+
...opts.profile !== void 0 ? { profile: opts.profile } : {}
|
|
883
|
+
});
|
|
884
|
+
return createGaruClient({
|
|
885
|
+
auth,
|
|
886
|
+
...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {}
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
async function refundRequestsListCommand(opts) {
|
|
890
|
+
const garu = await getClient4(opts);
|
|
891
|
+
const params = {};
|
|
892
|
+
if (opts.page !== void 0) params.page = opts.page;
|
|
893
|
+
if (opts.limit !== void 0) params.limit = opts.limit;
|
|
894
|
+
if (opts.planId !== void 0) params.planId = opts.planId;
|
|
895
|
+
if (opts.chargeId !== void 0) params.chargeId = opts.chargeId;
|
|
896
|
+
if (opts.status !== void 0 && opts.status.length > 0) {
|
|
897
|
+
params.status = opts.status.length === 1 ? opts.status[0] : opts.status;
|
|
898
|
+
}
|
|
899
|
+
const result = await garu.refundRequests.list(params);
|
|
900
|
+
printResult(result, { ...opts, prettyPrint: prettyRefundRequestList });
|
|
901
|
+
return result;
|
|
902
|
+
}
|
|
903
|
+
async function refundRequestsGetCommand(opts) {
|
|
904
|
+
const garu = await getClient4(opts);
|
|
905
|
+
const request = await garu.refundRequests.get(opts.uuid);
|
|
906
|
+
printResult(request, { ...opts, prettyPrint: prettyRefundRequest2 });
|
|
907
|
+
return request;
|
|
908
|
+
}
|
|
909
|
+
async function refundRequestsConfirmCommand(opts) {
|
|
910
|
+
const garu = await getClient4(opts);
|
|
911
|
+
const params = {};
|
|
912
|
+
if (opts.note !== void 0) params.note = opts.note;
|
|
913
|
+
const request = await garu.refundRequests.confirm(opts.uuid, params);
|
|
914
|
+
printResult(request, { ...opts, prettyPrint: prettyRefundRequest2 });
|
|
915
|
+
return request;
|
|
916
|
+
}
|
|
917
|
+
async function refundRequestsRejectCommand(opts) {
|
|
918
|
+
const garu = await getClient4(opts);
|
|
919
|
+
const params = {};
|
|
920
|
+
if (opts.note !== void 0) params.note = opts.note;
|
|
921
|
+
const request = await garu.refundRequests.reject(opts.uuid, params);
|
|
922
|
+
printResult(request, { ...opts, prettyPrint: prettyRefundRequest2 });
|
|
923
|
+
return request;
|
|
924
|
+
}
|
|
925
|
+
function prettyRefundRequestList(list) {
|
|
926
|
+
if (list.data.length === 0) return "No refund requests found";
|
|
927
|
+
const header = `Refund requests (${list.count} of ${list.totalCount} total, ${list.totalPages} page(s))`;
|
|
928
|
+
const rows = list.data.map(
|
|
929
|
+
(r) => ` ${r.uuid} ${r.status.padEnd(10)} ${String(r.amount).padStart(10)} ${r.installmentPlanId ?? r.chargeId ?? ""}`
|
|
930
|
+
);
|
|
931
|
+
return [header, ...rows].join("\n");
|
|
932
|
+
}
|
|
933
|
+
function prettyRefundRequest2(r) {
|
|
934
|
+
const lines = [`Refund request ${r.uuid}`, ` status: ${r.status}`, ` amount: ${r.amount}`];
|
|
935
|
+
if (r.reason) lines.push(` reason: ${r.reason}`);
|
|
936
|
+
if (r.installmentPlanId) lines.push(` installmentPlan: ${r.installmentPlanId}`);
|
|
937
|
+
if (r.chargeId) lines.push(` charge: ${r.chargeId}`);
|
|
938
|
+
if (r.resolvedAt) lines.push(` resolvedAt: ${r.resolvedAt}`);
|
|
939
|
+
if (r.sellerNote) lines.push(` sellerNote: ${r.sellerNote}`);
|
|
940
|
+
return lines.join("\n");
|
|
941
|
+
}
|
|
942
|
+
|
|
546
943
|
// src/commands/scheduled-charges.ts
|
|
547
|
-
async function
|
|
944
|
+
async function getClient5(opts) {
|
|
548
945
|
if (opts.garu) return opts.garu;
|
|
549
946
|
const auth = await resolveAuth({
|
|
550
947
|
...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
|
|
@@ -581,7 +978,7 @@ function assertPixAutomaticRequirements(opts) {
|
|
|
581
978
|
}
|
|
582
979
|
async function scheduledChargesCreateCommand(opts) {
|
|
583
980
|
assertPixAutomaticRequirements(opts);
|
|
584
|
-
const garu = await
|
|
981
|
+
const garu = await getClient5(opts);
|
|
585
982
|
const params = {
|
|
586
983
|
customerId: opts.customerId,
|
|
587
984
|
amount: opts.amount,
|
|
@@ -603,7 +1000,7 @@ async function scheduledChargesCreateCommand(opts) {
|
|
|
603
1000
|
return charge;
|
|
604
1001
|
}
|
|
605
1002
|
async function scheduledChargesListCommand(opts) {
|
|
606
|
-
const garu = await
|
|
1003
|
+
const garu = await getClient5(opts);
|
|
607
1004
|
const params = {};
|
|
608
1005
|
if (opts.page !== void 0) params.page = opts.page;
|
|
609
1006
|
if (opts.limit !== void 0) params.limit = opts.limit;
|
|
@@ -620,13 +1017,13 @@ async function scheduledChargesListCommand(opts) {
|
|
|
620
1017
|
return result;
|
|
621
1018
|
}
|
|
622
1019
|
async function scheduledChargesGetCommand(opts) {
|
|
623
|
-
const garu = await
|
|
1020
|
+
const garu = await getClient5(opts);
|
|
624
1021
|
const detail = await garu.scheduledCharges.get(opts.id);
|
|
625
1022
|
printResult(detail, { ...opts, prettyPrint: prettyScheduledChargeDetail });
|
|
626
1023
|
return detail;
|
|
627
1024
|
}
|
|
628
1025
|
async function scheduledChargesPostponeCommand(opts) {
|
|
629
|
-
const garu = await
|
|
1026
|
+
const garu = await getClient5(opts);
|
|
630
1027
|
const params = { newDueDate: opts.newDueDate };
|
|
631
1028
|
if (opts.reason !== void 0) params.reason = opts.reason;
|
|
632
1029
|
const charge = await garu.scheduledCharges.postpone(opts.id, params);
|
|
@@ -634,7 +1031,7 @@ async function scheduledChargesPostponeCommand(opts) {
|
|
|
634
1031
|
return charge;
|
|
635
1032
|
}
|
|
636
1033
|
async function scheduledChargesPauseCommand(opts) {
|
|
637
|
-
const garu = await
|
|
1034
|
+
const garu = await getClient5(opts);
|
|
638
1035
|
const params = {};
|
|
639
1036
|
if (opts.reason !== void 0) params.reason = opts.reason;
|
|
640
1037
|
const charge = await garu.scheduledCharges.pause(opts.id, params);
|
|
@@ -642,13 +1039,13 @@ async function scheduledChargesPauseCommand(opts) {
|
|
|
642
1039
|
return charge;
|
|
643
1040
|
}
|
|
644
1041
|
async function scheduledChargesResumeCommand(opts) {
|
|
645
|
-
const garu = await
|
|
1042
|
+
const garu = await getClient5(opts);
|
|
646
1043
|
const charge = await garu.scheduledCharges.resume(opts.id);
|
|
647
1044
|
printResult(charge, { ...opts, prettyPrint: prettyScheduledCharge });
|
|
648
1045
|
return charge;
|
|
649
1046
|
}
|
|
650
1047
|
async function scheduledChargesMarkPaidCommand(opts) {
|
|
651
|
-
const garu = await
|
|
1048
|
+
const garu = await getClient5(opts);
|
|
652
1049
|
const params = { paymentDate: opts.paymentDate };
|
|
653
1050
|
if (opts.externalReference !== void 0) params.externalReference = opts.externalReference;
|
|
654
1051
|
if (opts.cycleNumber !== void 0) params.cycleNumber = opts.cycleNumber;
|
|
@@ -657,7 +1054,7 @@ async function scheduledChargesMarkPaidCommand(opts) {
|
|
|
657
1054
|
return charge;
|
|
658
1055
|
}
|
|
659
1056
|
async function scheduledChargesCancelRecurrenceCommand(opts) {
|
|
660
|
-
const garu = await
|
|
1057
|
+
const garu = await getClient5(opts);
|
|
661
1058
|
const params = {};
|
|
662
1059
|
if (opts.reason !== void 0) params.reason = opts.reason;
|
|
663
1060
|
const charge = await garu.scheduledCharges.cancelRecurrence(opts.id, params);
|
|
@@ -665,14 +1062,14 @@ async function scheduledChargesCancelRecurrenceCommand(opts) {
|
|
|
665
1062
|
return charge;
|
|
666
1063
|
}
|
|
667
1064
|
async function scheduledChargesCancelAtPeriodEndCommand(opts) {
|
|
668
|
-
const garu = await
|
|
1065
|
+
const garu = await getClient5(opts);
|
|
669
1066
|
const params = { enabled: opts.enabled };
|
|
670
1067
|
const charge = await garu.scheduledCharges.setCancelAtPeriodEnd(opts.id, params);
|
|
671
1068
|
printResult(charge, { ...opts, prettyPrint: prettyScheduledCharge });
|
|
672
1069
|
return charge;
|
|
673
1070
|
}
|
|
674
1071
|
async function scheduledChargesChangePaymentMethodCommand(opts) {
|
|
675
|
-
const garu = await
|
|
1072
|
+
const garu = await getClient5(opts);
|
|
676
1073
|
const params = {
|
|
677
1074
|
paymentMethodId: opts.paymentMethodId
|
|
678
1075
|
};
|
|
@@ -681,13 +1078,13 @@ async function scheduledChargesChangePaymentMethodCommand(opts) {
|
|
|
681
1078
|
return charge;
|
|
682
1079
|
}
|
|
683
1080
|
async function scheduledChargesClearPaymentMethodCommand(opts) {
|
|
684
|
-
const garu = await
|
|
1081
|
+
const garu = await getClient5(opts);
|
|
685
1082
|
const charge = await garu.scheduledCharges.clearPaymentMethod(opts.id);
|
|
686
1083
|
printResult(charge, { ...opts, prettyPrint: prettyScheduledCharge });
|
|
687
1084
|
return charge;
|
|
688
1085
|
}
|
|
689
1086
|
async function scheduledChargesAttemptsCommand(opts) {
|
|
690
|
-
const garu = await
|
|
1087
|
+
const garu = await getClient5(opts);
|
|
691
1088
|
const params = {};
|
|
692
1089
|
if (opts.page !== void 0) params.page = opts.page;
|
|
693
1090
|
if (opts.limit !== void 0) params.limit = opts.limit;
|
|
@@ -697,7 +1094,7 @@ async function scheduledChargesAttemptsCommand(opts) {
|
|
|
697
1094
|
return result;
|
|
698
1095
|
}
|
|
699
1096
|
async function scheduledChargesChargeNowCommand(opts) {
|
|
700
|
-
const garu = await
|
|
1097
|
+
const garu = await getClient5(opts);
|
|
701
1098
|
const result = await garu.scheduledCharges.chargeNow(opts.id);
|
|
702
1099
|
printResult(result, { ...opts, prettyPrint: prettyChargeNow });
|
|
703
1100
|
if (result.outcome === "failed" || result.outcome === "not_sent") {
|
|
@@ -761,7 +1158,7 @@ function prettyChargeNow(result) {
|
|
|
761
1158
|
if (result.reason) lines.push(` reason: ${result.reason}`);
|
|
762
1159
|
return lines.join("\n");
|
|
763
1160
|
}
|
|
764
|
-
async function
|
|
1161
|
+
async function getClient6(opts) {
|
|
765
1162
|
if (opts.garu) return opts.garu;
|
|
766
1163
|
const auth = await resolveAuth({
|
|
767
1164
|
...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
|
|
@@ -773,7 +1170,7 @@ async function getClient4(opts) {
|
|
|
773
1170
|
});
|
|
774
1171
|
}
|
|
775
1172
|
async function webhooksEventsListCommand(opts) {
|
|
776
|
-
const garu = await
|
|
1173
|
+
const garu = await getClient6(opts);
|
|
777
1174
|
const params = {};
|
|
778
1175
|
if (opts.page !== void 0) params.page = opts.page;
|
|
779
1176
|
if (opts.limit !== void 0) params.limit = opts.limit;
|
|
@@ -785,19 +1182,19 @@ async function webhooksEventsListCommand(opts) {
|
|
|
785
1182
|
return result;
|
|
786
1183
|
}
|
|
787
1184
|
async function webhooksEventsGetCommand(opts) {
|
|
788
|
-
const garu = await
|
|
1185
|
+
const garu = await getClient6(opts);
|
|
789
1186
|
const event = await garu.webhookEvents.get(opts.id);
|
|
790
1187
|
printResult(event, { ...opts, prettyPrint: prettyWebhookEvent });
|
|
791
1188
|
return event;
|
|
792
1189
|
}
|
|
793
1190
|
async function webhooksEventsRetryCommand(opts) {
|
|
794
|
-
const garu = await
|
|
1191
|
+
const garu = await getClient6(opts);
|
|
795
1192
|
const event = await garu.webhookEvents.retry(opts.id);
|
|
796
1193
|
printResult(event, { ...opts, prettyPrint: prettyWebhookEvent });
|
|
797
1194
|
return event;
|
|
798
1195
|
}
|
|
799
1196
|
async function webhooksEventsResendCommand(opts) {
|
|
800
|
-
const garu = await
|
|
1197
|
+
const garu = await getClient6(opts);
|
|
801
1198
|
const clone = await garu.webhookEvents.resend(opts.id);
|
|
802
1199
|
printSuccess(`Resent event ${opts.id} \u2192 new event ${clone.id}`, opts);
|
|
803
1200
|
printResult(clone, { ...opts, prettyPrint: prettyWebhookEvent });
|
|
@@ -839,135 +1236,6 @@ function prettyWebhookEvent(event) {
|
|
|
839
1236
|
return lines.join("\n");
|
|
840
1237
|
}
|
|
841
1238
|
|
|
842
|
-
// src/lib/parse.ts
|
|
843
|
-
function parsePositiveIntId(raw, label) {
|
|
844
|
-
const id = Number.parseInt(raw, 10);
|
|
845
|
-
if (!Number.isFinite(id) || id <= 0) {
|
|
846
|
-
throw new CliError("invalid_input", `${label} must be a positive integer (got '${raw}')`);
|
|
847
|
-
}
|
|
848
|
-
return id;
|
|
849
|
-
}
|
|
850
|
-
function parsePaymentMethod(raw) {
|
|
851
|
-
if (raw === "pix" || raw === "credit_card" || raw === "boleto") return raw;
|
|
852
|
-
throw new CliError(
|
|
853
|
-
"invalid_input",
|
|
854
|
-
`--type must be 'pix', 'credit_card', or 'boleto' (got '${raw}')`
|
|
855
|
-
);
|
|
856
|
-
}
|
|
857
|
-
function parseWebhookEventStatus(raw) {
|
|
858
|
-
if (raw === "pending" || raw === "success" || raw === "failed") return raw;
|
|
859
|
-
throw new CliError(
|
|
860
|
-
"invalid_input",
|
|
861
|
-
`--status must be 'pending', 'success', or 'failed' (got '${raw}')`
|
|
862
|
-
);
|
|
863
|
-
}
|
|
864
|
-
function parseScheduledChargeType(raw) {
|
|
865
|
-
if (raw === "one_time" || raw === "recurring") return raw;
|
|
866
|
-
throw new CliError("invalid_input", `--type must be 'one_time' or 'recurring' (got '${raw}')`);
|
|
867
|
-
}
|
|
868
|
-
var SCHEDULED_PAYMENT_METHODS = [
|
|
869
|
-
"pix",
|
|
870
|
-
"boleto",
|
|
871
|
-
"card",
|
|
872
|
-
"pix_automatic"
|
|
873
|
-
];
|
|
874
|
-
function parseCsvList(raw) {
|
|
875
|
-
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
876
|
-
}
|
|
877
|
-
function parseScheduledPaymentMethods(raw) {
|
|
878
|
-
const parts = parseCsvList(raw);
|
|
879
|
-
if (parts.length === 0) {
|
|
880
|
-
throw new CliError(
|
|
881
|
-
"invalid_input",
|
|
882
|
-
`--methods must list at least one of: ${SCHEDULED_PAYMENT_METHODS.join(", ")}`
|
|
883
|
-
);
|
|
884
|
-
}
|
|
885
|
-
for (const p of parts) {
|
|
886
|
-
if (!SCHEDULED_PAYMENT_METHODS.includes(p)) {
|
|
887
|
-
throw new CliError(
|
|
888
|
-
"invalid_input",
|
|
889
|
-
`--methods entries must be one of ${SCHEDULED_PAYMENT_METHODS.join(", ")} (got '${p}')`
|
|
890
|
-
);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
return parts;
|
|
894
|
-
}
|
|
895
|
-
var RECURRENCE_INTERVALS = [
|
|
896
|
-
"weekly",
|
|
897
|
-
"biweekly",
|
|
898
|
-
"monthly",
|
|
899
|
-
"bimonthly",
|
|
900
|
-
"quarterly",
|
|
901
|
-
"biannual",
|
|
902
|
-
"yearly"
|
|
903
|
-
];
|
|
904
|
-
function parseRecurrenceInterval(raw) {
|
|
905
|
-
if (RECURRENCE_INTERVALS.includes(raw)) return raw;
|
|
906
|
-
throw new CliError(
|
|
907
|
-
"invalid_input",
|
|
908
|
-
`--recurrence-interval must be one of ${RECURRENCE_INTERVALS.join(", ")} (got '${raw}')`
|
|
909
|
-
);
|
|
910
|
-
}
|
|
911
|
-
var SCHEDULED_CHARGE_STATUSES = [
|
|
912
|
-
"scheduled",
|
|
913
|
-
"due_today",
|
|
914
|
-
"overdue",
|
|
915
|
-
"paid",
|
|
916
|
-
"paused",
|
|
917
|
-
"canceled",
|
|
918
|
-
"trial",
|
|
919
|
-
"pending_tokenization",
|
|
920
|
-
"recurrence_canceled"
|
|
921
|
-
];
|
|
922
|
-
function parseScheduledChargeStatus(raw) {
|
|
923
|
-
if (SCHEDULED_CHARGE_STATUSES.includes(raw)) return raw;
|
|
924
|
-
throw new CliError(
|
|
925
|
-
"invalid_input",
|
|
926
|
-
`--status must be one of ${SCHEDULED_CHARGE_STATUSES.join(", ")} (got '${raw}')`
|
|
927
|
-
);
|
|
928
|
-
}
|
|
929
|
-
function parseIntInRange(raw, label, min, max) {
|
|
930
|
-
const trimmed = raw.trim();
|
|
931
|
-
const n = Number.parseInt(trimmed, 10);
|
|
932
|
-
if (!Number.isFinite(n) || String(n) !== trimmed || n < min || n > max) {
|
|
933
|
-
throw new CliError(
|
|
934
|
-
"invalid_input",
|
|
935
|
-
`${label} must be an integer between ${min} and ${max} (got '${raw}')`
|
|
936
|
-
);
|
|
937
|
-
}
|
|
938
|
-
return n;
|
|
939
|
-
}
|
|
940
|
-
function parseNonNegativeInt(raw, label) {
|
|
941
|
-
const trimmed = raw.trim();
|
|
942
|
-
const n = Number.parseInt(trimmed, 10);
|
|
943
|
-
if (!Number.isFinite(n) || String(n) !== trimmed || n < 0) {
|
|
944
|
-
throw new CliError("invalid_input", `${label} must be a non-negative integer (got '${raw}')`);
|
|
945
|
-
}
|
|
946
|
-
return n;
|
|
947
|
-
}
|
|
948
|
-
function parseAmountBrl(raw) {
|
|
949
|
-
const n = Number(raw);
|
|
950
|
-
if (!Number.isFinite(n) || n <= 0) {
|
|
951
|
-
throw new CliError(
|
|
952
|
-
"invalid_input",
|
|
953
|
-
`--amount must be a positive decimal in BRL, e.g. 297.50 (got '${raw}')`
|
|
954
|
-
);
|
|
955
|
-
}
|
|
956
|
-
return n;
|
|
957
|
-
}
|
|
958
|
-
function parseMetadata(raw) {
|
|
959
|
-
let parsed;
|
|
960
|
-
try {
|
|
961
|
-
parsed = JSON.parse(raw);
|
|
962
|
-
} catch {
|
|
963
|
-
throw new CliError("invalid_input", `--metadata must be valid JSON (got '${raw}')`);
|
|
964
|
-
}
|
|
965
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
966
|
-
throw new CliError("invalid_input", `--metadata must be a JSON object`);
|
|
967
|
-
}
|
|
968
|
-
return parsed;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
1239
|
// src/index.ts
|
|
972
1240
|
function buildCli() {
|
|
973
1241
|
const program = new commander.Command();
|
|
@@ -979,7 +1247,7 @@ Recipes:
|
|
|
979
1247
|
|
|
980
1248
|
# 1. Create a product with Pix Autom\xE1tico enabled
|
|
981
1249
|
garu products create \\
|
|
982
|
-
--name "Plano Mensal" --value
|
|
1250
|
+
--name "Plano Mensal" --value 49.90 \\
|
|
983
1251
|
--pix --credit-card --pix-automatic \\
|
|
984
1252
|
--subscription --subscription-type monthly
|
|
985
1253
|
|
|
@@ -1018,16 +1286,19 @@ Recipes:
|
|
|
1018
1286
|
);
|
|
1019
1287
|
});
|
|
1020
1288
|
const charges = program.command("charges").description("Create, fetch, and refund charges");
|
|
1021
|
-
charges.command("list").description("List charges with pagination and filters").option("--page <n>", "page number (1-based)", (v) => parseInt(v, 10)).option("--limit <n>", "items per page (1-100)", (v) => parseInt(v, 10)).option(
|
|
1289
|
+
charges.command("list").description("List charges with pagination and filters").option("--page <n>", "page number (1-based)", (v) => parseInt(v, 10)).option("--limit <n>", "items per page (1-100)", (v) => parseInt(v, 10)).option(
|
|
1290
|
+
"--status <status>",
|
|
1291
|
+
"filter by status: pending, authorized, paid, failed, expired, canceled, refund_pending, refunded, chargeback"
|
|
1292
|
+
).option("--search <query>", "search by customer name, email, or document").option("--payment-method <method>", "filter: pix, credit_card, boleto").action(
|
|
1022
1293
|
async (cmdOpts) => {
|
|
1023
1294
|
const base = toCommandOptions(program);
|
|
1024
1295
|
await chargesListCommand({
|
|
1025
1296
|
...base,
|
|
1026
1297
|
page: cmdOpts.page,
|
|
1027
1298
|
limit: cmdOpts.limit,
|
|
1028
|
-
status: cmdOpts.status,
|
|
1299
|
+
status: cmdOpts.status ? parseChargeStatus(cmdOpts.status) : void 0,
|
|
1029
1300
|
search: cmdOpts.search,
|
|
1030
|
-
paymentMethod: cmdOpts.paymentMethod
|
|
1301
|
+
paymentMethod: cmdOpts.paymentMethod ? parseChargePaymentMethodFilter(cmdOpts.paymentMethod) : void 0
|
|
1031
1302
|
}).catch((err) => printErrorAndExit(err, base));
|
|
1032
1303
|
}
|
|
1033
1304
|
);
|
|
@@ -1053,20 +1324,21 @@ Recipes:
|
|
|
1053
1324
|
idempotencyKey: cmdOpts.idempotencyKey
|
|
1054
1325
|
}).catch((err) => printErrorAndExit(err, base));
|
|
1055
1326
|
});
|
|
1056
|
-
charges.command("get <id>").description("Fetch a single charge by
|
|
1327
|
+
charges.command("get <id>").description("Fetch a single charge by its uuid").action(async (id) => {
|
|
1057
1328
|
const base = toCommandOptions(program);
|
|
1058
|
-
await chargesGetCommand({ ...base, id
|
|
1059
|
-
(err) => printErrorAndExit(err, base)
|
|
1060
|
-
);
|
|
1329
|
+
await chargesGetCommand({ ...base, id }).catch((err) => printErrorAndExit(err, base));
|
|
1061
1330
|
});
|
|
1062
|
-
charges.command("refund <id>").description("Refund a charge (full or partial)").option(
|
|
1331
|
+
charges.command("refund <id>").description("Refund a charge by its uuid (full or partial)").option(
|
|
1332
|
+
"--amount <reais>",
|
|
1333
|
+
"partial refund amount in decimal BRL, e.g. 10.00",
|
|
1334
|
+
(v) => parseNonNegativeBrl(v, "--amount")
|
|
1335
|
+
).option("--reason <text>", "optional refund reason").action(async (id, cmdOpts) => {
|
|
1063
1336
|
const base = toCommandOptions(program);
|
|
1064
1337
|
await chargesRefundCommand({
|
|
1065
1338
|
...base,
|
|
1066
|
-
id
|
|
1339
|
+
id,
|
|
1067
1340
|
amount: cmdOpts.amount,
|
|
1068
|
-
reason: cmdOpts.reason
|
|
1069
|
-
idempotencyKey: cmdOpts.idempotencyKey
|
|
1341
|
+
reason: cmdOpts.reason
|
|
1070
1342
|
}).catch((err) => printErrorAndExit(err, base));
|
|
1071
1343
|
});
|
|
1072
1344
|
const scheduled = program.command("scheduled-charges").description("Schedule, inspect, and act on future-dated charges");
|
|
@@ -1283,11 +1555,149 @@ Recipes:
|
|
|
1283
1555
|
id: parsePositiveIntId(id, "Webhook event ID")
|
|
1284
1556
|
}).catch((err) => printErrorAndExit(err, base));
|
|
1285
1557
|
});
|
|
1558
|
+
const installmentPlans = program.command("installment-plans").description("Sell a product as boleto parcelado (carn\xEA) and manage its installments");
|
|
1559
|
+
installmentPlans.command("create").description("Sell a product as a carn\xEA (installments of boleto slips)").requiredOption("--product-id <uuid>", "product uuid (must have boleto parcelado enabled)").requiredOption(
|
|
1560
|
+
"--customer-id <n>",
|
|
1561
|
+
"customer id",
|
|
1562
|
+
(v) => parsePositiveIntId(v, "--customer-id")
|
|
1563
|
+
).requiredOption(
|
|
1564
|
+
"--installments <n>",
|
|
1565
|
+
"2-12 installments",
|
|
1566
|
+
(v) => parseIntInRange(v, "--installments", 2, 12)
|
|
1567
|
+
).option("--first-due-date <yyyy-mm-dd>", "first installment due date (default: today)").option(
|
|
1568
|
+
"--affiliate-id <n>",
|
|
1569
|
+
"attribute the sale to this affiliate (fixed for the whole plan)",
|
|
1570
|
+
(v) => parsePositiveIntId(v, "--affiliate-id")
|
|
1571
|
+
).option("--idempotency-key <key>", "idempotency key (auto-generated if omitted)").action(async (cmdOpts) => {
|
|
1572
|
+
const base = toCommandOptions(program);
|
|
1573
|
+
await installmentPlansCreateCommand({
|
|
1574
|
+
...base,
|
|
1575
|
+
productId: cmdOpts.productId,
|
|
1576
|
+
customerId: cmdOpts.customerId,
|
|
1577
|
+
installments: cmdOpts.installments,
|
|
1578
|
+
firstDueDate: cmdOpts.firstDueDate,
|
|
1579
|
+
affiliateId: cmdOpts.affiliateId,
|
|
1580
|
+
idempotencyKey: cmdOpts.idempotencyKey
|
|
1581
|
+
}).catch((err) => printErrorAndExit(err, base));
|
|
1582
|
+
});
|
|
1583
|
+
installmentPlans.command("list").description("List installment plans (carn\xEAs) with pagination and filters").option("--page <n>", "page number (1-based)", (v) => parseInt(v, 10)).option("--limit <n>", "items per page (1-100)", (v) => parseInt(v, 10)).option(
|
|
1584
|
+
"--customer-id <n>",
|
|
1585
|
+
"filter by customer id",
|
|
1586
|
+
(v) => parsePositiveIntId(v, "--customer-id")
|
|
1587
|
+
).option("--product-id <uuid>", "filter by product uuid").option(
|
|
1588
|
+
"--status <status>",
|
|
1589
|
+
"filter by status (repeatable)",
|
|
1590
|
+
(val, acc) => acc.concat(val),
|
|
1591
|
+
[]
|
|
1592
|
+
).option("--due-from <yyyy-mm-dd>", "lower bound for the first installment due date").option("--due-to <yyyy-mm-dd>", "upper bound for the first installment due date").action(
|
|
1593
|
+
async (cmdOpts) => {
|
|
1594
|
+
const base = toCommandOptions(program);
|
|
1595
|
+
await installmentPlansListCommand({
|
|
1596
|
+
...base,
|
|
1597
|
+
page: cmdOpts.page,
|
|
1598
|
+
limit: cmdOpts.limit,
|
|
1599
|
+
customerId: cmdOpts.customerId,
|
|
1600
|
+
productId: cmdOpts.productId,
|
|
1601
|
+
status: cmdOpts.status.length > 0 ? cmdOpts.status.map(parseInstallmentPlanStatus) : void 0,
|
|
1602
|
+
dueFrom: cmdOpts.dueFrom,
|
|
1603
|
+
dueTo: cmdOpts.dueTo
|
|
1604
|
+
}).catch((err) => printErrorAndExit(err, base));
|
|
1605
|
+
}
|
|
1606
|
+
);
|
|
1607
|
+
installmentPlans.command("get <uuid>").description("Fetch a carn\xEA with every installment: due date, status, barcode and PDF").action(async (uuid) => {
|
|
1608
|
+
const base = toCommandOptions(program);
|
|
1609
|
+
await installmentPlansGetCommand({ ...base, uuid }).catch(
|
|
1610
|
+
(err) => printErrorAndExit(err, base)
|
|
1611
|
+
);
|
|
1612
|
+
});
|
|
1613
|
+
installmentPlans.command("reissue <uuid> <number>").description("Issue a segunda via for one installment once its slip has expired").action(async (uuid, number) => {
|
|
1614
|
+
const base = toCommandOptions(program);
|
|
1615
|
+
await installmentPlansReissueCommand({
|
|
1616
|
+
...base,
|
|
1617
|
+
uuid,
|
|
1618
|
+
number: parsePositiveIntId(number, "installment number")
|
|
1619
|
+
}).catch((err) => printErrorAndExit(err, base));
|
|
1620
|
+
});
|
|
1621
|
+
installmentPlans.command("postpone <uuid> <number>").description("Move one installment to a later due date (its siblings keep theirs)").requiredOption("--new-due-date <yyyy-mm-dd>", "new due date for this installment").action(async (uuid, number, cmdOpts) => {
|
|
1622
|
+
const base = toCommandOptions(program);
|
|
1623
|
+
await installmentPlansPostponeCommand({
|
|
1624
|
+
...base,
|
|
1625
|
+
uuid,
|
|
1626
|
+
number: parsePositiveIntId(number, "installment number"),
|
|
1627
|
+
newDueDate: cmdOpts.newDueDate
|
|
1628
|
+
}).catch((err) => printErrorAndExit(err, base));
|
|
1629
|
+
});
|
|
1630
|
+
installmentPlans.command("mark-paid <uuid> <number>").description("Record an installment as paid when the buyer paid but the webhook never arrived").action(async (uuid, number) => {
|
|
1631
|
+
const base = toCommandOptions(program);
|
|
1632
|
+
await installmentPlansMarkPaidCommand({
|
|
1633
|
+
...base,
|
|
1634
|
+
uuid,
|
|
1635
|
+
number: parsePositiveIntId(number, "installment number")
|
|
1636
|
+
}).catch((err) => printErrorAndExit(err, base));
|
|
1637
|
+
});
|
|
1638
|
+
installmentPlans.command("cancel <uuid>").description("Cancel a carn\xEA \u2014 stops emission/reminders and cancels open provider slips").option("--note <text>", "optional note recorded on the plan").action(async (uuid, cmdOpts) => {
|
|
1639
|
+
const base = toCommandOptions(program);
|
|
1640
|
+
await installmentPlansCancelCommand({ ...base, uuid, note: cmdOpts.note }).catch(
|
|
1641
|
+
(err) => printErrorAndExit(err, base)
|
|
1642
|
+
);
|
|
1643
|
+
});
|
|
1644
|
+
installmentPlans.command("request-refund <uuid>").description(
|
|
1645
|
+
"Ask for a carn\xEA to be refunded (Garu records the request; transfer the money yourself, then confirm with `garu refund-requests confirm`)"
|
|
1646
|
+
).option(
|
|
1647
|
+
"--amount <reais>",
|
|
1648
|
+
"defaults to everything the carn\xEA has collected",
|
|
1649
|
+
(v) => parseNonNegativeBrl(v, "--amount")
|
|
1650
|
+
).option("--reason <text>", "optional reason").action(async (uuid, cmdOpts) => {
|
|
1651
|
+
const base = toCommandOptions(program);
|
|
1652
|
+
await installmentPlansRequestRefundCommand({
|
|
1653
|
+
...base,
|
|
1654
|
+
uuid,
|
|
1655
|
+
amount: cmdOpts.amount,
|
|
1656
|
+
reason: cmdOpts.reason
|
|
1657
|
+
}).catch((err) => printErrorAndExit(err, base));
|
|
1658
|
+
});
|
|
1659
|
+
const refundRequests = program.command("refund-requests").description("Inspect and resolve refund requests (carn\xEA and Pix/boleto)");
|
|
1660
|
+
refundRequests.command("list").description("List refund requests with pagination and filters").option("--page <n>", "page number (1-based)", (v) => parseInt(v, 10)).option("--limit <n>", "items per page (1-100)", (v) => parseInt(v, 10)).option("--plan-id <uuid>", "filter by carn\xEA uuid").option("--charge-id <uuid>", "filter by charge uuid (Pix/boleto)").option(
|
|
1661
|
+
"--status <status>",
|
|
1662
|
+
"filter by status (repeatable): pending, confirmed, rejected",
|
|
1663
|
+
(val, acc) => acc.concat(val),
|
|
1664
|
+
[]
|
|
1665
|
+
).action(
|
|
1666
|
+
async (cmdOpts) => {
|
|
1667
|
+
const base = toCommandOptions(program);
|
|
1668
|
+
await refundRequestsListCommand({
|
|
1669
|
+
...base,
|
|
1670
|
+
page: cmdOpts.page,
|
|
1671
|
+
limit: cmdOpts.limit,
|
|
1672
|
+
planId: cmdOpts.planId,
|
|
1673
|
+
chargeId: cmdOpts.chargeId,
|
|
1674
|
+
status: cmdOpts.status.length > 0 ? cmdOpts.status.map(parseRefundRequestStatus) : void 0
|
|
1675
|
+
}).catch((err) => printErrorAndExit(err, base));
|
|
1676
|
+
}
|
|
1677
|
+
);
|
|
1678
|
+
refundRequests.command("get <uuid>").description("Fetch a single refund request").action(async (uuid) => {
|
|
1679
|
+
const base = toCommandOptions(program);
|
|
1680
|
+
await refundRequestsGetCommand({ ...base, uuid }).catch(
|
|
1681
|
+
(err) => printErrorAndExit(err, base)
|
|
1682
|
+
);
|
|
1683
|
+
});
|
|
1684
|
+
refundRequests.command("confirm <uuid>").description("Record that you transferred the money back \u2014 call this AFTER the transfer").option("--note <text>", "optional note, e.g. a bank reference").action(async (uuid, cmdOpts) => {
|
|
1685
|
+
const base = toCommandOptions(program);
|
|
1686
|
+
await refundRequestsConfirmCommand({ ...base, uuid, note: cmdOpts.note }).catch(
|
|
1687
|
+
(err) => printErrorAndExit(err, base)
|
|
1688
|
+
);
|
|
1689
|
+
});
|
|
1690
|
+
refundRequests.command("reject <uuid>").description("Decline the request \u2014 the carn\xEA or charge is left untouched").option("--note <text>", "optional note explaining the rejection").action(async (uuid, cmdOpts) => {
|
|
1691
|
+
const base = toCommandOptions(program);
|
|
1692
|
+
await refundRequestsRejectCommand({ ...base, uuid, note: cmdOpts.note }).catch(
|
|
1693
|
+
(err) => printErrorAndExit(err, base)
|
|
1694
|
+
);
|
|
1695
|
+
});
|
|
1286
1696
|
const products = program.command("products").description("Create and update products");
|
|
1287
1697
|
products.command("create").description("Create a product").requiredOption("--name <name>", "product name").option(
|
|
1288
|
-
"--value <
|
|
1289
|
-
"price in
|
|
1290
|
-
(v) =>
|
|
1698
|
+
"--value <reais>",
|
|
1699
|
+
"price in reais / decimal BRL (e.g. 49.90)",
|
|
1700
|
+
(v) => parseNonNegativeBrl(v, "--value")
|
|
1291
1701
|
).option("--description <text>", "product description").option("--image <url>", "HTTPS URL of the product cover image").option("--tags <list>", "comma-separated tags", parseCsvList).option("--pix", "accept PIX").option("--no-pix", "do not accept PIX").option("--boleto", "accept boleto").option("--no-boleto", "do not accept boleto").option("--credit-card", "accept credit card").option("--no-credit-card", "do not accept credit card").option("--pix-automatic", "expose Pix Autom\xE1tico on the subscription checkout").option("--no-pix-automatic", "do not expose Pix Autom\xE1tico").option(
|
|
1292
1702
|
"--installments <n>",
|
|
1293
1703
|
"max credit-card installments",
|
|
@@ -1316,9 +1726,9 @@ Recipes:
|
|
|
1316
1726
|
products.command("update <id>").description(
|
|
1317
1727
|
"Update a product (partial \u2014 only the flags you pass change). <id> is the numeric id or UUID"
|
|
1318
1728
|
).option("--name <name>", "product name").option(
|
|
1319
|
-
"--value <
|
|
1320
|
-
"price in
|
|
1321
|
-
(v) =>
|
|
1729
|
+
"--value <reais>",
|
|
1730
|
+
"price in reais / decimal BRL (e.g. 49.90)",
|
|
1731
|
+
(v) => parseNonNegativeBrl(v, "--value")
|
|
1322
1732
|
).option("--description <text>", "product description").option("--image <url>", "HTTPS URL of the product cover image").option("--tags <list>", "comma-separated tags", parseCsvList).option("--pix", "accept PIX").option("--no-pix", "do not accept PIX").option("--boleto", "accept boleto").option("--no-boleto", "do not accept boleto").option("--credit-card", "accept credit card").option("--no-credit-card", "do not accept credit card").option("--pix-automatic", "expose Pix Autom\xE1tico on the subscription checkout").option("--no-pix-automatic", "do not expose Pix Autom\xE1tico").option(
|
|
1323
1733
|
"--installments <n>",
|
|
1324
1734
|
"max credit-card installments",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@garuhq/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Official command-line interface for the Garu payment gateway.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://garu.com.br",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"prepublishOnly": "npm run check:version-sync && npm run typecheck && npm test && npm run build"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@garuhq/node": "
|
|
49
|
+
"@garuhq/node": "1.1.0",
|
|
50
50
|
"@inquirer/prompts": "8.4.1",
|
|
51
51
|
"commander": "12.0.0",
|
|
52
52
|
"picocolors": "1.0.0",
|