@garuhq/node 0.7.0 → 0.10.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/README.md CHANGED
@@ -165,6 +165,94 @@ const customer = await garu.customers.create({
165
165
  const { data, meta } = await garu.customers.list({ search: 'maria', limit: 10 });
166
166
  ```
167
167
 
168
+ ## Products
169
+
170
+ Discover products and customize the per-product portal experience (B2B2C).
171
+
172
+ `portalConfig.*` methods accept `productId` as either the product UUID (preferred — same identifier returned by `list()` and webhook payloads) or the legacy numeric id (Garu v0.10.0+).
173
+
174
+ | Method | Description |
175
+ | ----------------------------------- | ----------------------------------------------------------------- |
176
+ | `list(params?)` | Paginated list of products for the seller. |
177
+ | `get(uuid)` | Fetch a single product by UUID — same id used by charges. |
178
+ | `portalConfig.get(productId)` | Read per-product portal customization. Returns `null` if unset. |
179
+ | `portalConfig.set(productId, p)` | Upsert with merge — only fields present are written. |
180
+ | `portalConfig.patch(productId, p)` | Same merge semantics as `set` — alias for HTTP-PATCH callers. |
181
+ | `portalConfig.clear(productId)` | Remove the customization; product falls back to seller config. |
182
+
183
+ ```ts
184
+ // SaaS de coaching: per-coach branding under one Seller account
185
+ await garu.products.portalConfig.set('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
186
+ businessName: 'Coach Maria — Corrida & Trilha',
187
+ primaryColor: '#257264',
188
+ logoUrl: 'https://cdn.exemplo.com/coaches/maria.png',
189
+ });
190
+
191
+ // Pass `null` on a field to inherit from the seller-level config
192
+ await garu.products.portalConfig.patch('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
193
+ primaryColor: null,
194
+ });
195
+ ```
196
+
197
+ ## Scheduled charges
198
+
199
+ Bill an existing customer on a future date — one-time or recurring with card tokenization. The Garu drives email reminders, dunning, retries, and the lifecycle state machine.
200
+
201
+ | Method | Description |
202
+ | --------------------------------------------- | -------------------------------------------------------------------------- |
203
+ | `create(params)` | Create one-time or recurring schedule. Auto-attaches `X-Idempotency-Key`. |
204
+ | `list(params?)` | Paginated list with status / type / dueFrom / dueTo / customerId filters. |
205
+ | `get(id)` | Detail bundle: charge + event timeline + linked transactions. |
206
+ | `markPaid(id, params)` | Mark cycle paid (off-Garu reconciliation). |
207
+ | `postpone(id, params)` | Move the next cycle's due date forward. |
208
+ | `pause(id, params?)` / `resume(id)` | Suspend / re-enable a series. |
209
+ | `cancelRecurrence(id, params?)` | Hard-stop future cycles (recurring only). |
210
+ | `cancelAtPeriodEnd(id, { enabled })` | Stripe-style soft-cancel; reversible. |
211
+ | `changePaymentMethod(id, params)` | Swap the saved card. |
212
+ | `clearPaymentMethod(id)` | Remove the saved card; future cycles email-with-link. |
213
+ | `listAttempts(id, params?)` | Per-attempt billing log — every silent-charge / retry / mark-paid (v0.8.2).|
214
+
215
+ ```ts
216
+ // Recurring with 7-day trial
217
+ const series = await garu.scheduledCharges.create({
218
+ customerId: 42,
219
+ productId: 17,
220
+ amount: 49.9,
221
+ type: 'recurring',
222
+ dueDate: '2026-06-01',
223
+ methods: ['card', 'pix'],
224
+ recurrence: { interval: 'monthly' },
225
+ trialDays: 7,
226
+ });
227
+
228
+ // Audit why cycle 3 failed (v0.8.2)
229
+ const { data } = await garu.scheduledCharges.listAttempts(series.id, {
230
+ cycleNumber: 3,
231
+ });
232
+ const declines = data.filter((a) => a.status === 'declined');
233
+ // → each declines[i].failureCode is one of GaruFailureCode (insufficient_funds,
234
+ // card_expired, card_declined, ...)
235
+ ```
236
+
237
+ ## Failure codes (v0.8.0)
238
+
239
+ Every `transaction.payment.failed`, `scheduled_charge.cycle_failed`, and `listAttempts()` row carries:
240
+
241
+ - `failureCode` — canonical `GaruFailureCode` enum (10 values, gateway-independent)
242
+ - `failureReason` — human-readable PT-BR
243
+ - `gatewayFailureCode` — raw code from Celcoin (ABECS for forensics)
244
+
245
+ ```ts
246
+ import type { GaruFailureCode } from '@garuhq/node';
247
+
248
+ const PERMANENT: GaruFailureCode[] = ['card_expired', 'card_canceled', 'fraud_suspected'];
249
+ function shouldAskForNewCard(code: GaruFailureCode): boolean {
250
+ return PERMANENT.includes(code);
251
+ }
252
+ ```
253
+
254
+ Full table at [docs.garu.com.br/api-reference/webhooks/codigos-de-falha](https://docs.garu.com.br/api-reference/webhooks/codigos-de-falha).
255
+
168
256
  ## Meta
169
257
 
170
258
  Discover available payment methods and webhook events. No authentication required.
package/dist/index.cjs CHANGED
@@ -475,11 +475,11 @@ var ProductPortalConfigResource = class {
475
475
  * portal config).
476
476
  *
477
477
  * @example
478
- * const cfg = await garu.products.portalConfig.get(57);
478
+ * const cfg = await garu.products.portalConfig.get('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f');
479
479
  */
480
480
  async get(productId) {
481
481
  return this.http.call(
482
- (signal) => this.http.client.GET(`/api/products/${productId}/portal-config`, {
482
+ (signal) => this.http.client.GET(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
483
483
  signal
484
484
  }).then((r) => r)
485
485
  );
@@ -491,7 +491,7 @@ var ProductPortalConfigResource = class {
491
491
  * value. Use `clear` to reset everything.
492
492
  *
493
493
  * @example
494
- * await garu.products.portalConfig.set(57, {
494
+ * await garu.products.portalConfig.set('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
495
495
  * businessName: 'Coach Maria — Corrida & Trilha',
496
496
  * primaryColor: '#257264',
497
497
  * logoUrl: 'https://cdn.atletia.com.br/coaches/maria.png'
@@ -499,7 +499,7 @@ var ProductPortalConfigResource = class {
499
499
  */
500
500
  async set(productId, params) {
501
501
  return this.http.call(
502
- (signal) => this.http.client.POST(`/api/products/${productId}/portal-config`, {
502
+ (signal) => this.http.client.POST(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
503
503
  body: params,
504
504
  signal
505
505
  }).then((r) => r)
@@ -508,7 +508,7 @@ var ProductPortalConfigResource = class {
508
508
  /** Same merge semantics as `set` — alias for HTTP-PATCH-prefering callers. */
509
509
  async patch(productId, params) {
510
510
  return this.http.call(
511
- (signal) => this.http.client.PATCH(`/api/products/${productId}/portal-config`, {
511
+ (signal) => this.http.client.PATCH(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
512
512
  body: params,
513
513
  signal
514
514
  }).then((r) => r)
@@ -520,11 +520,11 @@ var ProductPortalConfigResource = class {
520
520
  * deleted, `{ removed: false }` when there was nothing to remove.
521
521
  *
522
522
  * @example
523
- * await garu.products.portalConfig.clear(57);
523
+ * await garu.products.portalConfig.clear('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f');
524
524
  */
525
525
  async clear(productId) {
526
526
  return this.http.call(
527
- (signal) => this.http.client.DELETE(`/api/products/${productId}/portal-config`, {
527
+ (signal) => this.http.client.DELETE(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
528
528
  signal
529
529
  }).then((r) => r)
530
530
  );
@@ -802,6 +802,32 @@ var ScheduledCharges = class {
802
802
  }).then((r) => r)
803
803
  );
804
804
  }
805
+ /**
806
+ * Per-attempt billing log for the series (SPEC §4.2). One row per
807
+ * logical billing event — cycle 1 interactive charge, every silent
808
+ * charge attempt, every retry, every manual mark-paid. Carries the
809
+ * canonical `failureCode` for declines so you can audit billing
810
+ * outcomes without cross-referencing Transactions.
811
+ *
812
+ * @example
813
+ * const { data } = await garu.scheduledCharges.listAttempts('sch_abc', {
814
+ * cycleNumber: 3
815
+ * });
816
+ * const declines = data.filter((a) => a.status === 'declined');
817
+ */
818
+ async listAttempts(id, params = {}) {
819
+ const qs = new URLSearchParams();
820
+ if (params.page !== void 0) qs.set("page", String(params.page));
821
+ if (params.limit !== void 0) qs.set("limit", String(params.limit));
822
+ if (params.cycleNumber !== void 0) qs.set("cycleNumber", String(params.cycleNumber));
823
+ const query = qs.toString();
824
+ const url = `/api/scheduled-charges/${id}/attempts${query ? `?${query}` : ""}`;
825
+ return this.http.call(
826
+ (signal) => this.http.client.GET(url, { signal }).then(
827
+ (r) => r
828
+ )
829
+ );
830
+ }
805
831
  };
806
832
  var webhooks = {
807
833
  verify(params) {
package/dist/index.d.cts CHANGED
@@ -354,6 +354,33 @@ interface ScheduledChargeDetail {
354
354
  transactions: ScheduledChargeLinkedTransaction[];
355
355
  }
356
356
  type ScheduledChargeList = PaginatedList<ScheduledChargeRecord>;
357
+ /** Source of a billing attempt — see SPEC §3.1. */
358
+ type ScheduledChargeAttemptSource = 'cycle1_interactive' | 'silent_charge' | 'card_retry' | 'manual_mark_paid' | 'fallback_pix';
359
+ type ScheduledChargeAttemptStatus = 'pending' | 'succeeded' | 'declined' | 'canceled' | 'errored';
360
+ interface ScheduledChargeAttempt {
361
+ id: number;
362
+ cycleId: string;
363
+ cycleNumber: number;
364
+ attemptNumber: number;
365
+ attemptedAt: string;
366
+ source: ScheduledChargeAttemptSource;
367
+ paymentMethod: 'card' | 'pix' | 'boleto' | 'manual';
368
+ paymentMethodId: number | null;
369
+ cardLast4: string | null;
370
+ cardBrand: string | null;
371
+ status: ScheduledChargeAttemptStatus;
372
+ failureCode: GaruFailureCode | null;
373
+ failureReason: string | null;
374
+ gatewayFailureCode: string | null;
375
+ gatewayChargeId: number | null;
376
+ transactionId: number | null;
377
+ }
378
+ type ScheduledChargeAttemptList = PaginatedList<ScheduledChargeAttempt>;
379
+ interface ListScheduledChargeAttemptsParams {
380
+ page?: number;
381
+ limit?: number;
382
+ cycleNumber?: number;
383
+ }
357
384
  interface CreateScheduledChargeParams {
358
385
  customerId: number;
359
386
  /**
@@ -749,9 +776,13 @@ declare class Meta {
749
776
  }
750
777
 
751
778
  /**
752
- * Per-product portal customization (Garu v0.8.0). Used by B2B2C platforms
753
- * that model their professionals/coaches as Products under a single seller
754
- * and want per-product branding on the customer payment + portal pages.
779
+ * Per-product portal customization. Used by B2B2C platforms that model
780
+ * their professionals/coaches as Products under a single seller and want
781
+ * per-product branding on the customer payment + portal pages.
782
+ *
783
+ * `productId` accepts either the product UUID (preferred — same identifier
784
+ * returned by `garu.products.list()` and webhook payloads) or the legacy
785
+ * numeric id. UUID support added in Garu v0.10.0.
755
786
  */
756
787
  declare class ProductPortalConfigResource {
757
788
  private readonly http;
@@ -762,9 +793,9 @@ declare class ProductPortalConfigResource {
762
793
  * portal config).
763
794
  *
764
795
  * @example
765
- * const cfg = await garu.products.portalConfig.get(57);
796
+ * const cfg = await garu.products.portalConfig.get('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f');
766
797
  */
767
- get(productId: number): Promise<ProductPortalConfig | null>;
798
+ get(productId: string | number): Promise<ProductPortalConfig | null>;
768
799
  /**
769
800
  * Create or merge the portal customization (idempotent upsert). Both
770
801
  * `set` and `patch` have the same merge semantics — only fields present
@@ -772,24 +803,24 @@ declare class ProductPortalConfigResource {
772
803
  * value. Use `clear` to reset everything.
773
804
  *
774
805
  * @example
775
- * await garu.products.portalConfig.set(57, {
806
+ * await garu.products.portalConfig.set('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
776
807
  * businessName: 'Coach Maria — Corrida & Trilha',
777
808
  * primaryColor: '#257264',
778
809
  * logoUrl: 'https://cdn.atletia.com.br/coaches/maria.png'
779
810
  * });
780
811
  */
781
- set(productId: number, params: SetProductPortalConfigParams): Promise<ProductPortalConfig>;
812
+ set(productId: string | number, params: SetProductPortalConfigParams): Promise<ProductPortalConfig>;
782
813
  /** Same merge semantics as `set` — alias for HTTP-PATCH-prefering callers. */
783
- patch(productId: number, params: SetProductPortalConfigParams): Promise<ProductPortalConfig>;
814
+ patch(productId: string | number, params: SetProductPortalConfigParams): Promise<ProductPortalConfig>;
784
815
  /**
785
816
  * Remove the per-product config. The product falls back to the
786
817
  * seller-level portal config. Returns `{ removed: true }` when a row was
787
818
  * deleted, `{ removed: false }` when there was nothing to remove.
788
819
  *
789
820
  * @example
790
- * await garu.products.portalConfig.clear(57);
821
+ * await garu.products.portalConfig.clear('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f');
791
822
  */
792
- clear(productId: number): Promise<{
823
+ clear(productId: string | number): Promise<{
793
824
  removed: boolean;
794
825
  }>;
795
826
  }
@@ -971,6 +1002,20 @@ declare class ScheduledCharges {
971
1002
  * await garu.scheduledCharges.clearPaymentMethod('sch_abc123');
972
1003
  */
973
1004
  clearPaymentMethod(id: string): Promise<ScheduledChargeRecord>;
1005
+ /**
1006
+ * Per-attempt billing log for the series (SPEC §4.2). One row per
1007
+ * logical billing event — cycle 1 interactive charge, every silent
1008
+ * charge attempt, every retry, every manual mark-paid. Carries the
1009
+ * canonical `failureCode` for declines so you can audit billing
1010
+ * outcomes without cross-referencing Transactions.
1011
+ *
1012
+ * @example
1013
+ * const { data } = await garu.scheduledCharges.listAttempts('sch_abc', {
1014
+ * cycleNumber: 3
1015
+ * });
1016
+ * const declines = data.filter((a) => a.status === 'declined');
1017
+ */
1018
+ listAttempts(id: string, params?: ListScheduledChargeAttemptsParams): Promise<ScheduledChargeAttemptList>;
974
1019
  }
975
1020
 
976
1021
  interface GaruOptions {
@@ -1071,4 +1116,4 @@ declare class GaruServerError extends GaruAPIError {
1071
1116
  constructor(message: string, status: number, requestId: string | null, body: unknown);
1072
1117
  }
1073
1118
 
1074
- export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, 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 ListScheduledChargesParams, 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 ScheduledChargeActor, 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 WirePaymentMethodId, webhooks };
1119
+ export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, 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 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 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 WirePaymentMethodId, webhooks };
package/dist/index.d.ts CHANGED
@@ -354,6 +354,33 @@ interface ScheduledChargeDetail {
354
354
  transactions: ScheduledChargeLinkedTransaction[];
355
355
  }
356
356
  type ScheduledChargeList = PaginatedList<ScheduledChargeRecord>;
357
+ /** Source of a billing attempt — see SPEC §3.1. */
358
+ type ScheduledChargeAttemptSource = 'cycle1_interactive' | 'silent_charge' | 'card_retry' | 'manual_mark_paid' | 'fallback_pix';
359
+ type ScheduledChargeAttemptStatus = 'pending' | 'succeeded' | 'declined' | 'canceled' | 'errored';
360
+ interface ScheduledChargeAttempt {
361
+ id: number;
362
+ cycleId: string;
363
+ cycleNumber: number;
364
+ attemptNumber: number;
365
+ attemptedAt: string;
366
+ source: ScheduledChargeAttemptSource;
367
+ paymentMethod: 'card' | 'pix' | 'boleto' | 'manual';
368
+ paymentMethodId: number | null;
369
+ cardLast4: string | null;
370
+ cardBrand: string | null;
371
+ status: ScheduledChargeAttemptStatus;
372
+ failureCode: GaruFailureCode | null;
373
+ failureReason: string | null;
374
+ gatewayFailureCode: string | null;
375
+ gatewayChargeId: number | null;
376
+ transactionId: number | null;
377
+ }
378
+ type ScheduledChargeAttemptList = PaginatedList<ScheduledChargeAttempt>;
379
+ interface ListScheduledChargeAttemptsParams {
380
+ page?: number;
381
+ limit?: number;
382
+ cycleNumber?: number;
383
+ }
357
384
  interface CreateScheduledChargeParams {
358
385
  customerId: number;
359
386
  /**
@@ -749,9 +776,13 @@ declare class Meta {
749
776
  }
750
777
 
751
778
  /**
752
- * Per-product portal customization (Garu v0.8.0). Used by B2B2C platforms
753
- * that model their professionals/coaches as Products under a single seller
754
- * and want per-product branding on the customer payment + portal pages.
779
+ * Per-product portal customization. Used by B2B2C platforms that model
780
+ * their professionals/coaches as Products under a single seller and want
781
+ * per-product branding on the customer payment + portal pages.
782
+ *
783
+ * `productId` accepts either the product UUID (preferred — same identifier
784
+ * returned by `garu.products.list()` and webhook payloads) or the legacy
785
+ * numeric id. UUID support added in Garu v0.10.0.
755
786
  */
756
787
  declare class ProductPortalConfigResource {
757
788
  private readonly http;
@@ -762,9 +793,9 @@ declare class ProductPortalConfigResource {
762
793
  * portal config).
763
794
  *
764
795
  * @example
765
- * const cfg = await garu.products.portalConfig.get(57);
796
+ * const cfg = await garu.products.portalConfig.get('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f');
766
797
  */
767
- get(productId: number): Promise<ProductPortalConfig | null>;
798
+ get(productId: string | number): Promise<ProductPortalConfig | null>;
768
799
  /**
769
800
  * Create or merge the portal customization (idempotent upsert). Both
770
801
  * `set` and `patch` have the same merge semantics — only fields present
@@ -772,24 +803,24 @@ declare class ProductPortalConfigResource {
772
803
  * value. Use `clear` to reset everything.
773
804
  *
774
805
  * @example
775
- * await garu.products.portalConfig.set(57, {
806
+ * await garu.products.portalConfig.set('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
776
807
  * businessName: 'Coach Maria — Corrida & Trilha',
777
808
  * primaryColor: '#257264',
778
809
  * logoUrl: 'https://cdn.atletia.com.br/coaches/maria.png'
779
810
  * });
780
811
  */
781
- set(productId: number, params: SetProductPortalConfigParams): Promise<ProductPortalConfig>;
812
+ set(productId: string | number, params: SetProductPortalConfigParams): Promise<ProductPortalConfig>;
782
813
  /** Same merge semantics as `set` — alias for HTTP-PATCH-prefering callers. */
783
- patch(productId: number, params: SetProductPortalConfigParams): Promise<ProductPortalConfig>;
814
+ patch(productId: string | number, params: SetProductPortalConfigParams): Promise<ProductPortalConfig>;
784
815
  /**
785
816
  * Remove the per-product config. The product falls back to the
786
817
  * seller-level portal config. Returns `{ removed: true }` when a row was
787
818
  * deleted, `{ removed: false }` when there was nothing to remove.
788
819
  *
789
820
  * @example
790
- * await garu.products.portalConfig.clear(57);
821
+ * await garu.products.portalConfig.clear('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f');
791
822
  */
792
- clear(productId: number): Promise<{
823
+ clear(productId: string | number): Promise<{
793
824
  removed: boolean;
794
825
  }>;
795
826
  }
@@ -971,6 +1002,20 @@ declare class ScheduledCharges {
971
1002
  * await garu.scheduledCharges.clearPaymentMethod('sch_abc123');
972
1003
  */
973
1004
  clearPaymentMethod(id: string): Promise<ScheduledChargeRecord>;
1005
+ /**
1006
+ * Per-attempt billing log for the series (SPEC §4.2). One row per
1007
+ * logical billing event — cycle 1 interactive charge, every silent
1008
+ * charge attempt, every retry, every manual mark-paid. Carries the
1009
+ * canonical `failureCode` for declines so you can audit billing
1010
+ * outcomes without cross-referencing Transactions.
1011
+ *
1012
+ * @example
1013
+ * const { data } = await garu.scheduledCharges.listAttempts('sch_abc', {
1014
+ * cycleNumber: 3
1015
+ * });
1016
+ * const declines = data.filter((a) => a.status === 'declined');
1017
+ */
1018
+ listAttempts(id: string, params?: ListScheduledChargeAttemptsParams): Promise<ScheduledChargeAttemptList>;
974
1019
  }
975
1020
 
976
1021
  interface GaruOptions {
@@ -1071,4 +1116,4 @@ declare class GaruServerError extends GaruAPIError {
1071
1116
  constructor(message: string, status: number, requestId: string | null, body: unknown);
1072
1117
  }
1073
1118
 
1074
- export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, 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 ListScheduledChargesParams, 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 ScheduledChargeActor, 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 WirePaymentMethodId, webhooks };
1119
+ export { type CancelAtPeriodEndScheduledChargeParams, type CancelRecurrenceScheduledChargeParams, type CardInfo, type ChangePaymentMethodScheduledChargeParams, type Charge, type ChargeList, 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 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 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 WirePaymentMethodId, webhooks };
package/dist/index.js CHANGED
@@ -469,11 +469,11 @@ var ProductPortalConfigResource = class {
469
469
  * portal config).
470
470
  *
471
471
  * @example
472
- * const cfg = await garu.products.portalConfig.get(57);
472
+ * const cfg = await garu.products.portalConfig.get('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f');
473
473
  */
474
474
  async get(productId) {
475
475
  return this.http.call(
476
- (signal) => this.http.client.GET(`/api/products/${productId}/portal-config`, {
476
+ (signal) => this.http.client.GET(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
477
477
  signal
478
478
  }).then((r) => r)
479
479
  );
@@ -485,7 +485,7 @@ var ProductPortalConfigResource = class {
485
485
  * value. Use `clear` to reset everything.
486
486
  *
487
487
  * @example
488
- * await garu.products.portalConfig.set(57, {
488
+ * await garu.products.portalConfig.set('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f', {
489
489
  * businessName: 'Coach Maria — Corrida & Trilha',
490
490
  * primaryColor: '#257264',
491
491
  * logoUrl: 'https://cdn.atletia.com.br/coaches/maria.png'
@@ -493,7 +493,7 @@ var ProductPortalConfigResource = class {
493
493
  */
494
494
  async set(productId, params) {
495
495
  return this.http.call(
496
- (signal) => this.http.client.POST(`/api/products/${productId}/portal-config`, {
496
+ (signal) => this.http.client.POST(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
497
497
  body: params,
498
498
  signal
499
499
  }).then((r) => r)
@@ -502,7 +502,7 @@ var ProductPortalConfigResource = class {
502
502
  /** Same merge semantics as `set` — alias for HTTP-PATCH-prefering callers. */
503
503
  async patch(productId, params) {
504
504
  return this.http.call(
505
- (signal) => this.http.client.PATCH(`/api/products/${productId}/portal-config`, {
505
+ (signal) => this.http.client.PATCH(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
506
506
  body: params,
507
507
  signal
508
508
  }).then((r) => r)
@@ -514,11 +514,11 @@ var ProductPortalConfigResource = class {
514
514
  * deleted, `{ removed: false }` when there was nothing to remove.
515
515
  *
516
516
  * @example
517
- * await garu.products.portalConfig.clear(57);
517
+ * await garu.products.portalConfig.clear('b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f');
518
518
  */
519
519
  async clear(productId) {
520
520
  return this.http.call(
521
- (signal) => this.http.client.DELETE(`/api/products/${productId}/portal-config`, {
521
+ (signal) => this.http.client.DELETE(`/api/products/${encodeURIComponent(String(productId))}/portal-config`, {
522
522
  signal
523
523
  }).then((r) => r)
524
524
  );
@@ -796,6 +796,32 @@ var ScheduledCharges = class {
796
796
  }).then((r) => r)
797
797
  );
798
798
  }
799
+ /**
800
+ * Per-attempt billing log for the series (SPEC §4.2). One row per
801
+ * logical billing event — cycle 1 interactive charge, every silent
802
+ * charge attempt, every retry, every manual mark-paid. Carries the
803
+ * canonical `failureCode` for declines so you can audit billing
804
+ * outcomes without cross-referencing Transactions.
805
+ *
806
+ * @example
807
+ * const { data } = await garu.scheduledCharges.listAttempts('sch_abc', {
808
+ * cycleNumber: 3
809
+ * });
810
+ * const declines = data.filter((a) => a.status === 'declined');
811
+ */
812
+ async listAttempts(id, params = {}) {
813
+ const qs = new URLSearchParams();
814
+ if (params.page !== void 0) qs.set("page", String(params.page));
815
+ if (params.limit !== void 0) qs.set("limit", String(params.limit));
816
+ if (params.cycleNumber !== void 0) qs.set("cycleNumber", String(params.cycleNumber));
817
+ const query = qs.toString();
818
+ const url = `/api/scheduled-charges/${id}/attempts${query ? `?${query}` : ""}`;
819
+ return this.http.call(
820
+ (signal) => this.http.client.GET(url, { signal }).then(
821
+ (r) => r
822
+ )
823
+ );
824
+ }
799
825
  };
800
826
  var webhooks = {
801
827
  verify(params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@garuhq/node",
3
- "version": "0.7.0",
3
+ "version": "0.10.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",