@garuhq/node 1.0.0 → 2.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 +74 -0
- package/README.md +22 -15
- package/dist/index.cjs +326 -20
- package/dist/index.d.cts +415 -29
- package/dist/index.d.ts +415 -29
- package/dist/index.js +326 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -317,6 +317,299 @@ var Charges = class {
|
|
|
317
317
|
}
|
|
318
318
|
};
|
|
319
319
|
|
|
320
|
+
// src/resources/installment-plans.ts
|
|
321
|
+
var InstallmentPlans = class {
|
|
322
|
+
constructor(http) {
|
|
323
|
+
this.http = http;
|
|
324
|
+
}
|
|
325
|
+
http;
|
|
326
|
+
/**
|
|
327
|
+
* Sell a product as a carnê. Auto-attaches `X-Idempotency-Key` (UUIDv4 if
|
|
328
|
+
* you don't pass `idempotencyKey`), which matters more here than anywhere
|
|
329
|
+
* else in the API: this call registers a REAL boleto at the bank, so a
|
|
330
|
+
* blind retry can put two payable barcodes in one buyer's hands.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* const carne = await garu.installmentPlans.create({
|
|
334
|
+
* productId: '40381e8e-6ee7-4b8e-9393-766a6e2109d2',
|
|
335
|
+
* customerId: 4821,
|
|
336
|
+
* installments: 12
|
|
337
|
+
* });
|
|
338
|
+
* // A R$1.200 product at fator 1,30 bills R$130,00 a month:
|
|
339
|
+
* carne.totalScheduled; // 1560
|
|
340
|
+
* carne.installmentAmount; // 130
|
|
341
|
+
* carne.installmentsDetail?.[0]; // parcela 1, with its barcode
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* // Attribute the sale to an affiliate. Fixed at sale time: every later
|
|
345
|
+
* // parcela inherits it, so omitting it pays them nothing for the whole
|
|
346
|
+
* // carnê. The affiliate must already be active on this product.
|
|
347
|
+
* await garu.installmentPlans.create({
|
|
348
|
+
* productId: '40381e8e-6ee7-4b8e-9393-766a6e2109d2',
|
|
349
|
+
* customerId: 4821,
|
|
350
|
+
* installments: 6,
|
|
351
|
+
* firstDueDate: '2026-10-05',
|
|
352
|
+
* affiliateId: 5
|
|
353
|
+
* });
|
|
354
|
+
*/
|
|
355
|
+
async create(params) {
|
|
356
|
+
const idempotencyKey = params.idempotencyKey ?? generateIdempotencyKey();
|
|
357
|
+
const { idempotencyKey: _omit, ...body } = params;
|
|
358
|
+
return this.http.call(
|
|
359
|
+
(signal) => this.http.client.POST("/api/v1/installment-plans", {
|
|
360
|
+
body,
|
|
361
|
+
headers: { "X-Idempotency-Key": idempotencyKey },
|
|
362
|
+
signal
|
|
363
|
+
}).then((r) => r)
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* List carnês, newest first. `dueFrom`/`dueTo` filter on the FIRST
|
|
368
|
+
* parcela's due date, which is what identifies the plan; filtering on every
|
|
369
|
+
* parcela would return one carnê twelve times.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* const atRisk = await garu.installmentPlans.list({ status: 'defaulted' });
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* const live = await garu.installmentPlans.list({
|
|
376
|
+
* status: ['active', 'pending_activation'],
|
|
377
|
+
* customerId: 4821,
|
|
378
|
+
* limit: 50
|
|
379
|
+
* });
|
|
380
|
+
*/
|
|
381
|
+
async list(params = {}) {
|
|
382
|
+
const qs = new URLSearchParams();
|
|
383
|
+
if (params.page !== void 0) qs.set("page", String(params.page));
|
|
384
|
+
if (params.limit !== void 0) qs.set("limit", String(params.limit));
|
|
385
|
+
if (params.customerId !== void 0) qs.set("customerId", String(params.customerId));
|
|
386
|
+
if (params.productId) qs.set("productId", params.productId);
|
|
387
|
+
if (params.dueFrom) qs.set("dueFrom", params.dueFrom);
|
|
388
|
+
if (params.dueTo) qs.set("dueTo", params.dueTo);
|
|
389
|
+
if (params.status) {
|
|
390
|
+
const statuses = Array.isArray(params.status) ? params.status : [params.status];
|
|
391
|
+
for (const s of statuses) qs.append("status", s);
|
|
392
|
+
}
|
|
393
|
+
const query = qs.toString();
|
|
394
|
+
const url = `/api/v1/installment-plans${query ? `?${query}` : ""}`;
|
|
395
|
+
return this.http.call(
|
|
396
|
+
(signal) => this.http.client.GET(url, { signal }).then(
|
|
397
|
+
(r) => r
|
|
398
|
+
)
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Retrieve one carnê with every parcela: due date, status, barcode line and
|
|
403
|
+
* boleto PDF.
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* const carne = await garu.installmentPlans.get(uuid);
|
|
407
|
+
* const unpaid = carne.installmentsDetail?.filter((i) => i.status !== 'paid');
|
|
408
|
+
* carne.totalCollected; // what has actually cleared, not what was billed
|
|
409
|
+
*/
|
|
410
|
+
async get(uuid) {
|
|
411
|
+
return this.http.call(
|
|
412
|
+
(signal) => this.http.client.GET(`/api/v1/installment-plans/${uuid}`, { signal }).then(
|
|
413
|
+
(r) => r
|
|
414
|
+
)
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Issue a segunda via for one parcela, once the current slip has expired.
|
|
419
|
+
*
|
|
420
|
+
* A boleto stays payable at any bank until its due date plus five days, so
|
|
421
|
+
* Garu refuses while the old barcode is still live — two live barcodes for
|
|
422
|
+
* one parcela is how a buyer pays it twice. Once per parcela per day.
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* const result = await garu.installmentPlans.reissueInstallment(uuid, 4);
|
|
426
|
+
* if (result.status === 'emitted') {
|
|
427
|
+
* send(result.installment!.boleto!.barcodeLine);
|
|
428
|
+
* }
|
|
429
|
+
*/
|
|
430
|
+
async reissueInstallment(uuid, number) {
|
|
431
|
+
return this.http.call(
|
|
432
|
+
(signal) => this.http.client.POST(
|
|
433
|
+
`/api/v1/installment-plans/${uuid}/installments/${number}/reissue`,
|
|
434
|
+
{ signal }
|
|
435
|
+
).then((r) => r)
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Move one parcela to a later date. Its siblings keep theirs — this
|
|
440
|
+
* postpones a payment, it does not restructure the carnê. A slip already
|
|
441
|
+
* emitted stays payable on its original date until it expires.
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* await garu.installmentPlans.postponeInstallment(uuid, 4, {
|
|
445
|
+
* newDueDate: '2026-12-20'
|
|
446
|
+
* });
|
|
447
|
+
*/
|
|
448
|
+
async postponeInstallment(uuid, number, params) {
|
|
449
|
+
return this.http.call(
|
|
450
|
+
(signal) => this.http.client.POST(
|
|
451
|
+
`/api/v1/installment-plans/${uuid}/installments/${number}/postpone`,
|
|
452
|
+
{ body: params, signal }
|
|
453
|
+
).then((r) => r)
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Record a parcela as paid, for when the buyer paid the slip but the
|
|
458
|
+
* webhook never arrived.
|
|
459
|
+
*
|
|
460
|
+
* Garu asks the provider to confirm the charge really compensated before
|
|
461
|
+
* recording it, because this settles the transaction and pays affiliate and
|
|
462
|
+
* co-producer commissions. A provider outage refuses the action rather than
|
|
463
|
+
* trusting the assertion.
|
|
464
|
+
*
|
|
465
|
+
* @example
|
|
466
|
+
* const parcela = await garu.installmentPlans.markInstallmentPaid(uuid, 3);
|
|
467
|
+
* parcela.status; // 'paid'
|
|
468
|
+
*/
|
|
469
|
+
async markInstallmentPaid(uuid, number) {
|
|
470
|
+
return this.http.call(
|
|
471
|
+
(signal) => this.http.client.POST(
|
|
472
|
+
`/api/v1/installment-plans/${uuid}/installments/${number}/mark-paid`,
|
|
473
|
+
{ signal }
|
|
474
|
+
).then((r) => r)
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Cancel the carnê. Emission and reminders stop and open slips are
|
|
479
|
+
* cancelled at the provider.
|
|
480
|
+
*
|
|
481
|
+
* Money already collected is NOT returned — open a refund request for that.
|
|
482
|
+
* A cancelled carnê is never revived by a late payment; that money opens a
|
|
483
|
+
* refund request instead.
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* await garu.installmentPlans.cancel(uuid, { note: 'Comprador desistiu' });
|
|
487
|
+
*/
|
|
488
|
+
async cancel(uuid, params = {}) {
|
|
489
|
+
return this.http.call(
|
|
490
|
+
(signal) => this.http.client.POST(`/api/v1/installment-plans/${uuid}/cancel`, {
|
|
491
|
+
body: params,
|
|
492
|
+
signal
|
|
493
|
+
}).then((r) => r)
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Ask for this carnê to be refunded.
|
|
498
|
+
*
|
|
499
|
+
* Garu does NOT move the money. A boleto cannot be reversed and the funds
|
|
500
|
+
* already settled to you, so this records the request and notifies your
|
|
501
|
+
* team. Transfer the money to the buyer yourself, then close it with
|
|
502
|
+
* `garu.refundRequests.confirm`.
|
|
503
|
+
*
|
|
504
|
+
* @example
|
|
505
|
+
* const request = await garu.installmentPlans.requestRefund(uuid, {
|
|
506
|
+
* reason: 'Produto não entregue'
|
|
507
|
+
* });
|
|
508
|
+
* request.status; // 'pending' — nothing has moved yet
|
|
509
|
+
* request.amount; // defaults to everything the carnê collected
|
|
510
|
+
*/
|
|
511
|
+
async requestRefund(uuid, params = {}) {
|
|
512
|
+
return this.http.call(
|
|
513
|
+
(signal) => this.http.client.POST(`/api/v1/installment-plans/${uuid}/refund-requests`, {
|
|
514
|
+
body: params,
|
|
515
|
+
signal
|
|
516
|
+
}).then((r) => r)
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
// src/resources/refund-requests.ts
|
|
522
|
+
var RefundRequests = class {
|
|
523
|
+
constructor(http) {
|
|
524
|
+
this.http = http;
|
|
525
|
+
}
|
|
526
|
+
http;
|
|
527
|
+
/**
|
|
528
|
+
* List refund requests, newest first. Covers carnê and Pix/boleto alike.
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* // Everything you still owe a buyer.
|
|
532
|
+
* const owed = await garu.refundRequests.list({ status: 'pending' });
|
|
533
|
+
* const total = owed.data.reduce((sum, r) => sum + r.amount, 0);
|
|
534
|
+
*
|
|
535
|
+
* @example
|
|
536
|
+
* const forThisCarne = await garu.refundRequests.list({ planId: carne.uuid });
|
|
537
|
+
*/
|
|
538
|
+
async list(params = {}) {
|
|
539
|
+
const qs = new URLSearchParams();
|
|
540
|
+
if (params.page !== void 0) qs.set("page", String(params.page));
|
|
541
|
+
if (params.limit !== void 0) qs.set("limit", String(params.limit));
|
|
542
|
+
if (params.planId) qs.set("planId", params.planId);
|
|
543
|
+
if (params.chargeId) qs.set("chargeId", params.chargeId);
|
|
544
|
+
if (params.status) {
|
|
545
|
+
const statuses = Array.isArray(params.status) ? params.status : [params.status];
|
|
546
|
+
for (const s of statuses) qs.append("status", s);
|
|
547
|
+
}
|
|
548
|
+
const query = qs.toString();
|
|
549
|
+
const url = `/api/v1/refund-requests${query ? `?${query}` : ""}`;
|
|
550
|
+
return this.http.call(
|
|
551
|
+
(signal) => this.http.client.GET(url, { signal }).then(
|
|
552
|
+
(r) => r
|
|
553
|
+
)
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Retrieve one refund request.
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* const request = await garu.refundRequests.get(uuid);
|
|
561
|
+
* request.installmentPlanId ?? request.chargeId; // exactly one is set
|
|
562
|
+
*/
|
|
563
|
+
async get(uuid) {
|
|
564
|
+
return this.http.call(
|
|
565
|
+
(signal) => this.http.client.GET(`/api/v1/refund-requests/${uuid}`, { signal }).then(
|
|
566
|
+
(r) => r
|
|
567
|
+
)
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Record that you returned the money. Call this AFTER transferring it.
|
|
572
|
+
*
|
|
573
|
+
* Confirming closes a carnê as refunded, stops remaining parcelas, cancels
|
|
574
|
+
* open slips at the provider and claws back the affiliate and co-producer
|
|
575
|
+
* commissions on the parcelas that cleared. For a Pix or boleto charge it
|
|
576
|
+
* marks the charge reversed and fires `transaction.refunded`. Idempotent:
|
|
577
|
+
* confirming twice does not claw back twice.
|
|
578
|
+
*
|
|
579
|
+
* @example
|
|
580
|
+
* // 1. You send the money to the buyer, out of band.
|
|
581
|
+
* // 2. Then tell Garu it happened.
|
|
582
|
+
* await garu.refundRequests.confirm(uuid, {
|
|
583
|
+
* note: 'Pix devolvido em 14/08, e2e E12345678'
|
|
584
|
+
* });
|
|
585
|
+
*/
|
|
586
|
+
async confirm(uuid, params = {}) {
|
|
587
|
+
return this.http.call(
|
|
588
|
+
(signal) => this.http.client.POST(`/api/v1/refund-requests/${uuid}/confirm`, {
|
|
589
|
+
body: params,
|
|
590
|
+
signal
|
|
591
|
+
}).then((r) => r)
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Decline the request. The carnê is untouched and keeps running.
|
|
596
|
+
* Idempotent.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* await garu.refundRequests.reject(uuid, {
|
|
600
|
+
* note: 'Produto entregue e retirado na loja em 02/08'
|
|
601
|
+
* });
|
|
602
|
+
*/
|
|
603
|
+
async reject(uuid, params = {}) {
|
|
604
|
+
return this.http.call(
|
|
605
|
+
(signal) => this.http.client.POST(`/api/v1/refund-requests/${uuid}/reject`, {
|
|
606
|
+
body: params,
|
|
607
|
+
signal
|
|
608
|
+
}).then((r) => r)
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
|
|
320
613
|
// src/resources/customers.ts
|
|
321
614
|
var Customers = class {
|
|
322
615
|
constructor(http) {
|
|
@@ -334,10 +627,11 @@ var Customers = class {
|
|
|
334
627
|
* phone: '11987654321',
|
|
335
628
|
* personType: 'fisica'
|
|
336
629
|
* });
|
|
630
|
+
* customer.uuid;
|
|
337
631
|
*/
|
|
338
632
|
async create(params) {
|
|
339
633
|
return this.http.call(
|
|
340
|
-
(signal) => this.http.client.POST("/api/customers", {
|
|
634
|
+
(signal) => this.http.client.POST("/api/v1/customers", {
|
|
341
635
|
body: params,
|
|
342
636
|
signal
|
|
343
637
|
}).then((r) => r)
|
|
@@ -347,7 +641,11 @@ var Customers = class {
|
|
|
347
641
|
* List customers for the authenticated seller, with pagination and search.
|
|
348
642
|
*
|
|
349
643
|
* @example
|
|
350
|
-
* const { data,
|
|
644
|
+
* const { data, totalCount } = await garu.customers.list({ search: 'maria', limit: 10 });
|
|
645
|
+
*
|
|
646
|
+
* @example
|
|
647
|
+
* // Customers with at least one overdue scheduled charge (carnê included).
|
|
648
|
+
* const atRisk = await garu.customers.list({ status: 'overdue' });
|
|
351
649
|
*/
|
|
352
650
|
async list(params = {}) {
|
|
353
651
|
const query = {};
|
|
@@ -356,7 +654,7 @@ var Customers = class {
|
|
|
356
654
|
if (params.search) query.search = params.search;
|
|
357
655
|
if (params.status) query.status = params.status;
|
|
358
656
|
const qs = new URLSearchParams(query).toString();
|
|
359
|
-
const url = `/api/customers${qs ? `?${qs}` : ""}`;
|
|
657
|
+
const url = `/api/v1/customers${qs ? `?${qs}` : ""}`;
|
|
360
658
|
return this.http.call(
|
|
361
659
|
(signal) => this.http.client.GET(url, { signal }).then(
|
|
362
660
|
(r) => r
|
|
@@ -364,27 +662,28 @@ var Customers = class {
|
|
|
364
662
|
);
|
|
365
663
|
}
|
|
366
664
|
/**
|
|
367
|
-
* Fetch a single customer by
|
|
665
|
+
* Fetch a single customer by uuid.
|
|
368
666
|
*
|
|
369
667
|
* @example
|
|
370
|
-
* const customer = await garu.customers.get(
|
|
668
|
+
* const customer = await garu.customers.get('a1b2c3d4-e5f6-7890-abcd-ef1234567890');
|
|
371
669
|
*/
|
|
372
|
-
async get(
|
|
670
|
+
async get(uuid) {
|
|
373
671
|
return this.http.call(
|
|
374
|
-
(signal) => this.http.client.GET(`/api/customers/${
|
|
672
|
+
(signal) => this.http.client.GET(`/api/v1/customers/${uuid}`, { signal }).then(
|
|
375
673
|
(r) => r
|
|
376
674
|
)
|
|
377
675
|
);
|
|
378
676
|
}
|
|
379
677
|
/**
|
|
380
|
-
* Update a customer's profile for the current seller.
|
|
678
|
+
* Update a customer's profile for the current seller. Partial — only the
|
|
679
|
+
* fields you pass change.
|
|
381
680
|
*
|
|
382
681
|
* @example
|
|
383
|
-
* const updated = await garu.customers.update(
|
|
682
|
+
* const updated = await garu.customers.update('a1b2c3d4-...', { name: 'Maria Santos' });
|
|
384
683
|
*/
|
|
385
|
-
async update(
|
|
684
|
+
async update(uuid, params) {
|
|
386
685
|
return this.http.call(
|
|
387
|
-
(signal) => this.http.client.
|
|
686
|
+
(signal) => this.http.client.PATCH(`/api/v1/customers/${uuid}`, {
|
|
388
687
|
body: params,
|
|
389
688
|
signal
|
|
390
689
|
}).then((r) => r)
|
|
@@ -399,30 +698,31 @@ var Customers = class {
|
|
|
399
698
|
*
|
|
400
699
|
* @example
|
|
401
700
|
* // Set
|
|
402
|
-
* await garu.customers.setBillingEmailOverride(
|
|
701
|
+
* await garu.customers.setBillingEmailOverride('a1b2c3d4-...', {
|
|
403
702
|
* billingEmailOverride: 'cobrancas@empresa.com.br'
|
|
404
703
|
* });
|
|
405
704
|
*
|
|
406
705
|
* // Clear and fall back to the last-used email
|
|
407
|
-
* await garu.customers.setBillingEmailOverride(
|
|
706
|
+
* await garu.customers.setBillingEmailOverride('a1b2c3d4-...', { billingEmailOverride: null });
|
|
408
707
|
*/
|
|
409
|
-
async setBillingEmailOverride(
|
|
708
|
+
async setBillingEmailOverride(uuid, params) {
|
|
410
709
|
return this.http.call(
|
|
411
|
-
(signal) => this.http.client.PATCH(`/api/customers/${
|
|
710
|
+
(signal) => this.http.client.PATCH(`/api/v1/customers/${uuid}/billing-email-override`, {
|
|
412
711
|
body: params,
|
|
413
712
|
signal
|
|
414
713
|
}).then((r) => r)
|
|
415
714
|
);
|
|
416
715
|
}
|
|
417
716
|
/**
|
|
418
|
-
* Remove a customer from the current seller
|
|
717
|
+
* Remove a customer from the current seller (unlinks your profile — the
|
|
718
|
+
* global customer and other sellers' profiles are untouched).
|
|
419
719
|
*
|
|
420
720
|
* @example
|
|
421
|
-
* await garu.customers.delete(
|
|
721
|
+
* await garu.customers.delete('a1b2c3d4-e5f6-7890-abcd-ef1234567890');
|
|
422
722
|
*/
|
|
423
|
-
async delete(
|
|
424
|
-
|
|
425
|
-
(signal) => this.http.client.DELETE(`/api/customers/${
|
|
723
|
+
async delete(uuid) {
|
|
724
|
+
return this.http.call(
|
|
725
|
+
(signal) => this.http.client.DELETE(`/api/v1/customers/${uuid}`, {
|
|
426
726
|
body: {},
|
|
427
727
|
signal
|
|
428
728
|
}).then((r) => r)
|
|
@@ -1160,6 +1460,10 @@ var DEFAULT_MAX_RETRIES = 2;
|
|
|
1160
1460
|
var SDK_VERSION = "0.11.1";
|
|
1161
1461
|
var Garu = class {
|
|
1162
1462
|
charges;
|
|
1463
|
+
/** Boleto parcelado (carnê): one product sold as N monthly bank slips. */
|
|
1464
|
+
installmentPlans;
|
|
1465
|
+
/** Refunds Garu has been asked to make and cannot make for you. */
|
|
1466
|
+
refundRequests;
|
|
1163
1467
|
customers;
|
|
1164
1468
|
meta;
|
|
1165
1469
|
products;
|
|
@@ -1181,6 +1485,8 @@ var Garu = class {
|
|
|
1181
1485
|
fetch: options.fetch
|
|
1182
1486
|
});
|
|
1183
1487
|
this.charges = new Charges(http);
|
|
1488
|
+
this.installmentPlans = new InstallmentPlans(http);
|
|
1489
|
+
this.refundRequests = new RefundRequests(http);
|
|
1184
1490
|
this.customers = new Customers(http);
|
|
1185
1491
|
this.meta = new Meta(http);
|
|
1186
1492
|
this.products = new Products(http);
|