Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public async Task HandleAsync(Event parsedEvent)
Status: not StripeConstants.InvoiceStatus.Paid,
CollectionMethod: "charge_automatically",
BillingReason:
"subscription_cycle" or
"automatic_pending_invoice_item_invoice",
StripeConstants.BillingReasons.SubscriptionCycle or
StripeConstants.BillingReasons.AutomaticPendingInvoiceItemInvoice,
Parent.SubscriptionDetails.Subscription: not null
})
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ invoice is
AmountDue: > 0,
Status: not StripeConstants.InvoiceStatus.Paid,
CollectionMethod: "charge_automatically",
BillingReason: "subscription_cycle" or "automatic_pending_invoice_item_invoice",
BillingReason: StripeConstants.BillingReasons.SubscriptionCycle or StripeConstants.BillingReasons.AutomaticPendingInvoiceItemInvoice,
Parent.SubscriptionDetails: not null
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ invoice is
AmountDue: > 0,
Status: not StripeConstants.InvoiceStatus.Paid,
CollectionMethod: "charge_automatically",
BillingReason: "subscription_cycle" or "automatic_pending_invoice_item_invoice",
BillingReason: StripeConstants.BillingReasons.SubscriptionCycle or StripeConstants.BillingReasons.AutomaticPendingInvoiceItemInvoice,
Parent.SubscriptionDetails: not null
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,21 @@ public async Task HandleAsync(Event parsedEvent)

var currentPeriodEnd = subscription.GetCurrentPeriodEnd();

if (SubscriptionWentUnpaid(parsedEvent, subscription) ||
SubscriptionWentIncompleteExpired(parsedEvent, subscription))
var clearOrgBillingAutomationExemption = false;

if (SubscriptionWentUnpaid(parsedEvent, subscription))
{
if (await SkipUnpaidBillingAutomationsForExemptOrganizationAsync(subscriberId))
{
clearOrgBillingAutomationExemption = true;
}
else
{
await DisableSubscriberAsync(subscriberId, currentPeriodEnd);
await SetSubscriptionToCancelAsync(subscription);
}
}
Comment thread
kdenney marked this conversation as resolved.
else if (SubscriptionWentIncompleteExpired(parsedEvent, subscription))
{
await DisableSubscriberAsync(subscriberId, currentPeriodEnd);
await SetSubscriptionToCancelAsync(subscription);
Expand Down Expand Up @@ -114,6 +127,11 @@ await subscriberId.Match(
}

await RemovePasswordManagerCouponIfRemovingSecretsManagerTrialAsync(parsedEvent, subscription);

if (clearOrgBillingAutomationExemption)
{
await ClearBillingAutomationExemptionAsync(organizationId.Value);
}
},
_ => Task.CompletedTask);
}
Expand All @@ -130,7 +148,7 @@ SubscriptionStatus.Active or
} && currentSubscription is
{
Status: SubscriptionStatus.Unpaid,
LatestInvoice.BillingReason: BillingReasons.SubscriptionCreate or BillingReasons.SubscriptionCycle
LatestInvoice.BillingReason: BillingReasons.SubscriptionCycle
};

private static bool SubscriptionWentIncompleteExpired(
Expand Down Expand Up @@ -189,6 +207,44 @@ private Task DisableSubscriberAsync(SubscriberId subscriberId, DateTime? current
}
});

private Task<bool> SkipUnpaidBillingAutomationsForExemptOrganizationAsync(SubscriberId subscriberId) =>
subscriberId.Match(
_ => Task.FromResult(false),
async organizationId =>
{
var organization = await _organizationRepository.GetByIdAsync(organizationId.Value);

if (organization is not { Enabled: true, ExemptFromBillingAutomation: true })
{
return false;
}

_logger.LogInformation(
"Skipping billing automations for exempt organization ({OrganizationId}). Exemption will be cleared after handler completion",
organizationId.Value);

return true;
},
_ => Task.FromResult(false));

private async Task ClearBillingAutomationExemptionAsync(Guid organizationId)
{
var organization = await _organizationRepository.GetByIdAsync(organizationId);

if (organization == null)
{
return;
}

organization.ExemptFromBillingAutomation = false;
organization.RevisionDate = DateTime.UtcNow;
await _organizationRepository.ReplaceAsync(organization);

_logger.LogInformation(
"Exemption has been cleared for organization ({OrganizationId})",
organizationId);
}

private Task EnableSubscriberAsync(SubscriberId subscriberId, DateTime? currentPeriodEnd) =>
subscriberId.Match(
async userId =>
Expand Down
2 changes: 2 additions & 0 deletions src/Core/Billing/Constants/StripeConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ public static class AutomaticTaxStatus

public static class BillingReasons
{
public const string AutomaticPendingInvoiceItemInvoice = "automatic_pending_invoice_item_invoice";
public const string SubscriptionCreate = "subscription_create";
public const string SubscriptionCycle = "subscription_cycle";
public const string SubscriptionUpdate = "subscription_update";
}

public static class CollectionMethod
Expand Down
Loading
Loading