using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.WebUtilities; using Streetwriters.Common.Models; namespace Streetwriters.Common.Services { public class PaddleBillingService { #if DEBUG private const string PADDLE_BASE_URI = "https://sandbox-api.paddle.com"; #else private const string PADDLE_BASE_URI = "https://api.paddle.com"; #endif private readonly HttpClient httpClient = new(); public PaddleBillingService(string paddleApiKey) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", paddleApiKey); } public async Task GetSubscriptionAsync(string subscriptionId) { var url = $"{PADDLE_BASE_URI}/subscriptions/{subscriptionId}"; var response = await httpClient.GetAsync(url); return await response.Content.ReadFromJsonAsync(); } public async Task GetTransactionAsync(string transactionId) { var url = $"{PADDLE_BASE_URI}/transactions/{transactionId}"; var response = await httpClient.GetAsync(url); return await response.Content.ReadFromJsonAsync(); } public async Task GetTransactionInvoiceAsync(string transactionId) { var url = $"{PADDLE_BASE_URI}/transactions/{transactionId}/invoice"; var response = await httpClient.GetAsync(url); return await response.Content.ReadFromJsonAsync(); } public async Task ListTransactionsAsync(string? subscriptionId = null, string? customerId = null, string[]? status = null, string[]? origin = null) { var url = $"{PADDLE_BASE_URI}/transactions"; var parameters = new Dictionary() { { "subscription_id", subscriptionId }, { "customer_id", customerId }, { "status", string.Join(',', status ?? ["billed","completed"]) }, { "order_by", "billed_at[DESC]" } }; if (origin is not null) parameters.Add("origin", string.Join(',', origin)); var response = await httpClient.GetAsync(QueryHelpers.AddQueryString(url, parameters)); return await response.Content.ReadFromJsonAsync(); } public async Task RefundTransactionAsync(string transactionId, string transactionItemId, string reason = "") { var url = $"{PADDLE_BASE_URI}/adjustments"; var response = await httpClient.PostAsync(url, JsonContent.Create(new Dictionary { { "action", "refund" }, { "items", new object[] { new Dictionary { {"item_id", transactionItemId}, {"type", "full"} } } }, { "reason", reason }, { "transaction_id", transactionId } })); return await response.Content.ReadFromJsonAsync(); } public async Task PreviewSubscriptionChangeAsync(string subscriptionId, string newProductId, bool isTrialing) { var url = $"{PADDLE_BASE_URI}/subscriptions/{subscriptionId}/preview"; var response = await httpClient.PatchAsync(url, JsonContent.Create(new { proration_billing_mode = isTrialing ? "do_not_bill" : "prorated_immediately", items = new[] { new { price_id = newProductId, quantity = 1 } } })); return await response.Content.ReadFromJsonAsync(); } public async Task ChangeSubscriptionAsync(string subscriptionId, string newProductId, bool isTrialing) { var url = $"{PADDLE_BASE_URI}/subscriptions/{subscriptionId}"; var response = await httpClient.PatchAsync(url, JsonContent.Create(new { proration_billing_mode = isTrialing ? "do_not_bill" : "prorated_immediately", items = new[] { new { price_id = newProductId, quantity = 1 } } })); return await response.Content.ReadFromJsonAsync(); } public async Task CancelSubscriptionAsync(string subscriptionId) { var url = $"{PADDLE_BASE_URI}/subscriptions/{subscriptionId}/cancel"; var response = await httpClient.PostAsync(url, JsonContent.Create(new { effective_from = "immediately" })); return await response.Content.ReadFromJsonAsync(); } public async Task PauseSubscriptionAsync(string subscriptionId) { var url = $"{PADDLE_BASE_URI}/subscriptions/{subscriptionId}/pause"; var response = await httpClient.PostAsync(url, JsonContent.Create(new { })); return await response.Content.ReadFromJsonAsync(); } public async Task ResumeSubscriptionAsync(string subscriptionId) { var url = $"{PADDLE_BASE_URI}/subscriptions/{subscriptionId}"; var response = await httpClient.PatchAsync(url, JsonContent.Create(new Dictionary { {"scheduled_change", null} })); return await response.Content.ReadFromJsonAsync(); } public async Task FindCustomerFromTransactionAsync(string transactionId) { var transaction = await GetTransactionAsync(transactionId); if (transaction?.Transaction?.CustomerId == null) return null; var url = $"{PADDLE_BASE_URI}/customers/{transaction.Transaction.CustomerId}"; var response = await httpClient.GetFromJsonAsync(url); return response; } } }