@garuhq/node 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/index.cjs +445 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +393 -0
- package/dist/index.d.ts +393 -0
- package/dist/index.js +428 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import createClient from 'openapi-fetch';
|
|
2
|
+
|
|
3
|
+
interface VerifyWebhookParams {
|
|
4
|
+
/** Raw request body as received — do NOT re-serialize parsed JSON. */
|
|
5
|
+
payload: string | Buffer;
|
|
6
|
+
/** Value of the `X-Garu-Signature` header. Format: `t=<ts>,v1=<hex>`. */
|
|
7
|
+
signature: string;
|
|
8
|
+
/** The webhook endpoint's signing secret. */
|
|
9
|
+
secret: string;
|
|
10
|
+
/** Reject signatures older than this many seconds. Default: 300 (5 min). */
|
|
11
|
+
toleranceSec?: number;
|
|
12
|
+
/** Injectable for tests. Defaults to `Date.now()`. */
|
|
13
|
+
now?: () => number;
|
|
14
|
+
}
|
|
15
|
+
interface VerifiedWebhook {
|
|
16
|
+
/** Timestamp from the signature header, in seconds since epoch. */
|
|
17
|
+
timestamp: number;
|
|
18
|
+
/** Parsed JSON body. Throws {@link GaruSignatureVerificationError} if invalid JSON. */
|
|
19
|
+
event: unknown;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Webhook helpers.
|
|
23
|
+
*
|
|
24
|
+
* Garu signs outgoing webhooks with HMAC-SHA256 over `${timestamp}.${payload}`
|
|
25
|
+
* and delivers the signature in the `X-Garu-Signature` header as `t=<ts>,v1=<hex>`.
|
|
26
|
+
* This matches the format in the backend's `webhook-delivery.service.ts`.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Express example
|
|
30
|
+
* app.post('/webhooks/garu', express.raw({ type: 'application/json' }), (req, res) => {
|
|
31
|
+
* try {
|
|
32
|
+
* const { event } = Garu.webhooks.verify({
|
|
33
|
+
* payload: req.body,
|
|
34
|
+
* signature: req.header('x-garu-signature') ?? '',
|
|
35
|
+
* secret: process.env.GARU_WEBHOOK_SECRET!
|
|
36
|
+
* });
|
|
37
|
+
* // handle event
|
|
38
|
+
* res.sendStatus(200);
|
|
39
|
+
* } catch (err) {
|
|
40
|
+
* res.sendStatus(400);
|
|
41
|
+
* }
|
|
42
|
+
* });
|
|
43
|
+
*/
|
|
44
|
+
declare const webhooks: {
|
|
45
|
+
verify(params: VerifyWebhookParams): VerifiedWebhook;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
interface HttpClientConfig {
|
|
49
|
+
baseUrl: string;
|
|
50
|
+
apiKey?: string;
|
|
51
|
+
timeoutMs: number;
|
|
52
|
+
maxRetries: number;
|
|
53
|
+
userAgent: string;
|
|
54
|
+
/** Injectable for tests. Defaults to `globalThis.fetch`. */
|
|
55
|
+
fetch?: typeof fetch;
|
|
56
|
+
}
|
|
57
|
+
/** Typed openapi-fetch client keyed to the backend's OpenAPI paths. */
|
|
58
|
+
type GaruOpenapiClient = ReturnType<typeof createClient<paths>>;
|
|
59
|
+
/** Arg type for `HttpClient.call` — a thunk that issues one openapi-fetch request. */
|
|
60
|
+
type OpenapiCallResult<T> = Promise<{
|
|
61
|
+
data?: T;
|
|
62
|
+
error?: unknown;
|
|
63
|
+
response: Response;
|
|
64
|
+
}>;
|
|
65
|
+
/**
|
|
66
|
+
* HttpClient wraps the generated `openapi-fetch` client with:
|
|
67
|
+
* - retries (exponential backoff, full jitter, honors `Retry-After`)
|
|
68
|
+
* - typed error mapping (non-2xx → {@link GaruAPIError} subclass)
|
|
69
|
+
* - connection error wrapping
|
|
70
|
+
* - Authorization + User-Agent injection
|
|
71
|
+
*
|
|
72
|
+
* Resources call {@link call} with a thunk that returns an openapi-fetch
|
|
73
|
+
* `{ data, error, response }` tuple; the wrapper either returns `data` or
|
|
74
|
+
* throws the mapped error.
|
|
75
|
+
*/
|
|
76
|
+
declare class HttpClient {
|
|
77
|
+
readonly client: GaruOpenapiClient;
|
|
78
|
+
private readonly cfg;
|
|
79
|
+
constructor(cfg: HttpClientConfig);
|
|
80
|
+
/**
|
|
81
|
+
* Issue one HTTP call against the typed client, with retries + error mapping.
|
|
82
|
+
*
|
|
83
|
+
* `fn` is invoked up to `maxRetries + 1` times. The timeout in `cfg.timeoutMs`
|
|
84
|
+
* is enforced via `AbortController`.
|
|
85
|
+
*/
|
|
86
|
+
call<T>(fn: (signal: AbortSignal) => OpenapiCallResult<T>): Promise<T>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Public types for the Garu SDK.
|
|
91
|
+
*
|
|
92
|
+
* The wire-level types (`WireCreateTransactionRequest`, `WireMetaResponse`, ...)
|
|
93
|
+
* are generated from the backend's OpenAPI spec and live in
|
|
94
|
+
* `src/generated/schema.d.ts`. The friendly types in this file
|
|
95
|
+
* (`CreateChargeParams`, `Customer`, `Charge`, ...) are hand-curated for
|
|
96
|
+
* ergonomics — they rename `transactions` to `charges`, collapse wire enums
|
|
97
|
+
* into readable unions, and mark only truly required fields as required.
|
|
98
|
+
* The resource layer maps friendly → wire at the edge.
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
type PaymentMethod = 'pix' | 'credit_card' | 'boleto';
|
|
102
|
+
/** Payment-method identifier as sent to the backend over the wire. */
|
|
103
|
+
type WirePaymentMethodId = 'pix' | 'creditcard' | 'boleto';
|
|
104
|
+
type ChargeStatus = 'pending' | 'authorized' | 'paid' | 'failed' | 'refunded' | 'cancelled' | 'expired';
|
|
105
|
+
interface Customer {
|
|
106
|
+
/** Full legal name. 3–255 chars. */
|
|
107
|
+
name: string;
|
|
108
|
+
email: string;
|
|
109
|
+
/** CPF (11 digits) or CNPJ (14 digits), digits only. */
|
|
110
|
+
document: string;
|
|
111
|
+
/** 10 or 11 digits with area code, no formatting. */
|
|
112
|
+
phone: string;
|
|
113
|
+
/** 8 digits, no hyphen. Optional. */
|
|
114
|
+
zipCode?: string;
|
|
115
|
+
street?: string;
|
|
116
|
+
number?: string;
|
|
117
|
+
complement?: string;
|
|
118
|
+
neighborhood?: string;
|
|
119
|
+
city?: string;
|
|
120
|
+
/** 2-letter uppercase state code, e.g. `SP`. */
|
|
121
|
+
state?: string;
|
|
122
|
+
}
|
|
123
|
+
interface CardInfo {
|
|
124
|
+
/** 13–19 digits, no spaces or hyphens. */
|
|
125
|
+
cardNumber: string;
|
|
126
|
+
/** 3 or 4 digits. */
|
|
127
|
+
cvv: string;
|
|
128
|
+
/** `YYYY-MM`. */
|
|
129
|
+
expirationDate: string;
|
|
130
|
+
/** As printed on the card. */
|
|
131
|
+
holderName: string;
|
|
132
|
+
/** 1–12. */
|
|
133
|
+
installments: number;
|
|
134
|
+
}
|
|
135
|
+
interface CreateChargeParams {
|
|
136
|
+
/** Customer buying the product. */
|
|
137
|
+
customer: Customer;
|
|
138
|
+
/** UUID of the product being charged. */
|
|
139
|
+
productId: string;
|
|
140
|
+
/** Payment method. */
|
|
141
|
+
paymentMethod: PaymentMethod;
|
|
142
|
+
/** Required when `paymentMethod` is `credit_card`. */
|
|
143
|
+
cardInfo?: CardInfo;
|
|
144
|
+
/** Free-form metadata attached to the charge. */
|
|
145
|
+
additionalInfo?: string;
|
|
146
|
+
/** Original checkout link, if any. */
|
|
147
|
+
link?: string | null;
|
|
148
|
+
/** Associated affiliate ID, if any. */
|
|
149
|
+
affiliateId?: number | null;
|
|
150
|
+
/** Subscription price ID (`price_*`), for subscription charges only. */
|
|
151
|
+
priceId?: string | null;
|
|
152
|
+
/** Optional pre-created checkout session token. */
|
|
153
|
+
checkoutSessionToken?: string;
|
|
154
|
+
/**
|
|
155
|
+
* Idempotency key. If omitted, the SDK generates a UUIDv4.
|
|
156
|
+
* Keys are valid for 24h on the backend.
|
|
157
|
+
*/
|
|
158
|
+
idempotencyKey?: string;
|
|
159
|
+
}
|
|
160
|
+
interface Charge {
|
|
161
|
+
id: number;
|
|
162
|
+
status: ChargeStatus;
|
|
163
|
+
amount: number;
|
|
164
|
+
paymentMethodId: WirePaymentMethodId;
|
|
165
|
+
/** ISO-8601. */
|
|
166
|
+
date: string;
|
|
167
|
+
/** ISO-8601. */
|
|
168
|
+
deadline?: string;
|
|
169
|
+
/** Product this charge belongs to. */
|
|
170
|
+
product?: {
|
|
171
|
+
id: number;
|
|
172
|
+
uuid?: string;
|
|
173
|
+
name?: string;
|
|
174
|
+
};
|
|
175
|
+
[key: string]: unknown;
|
|
176
|
+
}
|
|
177
|
+
interface RefundChargeParams {
|
|
178
|
+
/** Partial refund in centavos. Omit for full refund. */
|
|
179
|
+
amount?: number;
|
|
180
|
+
/** Free-form reason stored on the refund. */
|
|
181
|
+
reason?: string;
|
|
182
|
+
idempotencyKey?: string;
|
|
183
|
+
}
|
|
184
|
+
interface MetaFeatures {
|
|
185
|
+
subscriptions: boolean;
|
|
186
|
+
checkout_sessions: boolean;
|
|
187
|
+
idempotency_keys: boolean;
|
|
188
|
+
test_mode: boolean;
|
|
189
|
+
webhooks: boolean;
|
|
190
|
+
}
|
|
191
|
+
interface MetaResponse {
|
|
192
|
+
name: string;
|
|
193
|
+
version: string;
|
|
194
|
+
environment: 'production' | 'staging' | 'development' | string;
|
|
195
|
+
api_version: string;
|
|
196
|
+
payment_methods: string[];
|
|
197
|
+
currencies: string[];
|
|
198
|
+
billing_intervals: string[];
|
|
199
|
+
webhook_events: string[];
|
|
200
|
+
features: MetaFeatures;
|
|
201
|
+
docs_url: string;
|
|
202
|
+
dashboard_url: string;
|
|
203
|
+
support_email: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Charges — the core of the Garu API.
|
|
208
|
+
*
|
|
209
|
+
* A charge represents a single payment attempt against a product. The SDK
|
|
210
|
+
* surfaces charges under `garu.charges` even though the backend route is
|
|
211
|
+
* `/api/transactions` — this matches Stripe convention and is the name every
|
|
212
|
+
* other Garu surface (MCP, CLI, docs) uses.
|
|
213
|
+
*/
|
|
214
|
+
declare class Charges {
|
|
215
|
+
private readonly http;
|
|
216
|
+
constructor(http: HttpClient);
|
|
217
|
+
/**
|
|
218
|
+
* Create a charge (PIX, credit card, or boleto).
|
|
219
|
+
*
|
|
220
|
+
* Automatically attaches an `X-Idempotency-Key` header — if you don't pass
|
|
221
|
+
* `idempotencyKey`, the SDK generates a UUIDv4. Safe to retry: the backend
|
|
222
|
+
* caches the first response for 24h.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* // PIX charge
|
|
226
|
+
* const charge = await garu.charges.create({
|
|
227
|
+
* productId: 'b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f',
|
|
228
|
+
* paymentMethod: 'pix',
|
|
229
|
+
* customer: {
|
|
230
|
+
* name: 'Maria Silva',
|
|
231
|
+
* email: 'maria@exemplo.com.br',
|
|
232
|
+
* document: '12345678909',
|
|
233
|
+
* phone: '11987654321'
|
|
234
|
+
* }
|
|
235
|
+
* });
|
|
236
|
+
* console.log(charge.id, charge.status);
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* // Credit card charge, 3 installments
|
|
240
|
+
* const charge = await garu.charges.create({
|
|
241
|
+
* productId: 'b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f',
|
|
242
|
+
* paymentMethod: 'credit_card',
|
|
243
|
+
* customer: { name: 'Maria Silva', email: 'maria@exemplo.com.br', document: '12345678909', phone: '11987654321' },
|
|
244
|
+
* cardInfo: {
|
|
245
|
+
* cardNumber: '4111111111111111',
|
|
246
|
+
* cvv: '123',
|
|
247
|
+
* expirationDate: '2030-12',
|
|
248
|
+
* holderName: 'MARIA SILVA',
|
|
249
|
+
* installments: 3
|
|
250
|
+
* }
|
|
251
|
+
* });
|
|
252
|
+
*/
|
|
253
|
+
create(params: CreateChargeParams): Promise<Charge>;
|
|
254
|
+
/**
|
|
255
|
+
* Fetch a single charge by numeric ID.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* const charge = await garu.charges.get(4472);
|
|
259
|
+
* if (charge.status === 'paid') { ... }
|
|
260
|
+
*/
|
|
261
|
+
get(id: number): Promise<Charge>;
|
|
262
|
+
/**
|
|
263
|
+
* Refund a charge — fully, or partially by passing `amount` in centavos.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* // Full refund
|
|
267
|
+
* await garu.charges.refund(4472);
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* // Partial refund of R$ 10,00
|
|
271
|
+
* await garu.charges.refund(4472, { amount: 1000, reason: 'customer_request' });
|
|
272
|
+
*/
|
|
273
|
+
refund(id: number, params?: RefundChargeParams): Promise<Charge>;
|
|
274
|
+
private buildCreateBody;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Meta — capability introspection.
|
|
279
|
+
*
|
|
280
|
+
* Unauthenticated. Used by `garu doctor`, MCP tool `doctor`, and by SDK
|
|
281
|
+
* consumers that want to know which payment methods and webhook events are
|
|
282
|
+
* currently supported.
|
|
283
|
+
*/
|
|
284
|
+
declare class Meta {
|
|
285
|
+
private readonly http;
|
|
286
|
+
constructor(http: HttpClient);
|
|
287
|
+
/**
|
|
288
|
+
* Fetch the API's current capability payload.
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* const meta = await garu.meta.get();
|
|
292
|
+
* console.log(meta.version, meta.payment_methods);
|
|
293
|
+
* if (meta.features.subscriptions) { ... }
|
|
294
|
+
*/
|
|
295
|
+
get(): Promise<MetaResponse>;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
interface GaruOptions {
|
|
299
|
+
/**
|
|
300
|
+
* Your Garu API key. `sk_live_…` for production, `sk_test_…` for test mode.
|
|
301
|
+
* Optional — public endpoints (`meta.get`, public charge creation) work without one.
|
|
302
|
+
*/
|
|
303
|
+
apiKey?: string;
|
|
304
|
+
/** Override the API base URL. Default: `https://garu.com.br/api`. */
|
|
305
|
+
baseUrl?: string;
|
|
306
|
+
/** Per-request timeout in ms. Default: 30000. */
|
|
307
|
+
timeoutMs?: number;
|
|
308
|
+
/** Max retries on retryable errors (connection, 408, 429, 5xx). Default: 2. */
|
|
309
|
+
maxRetries?: number;
|
|
310
|
+
/** Injectable for tests. Defaults to `globalThis.fetch`. */
|
|
311
|
+
fetch?: typeof fetch;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* The Garu SDK client.
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* import { Garu } from '@garuhq/node';
|
|
318
|
+
*
|
|
319
|
+
* const garu = new Garu({ apiKey: process.env.GARU_API_KEY });
|
|
320
|
+
*
|
|
321
|
+
* const charge = await garu.charges.create({
|
|
322
|
+
* productId: 'b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f',
|
|
323
|
+
* paymentMethod: 'pix',
|
|
324
|
+
* customer: {
|
|
325
|
+
* name: 'Maria Silva',
|
|
326
|
+
* email: 'maria@exemplo.com.br',
|
|
327
|
+
* document: '12345678909',
|
|
328
|
+
* phone: '11987654321'
|
|
329
|
+
* }
|
|
330
|
+
* });
|
|
331
|
+
*/
|
|
332
|
+
declare class Garu {
|
|
333
|
+
readonly charges: Charges;
|
|
334
|
+
readonly meta: Meta;
|
|
335
|
+
/**
|
|
336
|
+
* Webhook helpers. Available both as an instance member and as a static —
|
|
337
|
+
* `Garu.webhooks.verify(...)` works without constructing a client.
|
|
338
|
+
*/
|
|
339
|
+
static readonly webhooks: {
|
|
340
|
+
verify(params: VerifyWebhookParams): VerifiedWebhook;
|
|
341
|
+
};
|
|
342
|
+
readonly webhooks: {
|
|
343
|
+
verify(params: VerifyWebhookParams): VerifiedWebhook;
|
|
344
|
+
};
|
|
345
|
+
constructor(options?: GaruOptions);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Error hierarchy for the Garu SDK.
|
|
350
|
+
*
|
|
351
|
+
* Every error has a stable `code` string so agents and typed clients can switch on it
|
|
352
|
+
* without parsing messages. Non-2xx API responses are mapped to the most specific
|
|
353
|
+
* subclass of `GaruAPIError` by {@link mapApiError}.
|
|
354
|
+
*/
|
|
355
|
+
type GaruErrorCode = 'authentication_error' | 'permission_error' | 'not_found' | 'validation_error' | 'rate_limited' | 'server_error' | 'api_error' | 'connection_error' | 'signature_verification_failed';
|
|
356
|
+
declare class GaruError extends Error {
|
|
357
|
+
readonly code: GaruErrorCode;
|
|
358
|
+
constructor(code: GaruErrorCode, message: string);
|
|
359
|
+
}
|
|
360
|
+
declare class GaruConnectionError extends GaruError {
|
|
361
|
+
readonly connectionCause: unknown;
|
|
362
|
+
constructor(message: string, connectionCause?: unknown);
|
|
363
|
+
}
|
|
364
|
+
declare class GaruSignatureVerificationError extends GaruError {
|
|
365
|
+
constructor(message: string);
|
|
366
|
+
}
|
|
367
|
+
declare class GaruAPIError extends GaruError {
|
|
368
|
+
readonly status: number;
|
|
369
|
+
readonly requestId: string | null;
|
|
370
|
+
readonly body: unknown;
|
|
371
|
+
constructor(code: GaruErrorCode, message: string, status: number, requestId: string | null, body: unknown);
|
|
372
|
+
}
|
|
373
|
+
declare class GaruAuthenticationError extends GaruAPIError {
|
|
374
|
+
constructor(message: string, status: number, requestId: string | null, body: unknown);
|
|
375
|
+
}
|
|
376
|
+
declare class GaruPermissionError extends GaruAPIError {
|
|
377
|
+
constructor(message: string, status: number, requestId: string | null, body: unknown);
|
|
378
|
+
}
|
|
379
|
+
declare class GaruNotFoundError extends GaruAPIError {
|
|
380
|
+
constructor(message: string, status: number, requestId: string | null, body: unknown);
|
|
381
|
+
}
|
|
382
|
+
declare class GaruValidationError extends GaruAPIError {
|
|
383
|
+
constructor(message: string, status: number, requestId: string | null, body: unknown);
|
|
384
|
+
}
|
|
385
|
+
declare class GaruRateLimitError extends GaruAPIError {
|
|
386
|
+
readonly retryAfterSec: number | null;
|
|
387
|
+
constructor(message: string, status: number, requestId: string | null, body: unknown, retryAfterSec: number | null);
|
|
388
|
+
}
|
|
389
|
+
declare class GaruServerError extends GaruAPIError {
|
|
390
|
+
constructor(message: string, status: number, requestId: string | null, body: unknown);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export { type CardInfo, type Charge, type ChargeStatus, type CreateChargeParams, type Customer, Garu, GaruAPIError, GaruAuthenticationError, GaruConnectionError, GaruError, type GaruErrorCode, GaruNotFoundError, type GaruOptions, GaruPermissionError, GaruRateLimitError, GaruServerError, GaruSignatureVerificationError, GaruValidationError, type MetaFeatures, type MetaResponse, type PaymentMethod, type RefundChargeParams, type VerifiedWebhook, type VerifyWebhookParams, type WirePaymentMethodId, webhooks };
|