@criapix/saas-assinaturas-client
Version:
SDK JavaScript/TypeScript para o AssinaturasService - Sistema de gestão de assinaturas SaaS com processamento de pagamentos de faturas (cartão, PIX, débito), gerenciamento de métodos de pagamento, pagamentos recorrentes e análise de falhas de pagamento
597 lines (529 loc) • 19.9 kB
text/typescript
import axios, { AxiosInstance, AxiosError } from 'axios';
import { normalizeCardExpiryDate } from '../utils/cardExpiryDateValidator';
import type {
PaginatedResponse,
CreateCustomerRequest,
CustomerResponse,
CreateOneTimePaymentRequest,
CreateRecurringPaymentRequest,
ProcessPaymentRequest,
CancelPaymentRequest,
PaymentListQuery,
PaymentResponse,
CreatePaymentAccountRequest,
PaymentAccountResponse,
SubscriptionStatusResponse,
SimpleActiveStatusResponse,
PaymentMethodInfoResponse,
ProcessInvoicePaymentResponse,
ProcessFirstPaymentRequest,
ProcessRecurringPaymentRequest,
ProcessPixPaymentRequest,
ProcessDebitPaymentRequest,
SubscriptionPlanResponse,
CreateSubscriptionPlanRequest,
UpdateSubscriptionPlanRequest,
SubscriptionPlanFilterRequest,
SubscriptionResponse,
CreateSubscriptionRequest,
UpdateSubscriptionRequest,
UpdatePaymentMethodRequest,
ActivateSubscriptionWithPaymentRequest,
SubscriptionActivationResponse,
SubscriptionFilterRequest,
InvoiceResponse,
InvoiceStatisticsResponse,
GenerateInvoiceRequest,
InvoiceFilterRequest,
UpdateInvoiceStatusRequest,
GenerateBatchInvoicesRequest,
BatchInvoiceResponse,
SubscriptionAccessResponse,
CheckAccessBatchRequest,
CustomerAccessBatchItem,
} from '../types';
export class AssinaturasServiceClient {
private client: AxiosInstance;
private authToken: string | null = null;
constructor(baseURL: string) {
this.client = axios.create({
baseURL,
timeout: 30000,
headers: {
'Content-Type': 'application/json',
},
});
// Interceptor para adicionar token de autenticação
this.client.interceptors.request.use((config) => {
if (this.authToken) {
config.headers.Authorization = `Bearer ${this.authToken}`;
}
return config;
});
// Interceptor para tratamento de erros
this.client.interceptors.response.use(
(response) => response,
(error: AxiosError) => {
if (error.response?.status === 404) {
// Retornar null para 404 em alguns casos específicos
return Promise.resolve({ data: null });
}
return Promise.reject(error);
}
);
}
/**
* Define o token de autenticação JWT
*/
public setAuthorizationToken(token: string): void {
this.authToken = token;
}
// ==================== Customers ====================
/**
* Cria um novo cliente
*/
async createCustomer(request: CreateCustomerRequest): Promise<CustomerResponse> {
const { data } = await this.client.post<CustomerResponse>('/Customers', request);
return data;
}
/**
* Busca um cliente por ID
* @returns O cliente encontrado ou null se não existir
*/
async getCustomer(id: string): Promise<CustomerResponse | null> {
const { data } = await this.client.get<CustomerResponse>(`/Customers/${id}`);
return data;
}
// ==================== Subscription Plans ====================
/**
* Cria um novo plano de assinatura
*/
async createSubscriptionPlan(request: CreateSubscriptionPlanRequest): Promise<SubscriptionPlanResponse> {
const { data } = await this.client.post<SubscriptionPlanResponse>('/SubscriptionPlans', request);
return data;
}
/**
* Busca um plano de assinatura por ID
* @returns O plano encontrado ou null se não existir
*/
async getSubscriptionPlan(id: string): Promise<SubscriptionPlanResponse | null> {
const { data } = await this.client.get<SubscriptionPlanResponse>(`/SubscriptionPlans/${id}`);
return data;
}
/**
* Lista planos de assinatura com filtros opcionais
*/
async listSubscriptionPlans(
filter?: SubscriptionPlanFilterRequest
): Promise<PaginatedResponse<SubscriptionPlanResponse>> {
const params: Record<string, string | number | boolean> = {
page: filter?.page ?? 1,
pageSize: filter?.pageSize ?? 10,
};
if (filter?.name) params.name = filter.name;
if (filter?.isActive !== undefined) params.isActive = filter.isActive;
if (filter?.isDefault !== undefined) params.isDefault = filter.isDefault;
const { data } = await this.client.get<PaginatedResponse<SubscriptionPlanResponse>>(
'/SubscriptionPlans',
{ params }
);
return data;
}
/**
* Atualiza um plano de assinatura existente
*/
async updateSubscriptionPlan(
id: string,
request: UpdateSubscriptionPlanRequest
): Promise<SubscriptionPlanResponse> {
const { data } = await this.client.put<SubscriptionPlanResponse>(`/SubscriptionPlans/${id}`, request);
return data;
}
/**
* Deleta um plano de assinatura
*/
async deleteSubscriptionPlan(id: string): Promise<void> {
await this.client.delete(`/SubscriptionPlans/${id}`);
}
// ==================== Subscriptions ====================
/**
* Cria uma nova assinatura
*/
async createSubscription(request: CreateSubscriptionRequest): Promise<SubscriptionResponse> {
const { data } = await this.client.post<SubscriptionResponse>('/Subscriptions', request);
return data;
}
/**
* Busca uma assinatura por ID
* @returns A assinatura encontrada ou null se não existir
*/
async getSubscription(id: string): Promise<SubscriptionResponse | null> {
const { data } = await this.client.get<SubscriptionResponse>(`/Subscriptions/${id}`);
return data;
}
/**
* Lista assinaturas com filtros opcionais
*/
async listSubscriptions(filter?: SubscriptionFilterRequest): Promise<PaginatedResponse<SubscriptionResponse>> {
const params: Record<string, string | number> = {
page: filter?.page ?? 1,
pageSize: filter?.pageSize ?? 10,
};
if (filter?.customerId) params.customerId = filter.customerId;
if (filter?.subscriptionPlanId) params.subscriptionPlanId = filter.subscriptionPlanId;
if (filter?.status !== undefined) params.status = filter.status;
const { data } = await this.client.get<PaginatedResponse<SubscriptionResponse>>('/Subscriptions', {
params,
});
return data;
}
/**
* Atualiza uma assinatura existente
*/
async updateSubscription(id: string, request: UpdateSubscriptionRequest): Promise<SubscriptionResponse> {
const { data } = await this.client.put<SubscriptionResponse>(`/Subscriptions/${id}`, request);
return data;
}
/**
* Cancela uma assinatura
*/
async cancelSubscription(id: string, cancellationReason?: string): Promise<SubscriptionResponse> {
const { data } = await this.client.post<SubscriptionResponse>(`/Subscriptions/${id}/cancel`, {
cancellationReason,
});
return data;
}
/**
* Ativa uma assinatura com primeiro pagamento após período de trial
* Este endpoint encontra a fatura pendente atual e processa o pagamento
* @param subscriptionId - ID da assinatura
* @param request - Requisição com detalhes do cartão para ativação
* @returns Resultado da ativação da assinatura
*/
async activateSubscriptionWithPayment(
subscriptionId: string,
request: ActivateSubscriptionWithPaymentRequest
): Promise<SubscriptionActivationResponse> {
const { data } = await this.client.post<SubscriptionActivationResponse>(
`/Subscriptions/${subscriptionId}/activate-with-payment`,
request
);
return data;
}
/**
* Atualiza o método de pagamento de uma assinatura
* Pode opcionalmente processar um pagamento de fatura pendente ao atualizar o método
* @param subscriptionId - ID da assinatura
* @param request - Requisição com novos detalhes do cartão
* @returns Resultado do processamento do pagamento (se fatura fornecida) ou confirmação de sucesso
*/
async updatePaymentMethod(
subscriptionId: string,
request: UpdatePaymentMethodRequest
): Promise<ProcessInvoicePaymentResponse> {
// Normalize cardExpiryDate to ISO 8601 format before sending to API
const normalizedRequest = {
...request,
cardExpiryDate: normalizeCardExpiryDate(request.cardExpiryDate),
};
const { data } = await this.client.put<ProcessInvoicePaymentResponse>(
`/Subscriptions/${subscriptionId}/payment-method`,
normalizedRequest
);
return data;
}
// ==================== Invoices ====================
/**
* Busca uma fatura por ID
* @returns A fatura encontrada ou null se não existir
*/
async getInvoice(id: string): Promise<InvoiceResponse | null> {
const { data } = await this.client.get<InvoiceResponse>(`/Invoices/${id}`);
return data;
}
/**
* Lista faturas com filtros opcionais
*/
async listInvoices(filter?: InvoiceFilterRequest): Promise<PaginatedResponse<InvoiceResponse>> {
const params: Record<string, string | number> = {
page: filter?.page ?? 1,
pageSize: filter?.pageSize ?? 10,
};
if (filter?.subscriptionId) params.subscriptionId = filter.subscriptionId;
if (filter?.status !== undefined) params.status = filter.status;
if (filter?.dueDateFrom) params.dueDateFrom = filter.dueDateFrom;
if (filter?.dueDateTo) params.dueDateTo = filter.dueDateTo;
const { data } = await this.client.get<PaginatedResponse<InvoiceResponse>>('/Invoices', { params });
return data;
}
/**
* Gera uma nova fatura
*/
async generateInvoice(request: GenerateInvoiceRequest): Promise<InvoiceResponse> {
const { data } = await this.client.post<InvoiceResponse>('/Invoices/generate', request);
return data;
}
/**
* Atualiza o status de uma fatura
*/
async updateInvoiceStatus(id: string, request: UpdateInvoiceStatusRequest): Promise<InvoiceResponse> {
const { data } = await this.client.put<InvoiceResponse>(`/Invoices/${id}/status`, request);
return data;
}
/**
* Gera faturas em lote para todas as assinaturas ativas
*/
async generateBatchInvoices(request: GenerateBatchInvoicesRequest): Promise<BatchInvoiceResponse> {
const { data } = await this.client.post<BatchInvoiceResponse>('/Invoices/generate-batch', request);
return data;
}
/**
* Obtém estatísticas agregadas de faturas
* @param startDate - Data inicial do filtro (opcional)
* @param endDate - Data final do filtro (opcional)
* @param condominiumId - ID do condomínio para filtrar (opcional)
* @returns Estatísticas das faturas
*/
async getInvoiceStatistics(
startDate?: string,
endDate?: string,
condominiumId?: string
): Promise<InvoiceStatisticsResponse> {
const params: Record<string, string> = {};
if (startDate) params.startDate = startDate;
if (endDate) params.endDate = endDate;
if (condominiumId) params.condominiumId = condominiumId;
const { data } = await this.client.get<InvoiceStatisticsResponse>('/Invoices/statistics', { params });
return data;
}
// ==================== Invoice Payments ====================
/**
* Processa o primeiro pagamento após período de trial com tokenização de cartão
* Armazena o método de pagamento para pagamentos recorrentes futuros
* @param invoiceId - ID da fatura
* @param request - Requisição com token do cartão e informações do cliente
* @returns Resultado do processamento do pagamento
*/
async processFirstPayment(
invoiceId: string,
request: ProcessFirstPaymentRequest
): Promise<ProcessInvoicePaymentResponse> {
// Normalize cardExpiryDate to ISO 8601 format before sending to API
const normalizedRequest = {
...request,
cardExpiryDate: normalizeCardExpiryDate(request.cardExpiryDate),
};
const { data } = await this.client.post<ProcessInvoicePaymentResponse>(
`/Invoices/${invoiceId}/process-first-payment`,
normalizedRequest
);
return data;
}
/**
* Processa pagamento recorrente mensal usando método de pagamento armazenado
* @param invoiceId - ID da fatura
* @param request - Requisição de pagamento recorrente (vazio, apenas invoiceId na URL)
* @returns Resultado do processamento do pagamento
*/
async processRecurringPayment(
invoiceId: string,
request: ProcessRecurringPaymentRequest = {}
): Promise<ProcessInvoicePaymentResponse> {
const { data } = await this.client.post<ProcessInvoicePaymentResponse>(
`/Invoices/${invoiceId}/process-recurring-payment`,
request
);
return data;
}
/**
* Processa pagamento PIX
* Gera QR Code para pagamento instantâneo
* @param invoiceId - ID da fatura
* @param request - Requisição de pagamento PIX com informações do cliente
* @returns Resultado do processamento com QR Code
*/
async processPixPayment(
invoiceId: string,
request: ProcessPixPaymentRequest
): Promise<ProcessInvoicePaymentResponse> {
const { data } = await this.client.post<ProcessInvoicePaymentResponse>(
`/Invoices/${invoiceId}/process-pix-payment`,
request
);
return data;
}
/**
* Processa pagamento com cartão de débito
* Pagamento imediato, sem capacidade de recorrência
* @param invoiceId - ID da fatura
* @param request - Requisição de pagamento com token do cartão de débito e informações do cliente
* @returns Resultado do processamento do pagamento
*/
async processDebitPayment(
invoiceId: string,
request: ProcessDebitPaymentRequest
): Promise<ProcessInvoicePaymentResponse> {
// Normalize cardExpiryDate to ISO 8601 format before sending to API
const normalizedRequest = {
...request,
cardExpiryDate: normalizeCardExpiryDate(request.cardExpiryDate),
};
const { data } = await this.client.post<ProcessInvoicePaymentResponse>(
`/Invoices/${invoiceId}/process-debit-payment`,
normalizedRequest
);
return data;
}
// ==================== Subscription Status ====================
/**
* Obtém o status completo da assinatura de um cliente
* @returns Status completo ou null se não encontrado
*/
async getSubscriptionStatus(customerId: string): Promise<SubscriptionStatusResponse | null> {
const { data } = await this.client.get<SubscriptionStatusResponse>(`/subscription-status/${customerId}`);
return data;
}
/**
* Verifica se o cliente está ativo (status simples)
*/
async getSimpleStatus(customerId: string): Promise<SimpleActiveStatusResponse> {
const { data } = await this.client.get<SimpleActiveStatusResponse>(
`/subscription-status/${customerId}/is-active`
);
return data;
}
/**
* Verifica se o cliente está inadimplente
*/
async isDelinquent(customerId: string): Promise<boolean> {
const { data } = await this.client.get<boolean>(`/subscription-status/${customerId}/is-delinquent`);
return data;
}
// ==================== Payments ====================
/**
* Cria um pagamento avulso (one-time)
*/
async createOneTimePayment(request: CreateOneTimePaymentRequest): Promise<PaymentResponse> {
const { data } = await this.client.post<PaymentResponse>('/Payments/one-time', request);
return data;
}
/**
* Cria um pagamento recorrente
*/
async createRecurringPayment(request: CreateRecurringPaymentRequest): Promise<PaymentResponse> {
const { data } = await this.client.post<PaymentResponse>('/Payments/recurring', request);
return data;
}
/**
* Processa um pagamento com token de cartão
*/
async processPayment(paymentId: string, request: ProcessPaymentRequest): Promise<PaymentResponse> {
const { data } = await this.client.post<PaymentResponse>(`/Payments/${paymentId}/process`, request);
return data;
}
/**
* Busca um pagamento por ID
* @returns O pagamento encontrado ou null se não existir
*/
async getPayment(paymentId: string): Promise<PaymentResponse | null> {
const { data } = await this.client.get<PaymentResponse>(`/Payments/${paymentId}`);
return data;
}
/**
* Lista pagamentos com filtros opcionais
* @param query - Filtros de busca (customerId, subscriptionId, status)
*/
async listPayments(query?: PaymentListQuery & { customerId?: string; subscriptionId?: string }): Promise<PaymentResponse[]> {
const params: Record<string, string | number> = {};
if (query?.customerId) params.customerId = query.customerId;
if (query?.subscriptionId) params.subscriptionId = query.subscriptionId;
if (query?.status !== undefined) params.status = query.status;
if (query?.page) params.page = query.page;
if (query?.pageSize) params.pageSize = query.pageSize;
const { data } = await this.client.get<PaymentResponse[]>('/Payments', { params });
return data;
}
/**
* Obtém o método de pagamento de uma assinatura
* @param subscriptionId - ID da assinatura
* @returns Informações do método de pagamento ou null se não existir
*/
async getPaymentMethod(subscriptionId: string): Promise<PaymentMethodInfoResponse | null> {
const { data } = await this.client.get<PaymentMethodInfoResponse>(`/Subscriptions/${subscriptionId}/payment-method`);
return data;
}
/**
* Cancela um pagamento recorrente
*/
async cancelPayment(paymentId: string, request?: CancelPaymentRequest): Promise<PaymentResponse> {
const { data } = await this.client.delete<PaymentResponse>(`/Payments/${paymentId}`, { data: request });
return data;
}
// ==================== Payment Accounts ====================
/**
* Cria uma nova conta de pagamento (credenciais Mercado Pago)
*/
async createPaymentAccount(request: CreatePaymentAccountRequest): Promise<PaymentAccountResponse> {
const { data } = await this.client.post<PaymentAccountResponse>('/PaymentAccounts', request);
return data;
}
/**
* Busca uma conta de pagamento por ID
* @returns A conta encontrada ou null se não existir
*/
async getPaymentAccount(accountId: string): Promise<PaymentAccountResponse | null> {
const { data } = await this.client.get<PaymentAccountResponse>(`/PaymentAccounts/${accountId}`);
return data;
}
/**
* Busca a conta de pagamento padrão
* @returns A conta padrão ou null se não existir
*/
async getDefaultPaymentAccount(): Promise<PaymentAccountResponse | null> {
const { data } = await this.client.get<PaymentAccountResponse>('/PaymentAccounts/default');
return data;
}
/**
* Lista todas as contas de pagamento
*/
async listPaymentAccounts(): Promise<PaymentAccountResponse[]> {
const { data } = await this.client.get<PaymentAccountResponse[]>('/PaymentAccounts');
return data;
}
// ==================== Subscription Access ====================
/**
* Verifica se um cliente tem acesso ao sistema
* @param customerId - ID do cliente
* @returns Informações de acesso do cliente
*/
async checkAccess(customerId: string): Promise<SubscriptionAccessResponse> {
const { data } = await this.client.get<SubscriptionAccessResponse>(
`/SubscriptionAccess/check-access`,
{ params: { customerId } }
);
return data;
}
/**
* Verifica acesso para um cliente específico (rota alternativa)
* @param customerId - ID do cliente
* @returns Informações de acesso do cliente
*/
async checkAccessByCustomer(customerId: string): Promise<SubscriptionAccessResponse> {
const { data } = await this.client.get<SubscriptionAccessResponse>(
`/SubscriptionAccess/customers/${customerId}/access`
);
return data;
}
/**
* Verifica acesso para múltiplos clientes em uma única requisição
* @param request - Lista de IDs de clientes (máx 100)
* @returns Lista de status de acesso para cada cliente
*/
async checkAccessBatch(request: CheckAccessBatchRequest): Promise<CustomerAccessBatchItem[]> {
const { data } = await this.client.post<CustomerAccessBatchItem[]>(
'/SubscriptionAccess/check-access-batch',
request
);
return data;
}
}