Skip to main content
Manter o estado do cliente sincronizado com a Chargefy é essencial para controlar o acesso a funcionalidades e verificar assinaturas ativas. Este guia apresenta os padrões recomendados.

Obtendo o Estado do Cliente

Use o SDK para buscar as informações completas do cliente, incluindo assinaturas.
import { Chargefy } from '@chargefy/sdk';

const chargefy = new Chargefy({
  accessToken: process.env.CHARGEFY_ACCESS_TOKEN,
  server: 'production',
});

// Buscar cliente por ID
const customer = await chargefy.customers.get({
  id: 'customer_id',
});

// Buscar cliente por email
const customers = await chargefy.customers.list({
  email: '[email protected]',
});

Verificando Status da Assinatura

O padrão mais comum é verificar se o cliente possui uma assinatura ativa antes de liberar acesso a recursos.
async function checkSubscriptionStatus(customerId: string) {
  const subscriptions = await chargefy.subscriptions.list({
    customerId,
  });

  const activeSubscription = subscriptions.result.items.find(
    (sub) => sub.status === 'active'
  );

  return {
    isActive: !!activeSubscription,
    subscription: activeSubscription,
    plan: activeSubscription?.productId,
    currentPeriodEnd: activeSubscription?.currentPeriodEnd,
  };
}

Status Possíveis da Assinatura

StatusDescrição
activeAssinatura ativa e pagamento em dia
past_duePagamento atrasado, ainda dentro do período de tolerância
canceledAssinatura cancelada pelo cliente ou sistema
incompletePagamento inicial pendente
trialingPeríodo de teste ativo

Padrão de Cache Local

Para evitar chamadas excessivas à API, implemente um cache local do estado do cliente.
interface CustomerState {
  customerId: string;
  isActive: boolean;
  plan: string | null;
  lastSyncedAt: Date;
}

class CustomerStateManager {
  private cache: Map<string, CustomerState> = new Map();
  private cacheTTL = 5 * 60 * 1000; // 5 minutos

  async getState(customerId: string): Promise<CustomerState> {
    const cached = this.cache.get(customerId);

    if (cached && Date.now() - cached.lastSyncedAt.getTime() < this.cacheTTL) {
      return cached;
    }

    return this.syncState(customerId);
  }

  async syncState(customerId: string): Promise<CustomerState> {
    const subscriptions = await chargefy.subscriptions.list({ customerId });

    const activeSubscription = subscriptions.result.items.find(
      (sub) => sub.status === 'active' || sub.status === 'trialing'
    );

    const state: CustomerState = {
      customerId,
      isActive: !!activeSubscription,
      plan: activeSubscription?.productId ?? null,
      lastSyncedAt: new Date(),
    };

    this.cache.set(customerId, state);
    return state;
  }

  invalidate(customerId: string) {
    this.cache.delete(customerId);
  }
}

Sincronização via Webhooks

A forma mais confiável de manter o estado atualizado é reagir a eventos via webhooks. Isso garante que mudanças feitas fora da sua aplicação (como cancelamentos ou renovações) sejam refletidas imediatamente.

Eventos Relevantes

EventoQuando Ocorre
subscription.createdNova assinatura criada
subscription.updatedAssinatura atualizada (plano, status)
subscription.canceledAssinatura cancelada
subscription.activeAssinatura ativada após pagamento
checkout.updatedStatus do checkout mudou

Exemplo de Handler de Webhook

import express from 'express';
import { validateWebhookSignature } from '@chargefy/sdk';

const app = express();

const stateManager = new CustomerStateManager();

app.post('/webhooks/chargefy', express.raw({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['webhook-signature'] as string;
  const payload = req.body.toString();

  // Validar assinatura do webhook
  if (!validateWebhookSignature(payload, signature, process.env.CHARGEFY_WEBHOOK_SECRET!)) {
    return res.status(401).send('Assinatura inválida');
  }

  const event = JSON.parse(payload);

  switch (event.type) {
    case 'subscription.active':
    case 'subscription.updated':
    case 'subscription.canceled':
      // Invalidar cache para forçar re-sincronização
      stateManager.invalidate(event.data.customerId);

      // Atualizar banco de dados
      await updateCustomerInDatabase(event.data.customerId, {
        subscriptionStatus: event.data.status,
        plan: event.data.productId,
      });
      break;
  }

  res.status(200).json({ received: true });
});

Hook React para Estado do Cliente

Use um hook customizado para acessar o estado do cliente em componentes React.
import { useState, useEffect, createContext, useContext } from 'react';

interface CustomerState {
  isActive: boolean;
  plan: string | null;
  loading: boolean;
  error: Error | null;
}

const CustomerStateContext = createContext<CustomerState | null>(null);

export function CustomerStateProvider({ customerId, children }: {
  customerId: string;
  children: React.ReactNode;
}) {
  const [state, setState] = useState<CustomerState>({
    isActive: false,
    plan: null,
    loading: true,
    error: null,
  });

  useEffect(() => {
    let cancelled = false;

    async function fetchState() {
      try {
        const response = await fetch(`/api/customers/${customerId}/state`);
        const data = await response.json();

        if (!cancelled) {
          setState({
            isActive: data.isActive,
            plan: data.plan,
            loading: false,
            error: null,
          });
        }
      } catch (error) {
        if (!cancelled) {
          setState((prev) => ({
            ...prev,
            loading: false,
            error: error as Error,
          }));
        }
      }
    }

    fetchState();

    // Re-sincronizar a cada 5 minutos
    const interval = setInterval(fetchState, 5 * 60 * 1000);

    return () => {
      cancelled = true;
      clearInterval(interval);
    };
  }, [customerId]);

  return (
    <CustomerStateContext.Provider value={state}>
      {children}
    </CustomerStateContext.Provider>
  );
}

export function useCustomerState() {
  const context = useContext(CustomerStateContext);
  if (!context) {
    throw new Error('useCustomerState deve ser usado dentro de CustomerStateProvider');
  }
  return context;
}

Uso do Hook

function PremiumFeature() {
  const { isActive, plan, loading } = useCustomerState();

  if (loading) {
    return <div>Carregando...</div>;
  }

  if (!isActive) {
    return (
      <div>
        <p>Este recurso requer uma assinatura ativa.</p>
        <a href="/pricing">Ver planos</a>
      </div>
    );
  }

  return <div>Conteúdo premium disponível no plano {plan}.</div>;
}

Boas Práticas

Não dependa apenas de polling. Configure webhooks para receber atualizações em tempo real e use chamadas à API apenas como fallback ou para sincronização inicial.
Cache o estado do cliente por um período curto (3-5 minutos) para reduzir chamadas à API, mas mantenha um mecanismo de invalidação via webhooks.
Se a API estiver indisponível, use o último estado conhecido do cache ou banco de dados. Nunca bloqueie o acesso do usuário por falha temporária na verificação.
Além do cache em memória, salve o estado do cliente no seu banco de dados. Isso garante que a aplicação funcione mesmo após reinicializações.