@garuhq/node 0.14.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,59 @@
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
+
37
+ ## [0.15.0] — 2026-05-31
38
+
39
+ ### Added
40
+
41
+ - **Product writes** — the `Products` resource now wraps the create and update
42
+ endpoints, not just reads:
43
+ - `products.create(params)` — `POST /api/products`, returns the created
44
+ `Product`. Only `name` is required; all other fields fall back to
45
+ seller/server defaults. Auto-attaches an `X-Idempotency-Key` (override
46
+ via `params.idempotencyKey`), so the built-in retry can't create a
47
+ duplicate product.
48
+ - `products.update(id, params)` — `PATCH /api/products/{id}`, partial update
49
+ returning the updated `Product`. `id` accepts the numeric id or the
50
+ product UUID, matching the `/api/products/:id` portal-config methods.
51
+ - New exported param types `CreateProductParams` and `UpdateProductParams`,
52
+ covering `name`, `value` (centavos), `description`, `image`, `tags`,
53
+ `pix`, `boleto`, `creditCard`, `pixAutomatic`, `installments`,
54
+ `isSubscription`, `subscriptionType`, `unitLabel`, `returnUrl`,
55
+ `returnUrlButtonText`, and `statementDescriptor`.
56
+ - Both param types include `pixAutomatic` so you can toggle Pix Automático
57
+ on the subscription checkout at create/update time.
58
+
6
59
  ## [0.14.0] — 2026-05-31
7
60
 
8
61
  ### Added
package/dist/index.cjs CHANGED
@@ -480,9 +480,12 @@ var ProductPortalConfigResource = class {
480
480
  */
481
481
  async get(productId) {
482
482
  return this.http.call(
483
- (signal) => this.http.client.GET(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
484
- signal
485
- }).then((r) => r)
483
+ (signal) => this.http.client.GET(
484
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
485
+ {
486
+ signal
487
+ }
488
+ ).then((r) => r)
486
489
  );
487
490
  }
488
491
  /**
@@ -500,19 +503,25 @@ var ProductPortalConfigResource = class {
500
503
  */
501
504
  async set(productId, params) {
502
505
  return this.http.call(
503
- (signal) => this.http.client.POST(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
504
- body: params,
505
- signal
506
- }).then((r) => r)
506
+ (signal) => this.http.client.POST(
507
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
508
+ {
509
+ body: params,
510
+ signal
511
+ }
512
+ ).then((r) => r)
507
513
  );
508
514
  }
509
515
  /** Same merge semantics as `set` — alias for HTTP-PATCH-prefering callers. */
510
516
  async patch(productId, params) {
511
517
  return this.http.call(
512
- (signal) => this.http.client.PATCH(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
513
- body: params,
514
- signal
515
- }).then((r) => r)
518
+ (signal) => this.http.client.PATCH(
519
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
520
+ {
521
+ body: params,
522
+ signal
523
+ }
524
+ ).then((r) => r)
516
525
  );
517
526
  }
518
527
  /**
@@ -525,10 +534,13 @@ var ProductPortalConfigResource = class {
525
534
  */
526
535
  async clear(productId) {
527
536
  return this.http.call(
528
- (signal) => this.http.client.DELETE(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
529
- body: {},
530
- signal
531
- }).then((r) => r)
537
+ (signal) => this.http.client.DELETE(
538
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
539
+ {
540
+ body: {},
541
+ signal
542
+ }
543
+ ).then((r) => r)
532
544
  );
533
545
  }
534
546
  };
