@garuhq/node 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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) {
@@ -1160,6 +1453,10 @@ var DEFAULT_MAX_RETRIES = 2;
1160
1453
  var SDK_VERSION = "0.11.1";
1161
1454
  var Garu = class {
1162
1455
  charges;
1456
+ /** Boleto parcelado (carnê): one product sold as N monthly bank slips. */
1457
+ installmentPlans;
1458
+ /** Refunds Garu has been asked to make and cannot make for you. */
1459
+ refundRequests;
1163
1460
  customers;
1164
1461
  meta;
1165
1462
  products;
@@ -1181,6 +1478,8 @@ var Garu = class {
1181
1478
  fetch: options.fetch
1182
1479
  });
1183
1480
  this.charges = new Charges(http);
1481
+ this.installmentPlans = new InstallmentPlans(http);
1482
+ this.refundRequests = new RefundRequests(http);
1184
1483
  this.customers = new Customers(http);
1185
1484
  this.meta = new Meta(http);
1186
1485
  this.products = new Products(http);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garuhq/node",
3
- "version": "1.0.0",
3
+ "version": "1.1.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",