Add a 'Pending invoice' pill in portal subscription when invoice processing

This commit is contained in:
Nicolas Dorier
2025-11-19 15:00:39 +09:00
parent 414c71ec09
commit 2f8be8c9f0
5 changed files with 22 additions and 1 deletions

View File

@@ -56,6 +56,9 @@ public class SubscriberData : BaseEntityData
public decimal MissingCredit() public decimal MissingCredit()
=> Math.Max(0m, NextPlan.Price - GetCredit(NextPlan.Currency)); => Math.Max(0m, NextPlan.Price - GetCredit(NextPlan.Currency));
[Column("processing_invoice_id")]
public string? ProcessingInvoiceId { get; set; }
[Required] [Required]
[Column("phase")] [Column("phase")]
public PhaseTypes Phase { get; set; } = PhaseTypes.Expired; public PhaseTypes Phase { get; set; } = PhaseTypes.Expired;

View File

@@ -200,6 +200,7 @@ namespace BTCPayServer.Migrations
plan_id = table.Column<string>(type: "text", nullable: false), plan_id = table.Column<string>(type: "text", nullable: false),
new_plan_id = table.Column<string>(type: "text", nullable: true), new_plan_id = table.Column<string>(type: "text", nullable: true),
paid_amount = table.Column<decimal>(type: "numeric", nullable: true), paid_amount = table.Column<decimal>(type: "numeric", nullable: true),
processing_invoice_id = table.Column<string>(type: "text", nullable: true),
phase = table.Column<string>(type: "text", nullable: false, defaultValueSql: "'Expired'::TEXT"), phase = table.Column<string>(type: "text", nullable: false, defaultValueSql: "'Expired'::TEXT"),
plan_started = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), plan_started = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
period_end = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true), period_end = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),

View File

@@ -1567,6 +1567,10 @@ namespace BTCPayServer.Migrations
.HasColumnName("plan_started") .HasColumnName("plan_started")
.HasDefaultValueSql("now()"); .HasDefaultValueSql("now()");
b.Property<string>("ProcessingInvoiceId")
.HasColumnType("text")
.HasColumnName("processing_invoice_id");
b.Property<string>("SuspensionReason") b.Property<string>("SuspensionReason")
.HasColumnType("text") .HasColumnType("text")
.HasColumnName("suspension_reason"); .HasColumnName("suspension_reason");

View File

@@ -375,7 +375,6 @@ public class SubscriptionHostedService(
var checkout = await ctx.PlanCheckouts.GetCheckout(checkoutId); var checkout = await ctx.PlanCheckouts.GetCheckout(checkoutId);
var plan = checkout?.Plan; var plan = checkout?.Plan;
if (checkout is null || plan is null || if (checkout is null || plan is null ||
(invoice.Status == InvoiceStatus.Processing && !plan.OptimisticActivation) ||
checkout.Plan.Offering.App.StoreDataId != invoice.StoreId) checkout.Plan.Offering.App.StoreDataId != invoice.StoreId)
return; return;
@@ -384,6 +383,16 @@ public class SubscriptionHostedService(
throw new InvalidOperationException("Bug: Subscriber is null and not a new subscriber"); throw new InvalidOperationException("Bug: Subscriber is null and not a new subscriber");
var sub = checkout.Subscriber; var sub = checkout.Subscriber;
var processingInvoiceId = invoice.Status == InvoiceStatus.Processing ? invoice.Id : null;
if (sub is not null &&
sub.ProcessingInvoiceId != processingInvoiceId)
{
sub.ProcessingInvoiceId = processingInvoiceId;
await ctx.SaveChangesAsync();
}
if (invoice.Status == InvoiceStatus.Processing && !plan.OptimisticActivation)
return;
if (invoice.Status is InvoiceStatus.Settled or InvoiceStatus.Processing) if (invoice.Status is InvoiceStatus.Settled or InvoiceStatus.Processing)
{ {

View File

@@ -280,6 +280,10 @@
{ {
<span class="badge badge-translucent rounded-pill text-bg-warning">Grace</span> <span class="badge badge-translucent rounded-pill text-bg-warning">Grace</span>
} }
@if (Model.Data.Subscriber is { ProcessingInvoiceId: {} pendingInvoiceId, OptimisticActivation: false })
{
<span class="badge badge-translucent rounded-pill text-bg-warning" title="@pendingInvoiceId">Pending invoice</span>
}
</h5> </h5>
@if (Model.Subscriber.Plan.Description is not null) @if (Model.Subscriber.Plan.Description is not null)
{ {