@@ -544,16 +556,15 @@ var Products = class {
544
556
  * List products for the authenticated seller, with pagination and search.
545
557
  *
546
558
  * @example
547
- * const { data, meta } = await garu.products.list({ search: 'curso', limit: 10 });
559
+ * const { data, totalCount } = await garu.products.list({ search: 'curso', limit: 10 });
548
560
  */
549
561
  async list(params = {}) {
550
562
  const query = {};
551
563
  if (params.page !== void 0) query.page = String(params.page);
552
564
  if (params.limit !== void 0) query.limit = String(params.limit);
553
565
  if (params.search) query.search = params.search;
554
- if (params.tab) query.tab = params.tab;
555
566
  const qs = new URLSearchParams(query).toString();
556
- const url = `/api/products/seller${qs ? `?${qs}` : ""}`;
567
+ const url = `/api/v1/products${qs ? `?${qs}` : ""}`;
557
568
  return this.http.call(
558
569
  (signal) => this.http.client.GET(url, { signal }).then(
559
570
  (r) => r
@@ -569,11 +580,66 @@ var Products = class {
569
580
  */
570
581
  async get(uuid) {
571
582
  return this.http.call(
572
- (signal) => this.http.client.GET(`/api/products/uuid/${uuid}`, { signal }).then(
583
+ (signal) => this.http.client.GET(`/api/v1/products/${uuid}`, { signal }).then(
573
584
  (r) => r
574
585
  )
575
586
  );
576
587
  }
588
+ /**
589
+ * Create a product for the authenticated seller. Returns the created
590
+ * product (HTTP 201). Only `name` is required; everything else falls back
591
+ * to seller/server defaults.
592
+ *
593
+ * Automatically attaches an `X-Idempotency-Key` header — if you don't pass
594
+ * `idempotencyKey`, the SDK generates a UUIDv4. This makes the built-in
595
+ * retry on transient failures safe: a retried POST returns the original
596
+ * product instead of creating a duplicate.
597
+ *
598
+ * @example
599
+ * const product = await garu.products.create({
600
+ * name: 'Plano Mensal',
601
+ * value: 49.90, // R$ 49,90 in reais (decimal BRL), NOT centavos
602
+ * description: 'Acesso completo à plataforma',
603
+ * pix: true,
604
+ * creditCard: true,
605
+ * isSubscription: true,
606
+ * subscriptionType: 'monthly',
607
+ * pixAutomatic: true // expose Pix Automático on the subscription checkout
608
+ * });
609
+ */
610
+ async create(params) {
611
+ const { idempotencyKey, ...body } = params;
612
+ const key = idempotencyKey ?? generateIdempotencyKey();
613
+ return this.http.call(
614
+ (signal) => this.http.client.POST("/api/v1/products", {
615
+ body,
616
+ headers: { "X-Idempotency-Key": key },
617
+ signal
618
+ }).then((r) => r)
619
+ );
620
+ }
621
+ /**
622
+ * Update a product (partial PATCH — only the fields you pass are changed).
623
+ * Returns the updated product.
624
+ *
625
+ * `id` accepts the product UUID (recommended) or the legacy numeric id —
626
+ * both resolve on the `/api/v1/products/:id` path (see
627
+ * {@link ProductPortalConfigResource}).
628
+ *
629
+ * @example
630
+ * const updated = await garu.products.update('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
631
+ * value: 59.90, // reais (decimal BRL), NOT centavos
632
+ * pixAutomatic: true // turn on Pix Automático for this product
633
+ * });
634
+ */
635
+ async update(id, params) {
636
+ return this.http.call(
637
+ (signal) => this.http.client.PATCH(`/api/v1/products/${encodeURIComponent(String(id))}`, {
638
+ body: params,
639
+ signal
640
+ }).then((r) => r)
641
+ );
642
+ }
577
643
  };
578
644
 
579
645
  // src/resources/scheduled-charges.ts
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,15 +547,85 @@ 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
  }
577
+ interface CreateProductParams {
578
+ name: string;
579
+ /** Price in decimal BRL / reais (e.g. `297.50`) — NOT centavos. */
580
+ value?: number;
581
+ description?: string;
582
+ /** HTTPS URL of the product cover image. */
583
+ image?: string;
584
+ tags?: string[];
585
+ pix?: boolean;
586
+ boleto?: boolean;
587
+ creditCard?: boolean;
588
+ /**
589
+ * Enable Pix Automático (BACEN auto-debit recurring Pix) on the
590
+ * subscription checkout. Defaults to enabled server-side. Only the
591
+ * subscription checkout mode reads this flag. See {@link Product.pixAutomatic}.
592
+ */
593
+ pixAutomatic?: boolean;
594
+ /** Max number of installments offered on credit card. */
595
+ installments?: number;
596
+ isSubscription?: boolean;
597
+ subscriptionType?: string;
598
+ unitLabel?: string;
599
+ returnUrl?: string;
600
+ returnUrlButtonText?: string;
601
+ /** Text shown on the buyer's card/bank statement. */
602
+ statementDescriptor?: string;
603
+ /**
604
+ * Idempotency key for the create request. Defaults to a generated UUIDv4.
605
+ * Pass your own to make a retry across process restarts safe — the backend
606
+ * returns the original product instead of creating a duplicate.
607
+ */
608
+ idempotencyKey?: string;
609
+ }
610
+ interface UpdateProductParams {
611
+ name?: string;
612
+ /** Price in decimal BRL / reais (e.g. `297.50`) — NOT centavos. */
613
+ value?: number;
614
+ description?: string;
615
+ image?: string;
616
+ tags?: string[];
617
+ pix?: boolean;
618
+ boleto?: boolean;
619
+ creditCard?: boolean;
620
+ pixAutomatic?: boolean;
621
+ installments?: number;
622
+ isSubscription?: boolean;
623
+ subscriptionType?: string;
624
+ unitLabel?: string;
625
+ returnUrl?: string;
626
+ returnUrlButtonText?: string;
627
+ statementDescriptor?: string;
628
+ }
553
629
  interface MetaFeatures {
554
630
  subscriptions: boolean;
555
631
  checkout_sessions: boolean;
@@ -972,7 +1048,7 @@ declare class Products {
972
1048
  * List products for the authenticated seller, with pagination and search.
973
1049
  *
974
1050
  * @example
975
- * const { data, meta } = await garu.products.list({ search: 'curso', limit: 10 });
1051
+ * const { data, totalCount } = await garu.products.list({ search: 'curso', limit: 10 });
976
1052
  */
977
1053
  list(params?: ListProductsParams): Promise<ProductList>;
978
1054
  /**
@@ -983,6 +1059,44 @@ declare class Products {
983
1059
  * const product = await garu.products.get('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f');
984
1060
  */
985
1061
  get(uuid: string): Promise<Product>;
1062
+ /**
1063
+ * Create a product for the authenticated seller. Returns the created
1064
+ * product (HTTP 201). Only `name` is required; everything else falls back
1065
+ * to seller/server defaults.
1066
+ *
1067
+ * Automatically attaches an `X-Idempotency-Key` header — if you don't pass
1068
+ * `idempotencyKey`, the SDK generates a UUIDv4. This makes the built-in
1069
+ * retry on transient failures safe: a retried POST returns the original
1070
+ * product instead of creating a duplicate.
1071
+ *
1072
+ * @example
1073
+ * const product = await garu.products.create({
1074
+ * name: 'Plano Mensal',
1075
+ * value: 49.90, // R$ 49,90 in reais (decimal BRL), NOT centavos
1076
+ * description: 'Acesso completo à plataforma',
1077
+ * pix: true,
1078
+ * creditCard: true,
1079
+ * isSubscription: true,
1080
+ * subscriptionType: 'monthly',
1081
+ * pixAutomatic: true // expose Pix Automático on the subscription checkout
1082
+ * });
1083
+ */
1084
+ create(params: CreateProductParams): Promise<Product>;
1085
+ /**
1086
+ * Update a product (partial PATCH — only the fields you pass are changed).
1087
+ * Returns the updated product.
1088
+ *
1089
+ * `id` accepts the product UUID (recommended) or the legacy numeric id —
1090
+ * both resolve on the `/api/v1/products/:id` path (see
1091
+ * {@link ProductPortalConfigResource}).
1092
+ *
1093
+ * @example
1094
+ * const updated = await garu.products.update('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
1095
+ * value: 59.90, // reais (decimal BRL), NOT centavos
1096
+ * pixAutomatic: true // turn on Pix Automático for this product
1097
+ * });
1098
+ */
1099
+ update(id: string | number, params: UpdateProductParams): Promise<Product>;
986
1100
  }
987
1101
 
988
1102
  /**
@@ -1391,4 +1505,4 @@ declare class GaruServerError extends GaruAPIError {
1391
1505
  constructor(message: string, status: number, requestId: string | null, body: unknown);
1392
1506
  }
1393
1507
 
1394
- export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeNowOutcome, type ChargeNowReason, type ChargeNowResult, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ResendWebhookEventParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
1508
+ export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeNowOutcome, type ChargeNowReason, type ChargeNowResult, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateProductParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ResendWebhookEventParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type UpdateProductParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
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,15 +547,85 @@ 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
  }
577
+ interface CreateProductParams {
578
+ name: string;
579
+ /** Price in decimal BRL / reais (e.g. `297.50`) — NOT centavos. */
580
+ value?: number;
581
+ description?: string;
582
+ /** HTTPS URL of the product cover image. */
583
+ image?: string;
584
+ tags?: string[];
585
+ pix?: boolean;
586
+ boleto?: boolean;
587
+ creditCard?: boolean;
588
+ /**
589
+ * Enable Pix Automático (BACEN auto-debit recurring Pix) on the
590
+ * subscription checkout. Defaults to enabled server-side. Only the
591
+ * subscription checkout mode reads this flag. See {@link Product.pixAutomatic}.
592
+ */
593
+ pixAutomatic?: boolean;
594
+ /** Max number of installments offered on credit card. */
595
+ installments?: number;
596
+ isSubscription?: boolean;
597
+ subscriptionType?: string;
598
+ unitLabel?: string;
599
+ returnUrl?: string;
600
+ returnUrlButtonText?: string;
601
+ /** Text shown on the buyer's card/bank statement. */
602
+ statementDescriptor?: string;
603
+ /**
604
+ * Idempotency key for the create request. Defaults to a generated UUIDv4.
605
+ * Pass your own to make a retry across process restarts safe — the backend
606
+ * returns the original product instead of creating a duplicate.
607
+ */
608
+ idempotencyKey?: string;
609
+ }
610
+ interface UpdateProductParams {
611
+ name?: string;
612
+ /** Price in decimal BRL / reais (e.g. `297.50`) — NOT centavos. */
613
+ value?: number;
614
+ description?: string;
615
+ image?: string;
616
+ tags?: string[];
617
+ pix?: boolean;
618
+ boleto?: boolean;
619
+ creditCard?: boolean;
620
+ pixAutomatic?: boolean;
621
+ installments?: number;
622
+ isSubscription?: boolean;
623
+ subscriptionType?: string;
624
+ unitLabel?: string;
625
+ returnUrl?: string;
626
+ returnUrlButtonText?: string;
627
+ statementDescriptor?: string;
628
+ }
553
629
  interface MetaFeatures {
554
630
  subscriptions: boolean;
555
631
  checkout_sessions: boolean;
@@ -972,7 +1048,7 @@ declare class Products {
972
1048
  * List products for the authenticated seller, with pagination and search.
973
1049
  *
974
1050
  * @example
975
- * const { data, meta } = await garu.products.list({ search: 'curso', limit: 10 });
1051
+ * const { data, totalCount } = await garu.products.list({ search: 'curso', limit: 10 });
976
1052
  */
977
1053
  list(params?: ListProductsParams): Promise<ProductList>;
978
1054
  /**
@@ -983,6 +1059,44 @@ declare class Products {
983
1059
  * const product = await garu.products.get('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f');
984
1060
  */
985
1061
  get(uuid: string): Promise<Product>;
1062
+ /**
1063
+ * Create a product for the authenticated seller. Returns the created
1064
+ * product (HTTP 201). Only `name` is required; everything else falls back
1065
+ * to seller/server defaults.
1066
+ *
1067
+ * Automatically attaches an `X-Idempotency-Key` header — if you don't pass
1068
+ * `idempotencyKey`, the SDK generates a UUIDv4. This makes the built-in
1069
+ * retry on transient failures safe: a retried POST returns the original
1070
+ * product instead of creating a duplicate.
1071
+ *
1072
+ * @example
1073
+ * const product = await garu.products.create({
1074
+ * name: 'Plano Mensal',
1075
+ * value: 49.90, // R$ 49,90 in reais (decimal BRL), NOT centavos
1076
+ * description: 'Acesso completo à plataforma',
1077
+ * pix: true,
1078
+ * creditCard: true,
1079
+ * isSubscription: true,
1080
+ * subscriptionType: 'monthly',
1081
+ * pixAutomatic: true // expose Pix Automático on the subscription checkout
1082
+ * });
1083
+ */
1084
+ create(params: CreateProductParams): Promise<Product>;
1085
+ /**
1086
+ * Update a product (partial PATCH — only the fields you pass are changed).
1087
+ * Returns the updated product.
1088
+ *
1089
+ * `id` accepts the product UUID (recommended) or the legacy numeric id —
1090
+ * both resolve on the `/api/v1/products/:id` path (see
1091
+ * {@link ProductPortalConfigResource}).
1092
+ *
1093
+ * @example
1094
+ * const updated = await garu.products.update('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
1095
+ * value: 59.90, // reais (decimal BRL), NOT centavos
1096
+ * pixAutomatic: true // turn on Pix Automático for this product
1097
+ * });
1098
+ */
1099
+ update(id: string | number, params: UpdateProductParams): Promise<Product>;
986
1100
  }
987
1101
 
988
1102
  /**
@@ -1391,4 +1505,4 @@ declare class GaruServerError extends GaruAPIError {
1391
1505
  constructor(message: string, status: number, requestId: string | null, body: unknown);
1392
1506
  }
1393
1507
 
1394
- export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeNowOutcome, type ChargeNowReason, type ChargeNowResult, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ResendWebhookEventParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
1508
+ export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, type ChargeNowOutcome, type ChargeNowReason, type ChargeNowResult, type ChargeStatus, type CreateChargeParams, type CreateCustomerParams, type CreateProductParams, type CreateScheduledChargeParams, type Customer, type CustomerList, type CustomerRecord, type FailurePayload, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, type GaruFailureCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type ListChargesParams, type ListCustomersParams, type ListProductsParams, type ListScheduledChargeAttemptsParams, type ListScheduledChargesParams, type ListWebhookEventsParams, type MarkPaidScheduledChargeParams, type MetaFeatures, type MetaResponse, type PaginatedList, type PauseScheduledChargeParams, type PaymentMethod, type PaymentMethodExpiredPayload, type PaymentMethodExpiringPayload, type PostponeScheduledChargeParams, type Product, type ProductList, type ProductPortalConfig, type RecurrenceConfig, type RecurrenceInterval, type RefundChargeParams, type ResendWebhookEventParams, type ScheduledChargeActor, type ScheduledChargeAttempt, type ScheduledChargeAttemptList, type ScheduledChargeAttemptSource, type ScheduledChargeAttemptStatus, type ScheduledChargeDetail, type ScheduledChargeEvent, type ScheduledChargeEventType, type ScheduledChargeLinkedTransaction, type ScheduledChargeList, type ScheduledChargeRecord, type ScheduledChargeStatus, type ScheduledChargeType, type ScheduledPaymentMethod, type SetProductPortalConfigParams, type UpdateCustomerParams, type UpdateProductParams, type VerifiedWebhook, type VerifyWebhookParams, type WebhookEvent, type WebhookEventEndpoint, type WebhookEventList, type WebhookEventStatus, type WirePaymentMethodId, webhooks };
package/dist/index.js CHANGED
@@ -474,9 +474,12 @@ var ProductPortalConfigResource = class {
474
474
  */
475
475
  async get(productId) {
476
476
  return this.http.call(
477
- (signal) => this.http.client.GET(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
478
- signal
479
- }).then((r) => r)
477
+ (signal) => this.http.client.GET(
478
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
479
+ {
480
+ signal
481
+ }
482
+ ).then((r) => r)
480
483
  );
481
484
  }
482
485
  /**
@@ -494,19 +497,25 @@ var ProductPortalConfigResource = class {
494
497
  */
495
498
  async set(productId, params) {
496
499
  return this.http.call(
497
- (signal) => this.http.client.POST(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
498
- body: params,
499
- signal
500
- }).then((r) => r)
500
+ (signal) => this.http.client.POST(
501
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
502
+ {
503
+ body: params,
504
+ signal
505
+ }
506
+ ).then((r) => r)
501
507
  );
502
508
  }
503
509
  /** Same merge semantics as `set` — alias for HTTP-PATCH-prefering callers. */
504
510
  async patch(productId, params) {
505
511
  return this.http.call(
506
- (signal) => this.http.client.PATCH(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
507
- body: params,
508
- signal
509
- }).then((r) => r)
512
+ (signal) => this.http.client.PATCH(
513
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
514
+ {
515
+ body: params,
516
+ signal
517
+ }
518
+ ).then((r) => r)
510
519
  );
511
520
  }
512
521
  /**
@@ -519,10 +528,13 @@ var ProductPortalConfigResource = class {
519
528
  */
520
529
  async clear(productId) {
521
530
  return this.http.call(
522
- (signal) => this.http.client.DELETE(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
523
- body: {},
524
- signal
525
- }).then((r) => r)
531
+ (signal) => this.http.client.DELETE(
532
+ `/api/v1/products/${encodeURIComponent(String(productId))}/portal-config`,
533
+ {
534
+ body: {},
535
+ signal
536
+ }
537
+ ).then((r) => r)
526
538
  );
527
539
  }
528
540
  };
