@garuhq/node 0.12.0 → 0.13.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 +47 -0
- package/README.md +44 -1
- package/dist/index.cjs +107 -37
- package/dist/index.d.cts +80 -2
- package/dist/index.d.ts +80 -2
- package/dist/index.js +107 -37
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,53 @@
|
|
|
3
3
|
All notable changes to `@garuhq/node` 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.13.0] — 2026-05-25
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- `scheduledCharges.chargeNow(id)` — `POST /api/scheduled-charges/{id}/charge-now`.
|
|
11
|
+
Force-bills the current cycle right now instead of waiting for its due
|
|
12
|
+
date, running the same dispatch the daily billing cron would (customer
|
|
13
|
+
email/notification + outbound webhook + timeline event). Allowed only
|
|
14
|
+
from a billable status (`scheduled` / `due_today`); a recurring series
|
|
15
|
+
must have an open cycle. **Idempotent** — a cycle whose d-day was
|
|
16
|
+
already dispatched reports `already_sent` and does not re-charge.
|
|
17
|
+
Returns `{ outcome, cycleNumber, reason?, message }`:
|
|
18
|
+
- `outcome` is `'dispatched' | 'already_sent' | 'not_sent' | 'failed'`.
|
|
19
|
+
- `reason` (on `not_sent` / `failed`) is one of the documented literals
|
|
20
|
+
(`no_email`, `lock_lost`, `no_saved_payment_method`, `card_expired`,
|
|
21
|
+
`payment_method_missing`, `customer_missing`) or a raw gateway decline
|
|
22
|
+
code.
|
|
23
|
+
- `message` is a ready-to-show pt-BR string.
|
|
24
|
+
- `ChargeNowOutcome`, `ChargeNowReason`, and `ChargeNowResult` types
|
|
25
|
+
exported from the package root.
|
|
26
|
+
- `maxRecoveryDays?: number` (integer 1–365) on
|
|
27
|
+
`CreateScheduledChargeParams` — caps how many days past `dueDate` the
|
|
28
|
+
daily recovery sweep will still auto-bill a missed charge. Omit for the
|
|
29
|
+
system default (14). Also surfaced on the scheduled charge object as
|
|
30
|
+
`ScheduledChargeRecord.maxRecoveryDays: number | null`.
|
|
31
|
+
|
|
32
|
+
## [0.12.1] — 2026-05-19
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- `webhookEvents.resend(id)` now auto-attaches `X-Idempotency-Key`
|
|
37
|
+
(UUIDv4) so transient transport retries (5xx → SDK backoff) cannot
|
|
38
|
+
create duplicate clones. Previously, a 503 mid-flight after the
|
|
39
|
+
backend had already committed the clone could trigger an SDK retry
|
|
40
|
+
and produce a second clone with a different id. With the
|
|
41
|
+
idempotency key in place, the backend returns the original clone on
|
|
42
|
+
the second call within 24h. Pass `{ idempotencyKey }` to dedupe
|
|
43
|
+
across your own retry layer.
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- `ResendWebhookEventParams` type exported from the package root —
|
|
48
|
+
the optional `{ idempotencyKey?: string }` for `resend()`.
|
|
49
|
+
- README quickstart entry for `webhookEvents`
|
|
50
|
+
(`list` / `get` / `resend` / `retry`), including the audit-trail
|
|
51
|
+
contract and the SDK→gateway idempotency note.
|
|
52
|
+
|
|
6
53
|
## [0.12.0] — 2026-05-19
|
|
7
54
|
|
|
8
55
|
### Added
|
package/README.md
CHANGED
|
@@ -203,6 +203,7 @@ Bill an existing customer on a future date — one-time or recurring with card t
|
|
|
203
203
|
| `create(params)` | Create one-time or recurring schedule. Auto-attaches `X-Idempotency-Key`. |
|
|
204
204
|
| `list(params?)` | Paginated list with status / type / dueFrom / dueTo / customerId filters. |
|
|
205
205
|
| `get(id)` | Detail bundle: charge + event timeline + linked transactions. |
|
|
206
|
+
| `chargeNow(id)` | Force-bill the current cycle now instead of waiting for the due date. |
|
|
206
207
|
| `markPaid(id, params)` | Mark cycle paid (off-Garu reconciliation). |
|
|
207
208
|
| `postpone(id, params)` | Move the next cycle's due date forward. |
|
|
208
209
|
| `pause(id, params?)` / `resume(id)` | Suspend / re-enable a series. |
|
|
@@ -213,7 +214,8 @@ Bill an existing customer on a future date — one-time or recurring with card t
|
|
|
213
214
|
| `listAttempts(id, params?)` | Per-attempt billing log — every silent-charge / retry / mark-paid (v0.8.2).|
|
|
214
215
|
|
|
215
216
|
```ts
|
|
216
|
-
// Recurring with 7-day trial
|
|
217
|
+
// Recurring with 7-day trial. `maxRecoveryDays` caps how long past the due
|
|
218
|
+
// date the daily recovery sweep keeps auto-billing a missed charge (default 14).
|
|
217
219
|
const series = await garu.scheduledCharges.create({
|
|
218
220
|
customerId: 42,
|
|
219
221
|
productId: 17,
|
|
@@ -223,8 +225,17 @@ const series = await garu.scheduledCharges.create({
|
|
|
223
225
|
methods: ['card', 'pix'],
|
|
224
226
|
recurrence: { interval: 'monthly' },
|
|
225
227
|
trialDays: 7,
|
|
228
|
+
maxRecoveryDays: 30,
|
|
226
229
|
});
|
|
227
230
|
|
|
231
|
+
// Force-bill the current cycle now instead of waiting for the due date.
|
|
232
|
+
// Idempotent: a cycle already dispatched today reports `already_sent`.
|
|
233
|
+
const result = await garu.scheduledCharges.chargeNow(series.id);
|
|
234
|
+
if (result.outcome === 'failed') {
|
|
235
|
+
// result.reason is e.g. 'card_expired' or a gateway decline code
|
|
236
|
+
console.error(`${result.message} (${result.reason})`);
|
|
237
|
+
}
|
|
238
|
+
|
|
228
239
|
// Audit why cycle 3 failed (v0.8.2)
|
|
229
240
|
const { data } = await garu.scheduledCharges.listAttempts(series.id, {
|
|
230
241
|
cycleNumber: 3,
|
|
@@ -292,6 +303,38 @@ app.post('/webhooks/garu', express.raw({ type: 'application/json' }), (req, res)
|
|
|
292
303
|
> [!IMPORTANT]
|
|
293
304
|
> Always pass the raw request body to `verify()`. Parsing and re-serializing JSON will break the signature check.
|
|
294
305
|
|
|
306
|
+
## Webhook events
|
|
307
|
+
|
|
308
|
+
The seller-facing delivery log for outbound webhooks. Use it to audit deliveries, surface failures, and replay events when a customer's endpoint missed one. Webhook endpoint *configuration* (URL, subscribed events, secret) is still dashboard-only — this resource only covers the event log + manual retries.
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
// Surface anything that didn't make it through
|
|
312
|
+
const failed = await garu.webhookEvents.list({ status: 'failed', limit: 50 });
|
|
313
|
+
|
|
314
|
+
// Inspect one event end-to-end
|
|
315
|
+
const event = await garu.webhookEvents.get(42);
|
|
316
|
+
console.log(event.responseStatus, event.responseBody);
|
|
317
|
+
|
|
318
|
+
// Audit-trail-preserving replay (recommended)
|
|
319
|
+
const clone = await garu.webhookEvents.resend(42);
|
|
320
|
+
clone.id !== event.id; // true — fresh row with its own id
|
|
321
|
+
clone.manualResendOf === event.id; // true — points back at the source
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
`resend(id)` is the audit-preserving counterpart to `retry(id)` — the backend inserts a fresh event whose `manualResendOf` points back at the source, then dispatches that clone. The original row stays exactly as it was, so the historical record of the prior failure (status, response status/body, attempts) survives. Works on any source status (`success` / `failed` / `pending`).
|
|
325
|
+
|
|
326
|
+
Outbound deliveries of a resent event carry `Idempotency-Key: resend_<originalId>`, so recipient handlers can distinguish a resend from a fresh delivery both by the header prefix and by reading the response payload's `manualResendOf` field.
|
|
327
|
+
|
|
328
|
+
> [!NOTE]
|
|
329
|
+
> The SDK auto-attaches `X-Idempotency-Key` (UUIDv4) on `resend()` so transient transport retries can't create duplicate clones. Pass `{ idempotencyKey }` to dedupe across your own retry layer.
|
|
330
|
+
|
|
331
|
+
| Method | Purpose |
|
|
332
|
+
| ------------------------------- | ---------------------------------------------------------------------------------- |
|
|
333
|
+
| `list(params?)` | Paginated event log. Filter by `status`, `eventType`, `endpointId`. Newest first. |
|
|
334
|
+
| `get(id)` | One event — full payload, endpoint snapshot, most recent response. |
|
|
335
|
+
| `resend(id, params?)` | Clone-on-resend. Returns the new event; original is untouched. **Preferred.** |
|
|
336
|
+
| `retry(id)` | Legacy in-place reset (mutates the original row). Soft-deprecated. |
|
|
337
|
+
|
|
295
338
|
## Error handling
|
|
296
339
|
|
|
297
340
|
Every error extends `GaruError`. API errors include `status`, `requestId`, and `body`.
|
package/dist/index.cjs
CHANGED
|
@@ -653,9 +653,9 @@ var ScheduledCharges = class {
|
|
|
653
653
|
*/
|
|
654
654
|
async get(id) {
|
|
655
655
|
return this.http.call(
|
|
656
|
-
(signal) => this.http.client.GET(`/api/scheduled-charges/${id}`, {
|
|
657
|
-
|
|
658
|
-
)
|
|
656
|
+
(signal) => this.http.client.GET(`/api/scheduled-charges/${encodeURIComponent(id)}`, {
|
|
657
|
+
signal
|
|
658
|
+
}).then((r) => r)
|
|
659
659
|
);
|
|
660
660
|
}
|
|
661
661
|
/**
|
|
@@ -671,10 +671,13 @@ var ScheduledCharges = class {
|
|
|
671
671
|
*/
|
|
672
672
|
async postpone(id, params) {
|
|
673
673
|
return this.http.call(
|
|
674
|
-
(signal) => this.http.client.POST(
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
674
|
+
(signal) => this.http.client.POST(
|
|
675
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/postpone`,
|
|
676
|
+
{
|
|
677
|
+
body: params,
|
|
678
|
+
signal
|
|
679
|
+
}
|
|
680
|
+
).then((r) => r)
|
|
678
681
|
);
|
|
679
682
|
}
|
|
680
683
|
/**
|
|
@@ -687,10 +690,13 @@ var ScheduledCharges = class {
|
|
|
687
690
|
*/
|
|
688
691
|
async pause(id, params = {}) {
|
|
689
692
|
return this.http.call(
|
|
690
|
-
(signal) => this.http.client.POST(
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
693
|
+
(signal) => this.http.client.POST(
|
|
694
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/pause`,
|
|
695
|
+
{
|
|
696
|
+
body: params,
|
|
697
|
+
signal
|
|
698
|
+
}
|
|
699
|
+
).then((r) => r)
|
|
694
700
|
);
|
|
695
701
|
}
|
|
696
702
|
/**
|
|
@@ -701,10 +707,13 @@ var ScheduledCharges = class {
|
|
|
701
707
|
*/
|
|
702
708
|
async resume(id) {
|
|
703
709
|
return this.http.call(
|
|
704
|
-
(signal) => this.http.client.POST(
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
710
|
+
(signal) => this.http.client.POST(
|
|
711
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/resume`,
|
|
712
|
+
{
|
|
713
|
+
body: {},
|
|
714
|
+
signal
|
|
715
|
+
}
|
|
716
|
+
).then((r) => r)
|
|
708
717
|
);
|
|
709
718
|
}
|
|
710
719
|
/**
|
|
@@ -732,10 +741,52 @@ var ScheduledCharges = class {
|
|
|
732
741
|
*/
|
|
733
742
|
async markPaid(id, params) {
|
|
734
743
|
return this.http.call(
|
|
735
|
-
(signal) => this.http.client.POST(
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
744
|
+
(signal) => this.http.client.POST(
|
|
745
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/mark-paid`,
|
|
746
|
+
{
|
|
747
|
+
body: params,
|
|
748
|
+
signal
|
|
749
|
+
}
|
|
750
|
+
).then((r) => r)
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Force-bill the current cycle right now instead of waiting for its due
|
|
755
|
+
* date. Runs the same dispatch the daily billing cron would (customer
|
|
756
|
+
* email/notification + outbound webhook + timeline event). Allowed only
|
|
757
|
+
* from a billable status (`scheduled` / `due_today`); a recurring series
|
|
758
|
+
* must have an open cycle (else the backend returns 400).
|
|
759
|
+
*
|
|
760
|
+
* Idempotent: if this cycle's d-day was already dispatched it reports
|
|
761
|
+
* `already_sent` and does not re-charge. Inspect `outcome` to branch.
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* const result = await garu.scheduledCharges.chargeNow('sch_abc123');
|
|
765
|
+
* switch (result.outcome) {
|
|
766
|
+
* case 'dispatched':
|
|
767
|
+
* console.log(`Cobrança enviada (ciclo ${result.cycleNumber}).`);
|
|
768
|
+
* break;
|
|
769
|
+
* case 'already_sent':
|
|
770
|
+
* console.log('Já havia sido enviada — nada a fazer.');
|
|
771
|
+
* break;
|
|
772
|
+
* case 'failed':
|
|
773
|
+
* // result.reason is e.g. 'card_expired' or a gateway decline code
|
|
774
|
+
* console.error(`Falha na cobrança: ${result.reason}. ${result.message}`);
|
|
775
|
+
* break;
|
|
776
|
+
* case 'not_sent':
|
|
777
|
+
* console.warn(`Não enviada (${result.reason}): ${result.message}`);
|
|
778
|
+
* break;
|
|
779
|
+
* }
|
|
780
|
+
*/
|
|
781
|
+
async chargeNow(id) {
|
|
782
|
+
return this.http.call(
|
|
783
|
+
(signal) => this.http.client.POST(
|
|
784
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/charge-now`,
|
|
785
|
+
{
|
|
786
|
+
body: {},
|
|
787
|
+
signal
|
|
788
|
+
}
|
|
789
|
+
).then((r) => r)
|
|
739
790
|
);
|
|
740
791
|
}
|
|
741
792
|
/**
|
|
@@ -751,10 +802,13 @@ var ScheduledCharges = class {
|
|
|
751
802
|
*/
|
|
752
803
|
async cancelRecurrence(id, params = {}) {
|
|
753
804
|
return this.http.call(
|
|
754
|
-
(signal) => this.http.client.POST(
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
805
|
+
(signal) => this.http.client.POST(
|
|
806
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/cancel-recurrence`,
|
|
807
|
+
{
|
|
808
|
+
body: params,
|
|
809
|
+
signal
|
|
810
|
+
}
|
|
811
|
+
).then((r) => r)
|
|
758
812
|
);
|
|
759
813
|
}
|
|
760
814
|
/**
|
|
@@ -768,10 +822,13 @@ var ScheduledCharges = class {
|
|
|
768
822
|
*/
|
|
769
823
|
async setCancelAtPeriodEnd(id, params) {
|
|
770
824
|
return this.http.call(
|
|
771
|
-
(signal) => this.http.client.POST(
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
825
|
+
(signal) => this.http.client.POST(
|
|
826
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/cancel-at-period-end`,
|
|
827
|
+
{
|
|
828
|
+
body: params,
|
|
829
|
+
signal
|
|
830
|
+
}
|
|
831
|
+
).then((r) => r)
|
|
775
832
|
);
|
|
776
833
|
}
|
|
777
834
|
/**
|
|
@@ -784,10 +841,13 @@ var ScheduledCharges = class {
|
|
|
784
841
|
*/
|
|
785
842
|
async changePaymentMethod(id, params) {
|
|
786
843
|
return this.http.call(
|
|
787
|
-
(signal) => this.http.client.POST(
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
844
|
+
(signal) => this.http.client.POST(
|
|
845
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
|
|
846
|
+
{
|
|
847
|
+
body: params,
|
|
848
|
+
signal
|
|
849
|
+
}
|
|
850
|
+
).then((r) => r)
|
|
791
851
|
);
|
|
792
852
|
}
|
|
793
853
|
/**
|
|
@@ -800,10 +860,13 @@ var ScheduledCharges = class {
|
|
|
800
860
|
*/
|
|
801
861
|
async clearPaymentMethod(id) {
|
|
802
862
|
return this.http.call(
|
|
803
|
-
(signal) => this.http.client.DELETE(
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
863
|
+
(signal) => this.http.client.DELETE(
|
|
864
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
|
|
865
|
+
{
|
|
866
|
+
body: {},
|
|
867
|
+
signal
|
|
868
|
+
}
|
|
869
|
+
).then((r) => r)
|
|
807
870
|
);
|
|
808
871
|
}
|
|
809
872
|
/**
|
|
@@ -825,7 +888,7 @@ var ScheduledCharges = class {
|
|
|
825
888
|
if (params.limit !== void 0) qs.set("limit", String(params.limit));
|
|
826
889
|
if (params.cycleNumber !== void 0) qs.set("cycleNumber", String(params.cycleNumber));
|
|
827
890
|
const query = qs.toString();
|
|
828
|
-
const url = `/api/scheduled-charges/${id}/attempts${query ? `?${query}` : ""}`;
|
|
891
|
+
const url = `/api/scheduled-charges/${encodeURIComponent(id)}/attempts${query ? `?${query}` : ""}`;
|
|
829
892
|
return this.http.call(
|
|
830
893
|
(signal) => this.http.client.GET(url, { signal }).then(
|
|
831
894
|
(r) => r
|
|
@@ -943,6 +1006,11 @@ var WebhookEvents = class {
|
|
|
943
1006
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
944
1007
|
* the response payload's `manualResendOf` field.
|
|
945
1008
|
*
|
|
1009
|
+
* **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
|
|
1010
|
+
* (UUIDv4 unless you pass `idempotencyKey`) so transient transport
|
|
1011
|
+
* retries (5xx → SDK backoff) cannot create duplicate clones — the
|
|
1012
|
+
* backend returns the original clone on the second call within 24h.
|
|
1013
|
+
*
|
|
946
1014
|
* Returns the *clone* event (new id), not the original. The original is
|
|
947
1015
|
* unchanged on the server.
|
|
948
1016
|
*
|
|
@@ -952,10 +1020,12 @@ var WebhookEvents = class {
|
|
|
952
1020
|
* clone.id !== event.id; // true — clone has its own id
|
|
953
1021
|
* clone.manualResendOf === event.id; // true — points back at the source
|
|
954
1022
|
*/
|
|
955
|
-
async resend(id) {
|
|
1023
|
+
async resend(id, params = {}) {
|
|
1024
|
+
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
956
1025
|
return this.http.call(
|
|
957
1026
|
(signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
|
|
958
1027
|
body: {},
|
|
1028
|
+
headers: { "X-Idempotency-Key": idempotencyKey },
|
|
959
1029
|
signal
|
|
960
1030
|
}).then((r) => r)
|
|
961
1031
|
);
|
package/dist/index.d.cts
CHANGED
|
@@ -312,6 +312,11 @@ interface ScheduledChargeRecord {
|
|
|
312
312
|
methods: ScheduledPaymentMethod[];
|
|
313
313
|
status: ScheduledChargeStatus;
|
|
314
314
|
externalReference: string | null;
|
|
315
|
+
/**
|
|
316
|
+
* Max days past `dueDate` the daily recovery sweep will still auto-bill a
|
|
317
|
+
* missed charge. `null` means the system default (14) applies.
|
|
318
|
+
*/
|
|
319
|
+
maxRecoveryDays: number | null;
|
|
315
320
|
metadata: Record<string, unknown> | null;
|
|
316
321
|
createdAt: string;
|
|
317
322
|
updatedAt: string;
|
|
@@ -407,6 +412,11 @@ interface CreateScheduledChargeParams {
|
|
|
407
412
|
trialDays?: number;
|
|
408
413
|
externalReference?: string;
|
|
409
414
|
metadata?: Record<string, unknown>;
|
|
415
|
+
/**
|
|
416
|
+
* Max days past `dueDate` the daily recovery sweep will still auto-bill a
|
|
417
|
+
* missed charge (integer 1..365). Omit for the system default (14).
|
|
418
|
+
*/
|
|
419
|
+
maxRecoveryDays?: number;
|
|
410
420
|
/**
|
|
411
421
|
* Optional idempotency key for safe retries. The SDK auto-generates a
|
|
412
422
|
* UUIDv4 when omitted and forwards it as `X-Idempotency-Key`.
|
|
@@ -456,6 +466,30 @@ interface ChangePaymentMethodScheduledChargeParams {
|
|
|
456
466
|
/** PaymentMethod id to bind. Must belong to the same customerId. */
|
|
457
467
|
paymentMethodId: number;
|
|
458
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* Result of `scheduledCharges.chargeNow(id)` — what the immediate dispatch did:
|
|
471
|
+
*
|
|
472
|
+
* - `dispatched` — sent now (customer email/notification + outbound webhook + timeline event).
|
|
473
|
+
* - `already_sent` — this cycle's d-day was already dispatched; no-op (the action is idempotent).
|
|
474
|
+
* - `not_sent` — couldn't send; see `reason` (e.g. `no_email`, `lock_lost`, `no_saved_payment_method`).
|
|
475
|
+
* - `failed` — card charge failed; see `reason` (e.g. `card_expired`, or a gateway decline code).
|
|
476
|
+
*/
|
|
477
|
+
type ChargeNowOutcome = 'dispatched' | 'already_sent' | 'not_sent' | 'failed';
|
|
478
|
+
/**
|
|
479
|
+
* Why a `not_sent` / `failed` charge-now didn't go through. The documented
|
|
480
|
+
* literals are stable; `failed` may also surface a raw gateway decline code,
|
|
481
|
+
* so the type stays open (`string & {}`) without losing autocomplete.
|
|
482
|
+
*/
|
|
483
|
+
type ChargeNowReason = 'no_email' | 'lock_lost' | 'no_saved_payment_method' | 'card_expired' | 'payment_method_missing' | 'customer_missing' | (string & {});
|
|
484
|
+
interface ChargeNowResult {
|
|
485
|
+
outcome: ChargeNowOutcome;
|
|
486
|
+
/** Cycle that was dispatched/attempted, or `null` for one-time charges. */
|
|
487
|
+
cycleNumber: number | null;
|
|
488
|
+
/** Present on `not_sent` / `failed`. See {@link ChargeNowReason}. */
|
|
489
|
+
reason?: ChargeNowReason;
|
|
490
|
+
/** Ready-to-show pt-BR message describing the outcome. */
|
|
491
|
+
message: string;
|
|
492
|
+
}
|
|
459
493
|
interface Product {
|
|
460
494
|
id: number;
|
|
461
495
|
uuid: string;
|
|
@@ -618,6 +652,16 @@ interface ListWebhookEventsParams {
|
|
|
618
652
|
/** Filter by the destination endpoint that should receive (or received) the event. */
|
|
619
653
|
endpointId?: number;
|
|
620
654
|
}
|
|
655
|
+
interface ResendWebhookEventParams {
|
|
656
|
+
/**
|
|
657
|
+
* SDK→gateway idempotency key. If omitted, the SDK generates a UUIDv4
|
|
658
|
+
* and forwards it as `X-Idempotency-Key`. Within 24h the backend
|
|
659
|
+
* returns the original clone instead of creating a new one — pass a
|
|
660
|
+
* stable key from your own retry layer to dedupe across SDK
|
|
661
|
+
* invocations.
|
|
662
|
+
*/
|
|
663
|
+
idempotencyKey?: string;
|
|
664
|
+
}
|
|
621
665
|
/**
|
|
622
666
|
* Per-product portal customization (Atletia coach-as-product modeling and
|
|
623
667
|
* any other B2B2C platform). `null` fields inherit from the seller-level
|
|
@@ -1025,6 +1069,35 @@ declare class ScheduledCharges {
|
|
|
1025
1069
|
* });
|
|
1026
1070
|
*/
|
|
1027
1071
|
markPaid(id: string, params: MarkPaidScheduledChargeParams): Promise<ScheduledChargeRecord>;
|
|
1072
|
+
/**
|
|
1073
|
+
* Force-bill the current cycle right now instead of waiting for its due
|
|
1074
|
+
* date. Runs the same dispatch the daily billing cron would (customer
|
|
1075
|
+
* email/notification + outbound webhook + timeline event). Allowed only
|
|
1076
|
+
* from a billable status (`scheduled` / `due_today`); a recurring series
|
|
1077
|
+
* must have an open cycle (else the backend returns 400).
|
|
1078
|
+
*
|
|
1079
|
+
* Idempotent: if this cycle's d-day was already dispatched it reports
|
|
1080
|
+
* `already_sent` and does not re-charge. Inspect `outcome` to branch.
|
|
1081
|
+
*
|
|
1082
|
+
* @example
|
|
1083
|
+
* const result = await garu.scheduledCharges.chargeNow('sch_abc123');
|
|
1084
|
+
* switch (result.outcome) {
|
|
1085
|
+
* case 'dispatched':
|
|
1086
|
+
* console.log(`Cobrança enviada (ciclo ${result.cycleNumber}).`);
|
|
1087
|
+
* break;
|
|
1088
|
+
* case 'already_sent':
|
|
1089
|
+
* console.log('Já havia sido enviada — nada a fazer.');
|
|
1090
|
+
* break;
|
|
1091
|
+
* case 'failed':
|
|
1092
|
+
* // result.reason is e.g. 'card_expired' or a gateway decline code
|
|
1093
|
+
* console.error(`Falha na cobrança: ${result.reason}. ${result.message}`);
|
|
1094
|
+
* break;
|
|
1095
|
+
* case 'not_sent':
|
|
1096
|
+
* console.warn(`Não enviada (${result.reason}): ${result.message}`);
|
|
1097
|
+
* break;
|
|
1098
|
+
* }
|
|
1099
|
+
*/
|
|
1100
|
+
chargeNow(id: string): Promise<ChargeNowResult>;
|
|
1028
1101
|
/**
|
|
1029
1102
|
* Stop future cycles for a recurring series. The currently in-flight
|
|
1030
1103
|
* cycle (if any) remains active until paid, postponed, or marked-paid;
|
|
@@ -1163,6 +1236,11 @@ declare class WebhookEvents {
|
|
|
1163
1236
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
1164
1237
|
* the response payload's `manualResendOf` field.
|
|
1165
1238
|
*
|
|
1239
|
+
* **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
|
|
1240
|
+
* (UUIDv4 unless you pass `idempotencyKey`) so transient transport
|
|
1241
|
+
* retries (5xx → SDK backoff) cannot create duplicate clones — the
|
|
1242
|
+
* backend returns the original clone on the second call within 24h.
|
|
1243
|
+
*
|
|
1166
1244
|
* Returns the *clone* event (new id), not the original. The original is
|
|
1167
1245
|
* unchanged on the server.
|
|
1168
1246
|
*
|
|
@@ -1172,7 +1250,7 @@ declare class WebhookEvents {
|
|
|
1172
1250
|
* clone.id !== event.id; // true — clone has its own id
|
|
1173
1251
|
* clone.manualResendOf === event.id; // true — points back at the source
|
|
1174
1252
|
*/
|
|
1175
|
-
resend(id: number): Promise<WebhookEvent>;
|
|
1253
|
+
resend(id: number, params?: ResendWebhookEventParams): Promise<WebhookEvent>;
|
|
1176
1254
|
}
|
|
1177
1255
|
|
|
1178
1256
|
interface GaruOptions {
|
|
@@ -1274,4 +1352,4 @@ declare class GaruServerError extends GaruAPIError {
|
|
|
1274
1352
|
constructor(message: string, status: number, requestId: string | null, body: unknown);
|
|
1275
1353
|
}
|
|
1276
1354
|
|
|
1277
|
-
export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
|
|
1355
|
+
export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeNowOutcome, type ChargeNowReason, type ChargeNowResult, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ResendWebhookEventParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
|
package/dist/index.d.ts
CHANGED
|
@@ -312,6 +312,11 @@ interface ScheduledChargeRecord {
|
|
|
312
312
|
methods: ScheduledPaymentMethod[];
|
|
313
313
|
status: ScheduledChargeStatus;
|
|
314
314
|
externalReference: string | null;
|
|
315
|
+
/**
|
|
316
|
+
* Max days past `dueDate` the daily recovery sweep will still auto-bill a
|
|
317
|
+
* missed charge. `null` means the system default (14) applies.
|
|
318
|
+
*/
|
|
319
|
+
maxRecoveryDays: number | null;
|
|
315
320
|
metadata: Record<string, unknown> | null;
|
|
316
321
|
createdAt: string;
|
|
317
322
|
updatedAt: string;
|
|
@@ -407,6 +412,11 @@ interface CreateScheduledChargeParams {
|
|
|
407
412
|
trialDays?: number;
|
|
408
413
|
externalReference?: string;
|
|
409
414
|
metadata?: Record<string, unknown>;
|
|
415
|
+
/**
|
|
416
|
+
* Max days past `dueDate` the daily recovery sweep will still auto-bill a
|
|
417
|
+
* missed charge (integer 1..365). Omit for the system default (14).
|
|
418
|
+
*/
|
|
419
|
+
maxRecoveryDays?: number;
|
|
410
420
|
/**
|
|
411
421
|
* Optional idempotency key for safe retries. The SDK auto-generates a
|
|
412
422
|
* UUIDv4 when omitted and forwards it as `X-Idempotency-Key`.
|
|
@@ -456,6 +466,30 @@ interface ChangePaymentMethodScheduledChargeParams {
|
|
|
456
466
|
/** PaymentMethod id to bind. Must belong to the same customerId. */
|
|
457
467
|
paymentMethodId: number;
|
|
458
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* Result of `scheduledCharges.chargeNow(id)` — what the immediate dispatch did:
|
|
471
|
+
*
|
|
472
|
+
* - `dispatched` — sent now (customer email/notification + outbound webhook + timeline event).
|
|
473
|
+
* - `already_sent` — this cycle's d-day was already dispatched; no-op (the action is idempotent).
|
|
474
|
+
* - `not_sent` — couldn't send; see `reason` (e.g. `no_email`, `lock_lost`, `no_saved_payment_method`).
|
|
475
|
+
* - `failed` — card charge failed; see `reason` (e.g. `card_expired`, or a gateway decline code).
|
|
476
|
+
*/
|
|
477
|
+
type ChargeNowOutcome = 'dispatched' | 'already_sent' | 'not_sent' | 'failed';
|
|
478
|
+
/**
|
|
479
|
+
* Why a `not_sent` / `failed` charge-now didn't go through. The documented
|
|
480
|
+
* literals are stable; `failed` may also surface a raw gateway decline code,
|
|
481
|
+
* so the type stays open (`string & {}`) without losing autocomplete.
|
|
482
|
+
*/
|
|
483
|
+
type ChargeNowReason = 'no_email' | 'lock_lost' | 'no_saved_payment_method' | 'card_expired' | 'payment_method_missing' | 'customer_missing' | (string & {});
|
|
484
|
+
interface ChargeNowResult {
|
|
485
|
+
outcome: ChargeNowOutcome;
|
|
486
|
+
/** Cycle that was dispatched/attempted, or `null` for one-time charges. */
|
|
487
|
+
cycleNumber: number | null;
|
|
488
|
+
/** Present on `not_sent` / `failed`. See {@link ChargeNowReason}. */
|
|
489
|
+
reason?: ChargeNowReason;
|
|
490
|
+
/** Ready-to-show pt-BR message describing the outcome. */
|
|
491
|
+
message: string;
|
|
492
|
+
}
|
|
459
493
|
interface Product {
|
|
460
494
|
id: number;
|
|
461
495
|
uuid: string;
|
|
@@ -618,6 +652,16 @@ interface ListWebhookEventsParams {
|
|
|
618
652
|
/** Filter by the destination endpoint that should receive (or received) the event. */
|
|
619
653
|
endpointId?: number;
|
|
620
654
|
}
|
|
655
|
+
interface ResendWebhookEventParams {
|
|
656
|
+
/**
|
|
657
|
+
* SDK→gateway idempotency key. If omitted, the SDK generates a UUIDv4
|
|
658
|
+
* and forwards it as `X-Idempotency-Key`. Within 24h the backend
|
|
659
|
+
* returns the original clone instead of creating a new one — pass a
|
|
660
|
+
* stable key from your own retry layer to dedupe across SDK
|
|
661
|
+
* invocations.
|
|
662
|
+
*/
|
|
663
|
+
idempotencyKey?: string;
|
|
664
|
+
}
|
|
621
665
|
/**
|
|
622
666
|
* Per-product portal customization (Atletia coach-as-product modeling and
|
|
623
667
|
* any other B2B2C platform). `null` fields inherit from the seller-level
|
|
@@ -1025,6 +1069,35 @@ declare class ScheduledCharges {
|
|
|
1025
1069
|
* });
|
|
1026
1070
|
*/
|
|
1027
1071
|
markPaid(id: string, params: MarkPaidScheduledChargeParams): Promise<ScheduledChargeRecord>;
|
|
1072
|
+
/**
|
|
1073
|
+
* Force-bill the current cycle right now instead of waiting for its due
|
|
1074
|
+
* date. Runs the same dispatch the daily billing cron would (customer
|
|
1075
|
+
* email/notification + outbound webhook + timeline event). Allowed only
|
|
1076
|
+
* from a billable status (`scheduled` / `due_today`); a recurring series
|
|
1077
|
+
* must have an open cycle (else the backend returns 400).
|
|
1078
|
+
*
|
|
1079
|
+
* Idempotent: if this cycle's d-day was already dispatched it reports
|
|
1080
|
+
* `already_sent` and does not re-charge. Inspect `outcome` to branch.
|
|
1081
|
+
*
|
|
1082
|
+
* @example
|
|
1083
|
+
* const result = await garu.scheduledCharges.chargeNow('sch_abc123');
|
|
1084
|
+
* switch (result.outcome) {
|
|
1085
|
+
* case 'dispatched':
|
|
1086
|
+
* console.log(`Cobrança enviada (ciclo ${result.cycleNumber}).`);
|
|
1087
|
+
* break;
|
|
1088
|
+
* case 'already_sent':
|
|
1089
|
+
* console.log('Já havia sido enviada — nada a fazer.');
|
|
1090
|
+
* break;
|
|
1091
|
+
* case 'failed':
|
|
1092
|
+
* // result.reason is e.g. 'card_expired' or a gateway decline code
|
|
1093
|
+
* console.error(`Falha na cobrança: ${result.reason}. ${result.message}`);
|
|
1094
|
+
* break;
|
|
1095
|
+
* case 'not_sent':
|
|
1096
|
+
* console.warn(`Não enviada (${result.reason}): ${result.message}`);
|
|
1097
|
+
* break;
|
|
1098
|
+
* }
|
|
1099
|
+
*/
|
|
1100
|
+
chargeNow(id: string): Promise<ChargeNowResult>;
|
|
1028
1101
|
/**
|
|
1029
1102
|
* Stop future cycles for a recurring series. The currently in-flight
|
|
1030
1103
|
* cycle (if any) remains active until paid, postponed, or marked-paid;
|
|
@@ -1163,6 +1236,11 @@ declare class WebhookEvents {
|
|
|
1163
1236
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
1164
1237
|
* the response payload's `manualResendOf` field.
|
|
1165
1238
|
*
|
|
1239
|
+
* **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
|
|
1240
|
+
* (UUIDv4 unless you pass `idempotencyKey`) so transient transport
|
|
1241
|
+
* retries (5xx → SDK backoff) cannot create duplicate clones — the
|
|
1242
|
+
* backend returns the original clone on the second call within 24h.
|
|
1243
|
+
*
|
|
1166
1244
|
* Returns the *clone* event (new id), not the original. The original is
|
|
1167
1245
|
* unchanged on the server.
|
|
1168
1246
|
*
|
|
@@ -1172,7 +1250,7 @@ declare class WebhookEvents {
|
|
|
1172
1250
|
* clone.id !== event.id; // true — clone has its own id
|
|
1173
1251
|
* clone.manualResendOf === event.id; // true — points back at the source
|
|
1174
1252
|
*/
|
|
1175
|
-
resend(id: number): Promise<WebhookEvent>;
|
|
1253
|
+
resend(id: number, params?: ResendWebhookEventParams): Promise<WebhookEvent>;
|
|
1176
1254
|
}
|
|
1177
1255
|
|
|
1178
1256
|
interface GaruOptions {
|
|
@@ -1274,4 +1352,4 @@ declare class GaruServerError extends GaruAPIError {
|
|
|
1274
1352
|
constructor(message: string, status: number, requestId: string | null, body: unknown);
|
|
1275
1353
|
}
|
|
1276
1354
|
|
|
1277
|
-
export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
|
|
1355
|
+
export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeNowOutcome, type ChargeNowReason, type ChargeNowResult, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ResendWebhookEventParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
|
package/dist/index.js
CHANGED
|
@@ -647,9 +647,9 @@ var ScheduledCharges = class {
|
|
|
647
647
|
*/
|
|
648
648
|
async get(id) {
|
|
649
649
|
return this.http.call(
|
|
650
|
-
(signal) => this.http.client.GET(`/api/scheduled-charges/${id}`, {
|
|
651
|
-
|
|
652
|
-
)
|
|
650
|
+
(signal) => this.http.client.GET(`/api/scheduled-charges/${encodeURIComponent(id)}`, {
|
|
651
|
+
signal
|
|
652
|
+
}).then((r) => r)
|
|
653
653
|
);
|
|
654
654
|
}
|
|
655
655
|
/**
|
|
@@ -665,10 +665,13 @@ var ScheduledCharges = class {
|
|
|
665
665
|
*/
|
|
666
666
|
async postpone(id, params) {
|
|
667
667
|
return this.http.call(
|
|
668
|
-
(signal) => this.http.client.POST(
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
668
|
+
(signal) => this.http.client.POST(
|
|
669
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/postpone`,
|
|
670
|
+
{
|
|
671
|
+
body: params,
|
|
672
|
+
signal
|
|
673
|
+
}
|
|
674
|
+
).then((r) => r)
|
|
672
675
|
);
|
|
673
676
|
}
|
|
674
677
|
/**
|
|
@@ -681,10 +684,13 @@ var ScheduledCharges = class {
|
|
|
681
684
|
*/
|
|
682
685
|
async pause(id, params = {}) {
|
|
683
686
|
return this.http.call(
|
|
684
|
-
(signal) => this.http.client.POST(
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
687
|
+
(signal) => this.http.client.POST(
|
|
688
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/pause`,
|
|
689
|
+
{
|
|
690
|
+
body: params,
|
|
691
|
+
signal
|
|
692
|
+
}
|
|
693
|
+
).then((r) => r)
|
|
688
694
|
);
|
|
689
695
|
}
|
|
690
696
|
/**
|
|
@@ -695,10 +701,13 @@ var ScheduledCharges = class {
|
|
|
695
701
|
*/
|
|
696
702
|
async resume(id) {
|
|
697
703
|
return this.http.call(
|
|
698
|
-
(signal) => this.http.client.POST(
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
704
|
+
(signal) => this.http.client.POST(
|
|
705
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/resume`,
|
|
706
|
+
{
|
|
707
|
+
body: {},
|
|
708
|
+
signal
|
|
709
|
+
}
|
|
710
|
+
).then((r) => r)
|
|
702
711
|
);
|
|
703
712
|
}
|
|
704
713
|
/**
|
|
@@ -726,10 +735,52 @@ var ScheduledCharges = class {
|
|
|
726
735
|
*/
|
|
727
736
|
async markPaid(id, params) {
|
|
728
737
|
return this.http.call(
|
|
729
|
-
(signal) => this.http.client.POST(
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
738
|
+
(signal) => this.http.client.POST(
|
|
739
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/mark-paid`,
|
|
740
|
+
{
|
|
741
|
+
body: params,
|
|
742
|
+
signal
|
|
743
|
+
}
|
|
744
|
+
).then((r) => r)
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Force-bill the current cycle right now instead of waiting for its due
|
|
749
|
+
* date. Runs the same dispatch the daily billing cron would (customer
|
|
750
|
+
* email/notification + outbound webhook + timeline event). Allowed only
|
|
751
|
+
* from a billable status (`scheduled` / `due_today`); a recurring series
|
|
752
|
+
* must have an open cycle (else the backend returns 400).
|
|
753
|
+
*
|
|
754
|
+
* Idempotent: if this cycle's d-day was already dispatched it reports
|
|
755
|
+
* `already_sent` and does not re-charge. Inspect `outcome` to branch.
|
|
756
|
+
*
|
|
757
|
+
* @example
|
|
758
|
+
* const result = await garu.scheduledCharges.chargeNow('sch_abc123');
|
|
759
|
+
* switch (result.outcome) {
|
|
760
|
+
* case 'dispatched':
|
|
761
|
+
* console.log(`Cobrança enviada (ciclo ${result.cycleNumber}).`);
|
|
762
|
+
* break;
|
|
763
|
+
* case 'already_sent':
|
|
764
|
+
* console.log('Já havia sido enviada — nada a fazer.');
|
|
765
|
+
* break;
|
|
766
|
+
* case 'failed':
|
|
767
|
+
* // result.reason is e.g. 'card_expired' or a gateway decline code
|
|
768
|
+
* console.error(`Falha na cobrança: ${result.reason}. ${result.message}`);
|
|
769
|
+
* break;
|
|
770
|
+
* case 'not_sent':
|
|
771
|
+
* console.warn(`Não enviada (${result.reason}): ${result.message}`);
|
|
772
|
+
* break;
|
|
773
|
+
* }
|
|
774
|
+
*/
|
|
775
|
+
async chargeNow(id) {
|
|
776
|
+
return this.http.call(
|
|
777
|
+
(signal) => this.http.client.POST(
|
|
778
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/charge-now`,
|
|
779
|
+
{
|
|
780
|
+
body: {},
|
|
781
|
+
signal
|
|
782
|
+
}
|
|
783
|
+
).then((r) => r)
|
|
733
784
|
);
|
|
734
785
|
}
|
|
735
786
|
/**
|
|
@@ -745,10 +796,13 @@ var ScheduledCharges = class {
|
|
|
745
796
|
*/
|
|
746
797
|
async cancelRecurrence(id, params = {}) {
|
|
747
798
|
return this.http.call(
|
|
748
|
-
(signal) => this.http.client.POST(
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
799
|
+
(signal) => this.http.client.POST(
|
|
800
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/cancel-recurrence`,
|
|
801
|
+
{
|
|
802
|
+
body: params,
|
|
803
|
+
signal
|
|
804
|
+
}
|
|
805
|
+
).then((r) => r)
|
|
752
806
|
);
|
|
753
807
|
}
|
|
754
808
|
/**
|
|
@@ -762,10 +816,13 @@ var ScheduledCharges = class {
|
|
|
762
816
|
*/
|
|
763
817
|
async setCancelAtPeriodEnd(id, params) {
|
|
764
818
|
return this.http.call(
|
|
765
|
-
(signal) => this.http.client.POST(
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
819
|
+
(signal) => this.http.client.POST(
|
|
820
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/cancel-at-period-end`,
|
|
821
|
+
{
|
|
822
|
+
body: params,
|
|
823
|
+
signal
|
|
824
|
+
}
|
|
825
|
+
).then((r) => r)
|
|
769
826
|
);
|
|
770
827
|
}
|
|
771
828
|
/**
|
|
@@ -778,10 +835,13 @@ var ScheduledCharges = class {
|
|
|
778
835
|
*/
|
|
779
836
|
async changePaymentMethod(id, params) {
|
|
780
837
|
return this.http.call(
|
|
781
|
-
(signal) => this.http.client.POST(
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
838
|
+
(signal) => this.http.client.POST(
|
|
839
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
|
|
840
|
+
{
|
|
841
|
+
body: params,
|
|
842
|
+
signal
|
|
843
|
+
}
|
|
844
|
+
).then((r) => r)
|
|
785
845
|
);
|
|
786
846
|
}
|
|
787
847
|
/**
|
|
@@ -794,10 +854,13 @@ var ScheduledCharges = class {
|
|
|
794
854
|
*/
|
|
795
855
|
async clearPaymentMethod(id) {
|
|
796
856
|
return this.http.call(
|
|
797
|
-
(signal) => this.http.client.DELETE(
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
857
|
+
(signal) => this.http.client.DELETE(
|
|
858
|
+
`/api/scheduled-charges/${encodeURIComponent(id)}/payment-method`,
|
|
859
|
+
{
|
|
860
|
+
body: {},
|
|
861
|
+
signal
|
|
862
|
+
}
|
|
863
|
+
).then((r) => r)
|
|
801
864
|
);
|
|
802
865
|
}
|
|
803
866
|
/**
|
|
@@ -819,7 +882,7 @@ var ScheduledCharges = class {
|
|
|
819
882
|
if (params.limit !== void 0) qs.set("limit", String(params.limit));
|
|
820
883
|
if (params.cycleNumber !== void 0) qs.set("cycleNumber", String(params.cycleNumber));
|
|
821
884
|
const query = qs.toString();
|
|
822
|
-
const url = `/api/scheduled-charges/${id}/attempts${query ? `?${query}` : ""}`;
|
|
885
|
+
const url = `/api/scheduled-charges/${encodeURIComponent(id)}/attempts${query ? `?${query}` : ""}`;
|
|
823
886
|
return this.http.call(
|
|
824
887
|
(signal) => this.http.client.GET(url, { signal }).then(
|
|
825
888
|
(r) => r
|
|
@@ -937,6 +1000,11 @@ var WebhookEvents = class {
|
|
|
937
1000
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
938
1001
|
* the response payload's `manualResendOf` field.
|
|
939
1002
|
*
|
|
1003
|
+
* **SDK→gateway dedup**: the SDK auto-attaches `X-Idempotency-Key`
|
|
1004
|
+
* (UUIDv4 unless you pass `idempotencyKey`) so transient transport
|
|
1005
|
+
* retries (5xx → SDK backoff) cannot create duplicate clones — the
|
|
1006
|
+
* backend returns the original clone on the second call within 24h.
|
|
1007
|
+
*
|
|
940
1008
|
* Returns the *clone* event (new id), not the original. The original is
|
|
941
1009
|
* unchanged on the server.
|
|
942
1010
|
*
|
|
@@ -946,10 +1014,12 @@ var WebhookEvents = class {
|
|
|
946
1014
|
* clone.id !== event.id; // true — clone has its own id
|
|
947
1015
|
* clone.manualResendOf === event.id; // true — points back at the source
|
|
948
1016
|
*/
|
|
949
|
-
async resend(id) {
|
|
1017
|
+
async resend(id, params = {}) {
|
|
1018
|
+
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
950
1019
|
return this.http.call(
|
|
951
1020
|
(signal) => this.http.client.POST(`/api/webhook-events/${id}/resend`, {
|
|
952
1021
|
body: {},
|
|
1022
|
+
headers: { "X-Idempotency-Key": idempotencyKey },
|
|
953
1023
|
signal
|
|
954
1024
|
}).then((r) => r)
|
|
955
1025
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@garuhq/node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Official Node.js / TypeScript SDK for the Garu payment gateway.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://garu.com.br",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"typecheck": "tsc --noEmit",
|
|
47
47
|
"test": "vitest run",
|
|
48
48
|
"test:watch": "vitest",
|
|
49
|
-
"generate:fetch": "curl -sf ${GARU_SPEC_URL:-https://garu.com.br/api/
|
|
49
|
+
"generate:fetch": "curl -sf ${GARU_SPEC_URL:-https://garu.com.br/api/openapi.json} -o src/generated/openapi.json",
|
|
50
50
|
"generate:filter": "node scripts/filter-spec.mjs",
|
|
51
51
|
"generate": "npm run generate:fetch && npm run generate:filter && openapi-typescript src/generated/openapi-sdk.json -o src/generated/schema.d.ts",
|
|
52
52
|
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|