@garuhq/node 0.7.0 → 0.8.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,90 @@ 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, v0.8.0).
171
+
172
+ | Method | Description |
173
+ | ----------------------------------- | ----------------------------------------------------------------- |
174
+ | `list(params?)` | Paginated list of products for the seller. |
175
+ | `get(uuid)` | Fetch a single product by UUID — same id used by charges. |
176
+ | `portalConfig.get(productId)` | Read per-product portal customization. Returns `null` if unset. |
177
+ | `portalConfig.set(productId, p)` | Upsert with merge — only fields present are written. |
178
+ | `portalConfig.patch(productId, p)` | Same merge semantics as `set` — alias for HTTP-PATCH callers. |
179
+ | `portalConfig.clear(productId)` | Remove the customization; product falls back to seller config. |
180
+
181
+ ```ts
182
+ // SaaS de coaching: per-coach branding under one Seller account
183
+ await garu.products.portalConfig.set(57, {
184
+ businessName: 'Coach Maria — Corrida & Trilha',
185
+ primaryColor: '#257264',
186
+ logoUrl: 'https://cdn.exemplo.com/coaches/maria.png',
187
+ });
188
+
189
+ // Pass `null` on a field to inherit from the seller-level config
190
+ await garu.products.portalConfig.patch(57, { primaryColor: null });
191
+ ```
192
+
193
+ ## Scheduled charges
194
+
195
+ 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.
196
+
197
+ | Method | Description |
198
+ | --------------------------------------------- | -------------------------------------------------------------------------- |
199
+ | `create(params)` | Create one-time or recurring schedule. Auto-attaches `X-Idempotency-Key`. |
200
+ | `list(params?)` | Paginated list with status / type / dueFrom / dueTo / customerId filters. |
201
+ | `get(id)` | Detail bundle: charge + event timeline + linked transactions. |
202
+ | `markPaid(id, params)` | Mark cycle paid (off-Garu reconciliation). |
203
+ | `postpone(id, params)` | Move the next cycle's due date forward. |
204
+ | `pause(id, params?)` / `resume(id)` | Suspend / re-enable a series. |
205
+ | `cancelRecurrence(id, params?)` | Hard-stop future cycles (recurring only). |
206
+ | `cancelAtPeriodEnd(id, { enabled })` | Stripe-style soft-cancel; reversible. |
207
+ | `changePaymentMethod(id, params)` | Swap the saved card. |
208
+ | `clearPaymentMethod(id)` | Remove the saved card; future cycles email-with-link. |
209
+ | `listAttempts(id, params?)` | Per-attempt billing log — every silent-charge / retry / mark-paid (v0.8.2).|
210
+
211
+ ```ts
212
+ // Recurring with 7-day trial
213
+ const series = await garu.scheduledCharges.create({
214
+ customerId: 42,
215
+ productId: 17,
216
+ amount: 49.9,
217
+ type: 'recurring',
218
+ dueDate: '2026-06-01',
219
+ methods: ['card', 'pix'],
220
+ recurrence: { interval: 'monthly' },
221
+ trialDays: 7,
222
+ });
223
+
224
+ // Audit why cycle 3 failed (v0.8.2)
225
+ const { data } = await garu.scheduledCharges.listAttempts(series.id, {
226
+ cycleNumber: 3,
227
+ });
228
+ const declines = data.filter((a) => a.status === 'declined');
229
+ // → each declines[i].failureCode is one of GaruFailureCode (insufficient_funds,
230
+ // card_expired, card_declined, ...)
231
+ ```
232
+
233
+ ## Failure codes (v0.8.0)
234
+
235
+ Every `transaction.payment.failed`, `scheduled_charge.cycle_failed`, and `listAttempts()` row carries:
236
+
237
+ - `failureCode` — canonical `GaruFailureCode` enum (10 values, gateway-independent)
238
+ - `failureReason` — human-readable PT-BR
239
+ - `gatewayFailureCode` — raw code from Celcoin (ABECS for forensics)
240
+
241
+ ```ts
242
+ import type { GaruFailureCode } from '@garuhq/node';
243
+
244
+ const PERMANENT: GaruFailureCode[] = ['card_expired', 'card_canceled', 'fraud_suspected'];
245
+ function shouldAskForNewCard(code: GaruFailureCode): boolean {
246
+ return PERMANENT.includes(code);
247
+ }
248
+ ```
249
+
250
+ Full table at [docs.garu.com.br/api-reference/webhooks/codigos-de-falha](https://docs.garu.com.br/api-reference/webhooks/codigos-de-falha).
251
+
168
252
  ## Meta
169
253
 
170
254
  Discover available payment methods and webhook events. No authentication required.
package/dist/index.cjs CHANGED
@@ -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
  /**
@@ -971,6 +998,20 @@ declare class ScheduledCharges {
971
998
  * await garu.scheduledCharges.clearPaymentMethod('sch_abc123');
972
999
  */
973
1000
  clearPaymentMethod(id: string): Promise<ScheduledChargeRecord>;
1001
+ /**
1002
+ * Per-attempt billing log for the series (SPEC §4.2). One row per
1003
+ * logical billing event — cycle 1 interactive charge, every silent
1004
+ * charge attempt, every retry, every manual mark-paid. Carries the
1005
+ * canonical `failureCode` for declines so you can audit billing
1006
+ * outcomes without cross-referencing Transactions.
1007
+ *
1008
+ * @example
1009
+ * const { data } = await garu.scheduledCharges.listAttempts('sch_abc', {
1010
+ * cycleNumber: 3
1011
+ * });
1012
+ * const declines = data.filter((a) => a.status === 'declined');
1013
+ */
1014
+ listAttempts(id: string, params?: ListScheduledChargeAttemptsParams): Promise<ScheduledChargeAttemptList>;
974
1015
  }
975
1016
 
976
1017
  interface GaruOptions {
@@ -1071,4 +1112,4 @@ declare class GaruServerError extends GaruAPIError {
1071
1112
  constructor(message: string, status: number, requestId: string | null, body: unknown);
1072
1113
  }
1073
1114
 
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 };
1115
+ 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
  /**
@@ -971,6 +998,20 @@ declare class ScheduledCharges {
971
998
  * await garu.scheduledCharges.clearPaymentMethod('sch_abc123');
972
999
  */
973
1000
  clearPaymentMethod(id: string): Promise<ScheduledChargeRecord>;
1001
+ /**
1002
+ * Per-attempt billing log for the series (SPEC §4.2). One row per
1003
+ * logical billing event — cycle 1 interactive charge, every silent
1004
+ * charge attempt, every retry, every manual mark-paid. Carries the
1005
+ * canonical `failureCode` for declines so you can audit billing
1006
+ * outcomes without cross-referencing Transactions.
1007
+ *
1008
+ * @example
1009
+ * const { data } = await garu.scheduledCharges.listAttempts('sch_abc', {
1010
+ * cycleNumber: 3
1011
+ * });
1012
+ * const declines = data.filter((a) => a.status === 'declined');
1013
+ */
1014
+ listAttempts(id: string, params?: ListScheduledChargeAttemptsParams): Promise<ScheduledChargeAttemptList>;
974
1015
  }
975
1016
 
976
1017
  interface GaruOptions {
@@ -1071,4 +1112,4 @@ declare class GaruServerError extends GaruAPIError {
1071
1112
  constructor(message: string, status: number, requestId: string | null, body: unknown);
1072
1113
  }
1073
1114
 
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 };
1115
+ 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
@@ -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.8.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",