# Asaas Financial

Especialista em integração com Asaas para pagamentos, cobranças e gestão financeira no Brasil.

## Responsabilidades

1. **Integrar Asaas API** para pagamentos
2. **Criar clientes** e cobranças
3. **Processar webhooks** de eventos
4. **Gerenciar assinaturas** recorrentes

## Triggers

Keywords: `asaas`, `payment`, `pix`, `boleto`, `cobranca`, `subscription`, `billing`, `pagamento`

## Sobre o Asaas

- **Plataforma brasileira** de cobranças e pagamentos
- **Suporta**: PIX, Boleto, Cartão de Crédito
- **API REST** (sem SDK .NET oficial)
- **Webhooks** para notificações em tempo real

## Configuração Básica

```csharp
// appsettings.json
{
  "Asaas": {
    "BaseUrl": "https://sandbox.asaas.com/api/v3",
    "ApiKey": "${ASAAS_API_KEY}"
  }
}

// Services/AsaasOptions.cs
public class AsaasOptions
{
    public string BaseUrl { get; set; } = "";
    public string ApiKey { get; set; } = "";
}

// Program.cs
builder.Services.Configure<AsaasOptions>(
    builder.Configuration.GetSection("Asaas"));

builder.Services.AddHttpClient<IAsaasClient, AsaasClient>((sp, client) =>
{
    var options = sp.GetRequiredService<IOptions<AsaasOptions>>().Value;
    client.BaseAddress = new Uri(options.BaseUrl);
    client.DefaultRequestHeaders.Add("access_token", options.ApiKey);
});
```

## Cliente HTTP

```csharp
// Services/AsaasClient.cs
public interface IAsaasClient
{
    Task<AsaasCustomer> CreateCustomerAsync(CreateCustomerRequest request);
    Task<AsaasPayment> CreatePaymentAsync(CreatePaymentRequest request);
    Task<AsaasPayment> GetPaymentAsync(string paymentId);
    Task<AsaasSubscription> CreateSubscriptionAsync(CreateSubscriptionRequest request);
}

public class AsaasClient : IAsaasClient
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<AsaasClient> _logger;

    public AsaasClient(HttpClient httpClient, ILogger<AsaasClient> logger)
    {
        _httpClient = httpClient;
        _logger = logger;
    }

    public async Task<AsaasCustomer> CreateCustomerAsync(CreateCustomerRequest request)
    {
        var response = await _httpClient.PostAsJsonAsync("customers", request);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<AsaasCustomer>()
            ?? throw new InvalidOperationException("Failed to deserialize customer");
    }

    public async Task<AsaasPayment> CreatePaymentAsync(CreatePaymentRequest request)
    {
        _logger.LogInformation("Creating payment for customer {CustomerId}", request.Customer);

        var response = await _httpClient.PostAsJsonAsync("payments", request);

        if (!response.IsSuccessStatusCode)
        {
            var error = await response.Content.ReadAsStringAsync();
            _logger.LogError("Failed to create payment: {Error}", error);
            throw new AsaasException($"Failed to create payment: {error}");
        }

        return await response.Content.ReadFromJsonAsync<AsaasPayment>()!;
    }

    public async Task<AsaasPayment> GetPaymentAsync(string paymentId)
    {
        var response = await _httpClient.GetAsync($"payments/{paymentId}");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<AsaasPayment>()!;
    }

    public async Task<AsaasSubscription> CreateSubscriptionAsync(CreateSubscriptionRequest request)
    {
        var response = await _httpClient.PostAsJsonAsync("subscriptions", request);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<AsaasSubscription>()!;
    }
}
```

## DTOs

```csharp
// Models/Asaas/CreateCustomerRequest.cs
public record CreateCustomerRequest
{
    [JsonPropertyName("name")]
    public required string Name { get; init; }

    [JsonPropertyName("cpfCnpj")]
    public required string CpfCnpj { get; init; }

    [JsonPropertyName("email")]
    public string? Email { get; init; }

    [JsonPropertyName("phone")]
    public string? Phone { get; init; }

    [JsonPropertyName("mobilePhone")]
    public string? MobilePhone { get; init; }
}

// Models/Asaas/CreatePaymentRequest.cs
public record CreatePaymentRequest
{
    [JsonPropertyName("customer")]
    public required string Customer { get; init; }  // Asaas customer ID

    [JsonPropertyName("billingType")]
    public required string BillingType { get; init; }  // BOLETO, PIX, CREDIT_CARD

    [JsonPropertyName("value")]
    public required decimal Value { get; init; }

    [JsonPropertyName("dueDate")]
    public required string DueDate { get; init; }  // yyyy-MM-dd

    [JsonPropertyName("description")]
    public string? Description { get; init; }

    [JsonPropertyName("externalReference")]
    public string? ExternalReference { get; init; }  // Seu ID interno
}

// Models/Asaas/AsaasPayment.cs
public record AsaasPayment
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = "";

    [JsonPropertyName("customer")]
    public string Customer { get; init; } = "";

    [JsonPropertyName("value")]
    public decimal Value { get; init; }

    [JsonPropertyName("status")]
    public string Status { get; init; } = "";  // PENDING, RECEIVED, CONFIRMED, OVERDUE...

    [JsonPropertyName("billingType")]
    public string BillingType { get; init; } = "";

    [JsonPropertyName("invoiceUrl")]
    public string? InvoiceUrl { get; init; }

    [JsonPropertyName("bankSlipUrl")]
    public string? BankSlipUrl { get; init; }  // URL do boleto

    [JsonPropertyName("pixQrCode")]
    public AsaasPixQrCode? PixQrCode { get; init; }
}

public record AsaasPixQrCode
{
    [JsonPropertyName("encodedImage")]
    public string? EncodedImage { get; init; }  // Base64 QR Code

    [JsonPropertyName("payload")]
    public string? Payload { get; init; }  // PIX copia-e-cola
}
```

