// ==============================================================================
// MORPH-SPEC - Asaas Client Template
// HTTP Client para integração com Asaas API
// ==============================================================================
using System.Net.Http.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace {{Namespace}}.Infrastructure.Services;
// ==============================================================================
// OPTIONS
// ==============================================================================
public class AsaasOptions
{
public const string SectionName = "Asaas";
///
/// Base URL da API (sandbox ou produção)
/// Sandbox: https://sandbox.asaas.com/api/v3
/// Produção: https://www.asaas.com/api/v3
///
public string BaseUrl { get; set; } = "https://sandbox.asaas.com/api/v3";
///
/// API Key do Asaas
///
public string ApiKey { get; set; } = string.Empty;
}
// ==============================================================================
// INTERFACE
// ==============================================================================
public interface IAsaasClient
{
// Customers
Task CreateCustomerAsync(CreateCustomerRequest request, CancellationToken ct = default);
Task GetCustomerByIdAsync(string customerId, CancellationToken ct = default);
Task GetCustomerByCpfCnpjAsync(string cpfCnpj, CancellationToken ct = default);
// Payments
Task CreatePaymentAsync(CreatePaymentRequest request, CancellationToken ct = default);
Task GetPaymentAsync(string paymentId, CancellationToken ct = default);
Task GetPixQrCodeAsync(string paymentId, CancellationToken ct = default);
// Subscriptions
Task CreateSubscriptionAsync(CreateSubscriptionRequest request, CancellationToken ct = default);
Task GetSubscriptionAsync(string subscriptionId, CancellationToken ct = default);
Task CancelSubscriptionAsync(string subscriptionId, CancellationToken ct = default);
}
// ==============================================================================
// IMPLEMENTATION
// ==============================================================================
public class AsaasClient : IAsaasClient
{
private readonly HttpClient _httpClient;
private readonly ILogger _logger;
public AsaasClient(HttpClient httpClient, ILogger logger)
{
_httpClient = httpClient;
_logger = logger;
}
// =========================================================================
// CUSTOMERS
// =========================================================================
public async Task CreateCustomerAsync(CreateCustomerRequest request, CancellationToken ct = default)
{
_logger.LogInformation("Creating Asaas customer: {Name}", request.Name);
var response = await _httpClient.PostAsJsonAsync("customers", request, ct);
await EnsureSuccessAsync(response, "create customer");
return await response.Content.ReadFromJsonAsync(ct)
?? throw new AsaasException("Failed to deserialize customer response");
}
public async Task GetCustomerByIdAsync(string customerId, CancellationToken ct = default)
{
var response = await _httpClient.GetAsync($"customers/{customerId}", ct);
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
return null;
await EnsureSuccessAsync(response, "get customer");
return await response.Content.ReadFromJsonAsync(ct);
}
public async Task GetCustomerByCpfCnpjAsync(string cpfCnpj, CancellationToken ct = default)
{
var response = await _httpClient.GetAsync($"customers?cpfCnpj={cpfCnpj}", ct);
await EnsureSuccessAsync(response, "search customer");
var result = await response.Content.ReadFromJsonAsync>(ct);
return result?.Data?.FirstOrDefault();
}
// =========================================================================
// PAYMENTS
// =========================================================================
public async Task CreatePaymentAsync(CreatePaymentRequest request, CancellationToken ct = default)
{
_logger.LogInformation("Creating Asaas payment for customer {CustomerId}, type {BillingType}, value {Value}",
request.Customer, request.BillingType, request.Value);
var response = await _httpClient.PostAsJsonAsync("payments", request, ct);
await EnsureSuccessAsync(response, "create payment");
return await response.Content.ReadFromJsonAsync(ct)
?? throw new AsaasException("Failed to deserialize payment response");
}
public async Task GetPaymentAsync(string paymentId, CancellationToken ct = default)
{
var response = await _httpClient.GetAsync($"payments/{paymentId}", ct);
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
return null;
await EnsureSuccessAsync(response, "get payment");
return await response.Content.ReadFromJsonAsync(ct);
}
public async Task GetPixQrCodeAsync(string paymentId, CancellationToken ct = default)
{
var response = await _httpClient.GetAsync($"payments/{paymentId}/pixQrCode", ct);
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
return null;
await EnsureSuccessAsync(response, "get pix qrcode");
return await response.Content.ReadFromJsonAsync(ct);
}
// =========================================================================
// SUBSCRIPTIONS
// =========================================================================
public async Task CreateSubscriptionAsync(CreateSubscriptionRequest request, CancellationToken ct = default)
{
_logger.LogInformation("Creating Asaas subscription for customer {CustomerId}", request.Customer);
var response = await _httpClient.PostAsJsonAsync("subscriptions", request, ct);
await EnsureSuccessAsync(response, "create subscription");
return await response.Content.ReadFromJsonAsync(ct)
?? throw new AsaasException("Failed to deserialize subscription response");
}
public async Task GetSubscriptionAsync(string subscriptionId, CancellationToken ct = default)
{
var response = await _httpClient.GetAsync($"subscriptions/{subscriptionId}", ct);
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
return null;
await EnsureSuccessAsync(response, "get subscription");
return await response.Content.ReadFromJsonAsync(ct);
}
public async Task CancelSubscriptionAsync(string subscriptionId, CancellationToken ct = default)
{
_logger.LogInformation("Canceling Asaas subscription {SubscriptionId}", subscriptionId);
var response = await _httpClient.DeleteAsync($"subscriptions/{subscriptionId}", ct);
await EnsureSuccessAsync(response, "cancel subscription");
}
// =========================================================================
// HELPERS
// =========================================================================
private async Task EnsureSuccessAsync(HttpResponseMessage response, string operation)
{
if (!response.IsSuccessStatusCode)
{
var error = await response.Content.ReadAsStringAsync();
_logger.LogError("Asaas API error during {Operation}: {StatusCode} - {Error}",
operation, response.StatusCode, error);
throw new AsaasException($"Failed to {operation}: {response.StatusCode} - {error}");
}
}
}
// ==============================================================================
// DTOS
// ==============================================================================
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; }
[JsonPropertyName("externalReference")]
public string? ExternalReference { get; init; }
}
public record CreatePaymentRequest
{
[JsonPropertyName("customer")]
public required string Customer { get; init; }
[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; }
}
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; }
}
public record AsaasCustomer
{
[JsonPropertyName("id")]
public string Id { get; init; } = string.Empty;
[JsonPropertyName("name")]
public string Name { get; init; } = string.Empty;
[JsonPropertyName("cpfCnpj")]
public string CpfCnpj { get; init; } = string.Empty;
[JsonPropertyName("email")]
public string? Email { get; init; }
[JsonPropertyName("externalReference")]
public string? ExternalReference { get; init; }
}
public record AsaasPayment
{
[JsonPropertyName("id")]
public string Id { get; init; } = string.Empty;
[JsonPropertyName("customer")]
public string Customer { get; init; } = string.Empty;
[JsonPropertyName("value")]
public decimal Value { get; init; }
[JsonPropertyName("status")]
public string Status { get; init; } = string.Empty;
[JsonPropertyName("billingType")]
public string BillingType { get; init; } = string.Empty;
[JsonPropertyName("dueDate")]
public string DueDate { get; init; } = string.Empty;
[JsonPropertyName("invoiceUrl")]
public string? InvoiceUrl { get; init; }
[JsonPropertyName("bankSlipUrl")]
public string? BankSlipUrl { get; init; }
[JsonPropertyName("externalReference")]
public string? ExternalReference { get; init; }
}
public record AsaasPixQrCode
{
[JsonPropertyName("encodedImage")]
public string? EncodedImage { get; init; }
[JsonPropertyName("payload")]
public string? Payload { get; init; }
[JsonPropertyName("expirationDate")]
public string? ExpirationDate { get; init; }
}
public record AsaasSubscription
{
[JsonPropertyName("id")]
public string Id { get; init; } = string.Empty;
[JsonPropertyName("customer")]
public string Customer { get; init; } = string.Empty;
[JsonPropertyName("value")]
public decimal Value { get; init; }
[JsonPropertyName("status")]
public string Status { get; init; } = string.Empty;
[JsonPropertyName("cycle")]
public string Cycle { get; init; } = string.Empty;
[JsonPropertyName("nextDueDate")]
public string NextDueDate { get; init; } = string.Empty;
[JsonPropertyName("externalReference")]
public string? ExternalReference { get; init; }
}
public record AsaasListResponse
{
[JsonPropertyName("data")]
public List? Data { get; init; }
[JsonPropertyName("totalCount")]
public int TotalCount { get; init; }
}
// ==============================================================================
// EXCEPTION
// ==============================================================================
public class AsaasException : Exception
{
public AsaasException(string message) : base(message) { }
public AsaasException(string message, Exception innerException) : base(message, innerException) { }
}
// ==============================================================================
// DEPENDENCY INJECTION
// ==============================================================================
public static class AsaasServiceExtensions
{
public static IServiceCollection AddAsaasClient(this IServiceCollection services, IConfiguration configuration)
{
services.Configure(configuration.GetSection(AsaasOptions.SectionName));
services.AddHttpClient((sp, client) =>
{
var options = sp.GetRequiredService>().Value;
client.BaseAddress = new Uri(options.BaseUrl.TrimEnd('/') + "/");
client.DefaultRequestHeaders.Add("access_token", options.ApiKey);
client.DefaultRequestHeaders.Add("User-Agent", "MORPH-SPEC/1.0");
});
return services;
}
}