Repository navigation
fix: notify returning Play subscribers about renewal - #776
Conversation
|
Copy approval ( |
There was a problem hiding this comment.
Important
The new notice can tell a returning subscriber that renewal is off while Google Play will still renew and charge. Cancellation ownership is an intent marker, not confirmation that the provider accepted cancellation.
Reviewed changes: Reviewed the account-reactivation notification, billing ownership cleanup, both authentication paths, and their regression coverage.
- Shared reactivation flow: Email-code and Google sign-in invoke the new scoped
AccountReactivationservice within their existing transactions. - Play recovery notice: Deletion-owned Play subscriptions receive localized renewal instructions, and matching owed cancellation records are removed.
- Ownership and persistence: Reactivation clears Play ownership while retaining Stripe restoration behavior, with SQLite tests for notification persistence, entitlement preservation, retries, and rollback.
- Verification: All 63 focused tests passed; a separate temporary reproduction confirmed the failure-before-provider-mutation case described inline.
gpt-6.1-sol | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes: Reviewed 0fe80701 since the prior Pullfrog review at 98107fd5, with the full PR diff as context.
- Verified renewal before notification: Moved notice creation into a post-sign-in job that checks Play outside the database transaction and creates a notice only when renewal is confirmed off.
- Preserved safe delivery: Added account and purchase-token rechecks under the user row lock, provider-failure retries, and per-reactivation deduplication that also respects dismissed notices.
- Expanded regression coverage: Added failure-before-cancellation, unknown-purchase, account-change, rollback, and replay cases. All 131 focused Infrastructure, Application, and Domain tests passed.
gpt-6.1-sol | 𝕏
|
…d cancellations and the Play renewal notice (#746) (#778) * Deprecate AI memory HTTP contracts for ORB-229 (part 1) (#771) (cherry picked from commit f8abd02) * Deprecate challenge and accountability routes for ORB-212 (#772) (cherry picked from commit 572b8de) * Deprecate social API routes and required profile fields (ORB-233 part 1) (#773) (cherry picked from commit 9c414c7) * fix: exclude retired notifications from inbox counts (#774) (cherry picked from commit f5df366) * fix: retain owed cancellations after account erasure (#775) * fix: retain provider cancellation obligations after account erasure * fix: preserve erasure for accounts without billing obligations * fix: claim owed cancellations before provider calls * fix: alert owed cancellation exhaustion only after a failed attempt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit c369535) * fix: notify returning Play subscribers about renewal (#776) * fix: explain Play renewal recovery on account reactivation * test: register reactivation service in sign-in retry fixture * fix: verify Play renewal before reactivation notice (cherry picked from commit 96023f9) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>




A Play subscriber who reactivates after requesting account deletion now receives one unread notice explaining that renewal is off and how to restart it in Google Play. The same transaction clears deletion ownership and removes the matching owed Play cancellation, so a delayed cancellation retry cannot undo the person's recovery action.
Closes thomasluizon/orbit-tickets#1548
Approach
Auth/Commands/AccountReactivation.cscentralizes the ownership check and localized notification creation. BothVerifyCodeCommandandGoogleSignInFlowcall it inside their existing sign-in transaction, using the stored account language. The existing notification query exposes the notice to web and Android.User.CancelDeactivationclearsPlayRenewalStoppedByDeletionwhile retaining Stripe ownership for its existing restoration job. The existing paid-period entitlement is preserved.PlayReactivationTestsdrives the real deletion cancellation job, both real sign-in paths, relational persistence, the owed cancellation retry, and the shared notification query. Provider ports are mocked; SQLite uses the repository's existing unit-test factory. New reactivation code makes no provider calls.Google API evidence
Checked the live reference pages on 2026-10-19:
SUBSCRIPTION_STATE_CANCELEDwith renewal disabled.USER_REQUESTED_STOP_RENEWALSallows restoration by the subscriber. This is the cancellation type already used by Orbit.DEVELOPER_REQUESTED_STOP_PAYMENTScannot be restored.No new external API method or response field is read by this change. No invented provider response fixture is needed; the tests use Orbit's existing
IPlayBillingServiceport and domain factories.Assumptions
Test evidence
Regression:
VerifyCodeCommandHandlerTests.Reactivation_RestoresOnlyUnexpiredStripeRenewalandGoogleCodeAuthCommandHandlerTests.Reactivation_RestoresOnlyUnexpiredStripeRenewal.dotnet test tests/Orbit.Application.Tests --filter 'FullyQualifiedName~Reactivation_RestoresOnlyUnexpiredStripeRenewal' --verbosity quietpassed all 8 cases, exit 0.Additional focused coverage includes en and pt-BR, both sign-in methods, paid and expired subscriptions, duplicate sign-ins, independently canceled subscriptions, matching versus unrelated owed records, delayed cancellation retries, and save/commit rollback followed by a successful retry.
dotnet test tests/Orbit.Infrastructure.Tests --filter 'FullyQualifiedName~PlayReactivationTests|FullyQualifiedName~DeletionCancellationPersistenceTests' --verbosity quietpassed all 33 cases, exit 0.dotnet test tests/Orbit.Domain.Tests --filter 'FullyQualifiedName~UserDeletionCancellationTests' --verbosity quietpassed all 22 cases, exit 0.Final verification, all exit 0:
dotnet build Orbit.slnx --verbosity quiet: 0 errors; existing dependency, versioning, and obsolete-API warnings only. OpenAPI output is unchanged.dotnet test tests/Orbit.Infrastructure.Tests --filter 'FullyQualifiedName~PlayReactivationTests' --verbosity quiet: 14 passed.dotnet test tests/Orbit.Domain.Tests --verbosity quiet: 750 passed.dotnet test tests/Orbit.Application.Tests --verbosity quiet: 5,573 passed.dotnet test tests/Orbit.Infrastructure.Tests --verbosity quiet: 3,801 passed.dotnet test tests/Orbit.Infrastructure.Tests --filter FullyQualifiedName~PlayReactivationTests --verbosity minimal: 14 passed with the defect present.dotnet test tests/Orbit.Infrastructure.Tests --no-restore --filter FullyQualifiedName~SignIn_DeletionOwnedButPlayStillRenewing_DoesNotNotify --verbosity minimal: 2 failed, both finding one notice while Play still renewed.dotnet test tests/Orbit.Infrastructure.Tests --no-build --no-restore --filter FullyQualifiedName~PlayReactivationTests --verbosity minimal: 25 passed, including both regression cases through the queued job.dotnet build Orbit.slnx --no-restore --verbosity minimal: 0 errors. Generated OpenAPI files unchanged.Full projects, each run with
dotnet test <path> --no-build --no-restore --verbosity minimal:tests/Orbit.Domain.Teststests/Orbit.Application.Teststests/Orbit.Infrastructure.TestsCopy
English title: Your Google Play renewal is off
English body: Your Orbit account is active again. Deleting your account turned off your Google Play renewal. To renew again, open Google Play, go to Payments & subscriptions > Subscriptions, select Orbit, and tap Resubscribe. If that option is unavailable, subscribe to Pro again in Orbit.
pt-BR title: Sua renovação no Google Play está desativada
pt-BR body: Sua conta do Orbit está ativa de novo. A renovação no Google Play foi desativada quando você pediu a exclusão da conta. Para voltar a renovar, abra o Google Play, vá em Pagamentos e assinaturas > Assinaturas, escolha Orbit e toque em Assinar novamente. Se essa opção não aparecer, assine o Pro de novo pelo Orbit.
Reviewed both languages through the repository's
/second-opinionhelper withgpt-5.6-sol. The final review rejected the candidate finding that expired subscribers had no recovery path: both strings offer purchasing Pro again through Orbit, and the pt-BR wording was judged clear and natural. The copy is included for the orchestrator's approval.Manual steps
None beyond normal API deployment. No new configuration, secrets, console settings, migrations, or backfills are required.