[PR #6793] [PM-33946] feat: Add dynamic pricing and fix checkout flow #19625

Open
opened 2026-04-15 03:56:17 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/bitwarden/android/pull/6793
Author: @SaintPatrck
Created: 4/13/2026
Status: 🔄 Open

Base: mainHead: premium-upgrade/PM-33946-dynamic-pricing-impl


📝 Commits (6)

  • f38ae8b [PM-33946] feat: Replace static pricing with dynamic data from plans API
  • a78bd73 Address code review findings for pricing error handling
  • 9f00370 Fix checkout callback URL, dialog timing, and result handling
  • 383dca9 Use syncForResult to prevent stuck loading dialog on sync failure
  • 30669f6 Pass PremiumCheckoutCallbackResult through SpecialCircumstance directly
  • 4a6de0d Address review findings and add missing test coverage

📊 Changes

23 files changed (+1290 additions, -102 deletions)

View changed files

📝 app/src/main/kotlin/com/x8bit/bitwarden/MainViewModel.kt (+9 -5)
📝 app/src/main/kotlin/com/x8bit/bitwarden/data/billing/repository/BillingRepository.kt (+6 -0)
📝 app/src/main/kotlin/com/x8bit/bitwarden/data/billing/repository/BillingRepositoryImpl.kt (+15 -0)
app/src/main/kotlin/com/x8bit/bitwarden/data/billing/repository/model/PremiumPlanPricingResult.kt (+28 -0)
app/src/main/kotlin/com/x8bit/bitwarden/data/billing/util/PremiumCheckoutUtils.kt (+65 -0)
📝 app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/model/SpecialCircumstance.kt (+4 -1)
📝 app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanScreen.kt (+35 -1)
📝 app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanViewModel.kt (+287 -42)
📝 app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/handlers/PlanHandlers.kt (+10 -0)
📝 app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModel.kt (+1 -1)
📝 app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/util/ShortcutUtils.kt (+1 -1)
📝 app/src/test/kotlin/com/x8bit/bitwarden/MainViewModelTest.kt (+97 -7)
📝 app/src/test/kotlin/com/x8bit/bitwarden/data/billing/repository/BillingRepositoryTest.kt (+51 -0)
app/src/test/kotlin/com/x8bit/bitwarden/data/billing/util/PremiumCheckoutUtilsTest.kt (+65 -0)
📝 app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanScreenTest.kt (+148 -0)
📝 app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanViewModelTest.kt (+327 -42)
📝 app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/util/ShortcutUtilsTest.kt (+13 -2)
📝 network/src/main/kotlin/com/bitwarden/network/api/AuthenticatedBillingApi.kt (+8 -0)
network/src/main/kotlin/com/bitwarden/network/model/PremiumPlanResponseJson.kt (+51 -0)
📝 network/src/main/kotlin/com/bitwarden/network/service/BillingService.kt (+6 -0)

...and 3 more files

📄 Description

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-33946

📔 Objective

Replace the static placeholder rate with dynamic pricing from the plans API, and implement correct checkout result handling for the premium upgrade flow.

Dynamic pricing:

  • Fetch pricing from GET /api/plans/premium on PlanScreen load
  • Show loading and error states when pricing is unavailable
  • Format the monthly rate from the annual plan price

Checkout result handling:

  • Correct the callback URL to bitwarden://premium-checkout-result to match the server-configured Stripe redirect (PM-34173)
  • Parse ?result=success / ?result=canceled query parameter via PremiumCheckoutCallbackResult sealed class, following the existing CookieCallbackResult pattern
  • Observe specialCircumstanceStateFlow at runtime to detect when the user returns from checkout
  • On success: show syncing state, trigger syncForResult(), navigate back with snackbar if premium is detected, or show "Upgrade pending" dialog with Sync now / Continue options if not yet provisioned
  • On canceled: show "Payment not received yet" dialog per Figma designs
  • Track checkout return state via isAwaitingPremiumStatus on ViewState.Free

📸 Screenshots


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/bitwarden/android/pull/6793 **Author:** [@SaintPatrck](https://github.com/SaintPatrck) **Created:** 4/13/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `premium-upgrade/PM-33946-dynamic-pricing-impl` --- ### 📝 Commits (6) - [`f38ae8b`](https://github.com/bitwarden/android/commit/f38ae8bfe1f01224c14d3d68402aa5e8d72d9595) [PM-33946] feat: Replace static pricing with dynamic data from plans API - [`a78bd73`](https://github.com/bitwarden/android/commit/a78bd73b0be07d0edc60c45b81e85f2a8abee4c1) Address code review findings for pricing error handling - [`9f00370`](https://github.com/bitwarden/android/commit/9f00370eede8674d83897ed00a1b8584a064995b) Fix checkout callback URL, dialog timing, and result handling - [`383dca9`](https://github.com/bitwarden/android/commit/383dca91982d39b371b64b933e3c2c3f918a2f96) Use syncForResult to prevent stuck loading dialog on sync failure - [`30669f6`](https://github.com/bitwarden/android/commit/30669f65e67f90fedcdbe4c3f46a9c25f8aff310) Pass PremiumCheckoutCallbackResult through SpecialCircumstance directly - [`4a6de0d`](https://github.com/bitwarden/android/commit/4a6de0d5e10fe87ff6bfaddb69c6465ae343b99f) Address review findings and add missing test coverage ### 📊 Changes **23 files changed** (+1290 additions, -102 deletions) <details> <summary>View changed files</summary> 📝 `app/src/main/kotlin/com/x8bit/bitwarden/MainViewModel.kt` (+9 -5) 📝 `app/src/main/kotlin/com/x8bit/bitwarden/data/billing/repository/BillingRepository.kt` (+6 -0) 📝 `app/src/main/kotlin/com/x8bit/bitwarden/data/billing/repository/BillingRepositoryImpl.kt` (+15 -0) ➕ `app/src/main/kotlin/com/x8bit/bitwarden/data/billing/repository/model/PremiumPlanPricingResult.kt` (+28 -0) ➕ `app/src/main/kotlin/com/x8bit/bitwarden/data/billing/util/PremiumCheckoutUtils.kt` (+65 -0) 📝 `app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/model/SpecialCircumstance.kt` (+4 -1) 📝 `app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanScreen.kt` (+35 -1) 📝 `app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanViewModel.kt` (+287 -42) 📝 `app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/handlers/PlanHandlers.kt` (+10 -0) 📝 `app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/rootnav/RootNavViewModel.kt` (+1 -1) 📝 `app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/util/ShortcutUtils.kt` (+1 -1) 📝 `app/src/test/kotlin/com/x8bit/bitwarden/MainViewModelTest.kt` (+97 -7) 📝 `app/src/test/kotlin/com/x8bit/bitwarden/data/billing/repository/BillingRepositoryTest.kt` (+51 -0) ➕ `app/src/test/kotlin/com/x8bit/bitwarden/data/billing/util/PremiumCheckoutUtilsTest.kt` (+65 -0) 📝 `app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanScreenTest.kt` (+148 -0) 📝 `app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/premium/plan/PlanViewModelTest.kt` (+327 -42) 📝 `app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/util/ShortcutUtilsTest.kt` (+13 -2) 📝 `network/src/main/kotlin/com/bitwarden/network/api/AuthenticatedBillingApi.kt` (+8 -0) ➕ `network/src/main/kotlin/com/bitwarden/network/model/PremiumPlanResponseJson.kt` (+51 -0) 📝 `network/src/main/kotlin/com/bitwarden/network/service/BillingService.kt` (+6 -0) _...and 3 more files_ </details> ### 📄 Description ## 🎟️ Tracking https://bitwarden.atlassian.net/browse/PM-33946 ## 📔 Objective Replace the static placeholder rate with dynamic pricing from the plans API, and implement correct checkout result handling for the premium upgrade flow. **Dynamic pricing:** - Fetch pricing from `GET /api/plans/premium` on PlanScreen load - Show loading and error states when pricing is unavailable - Format the monthly rate from the annual plan price **Checkout result handling:** - Correct the callback URL to `bitwarden://premium-checkout-result` to match the server-configured Stripe redirect (PM-34173) - Parse `?result=success` / `?result=canceled` query parameter via `PremiumCheckoutCallbackResult` sealed class, following the existing `CookieCallbackResult` pattern - Observe `specialCircumstanceStateFlow` at runtime to detect when the user returns from checkout - On success: show syncing state, trigger `syncForResult()`, navigate back with snackbar if premium is detected, or show "Upgrade pending" dialog with Sync now / Continue options if not yet provisioned - On canceled: show "Payment not received yet" dialog per Figma designs - Track checkout return state via `isAwaitingPremiumStatus` on `ViewState.Free` ## 📸 Screenshots <!-- Upload images/videos here --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-04-15 03:56:17 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/android#19625