@rios-norte/sdk
v0.1.1
Published
SDK centralizado do ecossistema Rios Norte
Readme
@rios-norte/sdk
SDK oficial do ecossistema Rios Norte. Centraliza a comunicação entre backends de SaaS satélite e o Core API — autenticação JWT, multi-tenancy por workspace, pagamentos PIX, saques e webhooks.
Compatível exclusivamente com NestJS 10+.
Instalação
npm install @rios-norte/sdkPeer dependencies (já devem estar no seu projeto NestJS)
npm install @nestjs/common @nestjs/core reflect-metadataConfiguração
Importe RiosSdkModule uma única vez no AppModule:
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { RiosSdkModule } from '@rios-norte/sdk';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
RiosSdkModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
coreUrl: config.getOrThrow('CORE_URL'),
saasId: config.getOrThrow('SAAS_ID'),
sessionSecret: config.getOrThrow('SESSION_SECRET'),
webhookSecret: config.getOrThrow('WEBHOOK_SECRET'),
}),
}),
],
})
export class AppModule {}Variáveis de ambiente necessárias
CORE_URL=https://core.rios.com.br
SAAS_ID=meu-saas
SESSION_SECRET=segredo-compartilhado-com-o-core
WEBHOOK_SECRET=segredo-hmac-para-webhooksO que é registrado automaticamente ao importar o módulo:
RiosAuthGuard— guard global que valida o Session JWT em todas as rotasRiosPlanGuard— guard global que bloqueia planos expirados/canceladosRiosCoreClient— serviço injetável para chamar o Core APIWebhookValidator— serviço injetável para validar webhooks recebidos
Decoradores
import {
Public,
RequiresPlan,
CurrentSession,
CurrentUserId,
CurrentWorkspaceId,
CurrentPlanId,
} from '@rios-norte/sdk';| Decorador | Onde usar | O que faz |
|---|---|---|
| @Public() | Controller / método | Pula o RiosAuthGuard (rotas públicas) |
| @RequiresPlan('id1', 'id2') | Controller / método | Retorna 402 se o plano ativo não estiver na lista |
| @CurrentSession() | Parâmetro | Injeta o SessionPayload completo do JWT |
| @CurrentUserId() | Parâmetro | Injeta SessionPayload.sub (user id) |
| @CurrentWorkspaceId() | Parâmetro | Injeta SessionPayload.workspaceId |
| @CurrentPlanId() | Parâmetro | Injeta SessionPayload.planId |
@Controller('dashboard')
export class DashboardController {
// Rota protegida (padrão) — exige Session JWT válido
@Get()
get(
@CurrentUserId() userId: string,
@CurrentWorkspaceId() workspaceId: string,
) {
return { userId, workspaceId };
}
// Somente planos pro e enterprise
@Get('premium')
@RequiresPlan('plan_pro', 'plan_enterprise')
getPremium(@CurrentSession() session: SessionPayload) {
return session;
}
// Rota pública — sem autenticação
@Get('status')
@Public()
getStatus() {
return { ok: true };
}
}RiosCoreClient
Serviço injetável que agrupa todos os clientes de comunicação com o Core API.
import { Injectable } from '@nestjs/common';
import { RiosCoreClient } from '@rios-norte/sdk';
@Injectable()
export class AlgumService {
constructor(private readonly core: RiosCoreClient) {}
}core.auth
// Login — valida com Zod, seta Bearer automaticamente
const { user, accessToken } = await core.auth.login({ email, password });
// Registro
const { user } = await core.auth.register({ name, email, password });
// Usuário autenticado atual
const user = await core.auth.me();
// Logout — limpa o token do cliente HTTP
await core.auth.logout();core.workspaces
// Lista workspaces do usuário autenticado (cache 30s)
const workspaces = await core.workspaces.list();
// Busca um workspace específico (cache 30s)
const workspace = await core.workspaces.get(workspaceId);
// Seleciona workspace → troca Bearer para Fat Token com contexto completo
// A partir daqui, workspaceId e planId viajam em todas as requisições
const { accessToken } = await core.workspaces.select(workspaceId);
// Limpa o cache de workspaces/sessões
core.workspaces.invalidateCache();core.billing
// Status do plano do workspace (cache 60s)
const billing = await core.billing.getStatus(workspaceId);
// billing.planId, billing.status, billing.trialEndsAt, billing.currentPeriodEnd
// Invalida cache (use após receber webhook de plan.changed)
core.billing.invalidate(workspaceId);core.payments
// Criar cobrança PIX
const pix = await core.payments.createPixPayment({
amount: 150.00,
customer: { name, email, cpf },
});
// pix.pixCode, pix.pixQrCode, pix.expiresAt, pix.externalId
// Verificar status de um pagamento
const status = await core.payments.checkStatus(paymentId);
// Criar/listar clientes de pagamento
const customer = await core.payments.createCustomer({ name, email, cpf, cellphone });
const customers = await core.payments.listCustomers();
// Pagamento via gateway específico
const result = await core.payments.createGatewayPayment('asaas', dto);
// Transações e saldo de um cliente em um gateway
const transactions = await core.payments.getTransactions(clientId);
const balance = await core.payments.getBalance(clientId);
// Gateways disponíveis
const gateways = await core.payments.listAvailableGateways();
// CRUD de configuração de gateway por cliente
const config = await core.payments.createGatewayConfig({ clientId, gatewaySlug, credentials });
const configs = await core.payments.findGatewayConfigByClient(clientId);
await core.payments.updateGatewayConfig(id, dto);
await core.payments.deleteGatewayConfig(id);
await core.payments.testGatewayConnection(id);
await core.payments.setPrimaryGatewayConfig(id);
// Configuração global de webhooks de gateways
const webhooks = await core.payments.getAllWebhookConfigs();
const webhook = await core.payments.getWebhookConfig('asaas');
await core.payments.createOrUpdateWebhookConfig({ gatewaySlug: 'asaas', settings: {} });
const urls = await core.payments.generateWebhookUrls('asaas');
await core.payments.deactivateWebhook('asaas');
await core.payments.deleteWebhook('asaas');core.payouts
// Listar saques com filtros opcionais
const payouts = await core.payouts.findAll({ status: 'pending', clientId });
// Resumo de saques (totais agregados)
const summary = await core.payouts.summary(clientId);
// Solicitar novo saque
const payout = await core.payouts.create({ amount, clientId, description });core.workspaceGateway
// CRUD de configurações de gateway do workspace
const configs = await core.workspaceGateway.findAll();
const mode = await core.workspaceGateway.getMode(); // 'sandbox' | 'live' | 'split'
const config = await core.workspaceGateway.findOne(id);
await core.workspaceGateway.create(dto);
await core.workspaceGateway.update(id, dto);
await core.workspaceGateway.remove(id);
// Controles
await core.workspaceGateway.setPrimary(id);
await core.workspaceGateway.toggleActive(id);
await core.workspaceGateway.registerWebhook(id);
// Notificações
const notifications = await core.workspaceGateway.getNotifications(id);
await core.workspaceGateway.updateNotifications(id, notifications);
// Processar saques pendentes
await core.workspaceGateway.processPendingPayouts();Recebendo Webhooks do Core
// webhook.controller.ts
import { Controller, Post, Req, Headers, RawBodyRequest } from '@nestjs/common';
import { Request } from 'express';
import { WebhookValidator, Public } from '@rios-norte/sdk';
@Controller('webhooks')
export class WebhookController {
constructor(private readonly validator: WebhookValidator) {}
@Post('core')
@Public()
async handle(
@Req() req: RawBodyRequest<Request>,
@Headers('x-rios-signature') signature: string,
) {
// Lança WebhookSignatureError (401) se a assinatura for inválida
await this.validator.verify(req.rawBody, signature);
// Faz parse e valida com Zod — retorna WebhookPayload tipado
const event = await this.validator.parse(req.rawBody);
switch (event.type) {
case 'user.updated': /* ... */ break;
case 'user.deleted': /* ... */ break;
case 'workspace.updated': /* ... */ break;
case 'workspace.deleted': /* ... */ break;
case 'plan.changed':
await this.core.billing.invalidate(event.payload.workspaceId);
break;
}
}
}Atenção: para acessar
req.rawBody, configure o NestJS para preservar o body bruto:// main.ts app.use(express.json({ verify: (req: any, _res, buf) => { req.rawBody = buf; }, }));
Sincronização automática (SyncService)
Para espelhar dados do Core no banco local da SaaS:
// sync.module.ts
import { Module } from '@nestjs/common';
import { SyncService } from '@rios-norte/sdk';
import { MySyncRepository } from './my-sync.repository';
@Module({
providers: [
SyncService,
{ provide: 'SyncRepository', useClass: MySyncRepository },
],
})
export class SyncModule {}Implemente a interface SyncRepository:
import type { SyncRepository, RiosUser, RiosWorkspace } from '@rios-norte/sdk';
@Injectable()
export class MySyncRepository implements SyncRepository {
async upsertUser(user: RiosUser): Promise<void> {
await this.db.user.upsert({ where: { id: user.id }, ... });
}
async upsertWorkspace(workspace: RiosWorkspace): Promise<void> {
await this.db.workspace.upsert({ where: { id: workspace.id }, ... });
}
// Opcionais:
async deleteUser(userId: string): Promise<void> { ... }
async deleteWorkspace(workspaceId: string): Promise<void> { ... }
}Tratamento de erros
Todos os erros do SDK estendem RiosError e possuem code, statusCode e opcionalmente details:
import {
RiosError,
UnauthorizedError, // 401 — JWT inválido/ausente
ForbiddenError, // 403 — sem permissão
WorkspaceNotFoundError, // 404
PlanExpiredError, // 402 — plano expirado
NetworkError, // 503 — falha de rede com o Core
ValidationError, // 422 — dados inválidos (Zod)
WebhookSignatureError, // 401 — HMAC inválido
} from '@rios-norte/sdk';
try {
await core.auth.login({ email, password });
} catch (err) {
if (err instanceof ValidationError) {
console.log(err.details); // erros do Zod
}
if (err instanceof RiosError) {
console.log(err.code, err.statusCode, err.message);
}
}Fluxo completo de autenticação
1. SaaS recebe login do usuário final
│
2. core.auth.login(email, password)
→ Core valida credenciais
→ SDK seta Bearer token automaticamente
│
3. core.workspaces.list()
→ Retorna workspaces do usuário (cache 30s)
│
4. core.workspaces.select(workspaceId)
→ Core gera Fat Token (JWT com workspaceId + planId)
→ SDK troca Bearer para Fat Token automaticamente
│
5. Todas as requisições seguintes vão autenticadas com contexto do workspace
│
6. Nas rotas recebidas pelo SaaS:
→ RiosAuthGuard valida o Session JWT automaticamente
→ @CurrentUserId(), @CurrentWorkspaceId(), etc. extraem dados do JWTChangelog
0.1.0
- Lançamento inicial
- Módulos: auth, workspaces, billing, payments, payouts, workspaceGateway
- Guards globais: RiosAuthGuard, RiosPlanGuard
- Decoradores: @Public, @RequiresPlan, @CurrentSession, @CurrentUserId, @CurrentWorkspaceId, @CurrentPlanId
- WebhookValidator (HMAC-SHA256 timing-safe)
- SyncService + SyncRepository interface