@@ -538,16 +550,15 @@ var Products = class {
538
550
  * List products for the authenticated seller, with pagination and search.
539
551
  *
540
552
  * @example
541
- * const { data, meta } = await garu.products.list({ search: 'curso', limit: 10 });
553
+ * const { data, totalCount } = await garu.products.list({ search: 'curso', limit: 10 });
542
554
  */
543
555
  async list(params = {}) {
544
556
  const query = {};
545
557
  if (params.page !== void 0) query.page = String(params.page);
546
558
  if (params.limit !== void 0) query.limit = String(params.limit);
547
559
  if (params.search) query.search = params.search;
548
- if (params.tab) query.tab = params.tab;
549
560
  const qs = new URLSearchParams(query).toString();
550
- const url = `/api/products/seller${qs ? `?${qs}` : ""}`;
561
+ const url = `/api/v1/products${qs ? `?${qs}` : ""}`;
551
562
  return this.http.call(
552
563
  (signal) => this.http.client.GET(url, { signal }).then(
553
564
  (r) => r
@@ -563,11 +574,66 @@ var Products = class {
563
574
  */
564
575
  async get(uuid) {
565
576
  return this.http.call(
566
- (signal) => this.http.client.GET(`/api/products/uuid/${uuid}`, { signal }).then(
577
+ (signal) => this.http.client.GET(`/api/v1/products/${uuid}`, { signal }).then(
567
578
  (r) => r
568
579
  )
569
580
  );
570
581
  }
582
+ /**
583
+ * Create a product for the authenticated seller. Returns the created
584
+ * product (HTTP 201). Only `name` is required; everything else falls back
585
+ * to seller/server defaults.
586
+ *
587
+ * Automatically attaches an `X-Idempotency-Key` header — if you don't pass
588
+ * `idempotencyKey`, the SDK generates a UUIDv4. This makes the built-in
589
+ * retry on transient failures safe: a retried POST returns the original
590
+ * product instead of creating a duplicate.
591
+ *
592
+ * @example
593
+ * const product = await garu.products.create({
594
+ * name: 'Plano Mensal',
595
+ * value: 49.90, // R$ 49,90 in reais (decimal BRL), NOT centavos
596
+ * description: 'Acesso completo à plataforma',
597
+ * pix: true,
598
+ * creditCard: true,
599
+ * isSubscription: true,
600
+ * subscriptionType: 'monthly',
601
+ * pixAutomatic: true // expose Pix Automático on the subscription checkout
602
+ * });
603
+ */
604
+ async create(params) {
605
+ const { idempotencyKey, ...body } = params;
606
+ const key = idempotencyKey ?? generateIdempotencyKey();
607
+ return this.http.call(
608
+ (signal) => this.http.client.POST("/api/v1/products", {
609
+ body,
610
+ headers: { "X-Idempotency-Key": key },
611
+ signal
612
+ }).then((r) => r)
613
+ );
614
+ }
615
+ /**
616
+ * Update a product (partial PATCH — only the fields you pass are changed).
617
+ * Returns the updated product.
618
+ *
619
+ * `id` accepts the product UUID (recommended) or the legacy numeric id —
620
+ * both resolve on the `/api/v1/products/:id` path (see
621
+ * {@link ProductPortalConfigResource}).
622
+ *
623
+ * @example
624
+ * const updated = await garu.products.update('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
625
+ * value: 59.90, // reais (decimal BRL), NOT centavos
626
+ * pixAutomatic: true // turn on Pix Automático for this product
627
+ * });
628
+ */
629
+ async update(id, params) {
630
+ return this.http.call(
631
+ (signal) => this.http.client.PATCH(`/api/v1/products/${encodeURIComponent(String(id))}`, {
632
+ body: params,
633
+ signal
634
+ }).then((r) => r)
635
+ );
636
+ }
571
637
  };
572
638
 
573
639
  // src/resources/scheduled-charges.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garuhq/node",
3
- "version": "0.14.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",