## Webhooks

```csharp
// Controllers/AsaasWebhookController.cs
[ApiController]
[Route("api/webhooks/asaas")]
public class AsaasWebhookController : ControllerBase
{
    private readonly IPaymentService _paymentService;
    private readonly ILogger<AsaasWebhookController> _logger;

    public AsaasWebhookController(
        IPaymentService paymentService,
        ILogger<AsaasWebhookController> logger)
    {
        _paymentService = paymentService;
        _logger = logger;
    }

    [HttpPost]
    public async Task<IActionResult> HandleWebhook([FromBody] AsaasWebhookPayload payload)
    {
        _logger.LogInformation("Received Asaas webhook: {Event} for payment {PaymentId}",
            payload.Event, payload.Payment?.Id);

        try
        {
            switch (payload.Event)
            {
                case "PAYMENT_CONFIRMED":
                case "PAYMENT_RECEIVED":
                    await _paymentService.ConfirmPaymentAsync(payload.Payment!.Id);
                    break;

                case "PAYMENT_OVERDUE":
                    await _paymentService.MarkOverdueAsync(payload.Payment!.Id);
                    break;

                case "PAYMENT_REFUNDED":
                    await _paymentService.RefundPaymentAsync(payload.Payment!.Id);
                    break;

                default:
                    _logger.LogWarning("Unhandled webhook event: {Event}", payload.Event);
                    break;
            }

            return Ok();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error processing webhook");
            return StatusCode(500);
        }
    }
}

public record AsaasWebhookPayload
{
    [JsonPropertyName("event")]
    public string Event { get; init; } = "";

    [JsonPropertyName("payment")]
    public AsaasPayment? Payment { get; init; }
}
```

## Assinaturas Recorrentes

```csharp
// Models/Asaas/CreateSubscriptionRequest.cs
public record CreateSubscriptionRequest
{
    [JsonPropertyName("customer")]
    public required string Customer { get; init; }

    [JsonPropertyName("billingType")]
    public required string BillingType { get; init; }

    [JsonPropertyName("value")]
    public required decimal Value { get; init; }

    [JsonPropertyName("nextDueDate")]
    public required string NextDueDate { get; init; }

    [JsonPropertyName("cycle")]
    public required string Cycle { get; init; }  // MONTHLY, WEEKLY, BIWEEKLY, YEARLY

    [JsonPropertyName("description")]
    public string? Description { get; init; }

    [JsonPropertyName("externalReference")]
    public string? ExternalReference { get; init; }
}

// Criar assinatura
var subscription = await _asaasClient.CreateSubscriptionAsync(new()
{
    Customer = customerId,
    BillingType = "PIX",
    Value = 49.90m,
    NextDueDate = DateTime.Today.AddDays(1).ToString("yyyy-MM-dd"),
    Cycle = "MONTHLY",
    Description = "Plano Pro",
    ExternalReference = $"plan_{planId}"
});
```

## Ambientes

| Ambiente | Base URL |
|----------|----------|
| Sandbox | `https://sandbox.asaas.com/api/v3` |
| Produção | `https://www.asaas.com/api/v3` |

## Documentação de Referência

- [Asaas API Documentation](https://docs.asaas.com/)
- [Webhooks](https://docs.asaas.com/reference/webhooks)
- [Cobranças](https://docs.asaas.com/reference/criar-nova-cobranca)
- [Assinaturas](https://docs.asaas.com/reference/criar-nova-assinatura)

## Checklist de Integração

- [ ] API Key configurada (não hardcoded)
- [ ] HttpClient com retry policy (Polly)
- [ ] Webhook endpoint configurado
- [ ] Validação de webhook signature
- [ ] Logs estruturados
- [ ] Tratamento de erros Asaas
- [ ] Testes com sandbox
- [ ] ExternalReference para reconciliação

---

*MORPH-SPEC by Polymorphism Tech*
