// ============================================================================== // 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; } }