Skip to content

Migrate Hub billing page to the api's session-first flow#179

Open
tobihagemann wants to merge 4 commits into
developfrom
feature/hub-billing-api-migration
Open

Migrate Hub billing page to the api's session-first flow#179
tobihagemann wants to merge 4 commits into
developfrom
feature/hub-billing-api-migration

Conversation

@tobihagemann

Copy link
Copy Markdown
Member

Important

Requires cryptomator/api#34 (and its base cryptomator/api#33) to be deployed. The page hard depends on the new endpoints, so both sides need to ship together.

Moves the Hub billing page off the legacy store's manage endpoints and onto the api, with the billing session as the single credential. Opening the page from a Hub now starts with a customer lookup (redacted email for known Hubs, email entry for new ones) and a confirmation link. The emailed ?session= link resolves the session and routes on its billingId: linked sessions go to the manage view, new customers to checkout (card via Paddle, invoice via the new EspoCRM checkout). Manage calls authorize with Authorization: Bearer <session>, checkouts carry the session so the purchase gets linked back, and the license comes from GET /licenses/hub?session=&legacy= without a captcha. Delivery back to the Hub honors token_transfer: ?token= for older Hubs, ?session= for newer ones. Expired or reused links get their own screen.

State Machine

stateDiagram-v2
  [*] --> CREATE_SESSION: ?hub_id&return_url
  [*] --> LOADING: ?session=<id>
  CREATE_SESSION --> CREATE_SESSION_SUCCESS: link mailed
  LOADING --> LINK_EXPIRED: 404
  LOADING --> EXISTING_CUSTOMER: billingId set
  LOADING --> NEW_CUSTOMER: no billing yet
  NEW_CUSTOMER --> CHECKOUT_SUCCESS: card or invoice
  NEW_CUSTOMER --> MANUAL_INVOICE: custom billing
  EXISTING_CUSTOMER --> EXISTING_CUSTOMER: seats, pause, resume
Loading

…g-api-migration

Conflicts in assets/js/hubsubscription.js and layouts/hub-billing/single.html
were resolved by keeping this branch's versions wholesale: both sides rewrote
the same billing-page flow (this branch targets the new api manage/checkout
endpoints, develop's store-migration phases 1+2 kept the legacy store flow
with an api billing-session bootstrap), so a line-level merge is not
meaningful. Dropped from develop as superseded: customer lookup with redacted
email, the /billing/session + ?session= confirmation-link bootstrap, and
token_transfer=session delivery back to the Hub (this branch currently only
delivers the license via ?token=).
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR reworks the Hub billing subscription flow to support both card and invoice payment methods. hubsubscription.js introduces hub ID extraction, auth-header helpers, and new endpoints for card checkout, invoice checkout, and manage-subscription actions (pause, restart, seat changes), replacing older pay-link/PUT-based flows. single.html updates status/payment rendering to a new details shape and adds a payment-method switcher with separate card/invoice checkout templates and a checkout-success section. A new hub-license-block.html partial centralizes license key display/retry/transfer UI. New EU country YAML data files were added, and i18n strings for statuses, payment info, seat-change warnings, and invoice checkout were updated in both English and German.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: moving Hub billing to a session-first API flow.
Description check ✅ Passed The description matches the changeset and explains the session-first billing migration and related flows.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/hub-billing-api-migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (4)
data/de/eu_countries.yaml (1)

1-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider alphabetical ordering for the country dropdown.

Entries are listed in EU accession/treaty order rather than alphabetically by name (e.g., Frankreich, Deutschland, Griechenland appear out of alphabetic sequence). Since this list feeds a <select> populated via {{ range (index $.Site.Data $.Site.Language.Lang).eu_countries }} in layouts/hub-billing/single.html, sorting by localized name would improve usability for users scanning the dropdown.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data/de/eu_countries.yaml` around lines 1 - 54, The eu_countries list is not
sorted alphabetically by localized country name, which makes the dropdown harder
to scan. Reorder the entries in eu_countries so the names appear in alphabetical
sequence while keeping the same code/name pairs, and ensure the list consumed by
the range in layouts/hub-billing/single.html remains alphabetically sorted for
the German locale.
data/en/eu_countries.yaml (1)

1-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Same ordering concern as the German counterpart.

The English list mirrors the same non-alphabetical accession order (e.g., France, Germany, Greece interleaved with earlier entries). Sorting both locale files alphabetically by displayed name would make the country picker easier to navigate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@data/en/eu_countries.yaml` around lines 1 - 54, The English EU country list
in eu_countries.yaml should be reordered to match the alphabetical-by-name
sorting requested for the German counterpart. Update the entries in the existing
country list so the displayed names are in alphabetical order while keeping the
same code/name pairs, and keep the ordering consistent with the locale file’s
country-picker data structure.
layouts/hub-billing/single.html (2)

