Skip to content

[PM-35454] feat: Add subscription API, domain models, and status badge component#6818

Merged
SaintPatrck merged 6 commits intomainfrom
premium-upgrade/PM-35454-subscription-backend
Apr 27, 2026
Merged

[PM-35454] feat: Add subscription API, domain models, and status badge component#6818
SaintPatrck merged 6 commits intomainfrom
premium-upgrade/PM-35454-subscription-backend

Conversation

@SaintPatrck
Copy link
Copy Markdown
Contributor

@SaintPatrck SaintPatrck commented Apr 21, 2026

🎟️ Tracking

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

📔 Objective

Introduces the subscription retrieval path (network API, service, repository, domain models) so premium users can soon see their actual billed rate, line items, and next-charge details, plus a reusable BitwardenStatusBadge for rendering subscription status.

No user-visible behavior change in this PR — the new capabilities are consumed by the follow-up PM-35455, which stacks on top of this branch.

  • cadence, estimatedTax, discountAmount, and nextChargeTotal are surfaced through SubscriptionInfo. Currency fields use BigDecimal (not Double) to avoid floating-point precision loss on cart math; a BigDecimalSerializer preserves the server's unquoted JSON number contract. discountAmount and nextChargeTotal are resolved at mapping time since the server does not expose a precomputed total.
  • BitwardenStatusBadge ships with success / error / warning color variants that differentiate light and dark themes per the Figma spec; consumers supply the label and variant.

📸 Screenshots

@SaintPatrck SaintPatrck requested review from a team and david-livefront as code owners April 21, 2026 20:54
@github-actions github-actions Bot added app:password-manager Bitwarden Password Manager app context app:authenticator Bitwarden Authenticator app context t:feature Change Type - Feature Development labels Apr 21, 2026
Comment on lines +348 to +354
val statusBadgeGreenBorder: Color = Color(color = 0xFFB9F8CF)
val statusBadgeGreenBg: Color = Color(color = 0xFFF0FDF4)
val statusBadgeRedBorder: Color = Color(color = 0xFFFFC9C9)
val statusBadgeRedBg: Color = Color(color = 0xFFFEF2F2)
val statusBadgeOrangeBorder: Color = Color(color = 0xFFFCD9BD)
val statusBadgeOrangeBg: Color = Color(color = 0xFFFFF8F1)
val statusBadgeOrangeText: Color = Color(color = 0xFFB23300)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RishikaSG-28 @david-livefront I couldn't find {color}-{shade} names in our component library to stay consistent with the other primitive color names. Suggestions on how to proceed here? Will we have more primitives? Does it makes sense to continue using {color}-{shade} naming convention?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @SaintPatrck thanks for bringing this up! We're going to bring in the primitives from the Byte design system so everything stays consistent. I'll get those added to the component library and share it with you soon!

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 21, 2026

Codecov Report

❌ Patch coverage is 84.48276% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.53%. Comparing base (1aba32f) to head (e50bdb6).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...arden/core/data/serializer/BigDecimalSerializer.kt 35.71% 6 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6818      +/-   ##
==========================================
+ Coverage   85.44%   85.53%   +0.08%     
==========================================
  Files         875      835      -40     
  Lines       60570    59156    -1414     
  Branches     8642     8615      -27     
==========================================
- Hits        51757    50599    -1158     
+ Misses       5833     5587     -246     
+ Partials     2980     2970      -10     
Flag Coverage Δ
app-data 17.46% <74.13%> (+0.22%) ⬆️
app-ui-auth-tools 20.26% <0.00%> (-0.24%) ⬇️
app-ui-platform 15.47% <0.00%> (-0.15%) ⬇️
app-ui-vault 25.90% <0.00%> (-0.56%) ⬇️
authenticator 6.65% <0.00%> (-0.03%) ⬇️
lib-core-network-bridge 4.26% <13.79%> (+<0.01%) ⬆️
lib-data-ui 1.03% <3.44%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link
Copy Markdown
Contributor

Logo
Checkmarx One – Scan Summary & Details607fc986-dd77-43fe-8f1e-2df5a21e2579

Great job! No new security vulnerabilities introduced in this pull request

