using Bit.Api.Billing.Models.Requests; using Bit.Api.Billing.Models.Responses; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Billing.Constants; using Bit.Core.Billing.Models; using Bit.Core.Billing.Services; using Bit.Core.Context; using Bit.Core.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Stripe; namespace Bit.Api.Billing.Controllers; [Route("providers/{providerId:guid}/billing")] [Authorize("Application")] public class ProviderBillingController( ICurrentContext currentContext, IFeatureService featureService, IProviderBillingService providerBillingService, IProviderRepository providerRepository, IStripeAdapter stripeAdapter, ISubscriberService subscriberService) : BaseProviderController(currentContext, featureService, providerRepository) { [HttpGet("invoices")] public async Task GetInvoicesAsync([FromRoute] Guid providerId) { var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId); if (provider == null) { return result; } var invoices = await subscriberService.GetInvoices(provider); var response = InvoicesResponse.From(invoices); return TypedResults.Ok(response); } [HttpGet("invoices/{invoiceId}")] public async Task GenerateClientInvoiceReportAsync([FromRoute] Guid providerId, string invoiceId) { var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId); if (provider == null) { return result; } var reportContent = await providerBillingService.GenerateClientInvoiceReport(invoiceId); if (reportContent == null) { return TypedResults.NotFound(); } return TypedResults.File( reportContent, "text/csv"); } [HttpGet("payment-information")] public async Task GetPaymentInformationAsync([FromRoute] Guid providerId) { var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId); if (provider == null) { return result; } var paymentInformation = await subscriberService.GetPaymentInformation(provider); if (paymentInformation == null) { return TypedResults.NotFound(); } var response = PaymentInformationResponse.From(paymentInformation); return TypedResults.Ok(response); } [HttpGet("payment-method")] public async Task GetPaymentMethodAsync([FromRoute] Guid providerId) { var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId); if (provider == null) { return result; } var maskedPaymentMethod = await subscriberService.GetPaymentMethod(provider); if (maskedPaymentMethod == null) { return TypedResults.NotFound(); } var response = MaskedPaymentMethodResponse.From(maskedPaymentMethod); return TypedResults.Ok(response); } [HttpPut("payment-method")] public async Task UpdatePaymentMethodAsync( [FromRoute] Guid providerId, [FromBody] TokenizedPaymentMethodRequestBody requestBody) { var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId); if (provider == null) { return result; } var tokenizedPaymentMethod = new TokenizedPaymentMethodDTO( requestBody.Type, requestBody.Token); await subscriberService.UpdatePaymentMethod(provider, tokenizedPaymentMethod); await stripeAdapter.SubscriptionUpdateAsync(provider.GatewaySubscriptionId, new SubscriptionUpdateOptions { CollectionMethod = StripeConstants.CollectionMethod.ChargeAutomatically }); return TypedResults.Ok(); } [HttpPost] [Route("payment-method/verify-bank-account")] public async Task VerifyBankAccountAsync( [FromRoute] Guid providerId, [FromBody] VerifyBankAccountRequestBody requestBody) { var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId); if (provider == null) { return result; } await subscriberService.VerifyBankAccount(provider, (requestBody.Amount1, requestBody.Amount2)); return TypedResults.Ok(); } [HttpGet("subscription")] public async Task GetSubscriptionAsync([FromRoute] Guid providerId) { var (provider, result) = await TryGetBillableProviderForServiceUserOperation(providerId); if (provider == null) { return result; } var consolidatedBillingSubscription = await providerBillingService.GetConsolidatedBillingSubscription(provider); if (consolidatedBillingSubscription == null) { return TypedResults.NotFound(); } var response = ConsolidatedBillingSubscriptionResponse.From(consolidatedBillingSubscription); return TypedResults.Ok(response); } [HttpGet("tax-information")] public async Task GetTaxInformationAsync([FromRoute] Guid providerId) { var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId); if (provider == null) { return result; } var taxInformation = await subscriberService.GetTaxInformation(provider); if (taxInformation == null) { return TypedResults.NotFound(); } var response = TaxInformationResponse.From(taxInformation); return TypedResults.Ok(response); } [HttpPut("tax-information")] public async Task UpdateTaxInformationAsync( [FromRoute] Guid providerId, [FromBody] TaxInformationRequestBody requestBody) { var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId); if (provider == null) { return result; } var taxInformation = new TaxInformationDTO( requestBody.Country, requestBody.PostalCode, requestBody.TaxId, requestBody.Line1, requestBody.Line2, requestBody.City, requestBody.State); await subscriberService.UpdateTaxInformation(provider, taxInformation); return TypedResults.Ok(); } }