467-474: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider aria-pressed on the payment-method toggle buttons.

These act as a two-state switcher but don't expose pressed/selected state to assistive tech.

♿ Suggested addition
-<button type="button" ... :class="{...: subscriptionData.paymentMethod === 'card'}" `@click.prevent`="subscriptionData.paymentMethod = 'card'">
+<button type="button" ... :class="{...: subscriptionData.paymentMethod === 'card'}" :aria-pressed="subscriptionData.paymentMethod === 'card'" `@click.prevent`="subscriptionData.paymentMethod = 'card'">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layouts/hub-billing/single.html` around lines 467 - 474, The payment-method
toggle buttons in the hub billing checkout act like a two-state switch, but
their selected state is not exposed to assistive technologies. Update the button
markup in the payment method toggle section to include aria-pressed on both
buttons, binding it to whether subscriptionData.paymentMethod equals 'card' or
'invoice' so screen readers can announce the active choice.

127-197: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Optional: guard subscriptionData.details.* accesses against null.

details is initialized to null (line 6) and these x-show/x-text bindings (status, seats, current_period_end, processor) dereference it directly. If this block is ever reactive before details is populated (e.g. via a parent x-show rather than x-if), Alpine will log console errors. Optional chaining would make this resilient regardless of the parent gating mechanism.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layouts/hub-billing/single.html` around lines 127 - 197, Guard the direct
subscriptionData.details dereferences in the hub billing template so Alpine does
not evaluate them while details is still null. Update the bindings in the
status, seats, current_period_end, and processor checks within the single.html
subscription section to use null-safe access or an equivalent guard, especially
around the x-show, x-text, and disabled expressions tied to
subscriptionData.details. Use the existing subscriptionData object and its
details field as the locating symbols when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@data/de/eu_countries.yaml`:
- Around line 1-54: The eu_countries list is not sorted alphabetically by
localized country name, which makes the dropdown harder to scan. Reorder the
entries in eu_countries so the names appear in alphabetical sequence while
keeping the same code/name pairs, and ensure the list consumed by the range in
layouts/hub-billing/single.html remains alphabetically sorted for the German
locale.

In `@data/en/eu_countries.yaml`:
- Around line 1-54: The English EU country list in eu_countries.yaml should be
reordered to match the alphabetical-by-name sorting requested for the German
counterpart. Update the entries in the existing country list so the displayed
names are in alphabetical order while keeping the same code/name pairs, and keep
the ordering consistent with the locale file’s country-picker data structure.

In `@layouts/hub-billing/single.html`:
- Around line 467-474: The payment-method toggle buttons in the hub billing
checkout act like a two-state switch, but their selected state is not exposed to
assistive technologies. Update the button markup in the payment method toggle
section to include aria-pressed on both buttons, binding it to whether
subscriptionData.paymentMethod equals 'card' or 'invoice' so screen readers can
announce the active choice.
- Around line 127-197: Guard the direct subscriptionData.details dereferences in
the hub billing template so Alpine does not evaluate them while details is still
null. Update the bindings in the status, seats, current_period_end, and
processor checks within the single.html subscription section to use null-safe
access or an equivalent guard, especially around the x-show, x-text, and
disabled expressions tied to subscriptionData.details. Use the existing
subscriptionData object and its details field as the locating symbols when
making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3c864dee-8dad-4a11-9584-0c965e44e55e

📥 Commits

Reviewing files that changed from the base of the PR and between a39cb38 and 78b57b1.

📒 Files selected for processing (7)
  • assets/js/hubsubscription.js
  • data/de/eu_countries.yaml
  • data/en/eu_countries.yaml
  • i18n/de.yaml
  • i18n/en.yaml
  • layouts/hub-billing/single.html
  • layouts/partials/hub-license-block.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant