@garuhq/node 4.0.0 → 5.0.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 +67 -0
- package/dist/index.cjs +70 -41
- package/dist/index.d.cts +72 -25
- package/dist/index.d.ts +72 -25
- package/dist/index.js +71 -42
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,73 @@
|
|
|
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
|
+
## [5.0.0] — 2026-09-09
|
|
7
|
+
|
|
8
|
+
**Breaking:** requests that omit `idempotencyKey` no longer carry an
|
|
9
|
+
`X-Idempotency-Key` header. No API contract changed and no gateway endpoint
|
|
10
|
+
requires the header, so most integrations need no code change — but if you were
|
|
11
|
+
relying on a key always being present, you must now pass one, which is the only
|
|
12
|
+
way it ever gave you real protection. See below.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **The SDK no longer invents an idempotency key.** Every write that takes
|
|
17
|
+
`idempotencyKey` (`charges.create`, `charges.refund`, `customers.create`,
|
|
18
|
+
`products.create`, `scheduledCharges.create`, `installmentPlans.create`,
|
|
19
|
+
`installmentPlans.requestRefund`, `webhookEvents.resend`) used to generate a
|
|
20
|
+
fresh UUIDv4 when you omitted one. That protected nothing: an idempotency key
|
|
21
|
+
only works if the SAME key comes back on a retry, and a key invented per call
|
|
22
|
+
is different every time. The header is now sent only when you pass a key.
|
|
23
|
+
|
|
24
|
+
This was not theoretical. On 2026-09-08 an integrator's HTTP client timed out
|
|
25
|
+
at 30s on a card charge that was still being created, retried, drew a fresh
|
|
26
|
+
UUIDv4, and charged a real buyer twice.
|
|
27
|
+
|
|
28
|
+
Docstrings that promised "Safe to retry: the same key returns the original
|
|
29
|
+
charge for 24h" were describing a guarantee the code did not provide. They now
|
|
30
|
+
say what actually happens.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- Behaviour change, no API change: requests where you omit `idempotencyKey` now
|
|
35
|
+
carry no `X-Idempotency-Key` header. Nothing rejects a missing key — the header
|
|
36
|
+
is optional on every gateway endpoint — and the gateway also gained a duplicate
|
|
37
|
+
guard that replays the original charge when the same buyer, product, rail and
|
|
38
|
+
instalment count arrive again within 60 seconds.
|
|
39
|
+
|
|
40
|
+
**To get real protection, pass a key derived from your own domain** so a retry
|
|
41
|
+
reproduces it:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
await garu.charges.create({
|
|
45
|
+
productId,
|
|
46
|
+
paymentMethod: 'creditCard',
|
|
47
|
+
customer,
|
|
48
|
+
idempotencyKey: `booking:${booking.id}:charge`
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- `generateIdempotencyKey()` is still exported and unchanged. It is only useful
|
|
53
|
+
if you store the result and reuse it across retries of the same operation.
|
|
54
|
+
|
|
55
|
+
## [4.1.0] — 2026-08-22
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- `customers.create()`, `charges.refund()`, and
|
|
60
|
+
`installmentPlans.requestRefund()` now attach an `X-Idempotency-Key`
|
|
61
|
+
header automatically (UUIDv4 unless you pass `idempotencyKey`). The
|
|
62
|
+
gateway now caches and replays the first response for 24h, so a network
|
|
63
|
+
retry can no longer register a duplicate customer, open a second refund
|
|
64
|
+
request, or (via `scheduledCharges.create()` — see Fixed below)
|
|
65
|
+
double-book recurring billing.
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- `scheduledCharges.create()`'s docstring dropped the "the gateway does not
|
|
70
|
+
currently deduplicate" caveat — the backend now enforces it, so a retried
|
|
71
|
+
create is safe by default.
|
|
72
|
+
|
|
6
73
|
## [4.0.0] — 2026-08-22
|
|
7
74
|
|
|
8
75
|
**Breaking:** `scheduledCharges` now targets the versioned public API
|
package/dist/index.cjs
CHANGED
|
@@ -188,8 +188,8 @@ function backoffDelay(attempt, retryAfterSec) {
|
|
|
188
188
|
function sleep(ms) {
|
|
189
189
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
190
190
|
}
|
|
191
|
-
function
|
|
192
|
-
return
|
|
191
|
+
function idempotencyHeaders(key) {
|
|
192
|
+
return key ? { "X-Idempotency-Key": key } : {};
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
// src/resources/charges.ts
|
|
@@ -201,9 +201,11 @@ var Charges = class {
|
|
|
201
201
|
/**
|
|
202
202
|
* Create a charge (PIX, boleto, or credit card).
|
|
203
203
|
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
204
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
205
|
+
* original charge for 24h. Derive it from something stable in your own
|
|
206
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
207
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
208
|
+
* call is different every time and protects nothing.
|
|
207
209
|
*
|
|
208
210
|
* @example
|
|
209
211
|
* // PIX — render charge.pix.code as a QR in your own checkout
|
|
@@ -217,7 +219,7 @@ var Charges = class {
|
|
|
217
219
|
* phone: '11987654321'
|
|
218
220
|
* }
|
|
219
221
|
* });
|
|
220
|
-
*
|
|
222
|
+
* // charge.uuid, charge.pix?.code
|
|
221
223
|
*
|
|
222
224
|
* @example
|
|
223
225
|
* // Credit card, 2 installments. Server-to-server only (PCI scope).
|
|
@@ -236,7 +238,6 @@ var Charges = class {
|
|
|
236
238
|
* // charge.amount is the base price; charge.chargedTotal is what was charged.
|
|
237
239
|
*/
|
|
238
240
|
async create(params) {
|
|
239
|
-
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
240
241
|
const body = {
|
|
241
242
|
productId: params.productId,
|
|
242
243
|
paymentMethod: params.paymentMethod,
|
|
@@ -245,7 +246,7 @@ var Charges = class {
|
|
|
245
246
|
if (params.card) body.card = params.card;
|
|
246
247
|
if (params.checkoutSessionToken) body.checkoutSessionToken = params.checkoutSessionToken;
|
|
247
248
|
if (params.additionalInfo !== void 0) body.additionalInfo = params.additionalInfo;
|
|
248
|
-
return this.post("/api/v1/charges", body,
|
|
249
|
+
return this.post("/api/v1/charges", body, idempotencyHeaders(params.idempotencyKey));
|
|
249
250
|
}
|
|
250
251
|
/**
|
|
251
252
|
* Retrieve a charge by uuid.
|
|
@@ -262,7 +263,7 @@ var Charges = class {
|
|
|
262
263
|
*
|
|
263
264
|
* @example
|
|
264
265
|
* const { data, totalCount } = await garu.charges.list({ status: 'paid', limit: 50 });
|
|
265
|
-
*
|
|
266
|
+
* // data.length of totalCount paid charges
|
|
266
267
|
*/
|
|
267
268
|
async list(params = {}) {
|
|
268
269
|
const query = {};
|
|
@@ -285,6 +286,12 @@ var Charges = class {
|
|
|
285
286
|
* charge in `refund_pending`, reaching `refunded` only once the transfer
|
|
286
287
|
* settles.
|
|
287
288
|
*
|
|
289
|
+
* For Pix/boleto (which open a refund request instead of an automated
|
|
290
|
+
* reversal), pass `idempotencyKey` to make a retry return the original
|
|
291
|
+
* request rather than opening a second one; omit it and no key is sent.
|
|
292
|
+
* Ignored for card, which reverses automatically and has no manual request
|
|
293
|
+
* to duplicate.
|
|
294
|
+
*
|
|
288
295
|
* @example
|
|
289
296
|
* await garu.charges.refund('6f1c9b2e-...'); // full
|
|
290
297
|
* await garu.charges.refund('6f1c9b2e-...', { amount: 10.0 }); // R$10,00
|
|
@@ -293,7 +300,7 @@ var Charges = class {
|
|
|
293
300
|
const body = {};
|
|
294
301
|
if (params.amount !== void 0) body.amount = params.amount;
|
|
295
302
|
if (params.reason !== void 0) body.reason = params.reason;
|
|
296
|
-
return this.post(`/api/v1/charges/${encodeURIComponent(uuid)}/refund`, body);
|
|
303
|
+
return this.post(`/api/v1/charges/${encodeURIComponent(uuid)}/refund`, body, idempotencyHeaders(params.idempotencyKey));
|
|
297
304
|
}
|
|
298
305
|
/**
|
|
299
306
|
* Cancel an unpaid charge.
|
|
@@ -318,7 +325,11 @@ var Charges = class {
|
|
|
318
325
|
}
|
|
319
326
|
post(url, body, headers) {
|
|
320
327
|
return this.http.call(
|
|
321
|
-
(signal) => this.http.client.POST(url, {
|
|
328
|
+
(signal) => this.http.client.POST(url, {
|
|
329
|
+
body,
|
|
330
|
+
headers,
|
|
331
|
+
signal
|
|
332
|
+
})
|
|
322
333
|
);
|
|
323
334
|
}
|
|
324
335
|
};
|
|
@@ -330,10 +341,16 @@ var InstallmentPlans = class {
|
|
|
330
341
|
}
|
|
331
342
|
http;
|
|
332
343
|
/**
|
|
333
|
-
* Sell a product as a carnê.
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
344
|
+
* Sell a product as a carnê.
|
|
345
|
+
*
|
|
346
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
347
|
+
* original plan for 24h. Derive it from something stable in your own
|
|
348
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
349
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
350
|
+
* call is different every time and protects nothing.
|
|
351
|
+
* This matters more here than anywhere else in the API: the call registers a
|
|
352
|
+
* REAL boleto at the bank, so a retry without a key can put two payable
|
|
353
|
+
* barcodes in one buyer's hands.
|
|
337
354
|
*
|
|
338
355
|
* @example
|
|
339
356
|
* const carne = await garu.installmentPlans.create({
|
|
@@ -359,12 +376,11 @@ var InstallmentPlans = class {
|
|
|
359
376
|
* });
|
|
360
377
|
*/
|
|
361
378
|
async create(params) {
|
|
362
|
-
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
363
379
|
const { idempotencyKey: _omit, ...body } = params;
|
|
364
380
|
return this.http.call(
|
|
365
381
|
(signal) => this.http.client.POST("/api/v1/installment-plans", {
|
|
366
382
|
body,
|
|
367
|
-
headers:
|
|
383
|
+
headers: idempotencyHeaders(params.idempotencyKey),
|
|
368
384
|
signal
|
|
369
385
|
}).then((r) => r)
|
|
370
386
|
);
|
|
@@ -507,6 +523,10 @@ var InstallmentPlans = class {
|
|
|
507
523
|
* team. Transfer the money to the buyer yourself, then close it with
|
|
508
524
|
* `garu.refundRequests.confirm`.
|
|
509
525
|
*
|
|
526
|
+
* Pass `idempotencyKey` to cover the request-in-flight window; omit it and no
|
|
527
|
+
* key is sent. The backend already dedupes a second pending request for the
|
|
528
|
+
* same carnê, so this is defense-in-depth rather than the main guard.
|
|
529
|
+
*
|
|
510
530
|
* @example
|
|
511
531
|
* const request = await garu.installmentPlans.requestRefund(uuid, {
|
|
512
532
|
* reason: 'Produto não entregue'
|
|
@@ -515,9 +535,11 @@ var InstallmentPlans = class {
|
|
|
515
535
|
* request.amount; // defaults to everything the carnê collected
|
|
516
536
|
*/
|
|
517
537
|
async requestRefund(uuid, params = {}) {
|
|
538
|
+
const { idempotencyKey: _omit, ...body } = params;
|
|
518
539
|
return this.http.call(
|
|
519
540
|
(signal) => this.http.client.POST(`/api/v1/installment-plans/${uuid}/refund-requests`, {
|
|
520
|
-
body
|
|
541
|
+
body,
|
|
542
|
+
headers: idempotencyHeaders(params.idempotencyKey),
|
|
521
543
|
signal
|
|
522
544
|
}).then((r) => r)
|
|
523
545
|
);
|
|
@@ -625,6 +647,12 @@ var Customers = class {
|
|
|
625
647
|
/**
|
|
626
648
|
* Register a customer for the current seller.
|
|
627
649
|
*
|
|
650
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
651
|
+
* original customer for 24h. Derive it from something stable in your own
|
|
652
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
653
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
654
|
+
* call is different every time and protects nothing.
|
|
655
|
+
*
|
|
628
656
|
* @example
|
|
629
657
|
* const customer = await garu.customers.create({
|
|
630
658
|
* name: 'Maria Silva',
|
|
@@ -636,9 +664,11 @@ var Customers = class {
|
|
|
636
664
|
* customer.uuid;
|
|
637
665
|
*/
|
|
638
666
|
async create(params) {
|
|
667
|
+
const { idempotencyKey: _omit, ...body } = params;
|
|
639
668
|
return this.http.call(
|
|
640
669
|
(signal) => this.http.client.POST("/api/v1/customers", {
|
|
641
|
-
body
|
|
670
|
+
body,
|
|
671
|
+
headers: idempotencyHeaders(params.idempotencyKey),
|
|
642
672
|
signal
|
|
643
673
|
}).then((r) => r)
|
|
644
674
|
);
|
|
@@ -883,10 +913,11 @@ var Products = class {
|
|
|
883
913
|
* product (HTTP 201). Only `name` is required; everything else falls back
|
|
884
914
|
* to seller/server defaults.
|
|
885
915
|
*
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
889
|
-
*
|
|
916
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
917
|
+
* original product for 24h. Derive it from something stable in your own
|
|
918
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
919
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
920
|
+
* call is different every time and protects nothing.
|
|
890
921
|
*
|
|
891
922
|
* @example
|
|
892
923
|
* const product = await garu.products.create({
|
|
@@ -902,11 +933,10 @@ var Products = class {
|
|
|
902
933
|
*/
|
|
903
934
|
async create(params) {
|
|
904
935
|
const { idempotencyKey, ...body } = params;
|
|
905
|
-
const key = idempotencyKey ?? generateIdempotencyKey();
|
|
906
936
|
return this.http.call(
|
|
907
937
|
(signal) => this.http.client.POST("/api/v1/products", {
|
|
908
938
|
body,
|
|
909
|
-
headers:
|
|
939
|
+
headers: idempotencyHeaders(idempotencyKey),
|
|
910
940
|
signal
|
|
911
941
|
}).then((r) => r)
|
|
912
942
|
);
|
|
@@ -942,13 +972,15 @@ var ScheduledCharges = class {
|
|
|
942
972
|
}
|
|
943
973
|
http;
|
|
944
974
|
/**
|
|
945
|
-
* Create a new scheduled charge.
|
|
946
|
-
*
|
|
947
|
-
*
|
|
948
|
-
*
|
|
949
|
-
*
|
|
950
|
-
*
|
|
951
|
-
*
|
|
975
|
+
* Create a new scheduled charge.
|
|
976
|
+
*
|
|
977
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
978
|
+
* original series for 24h. Derive it from something stable in your own
|
|
979
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
980
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
981
|
+
* call is different every time and protects nothing.
|
|
982
|
+
* Worth the effort here: without a key, a retry double-books recurring
|
|
983
|
+
* billing for the same customer.
|
|
952
984
|
*
|
|
953
985
|
* @example
|
|
954
986
|
* const charge = await garu.scheduledCharges.create({
|
|
@@ -975,12 +1007,11 @@ var ScheduledCharges = class {
|
|
|
975
1007
|
* });
|
|
976
1008
|
*/
|
|
977
1009
|
async create(params) {
|
|
978
|
-
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
979
1010
|
const { idempotencyKey: _omit, ...body } = params;
|
|
980
1011
|
return this.http.call(
|
|
981
1012
|
(signal) => this.http.client.POST("/api/v1/scheduled-charges", {
|
|
982
1013
|
body,
|
|
983
|
-
headers:
|
|
1014
|
+
headers: idempotencyHeaders(params.idempotencyKey),
|
|
984
1015
|
signal
|
|
985
1016
|
}).then((r) => r)
|
|
986
1017
|
);
|
|
@@ -1369,11 +1400,10 @@ var WebhookEvents = class {
|
|
|
1369
1400
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
1370
1401
|
* the response payload's `manualResendOf` field.
|
|
1371
1402
|
*
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1374
|
-
*
|
|
1375
|
-
*
|
|
1376
|
-
* your own retry-suppression if that matters for your integration.
|
|
1403
|
+
* `idempotencyKey` is forwarded when you pass one and omitted when you don't.
|
|
1404
|
+
* Either way the gateway does not currently deduplicate `/resend` calls
|
|
1405
|
+
* against it, so retrying after a network failure can create more than one
|
|
1406
|
+
* clone — pair it with your own retry-suppression if that matters.
|
|
1377
1407
|
*
|
|
1378
1408
|
* Returns the *clone* event (new uuid), not the original. The original is
|
|
1379
1409
|
* unchanged on the server.
|
|
@@ -1385,11 +1415,10 @@ var WebhookEvents = class {
|
|
|
1385
1415
|
* clone.manualResendOf === event.uuid; // true — points back at the source
|
|
1386
1416
|
*/
|
|
1387
1417
|
async resend(uuid, params = {}) {
|
|
1388
|
-
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
1389
1418
|
return this.http.call(
|
|
1390
1419
|
(signal) => this.http.client.POST(`/api/v1/webhook-events/${uuid}/resend`, {
|
|
1391
1420
|
body: {},
|
|
1392
|
-
headers:
|
|
1421
|
+
headers: idempotencyHeaders(params.idempotencyKey),
|
|
1393
1422
|
signal
|
|
1394
1423
|
}).then((r) => r)
|
|
1395
1424
|
);
|
package/dist/index.d.cts
CHANGED
|
@@ -219,6 +219,13 @@ interface RefundChargeParams {
|
|
|
219
219
|
amount?: number;
|
|
220
220
|
/** Free-form reason stored on the refund. */
|
|
221
221
|
reason?: string;
|
|
222
|
+
/**
|
|
223
|
+
* Optional idempotency key for safe retries. The SDK auto-generates a
|
|
224
|
+
* UUIDv4 when omitted and forwards it as `X-Idempotency-Key`. Only used
|
|
225
|
+
* for Pix/boleto, which open a refund request instead of an automated
|
|
226
|
+
* reversal — ignored for card, which has no manual request to duplicate.
|
|
227
|
+
*/
|
|
228
|
+
idempotencyKey?: string;
|
|
222
229
|
}
|
|
223
230
|
interface ListChargesParams {
|
|
224
231
|
/** Page number (1-based). Default: 1. */
|
|
@@ -324,6 +331,13 @@ interface CreateCustomerParams {
|
|
|
324
331
|
city?: string;
|
|
325
332
|
/** 2-letter uppercase state code, e.g. `SP`. */
|
|
326
333
|
state?: string;
|
|
334
|
+
/**
|
|
335
|
+
* Optional idempotency key for safe retries. The SDK auto-generates a
|
|
336
|
+
* UUIDv4 when omitted and forwards it as `X-Idempotency-Key`. Document
|
|
337
|
+
* uniqueness already merges a retried create into the same customer, so
|
|
338
|
+
* this mainly protects registrations with no document set.
|
|
339
|
+
*/
|
|
340
|
+
idempotencyKey?: string;
|
|
327
341
|
}
|
|
328
342
|
interface UpdateCustomerParams {
|
|
329
343
|
name?: string;
|
|
@@ -1051,6 +1065,13 @@ interface RequestPlanRefundParams {
|
|
|
1051
1065
|
/** Defaults to everything the carnê has collected. */
|
|
1052
1066
|
amount?: number;
|
|
1053
1067
|
reason?: string;
|
|
1068
|
+
/**
|
|
1069
|
+
* Optional idempotency key for safe retries. The SDK auto-generates a
|
|
1070
|
+
* UUIDv4 when omitted and forwards it as `X-Idempotency-Key`. The backend
|
|
1071
|
+
* already dedupes a second pending request for the same carnê, so this is
|
|
1072
|
+
* mainly defense-in-depth for the request-in-flight window.
|
|
1073
|
+
*/
|
|
1074
|
+
idempotencyKey?: string;
|
|
1054
1075
|
}
|
|
1055
1076
|
interface ListRefundRequestsParams {
|
|
1056
1077
|
page?: number;
|
|
@@ -1079,9 +1100,11 @@ declare class Charges {
|
|
|
1079
1100
|
/**
|
|
1080
1101
|
* Create a charge (PIX, boleto, or credit card).
|
|
1081
1102
|
*
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1103
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
1104
|
+
* original charge for 24h. Derive it from something stable in your own
|
|
1105
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
1106
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
1107
|
+
* call is different every time and protects nothing.
|
|
1085
1108
|
*
|
|
1086
1109
|
* @example
|
|
1087
1110
|
* // PIX — render charge.pix.code as a QR in your own checkout
|
|
@@ -1095,7 +1118,7 @@ declare class Charges {
|
|
|
1095
1118
|
* phone: '11987654321'
|
|
1096
1119
|
* }
|
|
1097
1120
|
* });
|
|
1098
|
-
*
|
|
1121
|
+
* // charge.uuid, charge.pix?.code
|
|
1099
1122
|
*
|
|
1100
1123
|
* @example
|
|
1101
1124
|
* // Credit card, 2 installments. Server-to-server only (PCI scope).
|
|
@@ -1127,7 +1150,7 @@ declare class Charges {
|
|
|
1127
1150
|
*
|
|
1128
1151
|
* @example
|
|
1129
1152
|
* const { data, totalCount } = await garu.charges.list({ status: 'paid', limit: 50 });
|
|
1130
|
-
*
|
|
1153
|
+
* // data.length of totalCount paid charges
|
|
1131
1154
|
*/
|
|
1132
1155
|
list(params?: ListChargesParams): Promise<ChargeList>;
|
|
1133
1156
|
/**
|
|
@@ -1137,6 +1160,12 @@ declare class Charges {
|
|
|
1137
1160
|
* charge in `refund_pending`, reaching `refunded` only once the transfer
|
|
1138
1161
|
* settles.
|
|
1139
1162
|
*
|
|
1163
|
+
* For Pix/boleto (which open a refund request instead of an automated
|
|
1164
|
+
* reversal), pass `idempotencyKey` to make a retry return the original
|
|
1165
|
+
* request rather than opening a second one; omit it and no key is sent.
|
|
1166
|
+
* Ignored for card, which reverses automatically and has no manual request
|
|
1167
|
+
* to duplicate.
|
|
1168
|
+
*
|
|
1140
1169
|
* @example
|
|
1141
1170
|
* await garu.charges.refund('6f1c9b2e-...'); // full
|
|
1142
1171
|
* await garu.charges.refund('6f1c9b2e-...', { amount: 10.0 }); // R$10,00
|
|
@@ -1169,10 +1198,16 @@ declare class InstallmentPlans {
|
|
|
1169
1198
|
private readonly http;
|
|
1170
1199
|
constructor(http: HttpClient);
|
|
1171
1200
|
/**
|
|
1172
|
-
* Sell a product as a carnê.
|
|
1173
|
-
*
|
|
1174
|
-
*
|
|
1175
|
-
*
|
|
1201
|
+
* Sell a product as a carnê.
|
|
1202
|
+
*
|
|
1203
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
1204
|
+
* original plan for 24h. Derive it from something stable in your own
|
|
1205
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
1206
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
1207
|
+
* call is different every time and protects nothing.
|
|
1208
|
+
* This matters more here than anywhere else in the API: the call registers a
|
|
1209
|
+
* REAL boleto at the bank, so a retry without a key can put two payable
|
|
1210
|
+
* barcodes in one buyer's hands.
|
|
1176
1211
|
*
|
|
1177
1212
|
* @example
|
|
1178
1213
|
* const carne = await garu.installmentPlans.create({
|
|
@@ -1283,6 +1318,10 @@ declare class InstallmentPlans {
|
|
|
1283
1318
|
* team. Transfer the money to the buyer yourself, then close it with
|
|
1284
1319
|
* `garu.refundRequests.confirm`.
|
|
1285
1320
|
*
|
|
1321
|
+
* Pass `idempotencyKey` to cover the request-in-flight window; omit it and no
|
|
1322
|
+
* key is sent. The backend already dedupes a second pending request for the
|
|
1323
|
+
* same carnê, so this is defense-in-depth rather than the main guard.
|
|
1324
|
+
*
|
|
1286
1325
|
* @example
|
|
1287
1326
|
* const request = await garu.installmentPlans.requestRefund(uuid, {
|
|
1288
1327
|
* reason: 'Produto não entregue'
|
|
@@ -1372,6 +1411,12 @@ declare class Customers {
|
|
|
1372
1411
|
/**
|
|
1373
1412
|
* Register a customer for the current seller.
|
|
1374
1413
|
*
|
|
1414
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
1415
|
+
* original customer for 24h. Derive it from something stable in your own
|
|
1416
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
1417
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
1418
|
+
* call is different every time and protects nothing.
|
|
1419
|
+
*
|
|
1375
1420
|
* @example
|
|
1376
1421
|
* const customer = await garu.customers.create({
|
|
1377
1422
|
* name: 'Maria Silva',
|
|
@@ -1540,10 +1585,11 @@ declare class Products {
|
|
|
1540
1585
|
* product (HTTP 201). Only `name` is required; everything else falls back
|
|
1541
1586
|
* to seller/server defaults.
|
|
1542
1587
|
*
|
|
1543
|
-
*
|
|
1544
|
-
*
|
|
1545
|
-
*
|
|
1546
|
-
*
|
|
1588
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
1589
|
+
* original product for 24h. Derive it from something stable in your own
|
|
1590
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
1591
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
1592
|
+
* call is different every time and protects nothing.
|
|
1547
1593
|
*
|
|
1548
1594
|
* @example
|
|
1549
1595
|
* const product = await garu.products.create({
|
|
@@ -1593,13 +1639,15 @@ declare class ScheduledCharges {
|
|
|
1593
1639
|
private readonly http;
|
|
1594
1640
|
constructor(http: HttpClient);
|
|
1595
1641
|
/**
|
|
1596
|
-
* Create a new scheduled charge.
|
|
1597
|
-
*
|
|
1598
|
-
*
|
|
1599
|
-
*
|
|
1600
|
-
*
|
|
1601
|
-
*
|
|
1602
|
-
*
|
|
1642
|
+
* Create a new scheduled charge.
|
|
1643
|
+
*
|
|
1644
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
1645
|
+
* original series for 24h. Derive it from something stable in your own
|
|
1646
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
1647
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
1648
|
+
* call is different every time and protects nothing.
|
|
1649
|
+
* Worth the effort here: without a key, a retry double-books recurring
|
|
1650
|
+
* billing for the same customer.
|
|
1603
1651
|
*
|
|
1604
1652
|
* @example
|
|
1605
1653
|
* const charge = await garu.scheduledCharges.create({
|
|
@@ -1866,11 +1914,10 @@ declare class WebhookEvents {
|
|
|
1866
1914
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
1867
1915
|
* the response payload's `manualResendOf` field.
|
|
1868
1916
|
*
|
|
1869
|
-
*
|
|
1870
|
-
*
|
|
1871
|
-
*
|
|
1872
|
-
*
|
|
1873
|
-
* your own retry-suppression if that matters for your integration.
|
|
1917
|
+
* `idempotencyKey` is forwarded when you pass one and omitted when you don't.
|
|
1918
|
+
* Either way the gateway does not currently deduplicate `/resend` calls
|
|
1919
|
+
* against it, so retrying after a network failure can create more than one
|
|
1920
|
+
* clone — pair it with your own retry-suppression if that matters.
|
|
1874
1921
|
*
|
|
1875
1922
|
* Returns the *clone* event (new uuid), not the original. The original is
|
|
1876
1923
|
* unchanged on the server.
|
package/dist/index.d.ts
CHANGED
|
@@ -219,6 +219,13 @@ interface RefundChargeParams {
|
|
|
219
219
|
amount?: number;
|
|
220
220
|
/** Free-form reason stored on the refund. */
|
|
221
221
|
reason?: string;
|
|
222
|
+
/**
|
|
223
|
+
* Optional idempotency key for safe retries. The SDK auto-generates a
|
|
224
|
+
* UUIDv4 when omitted and forwards it as `X-Idempotency-Key`. Only used
|
|
225
|
+
* for Pix/boleto, which open a refund request instead of an automated
|
|
226
|
+
* reversal — ignored for card, which has no manual request to duplicate.
|
|
227
|
+
*/
|
|
228
|
+
idempotencyKey?: string;
|
|
222
229
|
}
|
|
223
230
|
interface ListChargesParams {
|
|
224
231
|
/** Page number (1-based). Default: 1. */
|
|
@@ -324,6 +331,13 @@ interface CreateCustomerParams {
|
|
|
324
331
|
city?: string;
|
|
325
332
|
/** 2-letter uppercase state code, e.g. `SP`. */
|
|
326
333
|
state?: string;
|
|
334
|
+
/**
|
|
335
|
+
* Optional idempotency key for safe retries. The SDK auto-generates a
|
|
336
|
+
* UUIDv4 when omitted and forwards it as `X-Idempotency-Key`. Document
|
|
337
|
+
* uniqueness already merges a retried create into the same customer, so
|
|
338
|
+
* this mainly protects registrations with no document set.
|
|
339
|
+
*/
|
|
340
|
+
idempotencyKey?: string;
|
|
327
341
|
}
|
|
328
342
|
interface UpdateCustomerParams {
|
|
329
343
|
name?: string;
|
|
@@ -1051,6 +1065,13 @@ interface RequestPlanRefundParams {
|
|
|
1051
1065
|
/** Defaults to everything the carnê has collected. */
|
|
1052
1066
|
amount?: number;
|
|
1053
1067
|
reason?: string;
|
|
1068
|
+
/**
|
|
1069
|
+
* Optional idempotency key for safe retries. The SDK auto-generates a
|
|
1070
|
+
* UUIDv4 when omitted and forwards it as `X-Idempotency-Key`. The backend
|
|
1071
|
+
* already dedupes a second pending request for the same carnê, so this is
|
|
1072
|
+
* mainly defense-in-depth for the request-in-flight window.
|
|
1073
|
+
*/
|
|
1074
|
+
idempotencyKey?: string;
|
|
1054
1075
|
}
|
|
1055
1076
|
interface ListRefundRequestsParams {
|
|
1056
1077
|
page?: number;
|
|
@@ -1079,9 +1100,11 @@ declare class Charges {
|
|
|
1079
1100
|
/**
|
|
1080
1101
|
* Create a charge (PIX, boleto, or credit card).
|
|
1081
1102
|
*
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1103
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
1104
|
+
* original charge for 24h. Derive it from something stable in your own
|
|
1105
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
1106
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
1107
|
+
* call is different every time and protects nothing.
|
|
1085
1108
|
*
|
|
1086
1109
|
* @example
|
|
1087
1110
|
* // PIX — render charge.pix.code as a QR in your own checkout
|
|
@@ -1095,7 +1118,7 @@ declare class Charges {
|
|
|
1095
1118
|
* phone: '11987654321'
|
|
1096
1119
|
* }
|
|
1097
1120
|
* });
|
|
1098
|
-
*
|
|
1121
|
+
* // charge.uuid, charge.pix?.code
|
|
1099
1122
|
*
|
|
1100
1123
|
* @example
|
|
1101
1124
|
* // Credit card, 2 installments. Server-to-server only (PCI scope).
|
|
@@ -1127,7 +1150,7 @@ declare class Charges {
|
|
|
1127
1150
|
*
|
|
1128
1151
|
* @example
|
|
1129
1152
|
* const { data, totalCount } = await garu.charges.list({ status: 'paid', limit: 50 });
|
|
1130
|
-
*
|
|
1153
|
+
* // data.length of totalCount paid charges
|
|
1131
1154
|
*/
|
|
1132
1155
|
list(params?: ListChargesParams): Promise<ChargeList>;
|
|
1133
1156
|
/**
|
|
@@ -1137,6 +1160,12 @@ declare class Charges {
|
|
|
1137
1160
|
* charge in `refund_pending`, reaching `refunded` only once the transfer
|
|
1138
1161
|
* settles.
|
|
1139
1162
|
*
|
|
1163
|
+
* For Pix/boleto (which open a refund request instead of an automated
|
|
1164
|
+
* reversal), pass `idempotencyKey` to make a retry return the original
|
|
1165
|
+
* request rather than opening a second one; omit it and no key is sent.
|
|
1166
|
+
* Ignored for card, which reverses automatically and has no manual request
|
|
1167
|
+
* to duplicate.
|
|
1168
|
+
*
|
|
1140
1169
|
* @example
|
|
1141
1170
|
* await garu.charges.refund('6f1c9b2e-...'); // full
|
|
1142
1171
|
* await garu.charges.refund('6f1c9b2e-...', { amount: 10.0 }); // R$10,00
|
|
@@ -1169,10 +1198,16 @@ declare class InstallmentPlans {
|
|
|
1169
1198
|
private readonly http;
|
|
1170
1199
|
constructor(http: HttpClient);
|
|
1171
1200
|
/**
|
|
1172
|
-
* Sell a product as a carnê.
|
|
1173
|
-
*
|
|
1174
|
-
*
|
|
1175
|
-
*
|
|
1201
|
+
* Sell a product as a carnê.
|
|
1202
|
+
*
|
|
1203
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
1204
|
+
* original plan for 24h. Derive it from something stable in your own
|
|
1205
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
1206
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
1207
|
+
* call is different every time and protects nothing.
|
|
1208
|
+
* This matters more here than anywhere else in the API: the call registers a
|
|
1209
|
+
* REAL boleto at the bank, so a retry without a key can put two payable
|
|
1210
|
+
* barcodes in one buyer's hands.
|
|
1176
1211
|
*
|
|
1177
1212
|
* @example
|
|
1178
1213
|
* const carne = await garu.installmentPlans.create({
|
|
@@ -1283,6 +1318,10 @@ declare class InstallmentPlans {
|
|
|
1283
1318
|
* team. Transfer the money to the buyer yourself, then close it with
|
|
1284
1319
|
* `garu.refundRequests.confirm`.
|
|
1285
1320
|
*
|
|
1321
|
+
* Pass `idempotencyKey` to cover the request-in-flight window; omit it and no
|
|
1322
|
+
* key is sent. The backend already dedupes a second pending request for the
|
|
1323
|
+
* same carnê, so this is defense-in-depth rather than the main guard.
|
|
1324
|
+
*
|
|
1286
1325
|
* @example
|
|
1287
1326
|
* const request = await garu.installmentPlans.requestRefund(uuid, {
|
|
1288
1327
|
* reason: 'Produto não entregue'
|
|
@@ -1372,6 +1411,12 @@ declare class Customers {
|
|
|
1372
1411
|
/**
|
|
1373
1412
|
* Register a customer for the current seller.
|
|
1374
1413
|
*
|
|
1414
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
1415
|
+
* original customer for 24h. Derive it from something stable in your own
|
|
1416
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
1417
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
1418
|
+
* call is different every time and protects nothing.
|
|
1419
|
+
*
|
|
1375
1420
|
* @example
|
|
1376
1421
|
* const customer = await garu.customers.create({
|
|
1377
1422
|
* name: 'Maria Silva',
|
|
@@ -1540,10 +1585,11 @@ declare class Products {
|
|
|
1540
1585
|
* product (HTTP 201). Only `name` is required; everything else falls back
|
|
1541
1586
|
* to seller/server defaults.
|
|
1542
1587
|
*
|
|
1543
|
-
*
|
|
1544
|
-
*
|
|
1545
|
-
*
|
|
1546
|
-
*
|
|
1588
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
1589
|
+
* original product for 24h. Derive it from something stable in your own
|
|
1590
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
1591
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
1592
|
+
* call is different every time and protects nothing.
|
|
1547
1593
|
*
|
|
1548
1594
|
* @example
|
|
1549
1595
|
* const product = await garu.products.create({
|
|
@@ -1593,13 +1639,15 @@ declare class ScheduledCharges {
|
|
|
1593
1639
|
private readonly http;
|
|
1594
1640
|
constructor(http: HttpClient);
|
|
1595
1641
|
/**
|
|
1596
|
-
* Create a new scheduled charge.
|
|
1597
|
-
*
|
|
1598
|
-
*
|
|
1599
|
-
*
|
|
1600
|
-
*
|
|
1601
|
-
*
|
|
1602
|
-
*
|
|
1642
|
+
* Create a new scheduled charge.
|
|
1643
|
+
*
|
|
1644
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
1645
|
+
* original series for 24h. Derive it from something stable in your own
|
|
1646
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
1647
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
1648
|
+
* call is different every time and protects nothing.
|
|
1649
|
+
* Worth the effort here: without a key, a retry double-books recurring
|
|
1650
|
+
* billing for the same customer.
|
|
1603
1651
|
*
|
|
1604
1652
|
* @example
|
|
1605
1653
|
* const charge = await garu.scheduledCharges.create({
|
|
@@ -1866,11 +1914,10 @@ declare class WebhookEvents {
|
|
|
1866
1914
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
1867
1915
|
* the response payload's `manualResendOf` field.
|
|
1868
1916
|
*
|
|
1869
|
-
*
|
|
1870
|
-
*
|
|
1871
|
-
*
|
|
1872
|
-
*
|
|
1873
|
-
* your own retry-suppression if that matters for your integration.
|
|
1917
|
+
* `idempotencyKey` is forwarded when you pass one and omitted when you don't.
|
|
1918
|
+
* Either way the gateway does not currently deduplicate `/resend` calls
|
|
1919
|
+
* against it, so retrying after a network failure can create more than one
|
|
1920
|
+
* clone — pair it with your own retry-suppression if that matters.
|
|
1874
1921
|
*
|
|
1875
1922
|
* Returns the *clone* event (new uuid), not the original. The original is
|
|
1876
1923
|
* unchanged on the server.
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import createClient from 'openapi-fetch';
|
|
2
|
-
import { createHmac, timingSafeEqual
|
|
2
|
+
import { createHmac, timingSafeEqual } from 'crypto';
|
|
3
3
|
|
|
4
4
|
// src/http.ts
|
|
5
5
|
|
|
@@ -182,8 +182,8 @@ function backoffDelay(attempt, retryAfterSec) {
|
|
|
182
182
|
function sleep(ms) {
|
|
183
183
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
184
184
|
}
|
|
185
|
-
function
|
|
186
|
-
return
|
|
185
|
+
function idempotencyHeaders(key) {
|
|
186
|
+
return key ? { "X-Idempotency-Key": key } : {};
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
// src/resources/charges.ts
|
|
@@ -195,9 +195,11 @@ var Charges = class {
|
|
|
195
195
|
/**
|
|
196
196
|
* Create a charge (PIX, boleto, or credit card).
|
|
197
197
|
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
198
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
199
|
+
* original charge for 24h. Derive it from something stable in your own
|
|
200
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
201
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
202
|
+
* call is different every time and protects nothing.
|
|
201
203
|
*
|
|
202
204
|
* @example
|
|
203
205
|
* // PIX — render charge.pix.code as a QR in your own checkout
|
|
@@ -211,7 +213,7 @@ var Charges = class {
|
|
|
211
213
|
* phone: '11987654321'
|
|
212
214
|
* }
|
|
213
215
|
* });
|
|
214
|
-
*
|
|
216
|
+
* // charge.uuid, charge.pix?.code
|
|
215
217
|
*
|
|
216
218
|
* @example
|
|
217
219
|
* // Credit card, 2 installments. Server-to-server only (PCI scope).
|
|
@@ -230,7 +232,6 @@ var Charges = class {
|
|
|
230
232
|
* // charge.amount is the base price; charge.chargedTotal is what was charged.
|
|
231
233
|
*/
|
|
232
234
|
async create(params) {
|
|
233
|
-
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
234
235
|
const body = {
|
|
235
236
|
productId: params.productId,
|
|
236
237
|
paymentMethod: params.paymentMethod,
|
|
@@ -239,7 +240,7 @@ var Charges = class {
|
|
|
239
240
|
if (params.card) body.card = params.card;
|
|
240
241
|
if (params.checkoutSessionToken) body.checkoutSessionToken = params.checkoutSessionToken;
|
|
241
242
|
if (params.additionalInfo !== void 0) body.additionalInfo = params.additionalInfo;
|
|
242
|
-
return this.post("/api/v1/charges", body,
|
|
243
|
+
return this.post("/api/v1/charges", body, idempotencyHeaders(params.idempotencyKey));
|
|
243
244
|
}
|
|
244
245
|
/**
|
|
245
246
|
* Retrieve a charge by uuid.
|
|
@@ -256,7 +257,7 @@ var Charges = class {
|
|
|
256
257
|
*
|
|
257
258
|
* @example
|
|
258
259
|
* const { data, totalCount } = await garu.charges.list({ status: 'paid', limit: 50 });
|
|
259
|
-
*
|
|
260
|
+
* // data.length of totalCount paid charges
|
|
260
261
|
*/
|
|
261
262
|
async list(params = {}) {
|
|
262
263
|
const query = {};
|
|
@@ -279,6 +280,12 @@ var Charges = class {
|
|
|
279
280
|
* charge in `refund_pending`, reaching `refunded` only once the transfer
|
|
280
281
|
* settles.
|
|
281
282
|
*
|
|
283
|
+
* For Pix/boleto (which open a refund request instead of an automated
|
|
284
|
+
* reversal), pass `idempotencyKey` to make a retry return the original
|
|
285
|
+
* request rather than opening a second one; omit it and no key is sent.
|
|
286
|
+
* Ignored for card, which reverses automatically and has no manual request
|
|
287
|
+
* to duplicate.
|
|
288
|
+
*
|
|
282
289
|
* @example
|
|
283
290
|
* await garu.charges.refund('6f1c9b2e-...'); // full
|
|
284
291
|
* await garu.charges.refund('6f1c9b2e-...', { amount: 10.0 }); // R$10,00
|
|
@@ -287,7 +294,7 @@ var Charges = class {
|
|
|
287
294
|
const body = {};
|
|
288
295
|
if (params.amount !== void 0) body.amount = params.amount;
|
|
289
296
|
if (params.reason !== void 0) body.reason = params.reason;
|
|
290
|
-
return this.post(`/api/v1/charges/${encodeURIComponent(uuid)}/refund`, body);
|
|
297
|
+
return this.post(`/api/v1/charges/${encodeURIComponent(uuid)}/refund`, body, idempotencyHeaders(params.idempotencyKey));
|
|
291
298
|
}
|
|
292
299
|
/**
|
|
293
300
|
* Cancel an unpaid charge.
|
|
@@ -312,7 +319,11 @@ var Charges = class {
|
|
|
312
319
|
}
|
|
313
320
|
post(url, body, headers) {
|
|
314
321
|
return this.http.call(
|
|
315
|
-
(signal) => this.http.client.POST(url, {
|
|
322
|
+
(signal) => this.http.client.POST(url, {
|
|
323
|
+
body,
|
|
324
|
+
headers,
|
|
325
|
+
signal
|
|
326
|
+
})
|
|
316
327
|
);
|
|
317
328
|
}
|
|
318
329
|
};
|
|
@@ -324,10 +335,16 @@ var InstallmentPlans = class {
|
|
|
324
335
|
}
|
|
325
336
|
http;
|
|
326
337
|
/**
|
|
327
|
-
* Sell a product as a carnê.
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
338
|
+
* Sell a product as a carnê.
|
|
339
|
+
*
|
|
340
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
341
|
+
* original plan for 24h. Derive it from something stable in your own
|
|
342
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
343
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
344
|
+
* call is different every time and protects nothing.
|
|
345
|
+
* This matters more here than anywhere else in the API: the call registers a
|
|
346
|
+
* REAL boleto at the bank, so a retry without a key can put two payable
|
|
347
|
+
* barcodes in one buyer's hands.
|
|
331
348
|
*
|
|
332
349
|
* @example
|
|
333
350
|
* const carne = await garu.installmentPlans.create({
|
|
@@ -353,12 +370,11 @@ var InstallmentPlans = class {
|
|
|
353
370
|
* });
|
|
354
371
|
*/
|
|
355
372
|
async create(params) {
|
|
356
|
-
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
357
373
|
const { idempotencyKey: _omit, ...body } = params;
|
|
358
374
|
return this.http.call(
|
|
359
375
|
(signal) => this.http.client.POST("/api/v1/installment-plans", {
|
|
360
376
|
body,
|
|
361
|
-
headers:
|
|
377
|
+
headers: idempotencyHeaders(params.idempotencyKey),
|
|
362
378
|
signal
|
|
363
379
|
}).then((r) => r)
|
|
364
380
|
);
|
|
@@ -501,6 +517,10 @@ var InstallmentPlans = class {
|
|
|
501
517
|
* team. Transfer the money to the buyer yourself, then close it with
|
|
502
518
|
* `garu.refundRequests.confirm`.
|
|
503
519
|
*
|
|
520
|
+
* Pass `idempotencyKey` to cover the request-in-flight window; omit it and no
|
|
521
|
+
* key is sent. The backend already dedupes a second pending request for the
|
|
522
|
+
* same carnê, so this is defense-in-depth rather than the main guard.
|
|
523
|
+
*
|
|
504
524
|
* @example
|
|
505
525
|
* const request = await garu.installmentPlans.requestRefund(uuid, {
|
|
506
526
|
* reason: 'Produto não entregue'
|
|
@@ -509,9 +529,11 @@ var InstallmentPlans = class {
|
|
|
509
529
|
* request.amount; // defaults to everything the carnê collected
|
|
510
530
|
*/
|
|
511
531
|
async requestRefund(uuid, params = {}) {
|
|
532
|
+
const { idempotencyKey: _omit, ...body } = params;
|
|
512
533
|
return this.http.call(
|
|
513
534
|
(signal) => this.http.client.POST(`/api/v1/installment-plans/${uuid}/refund-requests`, {
|
|
514
|
-
body
|
|
535
|
+
body,
|
|
536
|
+
headers: idempotencyHeaders(params.idempotencyKey),
|
|
515
537
|
signal
|
|
516
538
|
}).then((r) => r)
|
|
517
539
|
);
|
|
@@ -619,6 +641,12 @@ var Customers = class {
|
|
|
619
641
|
/**
|
|
620
642
|
* Register a customer for the current seller.
|
|
621
643
|
*
|
|
644
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
645
|
+
* original customer for 24h. Derive it from something stable in your own
|
|
646
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
647
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
648
|
+
* call is different every time and protects nothing.
|
|
649
|
+
*
|
|
622
650
|
* @example
|
|
623
651
|
* const customer = await garu.customers.create({
|
|
624
652
|
* name: 'Maria Silva',
|
|
@@ -630,9 +658,11 @@ var Customers = class {
|
|
|
630
658
|
* customer.uuid;
|
|
631
659
|
*/
|
|
632
660
|
async create(params) {
|
|
661
|
+
const { idempotencyKey: _omit, ...body } = params;
|
|
633
662
|
return this.http.call(
|
|
634
663
|
(signal) => this.http.client.POST("/api/v1/customers", {
|
|
635
|
-
body
|
|
664
|
+
body,
|
|
665
|
+
headers: idempotencyHeaders(params.idempotencyKey),
|
|
636
666
|
signal
|
|
637
667
|
}).then((r) => r)
|
|
638
668
|
);
|
|
@@ -877,10 +907,11 @@ var Products = class {
|
|
|
877
907
|
* product (HTTP 201). Only `name` is required; everything else falls back
|
|
878
908
|
* to seller/server defaults.
|
|
879
909
|
*
|
|
880
|
-
*
|
|
881
|
-
*
|
|
882
|
-
*
|
|
883
|
-
*
|
|
910
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
911
|
+
* original product for 24h. Derive it from something stable in your own
|
|
912
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
913
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
914
|
+
* call is different every time and protects nothing.
|
|
884
915
|
*
|
|
885
916
|
* @example
|
|
886
917
|
* const product = await garu.products.create({
|
|
@@ -896,11 +927,10 @@ var Products = class {
|
|
|
896
927
|
*/
|
|
897
928
|
async create(params) {
|
|
898
929
|
const { idempotencyKey, ...body } = params;
|
|
899
|
-
const key = idempotencyKey ?? generateIdempotencyKey();
|
|
900
930
|
return this.http.call(
|
|
901
931
|
(signal) => this.http.client.POST("/api/v1/products", {
|
|
902
932
|
body,
|
|
903
|
-
headers:
|
|
933
|
+
headers: idempotencyHeaders(idempotencyKey),
|
|
904
934
|
signal
|
|
905
935
|
}).then((r) => r)
|
|
906
936
|
);
|
|
@@ -936,13 +966,15 @@ var ScheduledCharges = class {
|
|
|
936
966
|
}
|
|
937
967
|
http;
|
|
938
968
|
/**
|
|
939
|
-
* Create a new scheduled charge.
|
|
940
|
-
*
|
|
941
|
-
*
|
|
942
|
-
*
|
|
943
|
-
*
|
|
944
|
-
*
|
|
945
|
-
*
|
|
969
|
+
* Create a new scheduled charge.
|
|
970
|
+
*
|
|
971
|
+
* Pass `idempotencyKey` to make this safe to retry: the same key returns the
|
|
972
|
+
* original series for 24h. Derive it from something stable in your own
|
|
973
|
+
* domain (an order id, a booking id) so a retry reproduces it. Omit it and
|
|
974
|
+
* no key is sent — the SDK does NOT invent one, because a key generated per
|
|
975
|
+
* call is different every time and protects nothing.
|
|
976
|
+
* Worth the effort here: without a key, a retry double-books recurring
|
|
977
|
+
* billing for the same customer.
|
|
946
978
|
*
|
|
947
979
|
* @example
|
|
948
980
|
* const charge = await garu.scheduledCharges.create({
|
|
@@ -969,12 +1001,11 @@ var ScheduledCharges = class {
|
|
|
969
1001
|
* });
|
|
970
1002
|
*/
|
|
971
1003
|
async create(params) {
|
|
972
|
-
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
973
1004
|
const { idempotencyKey: _omit, ...body } = params;
|
|
974
1005
|
return this.http.call(
|
|
975
1006
|
(signal) => this.http.client.POST("/api/v1/scheduled-charges", {
|
|
976
1007
|
body,
|
|
977
|
-
headers:
|
|
1008
|
+
headers: idempotencyHeaders(params.idempotencyKey),
|
|
978
1009
|
signal
|
|
979
1010
|
}).then((r) => r)
|
|
980
1011
|
);
|
|
@@ -1363,11 +1394,10 @@ var WebhookEvents = class {
|
|
|
1363
1394
|
* original — distinguishable both by the `resend_` prefix and by reading
|
|
1364
1395
|
* the response payload's `manualResendOf` field.
|
|
1365
1396
|
*
|
|
1366
|
-
*
|
|
1367
|
-
*
|
|
1368
|
-
*
|
|
1369
|
-
*
|
|
1370
|
-
* your own retry-suppression if that matters for your integration.
|
|
1397
|
+
* `idempotencyKey` is forwarded when you pass one and omitted when you don't.
|
|
1398
|
+
* Either way the gateway does not currently deduplicate `/resend` calls
|
|
1399
|
+
* against it, so retrying after a network failure can create more than one
|
|
1400
|
+
* clone — pair it with your own retry-suppression if that matters.
|
|
1371
1401
|
*
|
|
1372
1402
|
* Returns the *clone* event (new uuid), not the original. The original is
|
|
1373
1403
|
* unchanged on the server.
|
|
@@ -1379,11 +1409,10 @@ var WebhookEvents = class {
|
|
|
1379
1409
|
* clone.manualResendOf === event.uuid; // true — points back at the source
|
|
1380
1410
|
*/
|
|
1381
1411
|
async resend(uuid, params = {}) {
|
|
1382
|
-
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
1383
1412
|
return this.http.call(
|
|
1384
1413
|
(signal) => this.http.client.POST(`/api/v1/webhook-events/${uuid}/resend`, {
|
|
1385
1414
|
body: {},
|
|
1386
|
-
headers:
|
|
1415
|
+
headers: idempotencyHeaders(params.idempotencyKey),
|
|
1387
1416
|
signal
|
|
1388
1417
|
}).then((r) => r)
|
|
1389
1418
|
);
|