Persist subscription lapse reasons for #344 - #486
Conversation
|
I will add a lapse reason value and ending date to User, thread explicit reasons through both cancellation methods, split Stripe terminal statuses, mirror Play cancellation, and record invoice.payment_failed. Persistence configuration and an EF migration will retain the fields. SubscriptionStatusResponse and its query will append nullable values. Focused domain, webhook, and query tests will prove distinct reasons and null behavior. This keeps historical provider state on User because provider ids are deliberately cleared. I rejected deriving lapse state from live Stripe or Play records because the status endpoint must still answer after those ids are gone. |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes Reviewed the initial scaffold for the planned subscription lapse reason work.
- Empty scaffold: The sole commit changes no files, so there is no implementation, persistence, migration, API contract, or test surface to assess yet.
GPT Sol | 𝕏
There was a problem hiding this comment.
Important
Two billing state paths can persist a lapse reason that does not describe the user's current or latest entitlement and must be corrected before merge.
Reviewed changes Reviewed the implementation added since the prior Pullfrog review at 0cee9a8.
- Persisted lapse history Added nullable reason and ending timestamp state to
User, EF persistence, and the subscription status response. - Mapped provider lifecycles Added Stripe payment failure and cancellation mapping plus Play account hold, cancellation, and expiration mapping.
- Expanded lifecycle coverage Added domain, status query, Stripe webhook, and Play RTDN tests for the new state.
- Refreshed architecture artifacts Regenerated the architecture map after the application changes.
GPT Sol | 𝕏
There was a problem hiding this comment.
Important
No new high-severity issues were introduced, but the two open billing-state integrity findings remain unresolved, so this revision is still not mergeable.
Reviewed changes Reviewed the mapping coverage and generated-artifact changes added since the prior Pullfrog review.
- Expanded Play mapping coverage Added exact tests for account hold, cancellation preservation, expiration preservation, and revocation.
- Expanded Stripe mapping coverage Added cancellation-details fixtures and assertions for cancellation requested, payment failed, and payment disputed.
- Covered contract serialization Added direct tests for known and unknown lapse-reason API values.
- Refreshed generated artifacts Updated the architecture map after adding the mapping test class.
- Merged current base Incorporated the latest
mainchanges without altering subscription behavior.
GPT Sol | 𝕏
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes Reviewed the provider ownership and Stripe lifecycle ordering changes added since the prior Pullfrog review.
- Enforced provider ownership: Scoped pending lapse writes to the provider that owns the current entitlement and added coverage for both cross-provider directions.
- Ordered Stripe outcomes: Persisted the latest accepted Stripe event time and applied it across checkout, renewal, failure, update, and deletion paths so older outcomes cannot overwrite newer state.
- Covered stale delivery: Added a signed-webhook regression test that delivers
invoice.paidbefore an olderinvoice.payment_failedand verifies the renewed state remains clear. - Persisted the ordering boundary: Added the nullable user column, EF migration, generated model metadata, and snapshot update for the Stripe event watermark.
GPT Sol | 𝕏




Fixes #344.
Summary
External interface evidence
Stripe.net 52.2.0 is the installed package. Its generated XML documents InvoiceParent.SubscriptionDetails, InvoiceParentSubscriptionDetails.SubscriptionId, Subscription.CancellationDetails, SubscriptionCancellationDetails.Reason, and Event.Created. Event.Created is documented as seconds since the Unix epoch. The reason values used here are cancellation_requested, payment_failed, and payment_disputed. Reproduce with dotnet list src/Orbit.Application/Orbit.Application.csproj package and inspect the installed stripe.net 52.2.0 Stripe.net.xml members.
The redacted invoice shape read by the handler is:
The redacted subscription fields read by the handler are:
The redacted event ordering field read by the handler is:
Google documents Play RTDN subscription notification type 3 as canceled, type 5 as account hold, and type 13 as expired at https://developer.android.com/google/play/billing/rtdn-reference. The handler verifies the purchase through the existing provider API path before applying these values.
Verification
Assumptions