@garuhq/node
v0.4.0
Published
Official Node.js / TypeScript SDK for the Garu payment gateway.
Downloads
742
Maintainers
Readme
@garuhq/node
The official Node.js / TypeScript SDK for the Garu payment gateway.
Brazilian payments (PIX, credit card, boleto) in a few lines of code.
- Typed end-to-end — wire types generated from the backend's OpenAPI spec; the SDK can never drift from the API.
- Tiny footprint — one runtime dependency (
openapi-fetch, ~4 KB). Nativefetch, nativecrypto. - Safe to retry — automatic idempotency keys on every mutation, exponential backoff with full jitter, honors
Retry-After. - LLM-friendly — every public method has JSDoc
@exampleblocks for agent autocomplete. - ESM + CJS dual build.
Install
npm install @garuhq/node
# or
pnpm add @garuhq/node
# or
yarn add @garuhq/nodeQuickstart
import { Garu } from '@garuhq/node';
const garu = new Garu({ apiKey: process.env.GARU_API_KEY });
// Create a PIX charge
const charge = await garu.charges.create({
productId: 'b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f',
paymentMethod: 'pix',
customer: {
name: 'Maria Silva',
email: '[email protected]',
document: '12345678909', // CPF, digits only
phone: '11987654321',
},
});
console.log(charge.id, charge.status);Setup
Get your API key from the Garu dashboard → API Keys.
const garu = new Garu({ apiKey: process.env.GARU_API_KEY });[!NOTE] Use
sk_test_…for test mode andsk_live_…for production. Public endpoints likemeta.getwork without a key.
Configuration
const garu = new Garu({
apiKey: process.env.GARU_API_KEY,
timeoutMs: 30_000, // default
maxRetries: 2, // default (3 total attempts)
});Charges
| Method | Description |
| --------------------- | -------------------------------------------- |
| create(params) | Create a PIX, credit-card, or boleto charge. |
| list(params?) | List charges with pagination and filters. |
| get(id) | Fetch a single charge by ID. |
| refund(id, params?) | Refund a charge fully or partially. |
Create a PIX charge
const charge = await garu.charges.create({
productId: 'b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f',
paymentMethod: 'pix',
customer: {
name: 'Maria Silva',
email: '[email protected]',
document: '12345678909',
phone: '11987654321',
},
});Create a credit card charge
const charge = await garu.charges.create({
productId: 'b3f2c1e8-6e4a-4b9f-9d1c-2a1f6c3d4e5f',
paymentMethod: 'credit_card',
card: {
number: '4111111111111111',
holderName: 'MARIA SILVA',
expirationMonth: '12',
expirationYear: '2028',
cvv: '123',
},
customer: {
name: 'Maria Silva',
email: '[email protected]',
document: '12345678909',
phone: '11987654321',
},
});List charges
const { data, meta } = await garu.charges.list({ limit: 10 });Refund a charge
await garu.charges.refund(4472, { amount: 1000 }); // partial refund (R$10.00)[!TIP] Every mutation automatically attaches an
X-Idempotency-Keyheader (UUIDv4) unless you provide one viaparams.idempotencyKey. Safe to retry — the backend caches the first response for 24h.
Customers
| Method | Description |
| ------------------------- | --------------------------------------------- |
| create(params) | Create a new customer. |
| list(params?) | List customers with pagination and search. |
| get(id) | Fetch a single customer by ID. |
| update(id, params) | Update a customer's profile. |
| delete(id) | Delete a customer. |
const customer = await garu.customers.create({
name: 'Maria Silva',
email: '[email protected]',
document: '12345678909',
phone: '11987654321',
personType: 'fisica',
});
const { data, meta } = await garu.customers.list({ search: 'maria', limit: 10 });Meta
Discover available payment methods and webhook events. No authentication required.
const meta = await garu.meta.get();
console.log(meta.version, meta.payment_methods, meta.webhook_events);Webhooks
Verify incoming webhooks with HMAC-SHA256 and constant-time comparison.
import express from 'express';
import { Garu, GaruSignatureVerificationError } from '@garuhq/node';
const app = express();
app.post('/webhooks/garu', express.raw({ type: 'application/json' }), (req, res) => {
try {
const { event } = Garu.webhooks.verify({
payload: req.body, // raw Buffer — do NOT re-serialize parsed JSON
signature: req.header('x-garu-signature') ?? '',
secret: process.env.GARU_WEBHOOK_SECRET!,
});
console.log('Received', event);
res.sendStatus(200);
} catch (err) {
if (err instanceof GaruSignatureVerificationError) return res.sendStatus(400);
throw err;
}
});[!IMPORTANT] Always pass the raw request body to
verify(). Parsing and re-serializing JSON will break the signature check.
Error handling
Every error extends GaruError. API errors include status, requestId, and body.
import {
GaruAPIError,
GaruNotFoundError,
GaruRateLimitError,
GaruValidationError,
} from '@garuhq/node';
try {
await garu.charges.refund(4472, { amount: 1000 });
} catch (err) {
if (err instanceof GaruNotFoundError) {
/* 404 */
}
if (err instanceof GaruValidationError) {
/* 400 / 422 */
}
if (err instanceof GaruRateLimitError) {
console.log('Retry in', err.retryAfterSec, 'seconds');
}
if (err instanceof GaruAPIError) {
console.log(err.status, err.requestId, err.body);
}
}| Error class | HTTP status |
| ----------------------------------- | ------------------ |
| GaruAuthenticationError | 401 |
| GaruPermissionError | 403 |
| GaruNotFoundError | 404 |
| GaruValidationError | 400 / 422 |
| GaruRateLimitError | 429 |
| GaruServerError | 5xx |
| GaruConnectionError | Network failure |
| GaruSignatureVerificationError | Webhook mismatch |
Retries
The SDK retries automatically on connection errors, 408, 429, and 5xx responses. Exponential backoff with full jitter. Honors Retry-After. Never retries 4xx validation errors.
TypeScript
Ships with full .d.ts and strict types. All public types are re-exported from the root:
import type {
Charge,
ChargeStatus,
CreateChargeParams,
Customer,
CardInfo,
PaymentMethod,
MetaResponse,
} from '@garuhq/node';Security
To report a vulnerability, do not open a public issue. See SECURITY.md for responsible disclosure instructions.
License
MIT — see LICENSE for details.
