> For the complete documentation index, see [llms.txt](https://docs.skail.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.skail.dev/quickstart/mais-exemplos-praticos/faturamento-completo-cobranca-recorrencia-e-split-de-pagamento.md).

# Faturamento completo: cobrança, recorrência e split de pagamento

Faturamento é um dos lugares onde sistemas costumam ficar mais complexos do que deveriam.

A distância entre "o que quero fazer" e "o que preciso codar" costuma incomodar.

&#x20;

Você precisa:

* tentar cobrar
* esperar resposta do gateway
* tentar de novo quando falha
* acompanhar o cliente ao longo de dias
* aplicar multa
* suspender serviço
* reativar quando o pagamento chega

> **No legado, isso normalmente vira jobs, tabelas de controle e muita lógica espalhada.**
>
> <mark style="background-color:$success;">No skail, isso tudo é um método simples.</mark>

### Cobrança com retentativas (backoff)

Primeiro cenário:&#x20;

Você tenta cobrar o cliente algumas vezes antes de desistir.

```csharp
[SkailFunction]
public async SkailTask CobrarCartao(Guid faturaId)
{
    var backoff = new[]
    {
        TimeSpan.Zero,
        TimeSpan.FromMinutes(5),
        TimeSpan.FromHours(1),
        TimeSpan.FromHours(6)
    };

    foreach (var espera in backoff)
    {
        await SkailTask.Delay(espera);

        var resultado = await TentarCobranca(faturaId);

        if (resultado.Sucesso)
        {
            await ConfirmarPagamento(faturaId, resultado.TransacaoId);
            return;
        }

        if (resultado.ErroPermanente)
        {
            await MarcarComoRecusada(faturaId, resultado.Motivo);
            return;
        }
    }

    await EscalarParaCobrancaManual(faturaId);
}
```

Neste exemplo fazemos 4 tentativas com espaçamento crescente.&#x20;

Entre uma tentativa e outra, a execução é hibernada—não existe worker/thread/processo ocupado, você não consome mais nenhum recurso enquanto aguarda.

Quando o tempo passa, o skail retoma automaticamente o código de ponto onde parou (como se ele nunca tivesse parado).

Os métodos que chamam o gateway ou o banco — `TentarCobranca`, `ConfirmarPagamento`, `MarcarComoRecusada`, `EscalarParaCobrancaManual` — são `[SkailCommand]`, o que garante que cada um execute exatamente uma vez mesmo que a função principal seja retomada várias vezes ao longo das horas.<br>

### Régua de cobrança

Agora vamos além da cobrança: você precisa acompanhar a fatura ao longo do tempo e cuidar de seu ciclo de vida:

* enviar lembrete antes do vencimento
* avisar no vencimento
* aplicar multa
* suspender o serviço

E tudo isso deve parar imediatamente se o pagamento chegar.

```csharp
[SkailFunction]
public async SkailTask AcompanharFatura(Guid faturaId, DateTime vencimento)
{
    var pagamento = SkailTask.WaitForEvent("PAGAMENTO_CONFIRMADO", faturaId.ToString());

    await SkailTask.WhenAny(pagamento, SkailTask.Delay(vencimento.AddDays(-3) - DateTime.UtcNow));
    if (pagamento.IsCompleted) return;
    
    await EnviarLembrete(faturaId);

    await SkailTask.WhenAny(pagamento, SkailTask.Delay(vencimento - DateTime.UtcNow));
    if (pagamento.IsCompleted) return;
    
    await EnviarAvisoVencimento(faturaId);

    await SkailTask.WhenAny(pagamento, SkailTask.Delay(TimeSpan.FromDays(5)));
    if (pagamento.IsCompleted) return;
    
    await AplicarMulta(faturaId);

    await SkailTask.WhenAny(pagamento, SkailTask.Delay(TimeSpan.FromDays(10)));
    if (pagamento.IsCompleted) return;
    
    await SuspenderServico(faturaId);
}
```

O fluxo fica explícito:

* você espera duas coisas ao mesmo tempo: o pagamento ou algum tempo
* o que acontecer primeiro define o próximo passo

#### Como isso funciona na prática

[`WaitForEvent`](/funcionalidades/sdk/externalevents.md#waitforevent) faz o código “esperar” um evento externo (ex: pagamento confirmado).

Lembrando: durante essa espera nada fica rodando e nenhum recurso (cpu/processo/thread/worker...) é consumido.

Quando o evento chega, a execução continua exatamente de onde parou.

**Conclusão:** Se o pagamento chega antes do lembrete, no dia do vencimento ou depois da multa, o método retorna imediatamente e nenhuma das etapas seguintes executa.&#x20;

Se não chega, esperamos alguns dias e executamos a regra de negócio seguinte: enviar lembrete, avisar, aplicar multa, etc.

<mark style="background-color:$success;">Sem crontab, sem job scheduler, sem tabela de "próxima ação a tomar".</mark>&#x20;

O método descreve a jornada inteiro da fatura de forma clara.<br>

### Conectando com o gateway de pagamento

Quando o gateway avisa que o pagamento foi confirmado — via webhook, via callback assíncrono, via notificação do adquirente — você traduz isso para um [`FireEvent`](/funcionalidades/sdk/externalevents.md#fireevent) e toda execução aguardando aquela fatura retoma:

```csharp
[SkailFunction]
public async SkailTask RegistrarPagamento(Guid faturaId, PagamentoConfirmado dados)
{
    await SalvarPagamento(faturaId, dados);

    await SkailTask.FireEvent("PAGAMENTO_CONFIRMADO", faturaId.ToString(), dados);
}
```

A função `RegistrarPagamento` não sabe quem está aguardando, pouco importa. <br>

* Pode ser a `AcompanharFatura` no meio da régua de cobrança. &#x20;
* Pode ser uma função de liberação de acesso esperando o OK para liberar um curso comprado.
* Pode ser esperando alguém aprovar um pedido com valor acima de R$10.000, etc.

\
Não existe acoplamento direto entre quem envia e quem consome — a conexão é feita pelo evento.

### Estorno com prazo de análise

Quando o cliente solicita um estorno, a política da empresa aguarda 48 horas antes de processar — um período em que o próprio cliente pode desistir da solicitação ou o time de atendimento pode intervir em casos suspeitos. Se nada acontecer nesse prazo, o estorno segue automaticamente para o gateway de pagamento.

Um cenário clássico com decisão humana + tempo:

```csharp
[SkailFunction]
public async SkailTask ProcessarEstorno(Guid estornoId, Guid pagamentoId)
{
    await RegistrarSolicitacao(estornoId, pagamentoId);

    var cancelamento = SkailTask.WaitForEvent<MotivoCancelamento>(
        "ESTORNO_CANCELADO",
        estornoId.ToString()
    );
    var prazoDeAnalise = SkailTask.DelayHours(48);

    await SkailTask.WhenAny(cancelamento, prazoDeAnalise);

    if (cancelamento.IsCompleted)
    {
        var motivo = await cancelamento;
        await ArquivarEstorno(estornoId, motivo);
        return;
    }

    var transacao = await SolicitarEstornoNoGateway(pagamentoId);

    var confirmacao = await SkailTask.WaitForEvent<ConfirmacaoEstorno>(
        "ESTORNO_PROCESSADO",
        transacao.Id
    );

    await FinalizarEstorno(estornoId, confirmacao);
}
```

<mark style="background-color:$success;">Mesmo com horas ou dias de espera, o código continua simples.</mark>

Esse código tem dois momentos de espera:\
\
Primeiro, você aguarda até 48 horas (usando [`SkailTask.DelayHours`](/funcionalidades/sdk/delay.md)) para o cliente ou o time decidirem. Depois, usa um [`WaitForEvent`](/funcionalidades/sdk/externalevents.md#waitforevent) para aguardar a confirmação do gateway — que pode chegar em minutos, horas ou até dias.

Em ambos os casos, sua aplicação simplesmente hiberna (pausa) e continua do mesmo ponto quando algo acontece.<br>

### Split de pagamento em marketplace

Quando o pagamento de um pedido (multi-seller neste exemplo) é confirmado, você precisa orquestrar o split entre as partes — plataforma, logística, cada vendedor — e reter a parcela de cada vendedor até a confirmação de entrega, para cobrir a janela de cancelamento/disputa.&#x20;

Cada item segue seu próprio calendário e jornada, mas o pedido só é considerado concluído quando tudo termina.

Veja como seu código agora fica claro e óbvio de manter (sem medo!):

```csharp
[SkailFunction]
public async SkailTask ProcessarPedido(Guid pedidoId)
{
    var pedido = await CarregarPedido(pedidoId);

    await LiberarParaPlataforma(pedido.TaxaPlataforma);
    await LiberarParaLogistica(pedido.Frete);

    var liquidacoes = pedido.Itens
        .Select(item => ProcessarRepasseItem(pedidoId, item.SellerId, item.ItemId, item.Valor))
        .ToArray();

    await SkailTask.WhenAll(liquidacoes);

    await MarcarPedidoComoFinalizado(pedidoId);
}

[SkailFunction]
public async SkailTask ProcessarRepasseItem(Guid pedidoId, Guid sellerId, Guid itemId, decimal valor)
{
    await ReservarValorParaSeller(sellerId, itemId, valor);

    var entrega = SkailTask.WaitForEvent("ITEM_ENTREGUE", itemId.ToString());
    var prazoDeEntrega = SkailTask.DelayDays(7);

    await SkailTask.WhenAny(entrega, prazoDeEntrega);

    if (!entrega.IsCompleted)
    {
        await EscalarEntregaNaoConfirmada(pedidoId, itemId);
        return;
    }

    var disputa = SkailTask.WaitForEvent<MotivoDisputa>("DISPUTA_ABERTA", itemId.ToString());
    var janelaDeContestacao = SkailTask.DelayDays(7);

    await SkailTask.WhenAny(disputa, janelaDeContestacao);

    if (disputa.IsCompleted)
    {
        var motivo = await disputa;
        await TratarDisputa(pedidoId, itemId, motivo);
        return;
    }

    await LiberarPagamentoParaSeller(sellerId, itemId, valor);
}
```

O `ProcessarPedido` dispara um fluxo independente para cada item (chamando um `ProcessarRepasseItem` pra cada) e só termina quando todos estiverem concluídos — um item pode levar dois dias, outro pode levar semanas.&#x20;

Cada `ProcessarRepasseItem` segue seu próprio caminho, sem depender dos outros.\
\
A única coordenação necessária é o `WhenAll` no topo, que o skail gerencia automaticamente.

### Cobrança recorrente de assinatura

Uma assinatura é, essencialmente, um ciclo:

* cobrar
* esperar X dias
* repetir até o cliente cancelar

No skail isso pode ser literalmente um `while` simples:

```csharp
[SkailFunction]
public async SkailTask AssinaturaMensal(Guid assinaturaId)
{
    var cancelamento = SkailTask.WaitForEvent<MotivoCancelamento>(
        "ASSINATURA_CANCELADA",
        assinaturaId.ToString()
    );

    while (!cancelamento.IsCompleted)
    {
        var faturaId = await EmitirFatura(assinaturaId);
        await CobrarCartao(faturaId);

        var proximoCiclo = SkailTask.DelayDays(30);

        await SkailTask.WhenAny(cancelamento, proximoCiclo);
    }

    var motivo = await cancelamento;
    await EncerrarAssinatura(assinaturaId, motivo);
}
```

A execução dessa função pode durar todo o tempo da assinatura — meses ou até anos.

A cada ciclo ela:

* chama `EmitirFatura()` para gerar a próxima cobrança
* reutiliza o `CobrarCartao()` (com a política de retentativa mostrada anteriormente)
* e aguarda (hibernada) o próximo ciclo com SkailTask.DelayDays(30).&#x20;

Se o evento de cancelamento `(ASSINATURA_CANCELADA)` for disparado a qualquer momento — antes da próxima cobrança, no meio de uma retentativa, logo depois de uma fatura emitida — a espera do `WhenAny()` é interrompida e o loop termina.

Por fim, toda a lógica de:

* quando é o próximo ciclo
* se a assinatura ainda está ativa
* quantas cobranças já foram feitas

fica implícita no próprio estado da execução (podendo ser acompanhado pelo [Monitor](/funcionalidades/dashboard/monitor.md) do skail).

Você não precisa mais manter tabela no banco só para o scheduler saber o que fazer amanhã.\
\ <mark style="background-color:$success;">Ganho colateral imediato: seu banco de dados fica muito mais leve, e é bem menos requisitado.</mark>

#### Assinatura com período grátis / Recuperação de Pagamento

Em vez de encerrar a assinatura no primeiro erro, o padrão mais comum é dar ao cliente uma janela para atualizar o meio de pagamento.&#x20;

Se ele atualizar a tempo, o skail tenta cobrar de novo; \
Se não atualizar, a assinatura é marcada como inadimplente e encerrada.

```csharp
[SkailFunction]
public async SkailTask AssinaturaMensal(Guid assinaturaId)
{
    var cancelamento = SkailTask.WaitForEvent<MotivoCancelamento>(
        "ASSINATURA_CANCELADA",
        assinaturaId.ToString()
    );

    while (!cancelamento.IsCompleted)
    {
        var faturaId = await EmitirFatura(assinaturaId);
        var resultado = await CobrarCartao(faturaId);

        if (resultado != ResultadoCobranca.Sucesso)
        {
            if (!await AguardarRecuperacaoDoPagamento(assinaturaId, faturaId, cancelamento))
            {
                await MarcarComoInadimplente(assinaturaId, faturaId);
                await EncerrarAssinatura(assinaturaId, MotivoCancelamento.Inadimplencia);
                return;
            }
        }

        var proximoCiclo = SkailTask.DelayDays(30);
        await SkailTask.WhenAny(cancelamento, proximoCiclo);
    }

    var motivo = await cancelamento;
    await EncerrarAssinatura(assinaturaId, motivo);
}
```

Quando a cobrança falha, o fluxo entra em um período de graça controlado pela função `AguardarRecuperacaoDoPagamento()`:

```csharp
[SkailFunction]
private async SkailTask<bool> AguardarRecuperacaoDoPagamento(
    Guid assinaturaId,
    Guid faturaId,
    SkailTask cancelamento)
{
    await NotificarFalhaNaCobranca(assinaturaId, faturaId);

    var cartaoAtualizado = SkailTask.WaitForEvent(
        "CARTAO_ATUALIZADO",
        assinaturaId.ToString()
    );
    var carencia = SkailTask.DelayDays(7);

    await SkailTask.WhenAny(cartaoAtualizado, carencia, cancelamento);

    if (!cartaoAtualizado.IsCompleted) return false;

    var retry = await CobrarCartao(faturaId);
    return retry == ResultadoCobranca.Sucesso;
}
```

Se a cobrança não deu certo:<br>

1. o cliente é notificado (`NotificarFalhaNaCobranca`)
2. o sistema entra em um período grátis de 7 dias (`DelayDays`)
3. ao mesmo tempo, aguarda três possibilidades:
   * o cliente atualizar o cartão (`WaitForEvent`)
   * o prazo de 7 dias terminar
   * a assinatura ser cancelada

\
O `WhenAny` garante que o fluxo reage ao que acontecer primeiro.<br>

* Se o cartão for atualizado dentro do prazo, o sistema tenta cobrar novamente (`CobrarCartao`)
* Se essa tentativa falhar, ou se o prazo terminar sem atualização, a função retorna `false`
* Nesse caso, a assinatura é marcada como inadimplente e encerrada

<mark style="background-color:$success;">E tudo isso acontece sem a lógica ficar espalhada pelo sistema.</mark>

<mark style="background-color:$success;">Agora você mantém um código linear simples que 'conta a história' do seu sistema para qualquer dev que precisa mantê-lo.</mark>

### Observabilidade

No Monitor do portal do skail, cada fatura em acompanhamento aparece como uma execução ativa —  aguardando pagamento ou o próximo passo da jornada.&#x20;

\
Você consegue ver, por exemplo:

* quanto tempo os pagamentos estão levando
* quais faturas ainda estão em aberto aguadando
* e quais estão paradas há mais tempo

\ <mark style="background-color:$success;">Tudo isso vem do próprio estado das execuções — sem precisar montar dashboards ou cruzar dados em outros sistemas.</mark>
