mirror of
https://github.com/bitwarden/server.git
synced 2024-11-24 12:35:25 +01:00
[PM-12420] Stripe events recovery (#4793)
* Billing: Add event recovery endpoints * Core: Add InternalBilling to BaseServiceUriSettings * Admin: Scaffold billing section * Admin: Scaffold ProcessStripeEvents section * Admin: Implement event processing * Run dotnet format
This commit is contained in:
parent
3f629e0a5a
commit
05247d2525
@ -14,6 +14,10 @@
|
|||||||
<ProjectReference Include="..\Core\Core.csproj" />
|
<ProjectReference Include="..\Core\Core.csproj" />
|
||||||
<ProjectReference Include="..\..\util\SqliteMigrations\SqliteMigrations.csproj" />
|
<ProjectReference Include="..\..\util\SqliteMigrations\SqliteMigrations.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Billing\Controllers\" />
|
||||||
|
<Folder Include="Billing\Models\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<Choose>
|
<Choose>
|
||||||
<When Condition="!$(DefineConstants.Contains('OSS'))">
|
<When Condition="!$(DefineConstants.Contains('OSS'))">
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Bit.Admin.Billing.Models.ProcessStripeEvents;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Bit.Admin.Billing.Controllers;
|
||||||
|
|
||||||
|
[Authorize]
|
||||||
|
[Route("process-stripe-events")]
|
||||||
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
|
public class ProcessStripeEventsController(
|
||||||
|
IHttpClientFactory httpClientFactory,
|
||||||
|
IGlobalSettings globalSettings) : Controller
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult Index()
|
||||||
|
{
|
||||||
|
return View(new EventsFormModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> ProcessAsync([FromForm] EventsFormModel model)
|
||||||
|
{
|
||||||
|
var eventIds = model.GetEventIds();
|
||||||
|
|
||||||
|
const string baseEndpoint = "stripe/recovery/events";
|
||||||
|
|
||||||
|
var endpoint = model.Inspect ? $"{baseEndpoint}/inspect" : $"{baseEndpoint}/process";
|
||||||
|
|
||||||
|
var (response, failedResponseMessage) = await PostAsync(endpoint, new EventsRequestBody
|
||||||
|
{
|
||||||
|
EventIds = eventIds
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response == null)
|
||||||
|
{
|
||||||
|
return StatusCode((int)failedResponseMessage.StatusCode, "An error occurred during your request.");
|
||||||
|
}
|
||||||
|
|
||||||
|
response.ActionType = model.Inspect ? EventActionType.Inspect : EventActionType.Process;
|
||||||
|
|
||||||
|
return View("Results", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<(EventsResponseBody, HttpResponseMessage)> PostAsync(
|
||||||
|
string endpoint,
|
||||||
|
EventsRequestBody requestModel)
|
||||||
|
{
|
||||||
|
var client = httpClientFactory.CreateClient("InternalBilling");
|
||||||
|
client.BaseAddress = new Uri(globalSettings.BaseServiceUri.InternalBilling);
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(requestModel);
|
||||||
|
var requestBody = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
var responseMessage = await client.PostAsync(endpoint, requestBody);
|
||||||
|
|
||||||
|
if (!responseMessage.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return (null, responseMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseContent = await responseMessage.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
var response = JsonSerializer.Deserialize<EventsResponseBody>(responseContent);
|
||||||
|
|
||||||
|
return (response, null);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Admin.Billing.Models.ProcessStripeEvents;
|
||||||
|
|
||||||
|
public class EventsFormModel : IValidatableObject
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string EventIds { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[DisplayName("Inspect Only")]
|
||||||
|
public bool Inspect { get; set; }
|
||||||
|
|
||||||
|
public List<string> GetEventIds() =>
|
||||||
|
EventIds?.Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(eventId => eventId.Trim())
|
||||||
|
.ToList() ?? [];
|
||||||
|
|
||||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
|
{
|
||||||
|
var eventIds = GetEventIds();
|
||||||
|
|
||||||
|
if (eventIds.Any(eventId => !eventId.StartsWith("evt_")))
|
||||||
|
{
|
||||||
|
yield return new ValidationResult("Event Ids must start with 'evt_'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Bit.Admin.Billing.Models.ProcessStripeEvents;
|
||||||
|
|
||||||
|
public class EventsRequestBody
|
||||||
|
{
|
||||||
|
[JsonPropertyName("eventIds")]
|
||||||
|
public List<string> EventIds { get; set; }
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Bit.Admin.Billing.Models.ProcessStripeEvents;
|
||||||
|
|
||||||
|
public class EventsResponseBody
|
||||||
|
{
|
||||||
|
[JsonPropertyName("events")]
|
||||||
|
public List<EventResponseBody> Events { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public EventActionType ActionType { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EventResponseBody
|
||||||
|
{
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("url")]
|
||||||
|
public string URL { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("apiVersion")]
|
||||||
|
public string APIVersion { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("createdUTC")]
|
||||||
|
public DateTime CreatedUTC { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("processingError")]
|
||||||
|
public string ProcessingError { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EventActionType
|
||||||
|
{
|
||||||
|
Inspect,
|
||||||
|
Process
|
||||||
|
}
|
25
src/Admin/Billing/Views/ProcessStripeEvents/Index.cshtml
Normal file
25
src/Admin/Billing/Views/ProcessStripeEvents/Index.cshtml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
@model Bit.Admin.Billing.Models.ProcessStripeEvents.EventsFormModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Process Stripe Events";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h1>Process Stripe Events</h1>
|
||||||
|
<form method="post" asp-controller="ProcessStripeEvents" asp-action="Process">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-1">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="submit" value="Process" class="btn btn-primary mb-2"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-2">
|
||||||
|
<div class="form-group form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" asp-for="Inspect">
|
||||||
|
<label class="form-check-label" asp-for="Inspect"></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea id="event-ids" type="text" class="form-control" rows="100" asp-for="EventIds"></textarea>
|
||||||
|
</div>
|
||||||
|
</form>
|
49
src/Admin/Billing/Views/ProcessStripeEvents/Results.cshtml
Normal file
49
src/Admin/Billing/Views/ProcessStripeEvents/Results.cshtml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
@using Bit.Admin.Billing.Models.ProcessStripeEvents
|
||||||
|
@model Bit.Admin.Billing.Models.ProcessStripeEvents.EventsResponseBody
|
||||||
|
|
||||||
|
@{
|
||||||
|
var title = Model.ActionType == EventActionType.Inspect ? "Inspect Stripe Events" : "Process Stripe Events";
|
||||||
|
ViewData["Title"] = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
<h1>@title</h1>
|
||||||
|
<h2>Results</h2>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
@if (!Model.Events.Any())
|
||||||
|
{
|
||||||
|
<p>No data found.</p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>API Version</th>
|
||||||
|
<th>Created</th>
|
||||||
|
@if (Model.ActionType == EventActionType.Process)
|
||||||
|
{
|
||||||
|
<th>Processing Error</th>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var eventResponseBody in Model.Events)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td><a href="@eventResponseBody.URL">@eventResponseBody.Id</a></td>
|
||||||
|
<td>@eventResponseBody.Type</td>
|
||||||
|
<td>@eventResponseBody.APIVersion</td>
|
||||||
|
<td>@eventResponseBody.CreatedUTC</td>
|
||||||
|
@if (Model.ActionType == EventActionType.Process)
|
||||||
|
{
|
||||||
|
<td>@eventResponseBody.ProcessingError</td>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
</div>
|
5
src/Admin/Billing/Views/_ViewImports.cshtml
Normal file
5
src/Admin/Billing/Views/_ViewImports.cshtml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@using Microsoft.AspNetCore.Identity
|
||||||
|
@using Bit.Admin.AdminConsole
|
||||||
|
@using Bit.Admin.AdminConsole.Models
|
||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
@addTagHelper "*, Admin"
|
3
src/Admin/Billing/Views/_ViewStart.cshtml
Normal file
3
src/Admin/Billing/Views/_ViewStart.cshtml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
}
|
@ -47,5 +47,6 @@ public enum Permission
|
|||||||
Tools_GenerateLicenseFile,
|
Tools_GenerateLicenseFile,
|
||||||
Tools_ManageTaxRates,
|
Tools_ManageTaxRates,
|
||||||
Tools_ManageStripeSubscriptions,
|
Tools_ManageStripeSubscriptions,
|
||||||
Tools_CreateEditTransaction
|
Tools_CreateEditTransaction,
|
||||||
|
Tools_ProcessStripeEvents
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,7 @@ public class Startup
|
|||||||
services.AddDefaultServices(globalSettings);
|
services.AddDefaultServices(globalSettings);
|
||||||
services.AddScoped<IAccessControlService, AccessControlService>();
|
services.AddScoped<IAccessControlService, AccessControlService>();
|
||||||
services.AddBillingOperations();
|
services.AddBillingOperations();
|
||||||
|
services.AddHttpClient();
|
||||||
|
|
||||||
#if OSS
|
#if OSS
|
||||||
services.AddOosServices();
|
services.AddOosServices();
|
||||||
@ -108,6 +109,7 @@ public class Startup
|
|||||||
{
|
{
|
||||||
o.ViewLocationFormats.Add("/Auth/Views/{1}/{0}.cshtml");
|
o.ViewLocationFormats.Add("/Auth/Views/{1}/{0}.cshtml");
|
||||||
o.ViewLocationFormats.Add("/AdminConsole/Views/{1}/{0}.cshtml");
|
o.ViewLocationFormats.Add("/AdminConsole/Views/{1}/{0}.cshtml");
|
||||||
|
o.ViewLocationFormats.Add("/Billing/Views/{1}/{0}.cshtml");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Jobs service
|
// Jobs service
|
||||||
|
@ -161,7 +161,8 @@ public static class RolePermissionMapping
|
|||||||
Permission.Tools_GenerateLicenseFile,
|
Permission.Tools_GenerateLicenseFile,
|
||||||
Permission.Tools_ManageTaxRates,
|
Permission.Tools_ManageTaxRates,
|
||||||
Permission.Tools_ManageStripeSubscriptions,
|
Permission.Tools_ManageStripeSubscriptions,
|
||||||
Permission.Tools_CreateEditTransaction
|
Permission.Tools_CreateEditTransaction,
|
||||||
|
Permission.Tools_ProcessStripeEvents,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ "sales", new List<Permission>
|
{ "sales", new List<Permission>
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
var canGenerateLicense = AccessControlService.UserHasPermission(Permission.Tools_GenerateLicenseFile);
|
var canGenerateLicense = AccessControlService.UserHasPermission(Permission.Tools_GenerateLicenseFile);
|
||||||
var canManageTaxRates = AccessControlService.UserHasPermission(Permission.Tools_ManageTaxRates);
|
var canManageTaxRates = AccessControlService.UserHasPermission(Permission.Tools_ManageTaxRates);
|
||||||
var canManageStripeSubscriptions = AccessControlService.UserHasPermission(Permission.Tools_ManageStripeSubscriptions);
|
var canManageStripeSubscriptions = AccessControlService.UserHasPermission(Permission.Tools_ManageStripeSubscriptions);
|
||||||
|
var canProcessStripeEvents = AccessControlService.UserHasPermission(Permission.Tools_ProcessStripeEvents);
|
||||||
|
|
||||||
var canViewTools = canChargeBraintree || canCreateTransaction || canPromoteAdmin ||
|
var canViewTools = canChargeBraintree || canCreateTransaction || canPromoteAdmin ||
|
||||||
canGenerateLicense || canManageTaxRates || canManageStripeSubscriptions;
|
canGenerateLicense || canManageTaxRates || canManageStripeSubscriptions;
|
||||||
@ -107,6 +108,12 @@
|
|||||||
Manage Stripe Subscriptions
|
Manage Stripe Subscriptions
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
@if (canProcessStripeEvents)
|
||||||
|
{
|
||||||
|
<a class="dropdown-item" asp-controller="ProcessStripeEvents" asp-action="Index">
|
||||||
|
Process Stripe Events
|
||||||
|
</a>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
"internalApi": "http://localhost:4000",
|
"internalApi": "http://localhost:4000",
|
||||||
"internalVault": "https://localhost:8080",
|
"internalVault": "https://localhost:8080",
|
||||||
"internalSso": "http://localhost:51822",
|
"internalSso": "http://localhost:51822",
|
||||||
"internalScim": "http://localhost:44559"
|
"internalScim": "http://localhost:44559",
|
||||||
|
"internalBilling": "http://localhost:44519"
|
||||||
},
|
},
|
||||||
"mail": {
|
"mail": {
|
||||||
"smtp": {
|
"smtp": {
|
||||||
|
68
src/Billing/Controllers/RecoveryController.cs
Normal file
68
src/Billing/Controllers/RecoveryController.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
using Bit.Billing.Models.Recovery;
|
||||||
|
using Bit.Billing.Services;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Http.HttpResults;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Stripe;
|
||||||
|
|
||||||
|
namespace Bit.Billing.Controllers;
|
||||||
|
|
||||||
|
[Route("stripe/recovery")]
|
||||||
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
|
public class RecoveryController(
|
||||||
|
IStripeEventProcessor stripeEventProcessor,
|
||||||
|
IStripeFacade stripeFacade,
|
||||||
|
IWebHostEnvironment webHostEnvironment) : Controller
|
||||||
|
{
|
||||||
|
private readonly string _stripeURL = webHostEnvironment.IsDevelopment() || webHostEnvironment.IsEnvironment("QA")
|
||||||
|
? "https://dashboard.stripe.com/test"
|
||||||
|
: "https://dashboard.stripe.com";
|
||||||
|
|
||||||
|
// ReSharper disable once RouteTemplates.ActionRoutePrefixCanBeExtractedToControllerRoute
|
||||||
|
[HttpPost("events/inspect")]
|
||||||
|
public async Task<Ok<EventsResponseBody>> InspectEventsAsync([FromBody] EventsRequestBody requestBody)
|
||||||
|
{
|
||||||
|
var inspected = await Task.WhenAll(requestBody.EventIds.Select(async eventId =>
|
||||||
|
{
|
||||||
|
var @event = await stripeFacade.GetEvent(eventId);
|
||||||
|
return Map(@event);
|
||||||
|
}));
|
||||||
|
|
||||||
|
var response = new EventsResponseBody { Events = inspected.ToList() };
|
||||||
|
|
||||||
|
return TypedResults.Ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once RouteTemplates.ActionRoutePrefixCanBeExtractedToControllerRoute
|
||||||
|
[HttpPost("events/process")]
|
||||||
|
public async Task<Ok<EventsResponseBody>> ProcessEventsAsync([FromBody] EventsRequestBody requestBody)
|
||||||
|
{
|
||||||
|
var processed = await Task.WhenAll(requestBody.EventIds.Select(async eventId =>
|
||||||
|
{
|
||||||
|
var @event = await stripeFacade.GetEvent(eventId);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await stripeEventProcessor.ProcessEventAsync(@event);
|
||||||
|
return Map(@event);
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
return Map(@event, exception.Message);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
var response = new EventsResponseBody { Events = processed.ToList() };
|
||||||
|
|
||||||
|
return TypedResults.Ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventResponseBody Map(Event @event, string processingError = null) => new()
|
||||||
|
{
|
||||||
|
Id = @event.Id,
|
||||||
|
URL = $"{_stripeURL}/workbench/events/{@event.Id}",
|
||||||
|
APIVersion = @event.ApiVersion,
|
||||||
|
Type = @event.Type,
|
||||||
|
CreatedUTC = @event.Created,
|
||||||
|
ProcessingError = processingError
|
||||||
|
};
|
||||||
|
}
|
9
src/Billing/Models/Recovery/EventsRequestBody.cs
Normal file
9
src/Billing/Models/Recovery/EventsRequestBody.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Bit.Billing.Models.Recovery;
|
||||||
|
|
||||||
|
public class EventsRequestBody
|
||||||
|
{
|
||||||
|
[JsonPropertyName("eventIds")]
|
||||||
|
public List<string> EventIds { get; set; }
|
||||||
|
}
|
31
src/Billing/Models/Recovery/EventsResponseBody.cs
Normal file
31
src/Billing/Models/Recovery/EventsResponseBody.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Bit.Billing.Models.Recovery;
|
||||||
|
|
||||||
|
public class EventsResponseBody
|
||||||
|
{
|
||||||
|
[JsonPropertyName("events")]
|
||||||
|
public List<EventResponseBody> Events { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EventResponseBody
|
||||||
|
{
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("url")]
|
||||||
|
public string URL { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("apiVersion")]
|
||||||
|
public string APIVersion { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("createdUTC")]
|
||||||
|
public DateTime CreatedUTC { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("processingError")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string ProcessingError { get; set; }
|
||||||
|
}
|
@ -16,6 +16,12 @@ public interface IStripeFacade
|
|||||||
RequestOptions requestOptions = null,
|
RequestOptions requestOptions = null,
|
||||||
CancellationToken cancellationToken = default);
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<Event> GetEvent(
|
||||||
|
string eventId,
|
||||||
|
EventGetOptions eventGetOptions = null,
|
||||||
|
RequestOptions requestOptions = null,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
Task<Invoice> GetInvoice(
|
Task<Invoice> GetInvoice(
|
||||||
string invoiceId,
|
string invoiceId,
|
||||||
InvoiceGetOptions invoiceGetOptions = null,
|
InvoiceGetOptions invoiceGetOptions = null,
|
||||||
|
@ -6,6 +6,7 @@ public class StripeFacade : IStripeFacade
|
|||||||
{
|
{
|
||||||
private readonly ChargeService _chargeService = new();
|
private readonly ChargeService _chargeService = new();
|
||||||
private readonly CustomerService _customerService = new();
|
private readonly CustomerService _customerService = new();
|
||||||
|
private readonly EventService _eventService = new();
|
||||||
private readonly InvoiceService _invoiceService = new();
|
private readonly InvoiceService _invoiceService = new();
|
||||||
private readonly PaymentMethodService _paymentMethodService = new();
|
private readonly PaymentMethodService _paymentMethodService = new();
|
||||||
private readonly SubscriptionService _subscriptionService = new();
|
private readonly SubscriptionService _subscriptionService = new();
|
||||||
@ -19,6 +20,13 @@ public class StripeFacade : IStripeFacade
|
|||||||
CancellationToken cancellationToken = default) =>
|
CancellationToken cancellationToken = default) =>
|
||||||
await _chargeService.GetAsync(chargeId, chargeGetOptions, requestOptions, cancellationToken);
|
await _chargeService.GetAsync(chargeId, chargeGetOptions, requestOptions, cancellationToken);
|
||||||
|
|
||||||
|
public async Task<Event> GetEvent(
|
||||||
|
string eventId,
|
||||||
|
EventGetOptions eventGetOptions = null,
|
||||||
|
RequestOptions requestOptions = null,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
await _eventService.GetAsync(eventId, eventGetOptions, requestOptions, cancellationToken);
|
||||||
|
|
||||||
public async Task<Customer> GetCustomer(
|
public async Task<Customer> GetCustomer(
|
||||||
string customerId,
|
string customerId,
|
||||||
CustomerGetOptions customerGetOptions = null,
|
CustomerGetOptions customerGetOptions = null,
|
||||||
|
@ -140,6 +140,7 @@ public class GlobalSettings : IGlobalSettings
|
|||||||
private string _internalSso;
|
private string _internalSso;
|
||||||
private string _internalVault;
|
private string _internalVault;
|
||||||
private string _internalScim;
|
private string _internalScim;
|
||||||
|
private string _internalBilling;
|
||||||
|
|
||||||
public BaseServiceUriSettings(GlobalSettings globalSettings)
|
public BaseServiceUriSettings(GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
@ -218,6 +219,12 @@ public class GlobalSettings : IGlobalSettings
|
|||||||
get => _globalSettings.BuildInternalUri(_scim, "scim");
|
get => _globalSettings.BuildInternalUri(_scim, "scim");
|
||||||
set => _internalScim = value;
|
set => _internalScim = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string InternalBilling
|
||||||
|
{
|
||||||
|
get => _globalSettings.BuildInternalUri(_internalBilling, "billing");
|
||||||
|
set => _internalBilling = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SqlSettings
|
public class SqlSettings
|
||||||
|
@ -20,4 +20,5 @@ public interface IBaseServiceUriSettings
|
|||||||
public string InternalVault { get; set; }
|
public string InternalVault { get; set; }
|
||||||
public string InternalSso { get; set; }
|
public string InternalSso { get; set; }
|
||||||
public string InternalScim { get; set; }
|
public string InternalScim { get; set; }
|
||||||
|
public string InternalBilling { get; set; }
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user