@garuhq/node 0.15.0 → 0.16.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 CHANGED
@@ -3,6 +3,37 @@
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.16.0] — 2026-07-18
7
+
8
+ ### Changed
9
+
10
+ - **Products now use the versioned public API `/api/v1/products`.** Every product
11
+ method (`list`, `get`, `create`, `update`, `portalConfig.*`) moved from the
12
+ un-versioned `/api/products/*` (dashboard) paths to `/api/v1/products`. Method
13
+ signatures are unchanged — `get`/`update`/portal-config still accept the product
14
+ UUID (recommended) or the legacy numeric id.
15
+ - **`Product.value` is decimal reais (BRL), not centavos.** The type comments and
16
+ create/update examples wrongly said centavos — following them created a product
17
+ priced 100× (`4990` → R$ 4.990,00 instead of R$ 49,90). No behavior change: the
18
+ SDK already sent `value` as-is. Charges/refunds `amount` remain centavos.
19
+ - **`ProductList` is the real flat shape** `{ data, count, totalCount, totalPages }`
20
+ (previously mistyped `{ data, meta }`, which was never populated at runtime).
21
+ - **`Product.installments` is `Installment[]`** (`{ quantity, value }`) — was
22
+ `number[]`, which didn't match the API.
23
+
24
+ ### Deprecated
25
+
26
+ - **`Product.id`** — the v1 API no longer returns a numeric id; use `uuid`. It is
27
+ now optional and `undefined` on v1 responses, though still accepted as an input
28
+ identifier on `get`/`update`/portal-config.
29
+ - **`ListProductsParams.tab`** — not supported by v1 (ignored). `list()` returns
30
+ the authenticated seller's own products.
31
+
32
+ ### Migration
33
+
34
+ Read a product's `uuid`, not `.id`. Persisted numeric ids still work as **input**
35
+ identifiers, but responses now carry only `uuid`.
36
+
6
37
  ## [0.15.0] — 2026-05-31
7
38
 
8
39
  ### Added