@SaintPatrck SaintPatrck marked this pull request as draft April 21, 2026 21:01
@SaintPatrck SaintPatrck added the ai-review-vnext Request a Claude code review using the vNext workflow label Apr 21, 2026
@SaintPatrck SaintPatrck force-pushed the premium-upgrade/PM-33517-settings-premium_bak branch from 1ec1096 to a6452ec Compare April 22, 2026 12:37
}

private fun SubscriptionStatusJson.toPremiumSubscriptionStatus(): PremiumSubscriptionStatus =
when (this) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andrebispo5 do you know what status badge should that map to?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RishikaSG-28 I don't see "PAUSED" represented in Figma. Should we use the warning (orange) badge colors? Is "Paused" the desired text?

Copy link
Copy Markdown

@RishikaSG-28 RishikaSG-28 Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SaintPatrck Yes, orange/warning for Paused! Since the user is choosing to pause, "Paused" feels right. It's their action, their control.

Here's what I am thinking about the badge colors across all states:
Green (success) — subscription is running, everything's good
Orange (warning) — needs attention or action, but not terminated
Red (error) — subscription is ended or has a critical issue

Base automatically changed from premium-upgrade/PM-33517-settings-premium_bak to main April 22, 2026 14:57
val quantity: Long,

@SerialName("cost")
val cost: Double,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currency should be stored in a BigDecimal

@SaintPatrck SaintPatrck force-pushed the premium-upgrade/PM-35454-subscription-backend branch from cbf4111 to 0cb83ba Compare April 23, 2026 20:44
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📓 @andrebispo5 FYI, here are the new badge color primitive names.

@SaintPatrck SaintPatrck marked this pull request as ready for review April 23, 2026 20:56
@SaintPatrck SaintPatrck force-pushed the premium-upgrade/PM-35454-subscription-backend branch from 0a18f8a to 049e150 Compare April 24, 2026 16:32
val subscription = (result as SubscriptionResult.Success).subscription
assertEquals(PremiumSubscriptionStatus.ACTIVE, subscription.status)
assertEquals(BigDecimal("19.80"), subscription.seatsCost)
assertEquals(BigDecimal("19.80"), subscription.nextChargeTotal)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just compare the entire result

val cadence: CadenceTypeJson,

@SerialName("discount")
val discount: BitwardenDiscountJson? = null,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's with all the default nulls everywhere?

* @property storageCost The cost of additional storage, or null if none.
* @property discountAmount The money value of any applied discount, or null if
* no discount is present. Percent-off discounts are resolved against the
* password manager subtotal at mapping time.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These extra indents seem odd?

…e component

Introduces the subscription retrieval path (network API, service, repository, domain models) so subsequent work can render a premium user's actual billed line items and next-charge details. Adds a reusable `BitwardenStatusBadge` component with success / error / warning variants for displaying subscription status. No user-visible behavior change — consumed by PM-35455.
Rename status badge primitives from ad-hoc `statusBadge*` names to the
Figma-canonical `{color}{number}` scheme and add dark-variant primitives
so the dark theme stops reusing the soft light palette. Adds a dark
preview to verify the separation visually.
Double loses precision on currency arithmetic; switch cost, estimatedTax, and
discount value to BigDecimal with a JSON-number-preserving serializer.
Stripe (and the server's StripeConstants.SubscriptionStatus) emits 'paused' as a
valid subscription status; deserialization would otherwise throw on that value.
Fixed height 24dp, 12dp corner radius, and 4dp vertical inner padding match
the Status Badges component's published dimensions
Defaults on nullable serialized fields are redundant given the network
Json config (explicitNulls = false), so they obscured intent without
adding value. The repository test now compares the entire result to
match the convention used elsewhere in the file, and the SubscriptionInfo
KDoc continuation indents are normalized to standard Kotlin formatting.
@SaintPatrck SaintPatrck force-pushed the premium-upgrade/PM-35454-subscription-backend branch from 049e150 to e50bdb6 Compare April 27, 2026 18:50
@SaintPatrck SaintPatrck added this pull request to the merge queue Apr 27, 2026
Merged via the queue into main with commit 2b4ca43 Apr 27, 2026
22 of 25 checks passed
@SaintPatrck SaintPatrck deleted the premium-upgrade/PM-35454-subscription-backend branch April 27, 2026 20:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review-vnext Request a Claude code review using the vNext workflow app:authenticator Bitwarden Authenticator app context app:password-manager Bitwarden Password Manager app context t:feature Change Type - Feature Development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants