@cosmicdrift/kumiko-samples 0.386.0 → 0.388.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -57
- package/README.md +1 -1
- package/package.json +2 -2
- package/packages/bundled-features/package.json +9 -2
- package/packages/bundled-features/src/agent-tools/changes.json +6 -0
- package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -1
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +9 -9
- package/packages/bundled-features/src/audit/changes.json +12 -0
- package/packages/bundled-features/src/audit/handlers/audit-events.ts +1 -1
- package/packages/bundled-features/src/audit/screens.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +10 -10
- package/packages/bundled-features/src/auth-email-password/feature.ts +6 -6
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +18 -18
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +12 -12
- package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +24 -26
- package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +34 -37
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +6 -7
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +1 -5
- package/packages/bundled-features/src/auth-email-password/index.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/lockout-store.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/seeding.ts +63 -66
- package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +8 -8
- package/packages/bundled-features/src/auth-email-password/testing.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +57 -57
- package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +15 -16
- package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +11 -12
- package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +1 -1
- package/packages/bundled-features/src/auth-email-password/web/constants.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -11
- package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +17 -17
- package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +3 -4
- package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +16 -17
- package/packages/bundled-features/src/auth-email-password/web/session.tsx +34 -34
- package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +3 -3
- package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +26 -27
- package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +12 -13
- package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +17 -18
- package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +11 -12
- package/packages/bundled-features/src/auth-foundation/boot-checks.ts +4 -4
- package/packages/bundled-features/src/auth-foundation/resolve-anonymous-access.ts +4 -4
- package/packages/bundled-features/src/auth-foundation/types.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/changes.json +6 -0
- package/packages/bundled-features/src/auth-mfa/feature-exports.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/feature.ts +6 -0
- package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -5
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/token-secrets.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/web/i18n.ts +9 -4
- package/packages/bundled-features/src/auth-mfa/web/index.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +17 -16
- package/packages/bundled-features/src/auth-mfa/web/mfa-code-or-recovery-field.tsx +68 -0
- package/packages/bundled-features/src/auth-mfa/web/mfa-disable-dialog.tsx +23 -13
- package/packages/bundled-features/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +23 -13
- package/packages/bundled-features/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +15 -6
- package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +71 -24
- package/packages/bundled-features/src/billing-foundation/aggregate-id.ts +10 -11
- package/packages/bundled-features/src/billing-foundation/changes.json +55 -0
- package/packages/bundled-features/src/billing-foundation/checkout-core.ts +32 -42
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +8 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +14 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +3 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +12 -6
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +35 -19
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +12 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +14 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +5 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +8 -10
- package/packages/bundled-features/src/billing-foundation/entities.ts +27 -27
- package/packages/bundled-features/src/billing-foundation/events.ts +23 -21
- package/packages/bundled-features/src/billing-foundation/feature.ts +45 -44
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +3 -5
- package/packages/bundled-features/src/billing-foundation/handlers/billing-plans.query.ts +71 -6
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +19 -17
- package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +11 -12
- package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -4
- package/packages/bundled-features/src/billing-foundation/handlers/plan-switch-preview.query.ts +65 -0
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +29 -22
- package/packages/bundled-features/src/billing-foundation/handlers/start-plan-checkout.write.ts +44 -8
- package/packages/bundled-features/src/billing-foundation/handlers/switch-plan.write.ts +72 -82
- package/packages/bundled-features/src/billing-foundation/handlers/sync-subscription.write.ts +14 -1
- package/packages/bundled-features/src/billing-foundation/i18n.ts +22 -0
- package/packages/bundled-features/src/billing-foundation/index.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +62 -27
- package/packages/bundled-features/src/billing-foundation/plan-switch.ts +84 -0
- package/packages/bundled-features/src/billing-foundation/projection.ts +38 -37
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +7 -8
- package/packages/bundled-features/src/billing-foundation/types.ts +144 -66
- package/packages/bundled-features/src/billing-foundation/validate-options.ts +19 -0
- package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +123 -51
- package/packages/bundled-features/src/billing-foundation/web/checkout-consent-dialog.tsx +89 -42
- package/packages/bundled-features/src/billing-foundation/web/constants.ts +4 -0
- package/packages/bundled-features/src/billing-foundation/web/plan-price.ts +55 -0
- package/packages/bundled-features/src/billing-foundation/web/plan-switch-confirm-dialog.tsx +224 -0
- package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -1
- package/packages/bundled-features/src/cap-counter/aggregate-id.ts +8 -9
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +5 -1
- package/packages/bundled-features/src/cap-counter/changes.json +18 -0
- package/packages/bundled-features/src/cap-counter/constants.ts +11 -12
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +66 -68
- package/packages/bundled-features/src/cap-counter/entity.ts +29 -29
- package/packages/bundled-features/src/cap-counter/feature.ts +34 -35
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +31 -31
- package/packages/bundled-features/src/cap-counter/handlers/mark-soft-warned.write.ts +4 -5
- package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +9 -2
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -5
- package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -3
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
- package/packages/bundled-features/src/channel-email/changes.json +6 -0
- package/packages/bundled-features/src/channel-email/feature.ts +1 -1
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +34 -31
- package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +3 -4
- package/packages/bundled-features/src/compliance-profiles/feature.ts +15 -16
- package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +3 -6
- package/packages/bundled-features/src/compliance-profiles/handlers/list-profiles.query.ts +5 -5
- package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +4 -4
- package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +13 -14
- package/packages/bundled-features/src/compliance-profiles/handlers/sub-processors.query.ts +9 -11
- package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +7 -7
- package/packages/bundled-features/src/compliance-profiles/schema/profile-selection.ts +18 -23
- package/packages/bundled-features/src/compliance-profiles/seeding.ts +14 -15
- package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +3 -3
- package/packages/bundled-features/src/compliance-profiles-ops/index.ts +2 -2
- package/packages/bundled-features/src/config/changes.json +12 -0
- package/packages/bundled-features/src/config/handlers/cascade.query.ts +4 -2
- package/packages/bundled-features/src/config/handlers/readiness.query.ts +7 -7
- package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +4 -5
- package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
- package/packages/bundled-features/src/config/handlers/values.query.ts +4 -2
- package/packages/bundled-features/src/config/resolver.ts +4 -4
- package/packages/bundled-features/src/config/table.ts +10 -10
- package/packages/bundled-features/src/config/write-helpers.ts +28 -9
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +23 -21
- package/packages/bundled-features/src/custom-fields/aggregate-id.ts +16 -17
- package/packages/bundled-features/src/custom-fields/entity.ts +49 -47
- package/packages/bundled-features/src/custom-fields/events.ts +7 -8
- package/packages/bundled-features/src/custom-fields/feature.ts +70 -72
- package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +9 -8
- package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +28 -26
- package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +6 -7
- package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +12 -13
- package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +25 -28
- package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +6 -6
- package/packages/bundled-features/src/custom-fields/lib/field-access.ts +2 -2
- package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
- package/packages/bundled-features/src/custom-fields/lib/quota.ts +1 -1
- package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -12
- package/packages/bundled-features/src/custom-fields/run-retention.ts +3 -3
- package/packages/bundled-features/src/custom-fields/schemas.ts +14 -14
- package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +2 -5
- package/packages/bundled-features/src/custom-fields/web/constants.ts +2 -2
- package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +26 -26
- package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +65 -67
- package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +3 -3
- package/packages/bundled-features/src/data-retention/_internal/parse-override.ts +2 -3
- package/packages/bundled-features/src/data-retention/feature.ts +11 -18
- package/packages/bundled-features/src/data-retention/handlers/policy-for.query.ts +6 -6
- package/packages/bundled-features/src/data-retention/keep-for.ts +17 -17
- package/packages/bundled-features/src/data-retention/override-schema.ts +13 -15
- package/packages/bundled-features/src/data-retention/presets.ts +18 -23
- package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
- package/packages/bundled-features/src/data-retention/resolve-tenant-preset.ts +17 -16
- package/packages/bundled-features/src/data-retention/resolver.ts +33 -32
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +71 -58
- package/packages/bundled-features/src/data-retention/schema/tenant-retention-override.ts +13 -11
- package/packages/bundled-features/src/delivery/changes.json +30 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +9 -3
- package/packages/bundled-features/src/delivery/db/queries/preferences.ts +2 -2
- package/packages/bundled-features/src/delivery/delivery-service.ts +35 -5
- package/packages/bundled-features/src/delivery/feature.ts +3 -3
- package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
- package/packages/bundled-features/src/delivery/handlers/preferences.query.ts +20 -1
- package/packages/bundled-features/src/delivery/handlers/unsubscribe-page-chrome.query.ts +1 -1
- package/packages/bundled-features/src/delivery/tables.ts +9 -8
- package/packages/bundled-features/src/delivery/unsubscribe-link.ts +3 -1
- package/packages/bundled-features/src/delivery/unsubscribe-page-strings.ts +1 -0
- package/packages/bundled-features/src/delivery/unsubscribe-page.ts +6 -0
- package/packages/bundled-features/src/delivery/unsubscribe.ts +40 -19
- package/packages/bundled-features/src/delivery/upsert-preference.ts +3 -4
- package/packages/bundled-features/src/document-ingest-foundation/entity.ts +5 -5
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +2 -2
- package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/document-ingest-foundation/pages.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/tenant-destroy-hook.ts +4 -4
- package/packages/bundled-features/src/feature-toggles/compose-tier-resolver.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/feature.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +5 -5
- package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/web/constants.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/presets.ts +1 -1
- package/packages/bundled-features/src/file-foundation/feature.ts +6 -6
- package/packages/bundled-features/src/file-foundation/index.ts +1 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -1
- package/packages/bundled-features/src/file-provider-inmemory/feature.ts +16 -16
- package/packages/bundled-features/src/file-provider-s3/env-named-feature.ts +0 -2
- package/packages/bundled-features/src/file-provider-s3/feature.ts +16 -16
- package/packages/bundled-features/src/file-provider-s3/lib/s3-provider.ts +6 -6
- package/packages/bundled-features/src/files/README.md +1 -1
- package/packages/bundled-features/src/files/feature.ts +6 -6
- package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +5 -5
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +24 -26
- package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
- package/packages/bundled-features/src/folders/feature.ts +1 -1
- package/packages/bundled-features/src/folders/web/folder-manager.tsx +2 -2
- package/packages/bundled-features/src/folders/web/tree.ts +5 -5
- package/packages/bundled-features/src/form-draft/changes.json +6 -0
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +3 -3
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
- package/packages/bundled-features/src/form-draft/decrypt-draft.ts +4 -1
- package/packages/bundled-features/src/form-draft/entity.ts +3 -3
- package/packages/bundled-features/src/form-draft/feature.ts +4 -5
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
- package/packages/bundled-features/src/form-draft/release-file-refs.ts +6 -6
- package/packages/bundled-features/src/form-draft/schemas.ts +3 -3
- package/packages/bundled-features/src/form-draft/web/constants.ts +0 -2
- package/packages/bundled-features/src/host-routing/README.md +1 -1
- package/packages/bundled-features/src/host-routing/changes.json +6 -0
- package/packages/bundled-features/src/host-routing/dispatch.ts +16 -17
- package/packages/bundled-features/src/host-routing/options.ts +2 -1
- package/packages/bundled-features/src/inbound-mail-foundation/aggregate-id.ts +19 -19
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +18 -0
- package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +4 -3
- package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +30 -33
- package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +47 -48
- package/packages/bundled-features/src/inbound-mail-foundation/events.ts +54 -50
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +39 -39
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +8 -8
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +25 -26
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +7 -7
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +50 -51
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +8 -8
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +10 -10
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +3 -4
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +27 -15
- package/packages/bundled-features/src/inbound-mail-foundation/i18n.ts +0 -1
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +16 -15
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +31 -29
- package/packages/bundled-features/src/inbound-mail-foundation/provider-factory.ts +9 -9
- package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +45 -41
- package/packages/bundled-features/src/inbound-mail-foundation/screens.ts +0 -3
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +14 -16
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +70 -70
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +101 -84
- package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +13 -13
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +42 -40
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +20 -19
- package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +21 -21
- package/packages/bundled-features/src/jobs/changes.json +13 -0
- package/packages/bundled-features/src/jobs/db/queries/retention.ts +2 -2
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +3 -3
- package/packages/bundled-features/src/jobs/events.ts +1 -1
- package/packages/bundled-features/src/jobs/feature.ts +20 -11
- package/packages/bundled-features/src/jobs/handlers/detail.query.ts +4 -4
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/tenant-failures.query.ts +45 -40
- package/packages/bundled-features/src/jobs/handlers/tenant-runs.query.ts +63 -59
- package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
- package/packages/bundled-features/src/jobs/job-run-logger.ts +19 -19
- package/packages/bundled-features/src/jobs/job-run-table.ts +3 -3
- package/packages/bundled-features/src/jobs/screens.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
- package/packages/bundled-features/src/ledger/entity.ts +5 -5
- package/packages/bundled-features/src/ledger/feature.ts +3 -3
- package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +3 -3
- package/packages/bundled-features/src/ledger/recurring.ts +12 -12
- package/packages/bundled-features/src/ledger/schemas.ts +1 -1
- package/packages/bundled-features/src/ledger/web/constants.ts +2 -2
- package/packages/bundled-features/src/ledger/web/index.ts +7 -7
- package/packages/bundled-features/src/legal-pages/README.md +1 -1
- package/packages/bundled-features/src/legal-pages/feature.ts +40 -41
- package/packages/bundled-features/src/legal-pages/markdown.ts +3 -3
- package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +23 -24
- package/packages/bundled-features/src/legal-pages/web/index.ts +0 -2
- package/packages/bundled-features/src/mail-foundation/changes.json +6 -0
- package/packages/bundled-features/src/mail-foundation/feature.ts +31 -30
- package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +27 -28
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +20 -20
- package/packages/bundled-features/src/managed-pages/branding.ts +6 -6
- package/packages/bundled-features/src/managed-pages/feature.ts +62 -62
- package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +5 -6
- package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +8 -8
- package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +5 -7
- package/packages/bundled-features/src/managed-pages/screens/page-screens.ts +11 -11
- package/packages/bundled-features/src/managed-pages/seeding.ts +8 -8
- package/packages/bundled-features/src/managed-pages/table.ts +16 -15
- package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +6 -6
- package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -1
- package/packages/bundled-features/src/notes-history/entity.ts +4 -4
- package/packages/bundled-features/src/notes-history/feature.ts +2 -2
- package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
- package/packages/bundled-features/src/page-render/layout.ts +9 -9
- package/packages/bundled-features/src/page-render/markdown.ts +11 -11
- package/packages/bundled-features/src/page-render/security-headers.ts +7 -6
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +9 -6
- package/packages/bundled-features/src/personal-access-tokens/roles.ts +2 -2
- package/packages/bundled-features/src/readiness/handlers/status.query.ts +3 -3
- package/packages/bundled-features/src/renderer-foundation/api.ts +12 -13
- package/packages/bundled-features/src/renderer-foundation/constants.ts +2 -2
- package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
- package/packages/bundled-features/src/renderer-foundation/types.ts +11 -12
- package/packages/bundled-features/src/renderer-pdf/changes.json +6 -0
- package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +27 -10
- package/packages/bundled-features/src/secrets/feature.ts +9 -9
- package/packages/bundled-features/src/secrets/web/constants.ts +2 -2
- package/packages/bundled-features/src/seo/feature.ts +3 -3
- package/packages/bundled-features/src/sessions/constants.ts +1 -1
- package/packages/bundled-features/src/sessions/feature.ts +16 -11
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/sessions/schema/user-session.ts +4 -4
- package/packages/bundled-features/src/sessions/screens.ts +2 -2
- package/packages/bundled-features/src/sessions/session-callbacks.ts +2 -2
- package/packages/bundled-features/src/sessions/session-revoked-event.ts +2 -2
- package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +1 -1
- package/packages/bundled-features/src/shared/decrypt-stored-pii.ts +11 -1
- package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +1 -1
- package/packages/bundled-features/src/shared/index.ts +1 -1
- package/packages/bundled-features/src/shared/mail-template-body.ts +10 -3
- package/packages/bundled-features/src/shared/map-with-concurrency.ts +5 -4
- package/packages/bundled-features/src/shared/parent-visibility.ts +2 -2
- package/packages/bundled-features/src/shared/password-hashing.ts +1 -1
- package/packages/bundled-features/src/shared/signup-handover.ts +1 -2
- package/packages/bundled-features/src/shared/single-use-token-store.ts +1 -1
- package/packages/bundled-features/src/step-dispatcher/changes.json +6 -0
- package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -0
- package/packages/bundled-features/src/subscription-dodo/changes.json +14 -0
- package/packages/bundled-features/src/subscription-dodo/client.ts +45 -0
- package/packages/bundled-features/src/subscription-dodo/constants.ts +24 -0
- package/packages/bundled-features/src/subscription-dodo/feature.ts +207 -0
- package/packages/bundled-features/src/subscription-dodo/index.ts +16 -0
- package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +345 -0
- package/packages/bundled-features/src/subscription-dodo/runtime.ts +126 -0
- package/packages/bundled-features/src/subscription-dodo/verify-webhook.ts +262 -0
- package/packages/bundled-features/src/subscription-mollie/constants.ts +15 -17
- package/packages/bundled-features/src/subscription-mollie/feature.ts +41 -42
- package/packages/bundled-features/src/subscription-mollie/index.ts +3 -3
- package/packages/bundled-features/src/subscription-mollie/plugin-methods.ts +17 -16
- package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +34 -34
- package/packages/bundled-features/src/subscription-stripe/changes.json +12 -0
- package/packages/bundled-features/src/subscription-stripe/constants.ts +4 -6
- package/packages/bundled-features/src/subscription-stripe/feature.ts +45 -43
- package/packages/bundled-features/src/subscription-stripe/index.ts +6 -6
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +22 -27
- package/packages/bundled-features/src/subscription-stripe/runtime.ts +9 -8
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +62 -64
- package/packages/bundled-features/src/tags/feature.ts +3 -3
- package/packages/bundled-features/src/tags/screens.ts +1 -1
- package/packages/bundled-features/src/template-resolver/api.ts +41 -14
- package/packages/bundled-features/src/template-resolver/changes.json +48 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +1 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +27 -5
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/document-base.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +8 -7
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +19 -20
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +15 -5
- package/packages/bundled-features/src/template-resolver/handlers/shared.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +3 -3
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-counts.query.ts +9 -5
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +8 -6
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +44 -5
- package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +3 -4
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +4 -5
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +37 -26
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +7 -2
- package/packages/bundled-features/src/template-resolver/seeding.ts +36 -18
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +22 -4
- package/packages/bundled-features/src/template-resolver/table.ts +13 -14
- package/packages/bundled-features/src/template-resolver/template-delivery-policy.ts +0 -11
- package/packages/bundled-features/src/template-resolver/template-meta.ts +12 -4
- package/packages/bundled-features/src/template-resolver/template-preview.ts +11 -3
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +2 -2
- package/packages/bundled-features/src/template-resolver/web/constants.ts +7 -5
- package/packages/bundled-features/src/template-resolver/web/index.ts +7 -0
- package/packages/bundled-features/src/template-resolver/web/mail-layout-constants.ts +8 -0
- package/packages/bundled-features/src/tenant/changes.json +6 -0
- package/packages/bundled-features/src/tenant/feature.ts +7 -7
- package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +10 -4
- package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +8 -5
- package/packages/bundled-features/src/tenant/invitation-table.ts +37 -40
- package/packages/bundled-features/src/tenant/membership-table.ts +3 -3
- package/packages/bundled-features/src/tenant/schema/tenant.ts +7 -9
- package/packages/bundled-features/src/tenant/screens.ts +1 -1
- package/packages/bundled-features/src/tenant/seeding.ts +19 -18
- package/packages/bundled-features/src/tenant/testing.ts +5 -6
- package/packages/bundled-features/src/tenant/web/constants.ts +2 -2
- package/packages/bundled-features/src/tenant-handover/events.ts +5 -7
- package/packages/bundled-features/src/tenant-handover/feature.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +8 -9
- package/packages/bundled-features/src/tenant-handover/signup-handover-provider.ts +1 -2
- package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +4 -5
- package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
- package/packages/bundled-features/src/tenant-lifecycle/constants.ts +5 -0
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +7 -3
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +1 -1
- package/packages/bundled-features/src/tenant-settings/constants.ts +1 -1
- package/packages/bundled-features/src/tenant-settings/tenant-defaults.ts +12 -5
- package/packages/bundled-features/src/tier-engine/aggregate-id.ts +15 -16
- package/packages/bundled-features/src/tier-engine/changes.json +6 -0
- package/packages/bundled-features/src/tier-engine/compose-app.ts +14 -14
- package/packages/bundled-features/src/tier-engine/entity.ts +18 -19
- package/packages/bundled-features/src/tier-engine/feature.ts +88 -87
- package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +6 -7
- package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +4 -4
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +12 -9
- package/packages/bundled-features/src/tier-engine/tier-resolver.ts +2 -2
- package/packages/bundled-features/src/tier-engine/trial.ts +13 -13
- package/packages/bundled-features/src/tier-engine/web/constants.ts +3 -3
- package/packages/bundled-features/src/tier-engine/web/index.ts +4 -4
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +8 -8
- package/packages/bundled-features/src/user/handlers/create.write.ts +4 -4
- package/packages/bundled-features/src/user/schema/user.ts +45 -44
- package/packages/bundled-features/src/user/seeding.ts +40 -40
- package/packages/bundled-features/src/user/testing.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/audit-download.ts +27 -28
- package/packages/bundled-features/src/user-data-rights/boot-checks.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/feature.ts +112 -113
- package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +9 -9
- package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +8 -8
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +29 -30
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +40 -42
- package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +8 -9
- package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +7 -8
- package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +4 -4
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +30 -29
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +15 -18
- package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +42 -41
- package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +22 -19
- package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +8 -8
- package/packages/bundled-features/src/user-data-rights/index.ts +8 -10
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +24 -27
- package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +169 -280
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +87 -108
- package/packages/bundled-features/src/user-data-rights/run-user-export.ts +67 -69
- package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +11 -11
- package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +37 -41
- package/packages/bundled-features/src/user-data-rights/schema/export-job.ts +65 -67
- package/packages/bundled-features/src/user-data-rights/token-helpers.ts +21 -25
- package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +6 -6
- package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +6 -5
- package/packages/bundled-features/src/user-data-rights/web/constants.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/web/index.ts +5 -5
- package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +14 -13
- package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +13 -12
- package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +6 -6
- package/packages/bundled-features/src/user-data-rights/zip-path.ts +41 -40
- package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +15 -15
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +3 -5
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +45 -51
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +2 -2
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +1 -1
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +3 -3
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +32 -31
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +13 -13
- package/packages/bundled-features/src/user-profile/index.ts +3 -3
- package/packages/bundled-features/src/user-profile/web/client-plugin.ts +1 -1
- package/packages/bundled-features/src/user-profile/web/constants.ts +1 -1
- package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +1 -1
- package/packages/bundled-features/src/waitlist/changes.json +18 -0
- package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +2 -1
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +5 -5
- package/packages/bundled-features/src/waitlist/import.ts +5 -0
- package/packages/bundled-features/src/waitlist/legacy-migrate.ts +22 -3
- package/packages/bundled-features/src/waitlist/link-on-signup.ts +20 -10
- package/packages/bundled-features/src/waitlist/options.ts +3 -0
- package/packages/bundled-features/src/workflow-runner/aggregate-id.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -5
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +4 -15
- package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +4 -4
- package/packages/bundled-features/src/workflow-runner/pending-projection.ts +6 -7
- package/packages/bundled-features/src/workflow-runner/runner.ts +6 -6
- package/packages/bundled-features/src/workflow-runner/tables.ts +6 -6
- package/packages/bundled-features/src/workflow-runner/workflow-registry.ts +1 -1
- package/samples/apps/styleguide/src/features/widgets/i18n.ts +11 -0
- package/samples/apps/styleguide/src/features/widgets/web/Widgets.tsx +24 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-constants.ts +5 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +90 -15
- package/samples/apps/use-all-bundled/e2e-billing-gallery/server.ts +16 -6
- package/samples/apps/use-all-bundled/feature-manifest.json +120 -3
- package/samples/apps/use-all-bundled/src/run-config.ts +9 -3
- package/samples/recipes/auth-mfa/README.md +5 -0
- package/samples/recipes/auth-mfa/package.json +5 -2
- package/samples/recipes/auth-mfa/src/web/confirmation-code-step.tsx +49 -0
- package/samples/recipes/auth-mfa/tsconfig.json +8 -0
- package/samples/recipes/env-composition/README.md +3 -1
- package/samples/recipes/env-composition/src/env.ts +2 -0
- package/samples/recipes/legal-pages/README.md +1 -1
- package/samples/recipes/rate-limiting/README.md +18 -0
- package/samples/recipes/rate-limiting/src/feature.ts +12 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/stored-user-email.ts +0 -12
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
|
-
// Plugin
|
|
3
|
-
//
|
|
4
|
-
// `SubscriptionProviderPlugin`
|
|
2
|
+
// Plugin contract and domain types for subscription-foundation. Provider
|
|
3
|
+
// plugins (subscription-stripe, subscription-mollie, ...) implement
|
|
4
|
+
// `SubscriptionProviderPlugin` and register via
|
|
5
5
|
// `r.useExtension("subscriptionProvider", "<name>", { build })`.
|
|
6
6
|
//
|
|
7
7
|
// **Two-phase plugin contract:**
|
|
8
|
-
// 1. **Pre-tenant-resolution:** `verifyAndParseWebhook`
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// Stripe-/PayPal-
|
|
12
|
-
// secret
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// SYSTEM_TENANT_ID. Plugins
|
|
17
|
-
//
|
|
8
|
+
// 1. **Pre-tenant-resolution:** `verifyAndParseWebhook` runs BEFORE
|
|
9
|
+
// a tenant is resolved from the event, so no HandlerContext is
|
|
10
|
+
// available. The **webhook secret is app-wide** (the App Owner's
|
|
11
|
+
// Stripe-/PayPal-account, not tenant-specific). To let a plugin read
|
|
12
|
+
// the secret from the secrets feature AT RUNTIME (instead of a
|
|
13
|
+
// mount-time closure), the webhook handler passes an optional
|
|
14
|
+
// system-scoped `SecretsContext` as the third argument. The plugin
|
|
15
|
+
// reads its app-wide secrets from it without auditing under
|
|
16
|
+
// SYSTEM_TENANT_ID. Plugins that keep their keys in a closure
|
|
17
|
+
// (e.g. mollie) ignore the parameter → backward-compatible.
|
|
18
18
|
// 2. **Post-tenant-resolution:** `createPortalSession` +
|
|
19
|
-
// `cancelSubscription`
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
19
|
+
// `cancelSubscription` are called from regular write handlers with
|
|
20
|
+
// a fully resolved HandlerContext. The plugin can use ctx.config +
|
|
21
|
+
// ctx.secrets for tenant-specific configuration (e.g. tenant-owned
|
|
22
|
+
// customer-id mappings).
|
|
23
23
|
//
|
|
24
|
-
// Foundation
|
|
25
|
-
// proration, multi-currency, coupons etc.
|
|
26
|
-
//
|
|
24
|
+
// Foundation uses only the common subset of provider functionality;
|
|
25
|
+
// proration, multi-currency, coupons, etc. remain provider-specific and
|
|
26
|
+
// are available through the Customer Portal link.
|
|
27
27
|
|
|
28
28
|
import type { TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
29
29
|
import type { HandlerContext, TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -41,12 +41,12 @@ import {
|
|
|
41
41
|
import type { ConsentLocale } from "./web/consumer-protection/consent-locale.ts";
|
|
42
42
|
|
|
43
43
|
// =============================================================================
|
|
44
|
-
//
|
|
44
|
+
// Normalized Webhook Event
|
|
45
45
|
// =============================================================================
|
|
46
46
|
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
47
|
+
// The plugin parses the raw provider payload and returns this shape.
|
|
48
|
+
// Foundation has no direct knowledge of Stripe-/Mollie-types; the plugin
|
|
49
|
+
// provides the abstraction.
|
|
50
50
|
|
|
51
51
|
export type SubscriptionEvent = {
|
|
52
52
|
/** Discriminator against PaymentEvent. Optional (instead of required) so
|
|
@@ -55,32 +55,31 @@ export type SubscriptionEvent = {
|
|
|
55
55
|
* `parsed.kind === BillingEventKinds.payment` correctly, because only
|
|
56
56
|
* PaymentEvent["kind"] can take that value. */
|
|
57
57
|
readonly kind?: typeof BillingEventKinds.subscription;
|
|
58
|
-
/** Provider-
|
|
59
|
-
* Stripe: "evt_..."; Mollie: payment-id
|
|
58
|
+
/** Provider-owned event ID — UNIQUE key for idempotency.
|
|
59
|
+
* Stripe: "evt_..."; Mollie: payment-id or subscription-id. */
|
|
60
60
|
readonly providerEventId: string;
|
|
61
61
|
/** Discriminator — welcher Plugin diesen Event geliefert hat. */
|
|
62
62
|
readonly providerName: string;
|
|
63
63
|
/** Normalisierter Event-Type. Plugin filtert provider-spezifische
|
|
64
64
|
* Sub-Types raus (Stripe hat ~80 Event-types, wir kennen 5). */
|
|
65
65
|
readonly type: SubscriptionEventType;
|
|
66
|
-
/**
|
|
67
|
-
*
|
|
66
|
+
/** Platform tenant ID (provider-customer metadata or lookup via
|
|
67
|
+
* providerCustomerId). The plugin performs the resolution. */
|
|
68
68
|
readonly tenantId: string;
|
|
69
69
|
/** Consent id echoed back from checkout metadata; omitted when absent or
|
|
70
70
|
* malformed. */
|
|
71
71
|
readonly consentId?: string;
|
|
72
|
-
/** Provider-
|
|
73
|
-
*
|
|
72
|
+
/** Provider-owned customer ID for later lookup when events lack
|
|
73
|
+
* metadata. */
|
|
74
74
|
readonly providerCustomerId: string;
|
|
75
75
|
/** Provider-eigene subscription-id. */
|
|
76
76
|
readonly providerSubscriptionId: string;
|
|
77
77
|
/** Normalisierter status. */
|
|
78
78
|
readonly status: SubscriptionStatus;
|
|
79
|
-
/** Resolved tier —
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* ignore"). */
|
|
79
|
+
/** Resolved tier — the plugin reads the price ID from the event and
|
|
80
|
+
* maps it through the plugin's `<plugin>:config:price-to-tier` to a
|
|
81
|
+
* tier name. If the price ID is absent from the mapping, the plugin
|
|
82
|
+
* returns null from verifyAndParseWebhook (= "unknown event, ignore"). */
|
|
84
83
|
readonly tier: string;
|
|
85
84
|
/** ISO-timestamp wann die aktuelle Billing-Period endet. */
|
|
86
85
|
readonly currentPeriodEnd: string;
|
|
@@ -137,6 +136,9 @@ export type PaymentEvent = {
|
|
|
137
136
|
// =============================================================================
|
|
138
137
|
|
|
139
138
|
export type SubscriptionProviderPlugin = {
|
|
139
|
+
/** Human-readable provider name for the plan panel's provider switch. */
|
|
140
|
+
readonly displayName?: string;
|
|
141
|
+
|
|
140
142
|
/**
|
|
141
143
|
* Verify webhook signature + parse provider-event into normalized
|
|
142
144
|
* form. **Pre-tenant-resolution** — no HandlerContext, because at
|
|
@@ -172,32 +174,32 @@ export type SubscriptionProviderPlugin = {
|
|
|
172
174
|
) => Promise<SubscriptionEvent | PaymentEvent | null>;
|
|
173
175
|
|
|
174
176
|
/**
|
|
175
|
-
* **Post-tenant-resolution** —
|
|
176
|
-
* `create-checkout-session
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
177
|
+
* **Post-tenant-resolution** — called from the
|
|
178
|
+
* `create-checkout-session` write handler. The plugin builds a
|
|
179
|
+
* provider-specific checkout session and returns its hosted-page URL;
|
|
180
|
+
* the tenant admin is redirected there to complete payment, and the
|
|
181
|
+
* provider sends a `subscription.created` webhook with
|
|
182
|
+
* `metadata.tenantId`.
|
|
181
183
|
*
|
|
182
|
-
* Optional: Apple-IAP
|
|
183
|
-
* Apps
|
|
184
|
+
* Optional: Apple-IAP has no web checkout (everything happens in the app).
|
|
185
|
+
* Apps that use only Apple-IAP omit this field.
|
|
184
186
|
*/
|
|
185
187
|
readonly createCheckoutSession?: (
|
|
186
188
|
ctx: HandlerContext,
|
|
187
189
|
options: {
|
|
188
|
-
/** Provider-
|
|
190
|
+
/** Provider-owned price/plan ID that the customer subscribes to. */
|
|
189
191
|
readonly priceId: string;
|
|
190
|
-
/**
|
|
191
|
-
*
|
|
192
|
-
* `verifyAndParseWebhook`
|
|
192
|
+
/** Platform tenant ID — stored as metadata in the checkout session
|
|
193
|
+
* so the subsequent webhook can resolve the tenant (see the
|
|
194
|
+
* `verifyAndParseWebhook` metadata.tenantId lookup). */
|
|
193
195
|
readonly tenantId: string;
|
|
194
|
-
/**
|
|
196
|
+
/** Where the customer lands after a successful checkout. */
|
|
195
197
|
readonly successUrl: string;
|
|
196
|
-
/**
|
|
198
|
+
/** Where the customer lands if they cancel. */
|
|
197
199
|
readonly cancelUrl: string;
|
|
198
|
-
/** Optional:
|
|
199
|
-
*
|
|
200
|
-
*
|
|
200
|
+
/** Optional: existing provider customer ID when the tenant already
|
|
201
|
+
* has an account with the provider. Otherwise, the provider
|
|
202
|
+
* creates a new customer during checkout. */
|
|
201
203
|
readonly providerCustomerId?: string;
|
|
202
204
|
/** Checkout mode. `"subscription"` (default) starts a recurring
|
|
203
205
|
* billing flow; `"payment"` a one-off checkout (e.g. pay-per-use
|
|
@@ -215,17 +217,17 @@ export type SubscriptionProviderPlugin = {
|
|
|
215
217
|
) => Promise<{ readonly url: string }>;
|
|
216
218
|
|
|
217
219
|
/**
|
|
218
|
-
* **Post-tenant-resolution** —
|
|
219
|
-
*
|
|
220
|
+
* **Post-tenant-resolution** — called from a write handler with a
|
|
221
|
+
* fully resolved ctx. Creates a self-service Portal link.
|
|
220
222
|
* Stripe: customer-portal-session, Mollie: hosted-management-page.
|
|
221
|
-
*
|
|
222
|
-
* (cancel, payment
|
|
223
|
+
* The tenant admin uses it to manage the subscription themselves
|
|
224
|
+
* (cancel, payment method, ...).
|
|
223
225
|
*
|
|
224
|
-
* Optional
|
|
225
|
-
* (Apple-IAP managed
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
226
|
+
* Optional because not every provider has a Portal pattern
|
|
227
|
+
* (Apple-IAP subscriptions are managed in the Apple app, with no web
|
|
228
|
+
* Portal). A plugin that does not support this can omit the field;
|
|
229
|
+
* the foundation then returns a "portal_not_supported" error when a
|
|
230
|
+
* tenant admin requests the Portal link.
|
|
229
231
|
*/
|
|
230
232
|
readonly createPortalSession?: (
|
|
231
233
|
ctx: HandlerContext,
|
|
@@ -236,10 +238,10 @@ export type SubscriptionProviderPlugin = {
|
|
|
236
238
|
) => Promise<{ readonly url: string }>;
|
|
237
239
|
|
|
238
240
|
/**
|
|
239
|
-
* Optional:
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
241
|
+
* Optional: in-app cancellation API. If not implemented, the tenant
|
|
242
|
+
* can cancel only through the Customer Portal link (or not at all if
|
|
243
|
+
* createPortalSession is also missing — the cancellation flow is then
|
|
244
|
+
* provider-dashboard-only).
|
|
243
245
|
*/
|
|
244
246
|
readonly cancelSubscription?: (
|
|
245
247
|
ctx: HandlerContext,
|
|
@@ -314,6 +316,32 @@ export type SubscriptionProviderPlugin = {
|
|
|
314
316
|
},
|
|
315
317
|
) => Promise<{ readonly url: string }>;
|
|
316
318
|
|
|
319
|
+
/**
|
|
320
|
+
* In-app plan switch, step 1: what the switch would cost right now. Together
|
|
321
|
+
* with `applyPlanSwitch` an alternative to the hosted `createPlanSwitchSession`.
|
|
322
|
+
*/
|
|
323
|
+
readonly previewPlanSwitch?: (
|
|
324
|
+
ctx: HandlerContext,
|
|
325
|
+
options: {
|
|
326
|
+
readonly providerSubscriptionId: string;
|
|
327
|
+
readonly targetPriceId: string;
|
|
328
|
+
},
|
|
329
|
+
) => Promise<PlanSwitchPreview>;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* In-app plan switch, step 2: performs the switch at the provider. The
|
|
333
|
+
* state change itself arrives later through the webhook.
|
|
334
|
+
*/
|
|
335
|
+
readonly applyPlanSwitch?: (
|
|
336
|
+
ctx: HandlerContext,
|
|
337
|
+
options: {
|
|
338
|
+
readonly providerSubscriptionId: string;
|
|
339
|
+
readonly targetPriceId: string;
|
|
340
|
+
readonly tenantId: string;
|
|
341
|
+
readonly consentId?: string;
|
|
342
|
+
},
|
|
343
|
+
) => Promise<void>;
|
|
344
|
+
|
|
317
345
|
/**
|
|
318
346
|
* Fetches the live provider-side state of one subscription, for the
|
|
319
347
|
* `sync-subscriptions` backfill job — catches drift (e.g. a `cancel_at`
|
|
@@ -331,6 +359,18 @@ export type SubscriptionProviderPlugin = {
|
|
|
331
359
|
) => Promise<ProviderSubscriptionSnapshot | null>;
|
|
332
360
|
};
|
|
333
361
|
|
|
362
|
+
export type PlanSwitchPreview = {
|
|
363
|
+
/** Charged immediately, smallest currency unit, tax included. */
|
|
364
|
+
readonly amountDueNow: number;
|
|
365
|
+
/** Tax part of `amountDueNow`; null when the provider reports none. */
|
|
366
|
+
readonly tax: number | null;
|
|
367
|
+
/** Lower-case ISO currency code. */
|
|
368
|
+
readonly currency: string;
|
|
369
|
+
/** Credit for the unused time on the old plan, smallest currency unit. */
|
|
370
|
+
readonly credit: number;
|
|
371
|
+
readonly effectiveAt: string;
|
|
372
|
+
};
|
|
373
|
+
|
|
334
374
|
export type ProviderSubscriptionSnapshot = {
|
|
335
375
|
readonly providerCustomerId: string;
|
|
336
376
|
readonly providerSubscriptionId: string;
|
|
@@ -400,7 +440,8 @@ export type BillingPlanCatalog<TTier extends string = string> = {
|
|
|
400
440
|
* `baseUrl` itself when unset) and by `switch-plan` after a plan switch
|
|
401
441
|
* (defaults to `successPath` there instead). */
|
|
402
442
|
readonly returnPath?: string;
|
|
403
|
-
/**
|
|
443
|
+
/** Restricts the catalog to one provider; defaults to every registered
|
|
444
|
+
* provider exposing `priceToTier`. */
|
|
404
445
|
readonly providerName?: string;
|
|
405
446
|
/** Rendered as the last card, no price, visible to `viewRoles` regardless of `purchaseRoles`. */
|
|
406
447
|
readonly contact?: BillingPlanContactCard;
|
|
@@ -430,6 +471,19 @@ export type ConsumerProtectionOptions = {
|
|
|
430
471
|
readonly termsTextBlock: string;
|
|
431
472
|
/** Per-locale VAT note; must carry at least `de` and `en`. */
|
|
432
473
|
readonly vatNote: Readonly<Record<string, string>>;
|
|
474
|
+
/** Per-provider texts keyed by provider name, e.g. for a merchant-of-record
|
|
475
|
+
* provider that issues the invoice and VAT itself. `vatNote` here replaces
|
|
476
|
+
* the global one in that provider's confirmation mail; `checkoutNote` is
|
|
477
|
+
* shown under the provider choice. de and en are required for each text. */
|
|
478
|
+
readonly providerTexts?: Readonly<
|
|
479
|
+
Record<
|
|
480
|
+
string,
|
|
481
|
+
{
|
|
482
|
+
readonly vatNote?: Readonly<Record<string, string>>;
|
|
483
|
+
readonly checkoutNote?: Readonly<Record<string, string>>;
|
|
484
|
+
}
|
|
485
|
+
>
|
|
486
|
+
>;
|
|
433
487
|
readonly operatorEmail: string;
|
|
434
488
|
/** Where the public § 312k declaration is reachable. `"tenant-host"`
|
|
435
489
|
* (default): on a host that resolves a tenant. `"platform"`: also on a
|
|
@@ -491,6 +545,23 @@ export type BillingPlanView = {
|
|
|
491
545
|
readonly action: BillingPlanAction;
|
|
492
546
|
};
|
|
493
547
|
|
|
548
|
+
export type BillingProviderView = {
|
|
549
|
+
readonly name: string;
|
|
550
|
+
readonly displayName: string;
|
|
551
|
+
readonly note?: Readonly<Record<string, string>>;
|
|
552
|
+
readonly plans: readonly BillingPlanView[];
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
export function canSwitchPlansInApp(plugin: SubscriptionProviderPlugin): boolean {
|
|
556
|
+
return plugin.previewPlanSwitch !== undefined && plugin.applyPlanSwitch !== undefined;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** A provider switches plans via a hosted session or via in-app preview + apply. */
|
|
560
|
+
export function canSwitchPlans(plugin: SubscriptionProviderPlugin | null): boolean {
|
|
561
|
+
if (plugin === null) return false;
|
|
562
|
+
return plugin.createPlanSwitchSession !== undefined || canSwitchPlansInApp(plugin);
|
|
563
|
+
}
|
|
564
|
+
|
|
494
565
|
export type BillingPlansResult = {
|
|
495
566
|
readonly enabled: boolean;
|
|
496
567
|
readonly currentTier: {
|
|
@@ -507,6 +578,13 @@ export type BillingPlansResult = {
|
|
|
507
578
|
} | null;
|
|
508
579
|
readonly canPurchase: boolean;
|
|
509
580
|
readonly plans: readonly BillingPlanView[];
|
|
581
|
+
/** Present when more than one provider is offered, or the single offered
|
|
582
|
+
* provider has a `providerTexts` checkout note. `plans` above are the first
|
|
583
|
+
* entry's plans. */
|
|
584
|
+
readonly providers?: readonly BillingProviderView[];
|
|
585
|
+
/** True when the subscription's provider switches plans in-app (preview +
|
|
586
|
+
* confirm) instead of on a hosted page. */
|
|
587
|
+
readonly inAppPlanSwitch?: true;
|
|
510
588
|
/** Present only when `catalog.contact` is configured. */
|
|
511
589
|
readonly contact?: BillingPlanContactCard;
|
|
512
590
|
/** Present only when `consumerProtection` is configured. */
|
|
@@ -28,6 +28,24 @@ function isRootRelativeOrHttpsUrl(link: string): boolean {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function validateProviderTexts(cp: ConsumerProtectionOptions): void {
|
|
32
|
+
for (const [providerName, texts] of Object.entries(cp.providerTexts ?? {})) {
|
|
33
|
+
for (const [textName, text] of [
|
|
34
|
+
["vatNote", texts.vatNote],
|
|
35
|
+
["checkoutNote", texts.checkoutNote],
|
|
36
|
+
] as const) {
|
|
37
|
+
if (text === undefined) continue;
|
|
38
|
+
for (const locale of ["de", "en"] as const) {
|
|
39
|
+
if (!text[locale]?.trim()) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`createBillingFoundationFeature: consumerProtection.providerTexts.${providerName}.${textName} needs a non-empty "${locale}" entry.`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
function validateConsumerProtection(cp: ConsumerProtectionOptions): void {
|
|
32
50
|
if (cp.termsTextBlock.trim() === "") {
|
|
33
51
|
throw new Error("createBillingFoundationFeature: consumerProtection.termsTextBlock is empty.");
|
|
@@ -39,6 +57,7 @@ function validateConsumerProtection(cp: ConsumerProtectionOptions): void {
|
|
|
39
57
|
);
|
|
40
58
|
}
|
|
41
59
|
}
|
|
60
|
+
validateProviderTexts(cp);
|
|
42
61
|
if (!z.email().safeParse(cp.operatorEmail).success) {
|
|
43
62
|
throw new Error(
|
|
44
63
|
`createBillingFoundationFeature: consumerProtection.operatorEmail "${cp.operatorEmail}" is not a valid email address.`,
|
|
@@ -24,24 +24,13 @@ import {
|
|
|
24
24
|
SubscriptionFoundationQueries,
|
|
25
25
|
SubscriptionStatuses,
|
|
26
26
|
} from "./constants.ts";
|
|
27
|
+
import { resolveConsentLocale } from "./consumer-protection/consent-locale.ts";
|
|
27
28
|
import { localizedParams } from "./localized-params.ts";
|
|
29
|
+
import { cheapestPrice, intervalKey, pricesDiffer, renewalKey, tierPrices } from "./plan-price.ts";
|
|
30
|
+
import { PlanSwitchConfirmDialog } from "./plan-switch-confirm-dialog.tsx";
|
|
28
31
|
|
|
29
32
|
type UseTranslation = ReturnType<typeof useTranslation>;
|
|
30
33
|
|
|
31
|
-
function intervalKey(price: BillingPlanView["price"]): string | undefined {
|
|
32
|
-
if (price === null || price.interval === null) return undefined;
|
|
33
|
-
return price.intervalCount !== null && price.intervalCount > 1
|
|
34
|
-
? `billing-foundation.plans.everyInterval.${price.interval}`
|
|
35
|
-
: `billing-foundation.plans.perInterval.${price.interval}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function renewalKey(price: BillingPlanView["price"]): string | undefined {
|
|
39
|
-
if (price === null || price.interval === null) return undefined;
|
|
40
|
-
return price.intervalCount !== null && price.intervalCount > 1
|
|
41
|
-
? `billing-foundation.consent.renewsEvery.${price.interval}`
|
|
42
|
-
: `billing-foundation.consent.renews.${price.interval}`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
34
|
/** Which action the plan "would" offer if it weren't blocked — mirrors
|
|
46
35
|
* plan-catalog.ts's own decision tree, since `unavailable` collapses both
|
|
47
36
|
* "would checkout" and "would switch" into one action and the disabled CTA
|
|
@@ -78,10 +67,10 @@ function manageOrReactivateAction(
|
|
|
78
67
|
};
|
|
79
68
|
}
|
|
80
69
|
|
|
81
|
-
/** `!canPurchase` hides every CTA outright (
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
70
|
+
/** `!canPurchase` hides every CTA outright (see `purchaseNotAllowed`) — distinct
|
|
71
|
+
* from the `unavailable` action's disabled CTA, which still shows a button
|
|
72
|
+
* (for example, when a plan's price failed to load), so this check runs
|
|
73
|
+
* before the general unavailable fallback. */
|
|
85
74
|
|
|
86
75
|
function planCta(
|
|
87
76
|
plan: BillingPlanView,
|
|
@@ -135,12 +124,25 @@ export function BillingPlansPanel(_props: ExtensionSectionProps): ReactNode {
|
|
|
135
124
|
const [redirecting, setRedirecting] = useState(false);
|
|
136
125
|
const [consentTier, setConsentTier] = useState<string | null>(null);
|
|
137
126
|
const [cancelOpen, setCancelOpen] = useState(false);
|
|
127
|
+
const [chosenProvider, setChosenProvider] = useState<string | null>(null);
|
|
128
|
+
const [switchTier, setSwitchTier] = useState<string | null>(null);
|
|
129
|
+
const [switchRequested, setSwitchRequested] = useState(false);
|
|
130
|
+
|
|
131
|
+
// Only present when the tenant can choose (several offered providers) or
|
|
132
|
+
// the single offered provider has a note; absent means the server decides.
|
|
133
|
+
const providers = query.data?.providers;
|
|
134
|
+
const activeProvider = providers?.find((p) => p.name === chosenProvider) ?? providers?.[0];
|
|
135
|
+
const providerArg = activeProvider ? { providerName: activeProvider.name } : {};
|
|
138
136
|
|
|
139
137
|
async function orderWithConsent(
|
|
140
138
|
tier: string,
|
|
141
|
-
consent: CheckoutConsentPayload,
|
|
139
|
+
consent: CheckoutConsentPayload | undefined,
|
|
142
140
|
): Promise<WriteResult<{ readonly url: string }>> {
|
|
143
|
-
const result = await checkoutMutation.mutate({
|
|
141
|
+
const result = await checkoutMutation.mutate({
|
|
142
|
+
tier,
|
|
143
|
+
...(consent !== undefined && { consent }),
|
|
144
|
+
...providerArg,
|
|
145
|
+
});
|
|
144
146
|
if (result.isSuccess) {
|
|
145
147
|
setRedirecting(true);
|
|
146
148
|
window.location.assign(result.data.url);
|
|
@@ -149,13 +151,23 @@ export function BillingPlansPanel(_props: ExtensionSectionProps): ReactNode {
|
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
async function handleAction(tier: string, kind: "checkout" | "switch"): Promise<void> {
|
|
152
|
-
if (
|
|
154
|
+
if (
|
|
155
|
+
kind === "checkout" &&
|
|
156
|
+
(query.data?.consumerProtection !== undefined || (providers?.length ?? 0) > 1)
|
|
157
|
+
) {
|
|
158
|
+
setChosenProvider(null);
|
|
153
159
|
setConsentTier(tier);
|
|
154
160
|
return;
|
|
155
161
|
}
|
|
162
|
+
if (kind === "switch" && query.data?.inAppPlanSwitch) {
|
|
163
|
+
setSwitchTier(tier);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
156
166
|
setRedirecting(true);
|
|
157
|
-
const
|
|
158
|
-
|
|
167
|
+
const result =
|
|
168
|
+
kind === "checkout"
|
|
169
|
+
? await checkoutMutation.mutate({ tier, ...providerArg })
|
|
170
|
+
: await switchMutation.mutate({ tier });
|
|
159
171
|
if (result.isSuccess) {
|
|
160
172
|
window.location.assign(result.data.url);
|
|
161
173
|
} else {
|
|
@@ -189,6 +201,9 @@ export function BillingPlansPanel(_props: ExtensionSectionProps): ReactNode {
|
|
|
189
201
|
}
|
|
190
202
|
if (!query.data) return null;
|
|
191
203
|
const result: BillingPlansResult = query.data;
|
|
204
|
+
// Cards show the first provider's price; the dialog then follows the chosen one.
|
|
205
|
+
const shownPlans = providers?.[0]?.plans ?? result.plans;
|
|
206
|
+
const dialogPlans = activeProvider?.plans ?? result.plans;
|
|
192
207
|
|
|
193
208
|
// The consent dialog shows the checkout error itself.
|
|
194
209
|
const mutationError =
|
|
@@ -199,7 +214,11 @@ export function BillingPlansPanel(_props: ExtensionSectionProps): ReactNode {
|
|
|
199
214
|
result.subscription !== null && !result.subscription.terminal && result.canPurchase;
|
|
200
215
|
|
|
201
216
|
function toCardProps(plan: BillingPlanView) {
|
|
202
|
-
const
|
|
217
|
+
const comparable =
|
|
218
|
+
providers !== undefined && providers.length > 1 ? tierPrices(providers, plan.tier) : [];
|
|
219
|
+
const fromPrice = pricesDiffer(comparable) ? cheapestPrice(comparable) : undefined;
|
|
220
|
+
const shownPrice = fromPrice ?? plan.price;
|
|
221
|
+
const key = intervalKey(shownPrice);
|
|
203
222
|
return {
|
|
204
223
|
title: t(plan.labelKey),
|
|
205
224
|
description:
|
|
@@ -214,13 +233,14 @@ export function BillingPlansPanel(_props: ExtensionSectionProps): ReactNode {
|
|
|
214
233
|
price:
|
|
215
234
|
!result.enabled && plan.isCurrent
|
|
216
235
|
? undefined
|
|
217
|
-
:
|
|
236
|
+
: shownPrice === null
|
|
218
237
|
? null
|
|
219
238
|
: {
|
|
220
|
-
amount: formatMoney(
|
|
239
|
+
amount: formatMoney(shownPrice.unitAmount, shownPrice.currency, locale),
|
|
240
|
+
...(fromPrice !== undefined && { prefix: t("billing-foundation.plans.priceFrom") }),
|
|
221
241
|
...(key !== undefined && {
|
|
222
242
|
period: t(key, {
|
|
223
|
-
count: new Intl.NumberFormat(locale).format(
|
|
243
|
+
count: new Intl.NumberFormat(locale).format(shownPrice.intervalCount ?? 1),
|
|
224
244
|
}),
|
|
225
245
|
}),
|
|
226
246
|
},
|
|
@@ -239,12 +259,41 @@ export function BillingPlansPanel(_props: ExtensionSectionProps): ReactNode {
|
|
|
239
259
|
// rendered plans is the current one, a synthetic current-tier card (no
|
|
240
260
|
// price, no cta) keeps it visible regardless of enabled/disabled.
|
|
241
261
|
const consentPlan =
|
|
242
|
-
consentTier !== null ?
|
|
262
|
+
consentTier !== null ? dialogPlans.find((plan) => plan.tier === consentTier) : undefined;
|
|
263
|
+
const switchPlan =
|
|
264
|
+
switchTier !== null ? shownPlans.find((plan) => plan.tier === switchTier) : undefined;
|
|
243
265
|
const consumerProtection = result.consumerProtection;
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
266
|
+
const noteLocale = resolveConsentLocale(locale);
|
|
267
|
+
const noteOf = (provider: { readonly note?: Readonly<Record<string, string>> }) =>
|
|
268
|
+
provider.note?.[noteLocale] ?? provider.note?.["en"];
|
|
269
|
+
const singleProviderNote =
|
|
270
|
+
providers !== undefined && providers.length === 1 && providers[0] !== undefined
|
|
271
|
+
? noteOf(providers[0])
|
|
272
|
+
: undefined;
|
|
273
|
+
function providerChoiceFor(tier: string) {
|
|
274
|
+
if (providers === undefined || providers.length < 2 || activeProvider === undefined) {
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
const prices = tierPrices(providers, tier);
|
|
278
|
+
const showPrices = pricesDiffer(prices);
|
|
279
|
+
return {
|
|
280
|
+
value: activeProvider.name,
|
|
281
|
+
onChange: setChosenProvider,
|
|
282
|
+
options: providers.map((provider) => {
|
|
283
|
+
const price = provider.plans.find((plan) => plan.tier === tier)?.price;
|
|
284
|
+
const note = noteOf(provider);
|
|
285
|
+
return {
|
|
286
|
+
value: provider.name,
|
|
287
|
+
title: provider.displayName,
|
|
288
|
+
...(note !== undefined && { description: note }),
|
|
289
|
+
...(showPrices &&
|
|
290
|
+
price && { aside: formatMoney(price.unitAmount, price.currency, locale) }),
|
|
291
|
+
};
|
|
292
|
+
}),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
const currentTierInPlans = shownPlans.some((plan) => plan.isCurrent);
|
|
296
|
+
const visiblePlans = result.enabled ? shownPlans : shownPlans.filter((plan) => plan.isCurrent);
|
|
248
297
|
|
|
249
298
|
return (
|
|
250
299
|
<div className="flex flex-col gap-4" data-testid="billing-plans-panel">
|
|
@@ -258,6 +307,11 @@ export function BillingPlansPanel(_props: ExtensionSectionProps): ReactNode {
|
|
|
258
307
|
{t("billing-foundation.plans.purchaseNotAllowed")}
|
|
259
308
|
</Banner>
|
|
260
309
|
)}
|
|
310
|
+
{switchRequested && (
|
|
311
|
+
<Banner variant="info" testId="billing-plans-panel-switch-requested">
|
|
312
|
+
{t("billing-foundation.plans.planSwitchRequested")}
|
|
313
|
+
</Banner>
|
|
314
|
+
)}
|
|
261
315
|
{mutationError !== null && (
|
|
262
316
|
<Banner variant="error" testId="billing-plans-panel-mutation-error">
|
|
263
317
|
{t(mutationError.i18nKey, mutationError.i18nParams)}
|
|
@@ -318,25 +372,43 @@ export function BillingPlansPanel(_props: ExtensionSectionProps): ReactNode {
|
|
|
318
372
|
))}
|
|
319
373
|
{result.contact !== undefined && <BillingContactPlan contact={result.contact} />}
|
|
320
374
|
</PlanGrid>
|
|
321
|
-
{
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
open
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
price
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
375
|
+
{consentPlan !== undefined && consentPlan.price !== null && (
|
|
376
|
+
<CheckoutConsentDialog
|
|
377
|
+
open
|
|
378
|
+
onOpenChange={(open) => {
|
|
379
|
+
if (!open) setConsentTier(null);
|
|
380
|
+
}}
|
|
381
|
+
planName={t(consentPlan.labelKey)}
|
|
382
|
+
price={{
|
|
383
|
+
amount: formatMoney(consentPlan.price.unitAmount, consentPlan.price.currency, locale),
|
|
384
|
+
renewalKey: renewalKey(consentPlan.price),
|
|
385
|
+
count: new Intl.NumberFormat(locale).format(consentPlan.price.intervalCount ?? 1),
|
|
386
|
+
}}
|
|
387
|
+
consumerProtection={consumerProtection}
|
|
388
|
+
providerChoice={providerChoiceFor(consentPlan.tier)}
|
|
389
|
+
note={singleProviderNote}
|
|
390
|
+
onOrder={(consent) => orderWithConsent(consentPlan.tier, consent)}
|
|
391
|
+
onConsentTextOutdated={() => void query.refetch()}
|
|
392
|
+
/>
|
|
393
|
+
)}
|
|
394
|
+
{switchPlan !== undefined && (
|
|
395
|
+
<PlanSwitchConfirmDialog
|
|
396
|
+
open
|
|
397
|
+
onOpenChange={(open) => {
|
|
398
|
+
if (!open) setSwitchTier(null);
|
|
399
|
+
}}
|
|
400
|
+
tier={switchPlan.tier}
|
|
401
|
+
fromPlanName={t(result.currentTier.labelKey)}
|
|
402
|
+
toPlanName={t(switchPlan.labelKey)}
|
|
403
|
+
consumerProtection={consumerProtection}
|
|
404
|
+
onApplied={() => {
|
|
405
|
+
setSwitchTier(null);
|
|
406
|
+
setSwitchRequested(true);
|
|
407
|
+
void query.refetch();
|
|
408
|
+
}}
|
|
409
|
+
onConsentTextOutdated={() => void query.refetch()}
|
|
410
|
+
/>
|
|
411
|
+
)}
|
|
340
412
|
{consumerProtection !== undefined && canManage && (
|
|
341
413
|
<CancelContractDialog
|
|
342
414
|
open={cancelOpen}
|