package/dist/index.cjs CHANGED
@@ -481,7 +481,7 @@ var ProductPortalConfigResource = class {
481
481
  async get(productId) {
482
482
  return this.http.call(
483
483
  (signal) => this.http.client.GET(
484
- `/api/products/${encodeURIComponent(String(productId))}/portal-config`,
484
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
485
485
  {
486
486
  signal
487
487
  }
@@ -504,7 +504,7 @@ var ProductPortalConfigResource = class {
504
504
  async set(productId, params) {
505
505
  return this.http.call(
506
506
  (signal) => this.http.client.POST(
507
- `/api/products/${encodeURIComponent(String(productId))}/portal-config`,
507
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
508
508
  {
509
509
  body: params,
510
510
  signal
@@ -516,7 +516,7 @@ var ProductPortalConfigResource = class {
516
516
  async patch(productId, params) {
517
517
  return this.http.call(
518
518
  (signal) => this.http.client.PATCH(
519
- `/api/products/${encodeURIComponent(String(productId))}/portal-config`,
519
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
520
520
  {
521
521
  body: params,
522
522
  signal
@@ -535,7 +535,7 @@ var ProductPortalConfigResource = class {
535
535
  async clear(productId) {
536
536
  return this.http.call(
537
537
  (signal) => this.http.client.DELETE(
538
- `/api/products/${encodeURIComponent(String(productId))}/portal-config`,
538
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
539
539
  {
540
540
  body: {},
541
541
  signal
@@ -556,16 +556,15 @@ var Products = class {
556
556
  * List products for the authenticated seller, with pagination and search.
557
557
  *
558
558
  * @example
559
- * const { data, meta } = await garu.products.list({ search: 'curso', limit: 10 });
559
+ * const { data, totalCount } = await garu.products.list({ search: 'curso', limit: 10 });
560
560
  */
561
561
  async list(params = {}) {
562
562
  const query = {};
563
563
  if (params.page !== void 0) query.page = String(params.page);
564
564
  if (params.limit !== void 0) query.limit = String(params.limit);
565
565
  if (params.search) query.search = params.search;
566
- if (params.tab) query.tab = params.tab;
567
566
  const qs = new URLSearchParams(query).toString();
568
- const url = `/api/products/seller${qs ? `?${qs}` : ""}`;
567
+ const url = `/api/v1/products${qs ? `?${qs}` : ""}`;
569
568
  return this.http.call(
570
569
  (signal) => this.http.client.GET(url, { signal }).then(
571
570
  (r) => r
@@ -581,7 +580,7 @@ var Products = class {
581
580
  */
582
581
  async get(uuid) {
583
582
  return this.http.call(
584
- (signal) => this.http.client.GET(`/api/products/uuid/${uuid}`, { signal }).then(
583
+ (signal) => this.http.client.GET(`/api/v1/products/${uuid}`, { signal }).then(
585
584
  (r) => r
586
585
  )
587
586
  );
@@ -599,7 +598,7 @@ var Products = class {
599
598
  * @example
600
599
  * const product = await garu.products.create({
601
600
  * name: 'Plano Mensal',
602
- * value: 4990, // R$ 49,90 in centavos
601
+ * value: 49.90, // R$ 49,90 in reais (decimal BRL), NOT centavos
603
602
  * description: 'Acesso completo à plataforma',
604
603
  * pix: true,
605
604
  * creditCard: true,
@@ -612,7 +611,7 @@ var Products = class {
612
611
  const { idempotencyKey, ...body } = params;
613
612
  const key = idempotencyKey ?? generateIdempotencyKey();
614
613
  return this.http.call(
615
- (signal) => this.http.client.POST("/api/products", {
614
+ (signal) => this.http.client.POST("/api/v1/products", {
616
615
  body,
617
616
  headers: { "X-Idempotency-Key": key },
618
617
  signal
@@ -623,19 +622,19 @@ var Products = class {
623
622
  * Update a product (partial PATCH — only the fields you pass are changed).
624
623
  * Returns the updated product.
625
624
  *
626
- * `id` accepts the numeric id or the product UUID the same identifiers
627
- * accepted elsewhere on the `/api/products/:id` path (see
625
+ * `id` accepts the product UUID (recommended) or the legacy numeric id
626
+ * both resolve on the `/api/v1/products/:id` path (see
628
627
  * {@link ProductPortalConfigResource}).
629
628
  *
630
629
  * @example
631
630
  * const updated = await garu.products.update('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
632
- * value: 5990,
631
+ * value: 59.90, // reais (decimal BRL), NOT centavos
633
632
  * pixAutomatic: true // turn on Pix Automático for this product
634
633
  * });
635
634
  */
636
635
  async update(id, params) {
637
636
  return this.http.call(
638
- (signal) => this.http.client.PATCH(`/api/products/${encodeURIComponent(String(id))}`, {
637
+ (signal) => this.http.client.PATCH(`/api/v1/products/${encodeURIComponent(String(id))}`, {
639
638
  body: params,
640
639
  signal
641
640
  }).then((r) => r)
package/dist/index.d.cts CHANGED
@@ -509,12 +509,17 @@ interface ChargeNowResult {
509
509
  message: string;
510
510
  }
511
511
  interface Product {
512
- id: number;
512
+ /**
513
+ * @deprecated The v1 API no longer returns a numeric id — use `uuid` to
514
+ * address a product. Present only on legacy `/api/products/*` responses;
515
+ * `undefined` on v1.
516
+ */
517
+ id?: number;
513
518
  uuid: string;
514
519
  name: string;
515
520
  description: string;
516
521
  image: string;
517
- /** Price in centavos (BRL × 100). */
522
+ /** Price in decimal BRL / reais (e.g. `297.50`) — NOT centavos. */
518
523
  value: number;
519
524
  sellerId: number;
520
525
  sellerName?: string;
@@ -528,7 +533,8 @@ interface Product {
528
533
  * checkout mode reads this flag. See {@link ScheduledPaymentMethod}.
529
534
  */
530
535
  pixAutomatic: boolean;
531
- installments: number[];
536
+ /** Per-parcela credit-card breakdown (the amount charged per installment). */
537
+ installments: Installment[];
532
538
  tags?: string[];
533
539
  isSubscription?: boolean;
534
540
  subscriptionType?: string;
@@ -541,18 +547,36 @@ interface Product {
541
547
  updatedAt: string;
542
548
  [key: string]: unknown;
543
549
  }
544
- type ProductList = PaginatedList<Product>;
550
+ /** One entry in a product's credit-card installment breakdown. */
551
+ interface Installment {
552
+ /** Number of parcelas. */
553
+ quantity: number;
554
+ /** Amount charged per installment, in reais (BRL), with the fator markup applied. */
555
+ value: number;
556
+ }
557
+ /**
558
+ * List envelope returned by `products.list()`. Flat (not `{ data, meta }`) —
559
+ * matches the `/api/v1/products` response.
560
+ */
561
+ interface ProductList {
562
+ data: Product[];
563
+ /** Items returned on this page. */
564
+ count: number;
565
+ /** Total products matching the filter across all pages. */
566
+ totalCount: number;
567
+ totalPages: number;
568
+ }
545
569
  interface ListProductsParams {
546
570
  page?: number;
547
571
  limit?: number;
548
572
  /** Search by product name. */
549
573
  search?: string;
550
- /** Backend tab filter (e.g. `active`, `archived`). Backend default is used when omitted. */
574
+ /** @deprecated Not supported by the v1 API (ignored). `list()` returns the seller's own products. */
551
575
  tab?: string;
552
576
  }
553
577
  interface CreateProductParams {
554
578
  name: string;
555
- /** Price in centavos (BRL × 100). */
579
+ /** Price in decimal BRL / reais (e.g. `297.50`) — NOT centavos. */
556
580
  value?: number;
557
581
  description?: string;
558
582
  /** HTTPS URL of the product cover image. */
@@ -585,6 +609,7 @@ interface CreateProductParams {
585
609
  }
586
610
  interface UpdateProductParams {
587
611
  name?: string;
612
+ /** Price in decimal BRL / reais (e.g. `297.50`) — NOT centavos. */
588
613
  value?: number;
589
614
  description?: string;
590
615
  image?: string;
@@ -1023,7 +1048,7 @@ declare class Products {
1023
1048
  * List products for the authenticated seller, with pagination and search.
1024
1049
  *
1025
1050
  * @example
1026
- * const { data, meta } = await garu.products.list({ search: 'curso', limit: 10 });
1051
+ * const { data, totalCount } = await garu.products.list({ search: 'curso', limit: 10 });
1027
1052
  */
1028
1053
  list(params?: ListProductsParams): Promise<ProductList>;
1029
1054
  /**
@@ -1047,7 +1072,7 @@ declare class Products {
1047
1072
  * @example
1048
1073
  * const product = await garu.products.create({
1049
1074
  * name: 'Plano Mensal',
1050
- * value: 4990, // R$ 49,90 in centavos
1075
+ * value: 49.90, // R$ 49,90 in reais (decimal BRL), NOT centavos
1051
1076
  * description: 'Acesso completo à plataforma',
1052
1077
  * pix: true,
1053
1078
  * creditCard: true,
@@ -1061,13 +1086,13 @@ declare class Products {
1061
1086
  * Update a product (partial PATCH — only the fields you pass are changed).
1062
1087
  * Returns the updated product.
1063
1088
  *
1064
- * `id` accepts the numeric id or the product UUID the same identifiers
1065
- * accepted elsewhere on the `/api/products/:id` path (see
1089
+ * `id` accepts the product UUID (recommended) or the legacy numeric id
1090
+ * both resolve on the `/api/v1/products/:id` path (see
1066
1091
  * {@link ProductPortalConfigResource}).
1067
1092
  *
1068
1093
  * @example
1069
1094
  * const updated = await garu.products.update('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
1070
- * value: 5990,
1095
+ * value: 59.90, // reais (decimal BRL), NOT centavos
1071
1096
  * pixAutomatic: true // turn on Pix Automático for this product
1072
1097
  * });
1073
1098
  */
package/dist/index.d.ts CHANGED
@@ -509,12 +509,17 @@ interface ChargeNowResult {
509
509
  message: string;
510
510
  }
511
511
  interface Product {
512
- id: number;
512
+ /**
513
+ * @deprecated The v1 API no longer returns a numeric id — use `uuid` to
514
+ * address a product. Present only on legacy `/api/products/*` responses;
515
+ * `undefined` on v1.
516
+ */
517
+ id?: number;
513
518
  uuid: string;
514
519
  name: string;
515
520
  description: string;
516
521
  image: string;
517
- /** Price in centavos (BRL × 100). */
522
+ /** Price in decimal BRL / reais (e.g. `297.50`) — NOT centavos. */
518
523
  value: number;
519
524
  sellerId: number;
520
525
  sellerName?: string;
@@ -528,7 +533,8 @@ interface Product {
528
533
  * checkout mode reads this flag. See {@link ScheduledPaymentMethod}.
529
534
  */
530
535
  pixAutomatic: boolean;
531
- installments: number[];
536
+ /** Per-parcela credit-card breakdown (the amount charged per installment). */
537
+ installments: Installment[];
532
538
  tags?: string[];
533
539
  isSubscription?: boolean;
534
540
  subscriptionType?: string;
@@ -541,18 +547,36 @@ interface Product {
541
547
  updatedAt: string;
542
548
  [key: string]: unknown;
543
549
  }
544
- type ProductList = PaginatedList<Product>;
550
+ /** One entry in a product's credit-card installment breakdown. */
551
+ interface Installment {
552
+ /** Number of parcelas. */
553
+ quantity: number;
554
+ /** Amount charged per installment, in reais (BRL), with the fator markup applied. */
555
+ value: number;
556
+ }
557
+ /**
558
+ * List envelope returned by `products.list()`. Flat (not `{ data, meta }`) —
559
+ * matches the `/api/v1/products` response.
560
+ */
561
+ interface ProductList {
562
+ data: Product[];
563
+ /** Items returned on this page. */
564
+ count: number;
565
+ /** Total products matching the filter across all pages. */
566
+ totalCount: number;
567
+ totalPages: number;
568
+ }
545
569
  interface ListProductsParams {
546
570
  page?: number;
547
571
  limit?: number;
548
572
  /** Search by product name. */
549
573
  search?: string;
550
- /** Backend tab filter (e.g. `active`, `archived`). Backend default is used when omitted. */
574
+ /** @deprecated Not supported by the v1 API (ignored). `list()` returns the seller's own products. */
551
575
  tab?: string;
552
576
  }
553
577
  interface CreateProductParams {
554
578
  name: string;
555
- /** Price in centavos (BRL × 100). */
579
+ /** Price in decimal BRL / reais (e.g. `297.50`) — NOT centavos. */
556
580
  value?: number;
557
581
  description?: string;
558
582
  /** HTTPS URL of the product cover image. */
@@ -585,6 +609,7 @@ interface CreateProductParams {
585
609
  }
586
610
  interface UpdateProductParams {
587
611
  name?: string;
612
+ /** Price in decimal BRL / reais (e.g. `297.50`) — NOT centavos. */
588
613
  value?: number;
589
614
  description?: string;
590
615
  image?: string;
@@ -1023,7 +1048,7 @@ declare class Products {
1023
1048
  * List products for the authenticated seller, with pagination and search.
1024
1049
  *
1025
1050
  * @example
1026
- * const { data, meta } = await garu.products.list({ search: 'curso', limit: 10 });
1051
+ * const { data, totalCount } = await garu.products.list({ search: 'curso', limit: 10 });
1027
1052
  */
1028
1053
  list(params?: ListProductsParams): Promise<ProductList>;
1029
1054
  /**
@@ -1047,7 +1072,7 @@ declare class Products {
1047
1072
  * @example
1048
1073
  * const product = await garu.products.create({
1049
1074
  * name: 'Plano Mensal',
1050
- * value: 4990, // R$ 49,90 in centavos
1075
+ * value: 49.90, // R$ 49,90 in reais (decimal BRL), NOT centavos
1051
1076
  * description: 'Acesso completo à plataforma',
1052
1077
  * pix: true,
1053
1078
  * creditCard: true,
@@ -1061,13 +1086,13 @@ declare class Products {
1061
1086
  * Update a product (partial PATCH — only the fields you pass are changed).
1062
1087
  * Returns the updated product.
1063
1088
  *
1064
- * `id` accepts the numeric id or the product UUID the same identifiers
1065
- * accepted elsewhere on the `/api/products/:id` path (see
1089
+ * `id` accepts the product UUID (recommended) or the legacy numeric id
1090
+ * both resolve on the `/api/v1/products/:id` path (see
1066
1091
  * {@link ProductPortalConfigResource}).
1067
1092
  *
1068
1093
  * @example
1069
1094
  * const updated = await garu.products.update('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
1070
- * value: 5990,
1095
+ * value: 59.90, // reais (decimal BRL), NOT centavos
1071
1096
  * pixAutomatic: true // turn on Pix Automático for this product
1072
1097
  * });
1073
1098
  */
package/dist/index.js CHANGED
@@ -475,7 +475,7 @@ var ProductPortalConfigResource = class {
475
475
  async get(productId) {
476
476
  return this.http.call(
477
477
  (signal) => this.http.client.GET(
478
- `/api/products/${encodeURIComponent(String(productId))}/portal-config`,
478
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
479
479
  {
480
480
  signal
481
481
  }
@@ -498,7 +498,7 @@ var ProductPortalConfigResource = class {
498
498
  async set(productId, params) {
499
499
  return this.http.call(
500
500
  (signal) => this.http.client.POST(
501
- `/api/products/${encodeURIComponent(String(productId))}/portal-config`,
501
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
502
502
  {
503
503
  body: params,
504
504
  signal
@@ -510,7 +510,7 @@ var ProductPortalConfigResource = class {
510
510
  async patch(productId, params) {
511
511
  return this.http.call(
512
512
  (signal) => this.http.client.PATCH(
513
- `/api/products/${encodeURIComponent(String(productId))}/portal-config`,
513
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
514
514
  {
515
515
  body: params,
516
516
  signal
@@ -529,7 +529,7 @@ var ProductPortalConfigResource = class {
529
529
  async clear(productId) {
530
530
  return this.http.call(
531
531
  (signal) => this.http.client.DELETE(
532
- `/api/products/${encodeURIComponent(String(productId))}/portal-config`,
532
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
533
533
  {
534
534
  body: {},
535
535
  signal
@@ -550,16 +550,15 @@ var Products = class {
550
550
  * List products for the authenticated seller, with pagination and search.
551
551
  *
552
552
  * @example
553
- * const { data, meta } = await garu.products.list({ search: 'curso', limit: 10 });
553
+ * const { data, totalCount } = await garu.products.list({ search: 'curso', limit: 10 });
554
554
  */
555
555
  async list(params = {}) {
556
556
  const query = {};
557
557
  if (params.page !== void 0) query.page = String(params.page);
558
558
  if (params.limit !== void 0) query.limit = String(params.limit);
559
559
  if (params.search) query.search = params.search;
560
- if (params.tab) query.tab = params.tab;
561
560
  const qs = new URLSearchParams(query).toString();
562
- const url = `/api/products/seller${qs ? `?${qs}` : ""}`;
561
+ const url = `/api/v1/products${qs ? `?${qs}` : ""}`;
563
562
  return this.http.call(
564
563
  (signal) => this.http.client.GET(url, { signal }).then(
565
564
  (r) => r
@@ -575,7 +574,7 @@ var Products = class {
575
574
  */
576
575
  async get(uuid) {
577
576
  return this.http.call(
578
- (signal) => this.http.client.GET(`/api/products/uuid/${uuid}`, { signal }).then(
577
+ (signal) => this.http.client.GET(`/api/v1/products/${uuid}`, { signal }).then(
579
578
  (r) => r
580
579
  )
581
580
  );
@@ -593,7 +592,7 @@ var Products = class {
593
592
  * @example
594
593
  * const product = await garu.products.create({
595
594
  * name: 'Plano Mensal',
596
- * value: 4990, // R$ 49,90 in centavos
595
+ * value: 49.90, // R$ 49,90 in reais (decimal BRL), NOT centavos
597
596
  * description: 'Acesso completo à plataforma',
598
597
  * pix: true,
599
598
  * creditCard: true,
@@ -606,7 +605,7 @@ var Products = class {
606
605
  const { idempotencyKey, ...body } = params;
607
606
  const key = idempotencyKey ?? generateIdempotencyKey();
608
607
  return this.http.call(
609
- (signal) => this.http.client.POST("/api/products", {
608
+ (signal) => this.http.client.POST("/api/v1/products", {
610
609
  body,
611
610
  headers: { "X-Idempotency-Key": key },
612
611
  signal
@@ -617,19 +616,19 @@ var Products = class {
617
616
  * Update a product (partial PATCH — only the fields you pass are changed).
618
617
  * Returns the updated product.
619
618
  *
620
- * `id` accepts the numeric id or the product UUID the same identifiers
621
- * accepted elsewhere on the `/api/products/:id` path (see
619
+ * `id` accepts the product UUID (recommended) or the legacy numeric id
620
+ * both resolve on the `/api/v1/products/:id` path (see
622
621
  * {@link ProductPortalConfigResource}).
623
622
  *
624
623
  * @example
625
624
  * const updated = await garu.products.update('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
626
- * value: 5990,
625
+ * value: 59.90, // reais (decimal BRL), NOT centavos
627
626
  * pixAutomatic: true // turn on Pix Automático for this product
628
627
  * });
629
628
  */
630
629
  async update(id, params) {
631
630
  return this.http.call(
632
- (signal) => this.http.client.PATCH(`/api/products/${encodeURIComponent(String(id))}`, {
631
+ (signal) => this.http.client.PATCH(`/api/v1/products/${encodeURIComponent(String(id))}`, {
633
632
  body: params,
634
633
  signal
635
634
  }).then((r) => r)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garuhq/node",
3
- "version": "0.15.0",
3
+ "version": "0.16.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",