> ## Documentation Index
> Fetch the complete documentation index at: https://scaleup-c34c4386.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Estado do Cliente

> Gerencie o estado do cliente na sua aplicação e mantenha sincronizado com a API Chargefy

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.

```typescript theme={null}
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: 'cliente@exemplo.com',
});
```

## Verificando Status da Assinatura

O padrão mais comum é verificar se o cliente possui uma assinatura ativa antes de liberar acesso a recursos.

```typescript theme={null}
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

| Status | Descrição |
| - | - |
| `active` | Assinatura ativa e pagamento em dia |
| `past_due` | Pagamento atrasado, ainda dentro do período de tolerância |
| `canceled` | Assinatura cancelada pelo cliente ou sistema |
| `incomplete` | Pagamento inicial pendente |
| `trialing` | Período de teste ativo |

## Padrão de Cache Local

Para evitar chamadas excessivas à API, implemente um cache local do estado do cliente.

```typescript theme={null}
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

| Evento | Quando Ocorre |
| - | - |
| `subscription.created` | Nova assinatura criada |
| `subscription.updated` | Assinatura atualizada (plano, status) |
| `subscription.canceled` | Assinatura cancelada |
| `subscription.active` | Assinatura ativada após pagamento |
| `checkout.updated` | Status do checkout mudou |

### Exemplo de Handler de Webhook

```typescript theme={null}
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.

```tsx theme={null}
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

```tsx theme={null}
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

> [accordion] Use webhooks como fonte primária
>
> 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.

> [accordion] Implemente cache com TTL
>
> 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.

> [accordion] Trate falhas graciosamente
>
> 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.

> [accordion] Persista o estado no banco de dados
>
> 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.