@cosmicdrift/kumiko-bundled-features 0.337.1 → 0.339.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.
Files changed (235) hide show
  1. package/dist/agent-tools/tool-catalog.js +37 -17
  2. package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +3 -2
  3. package/dist/auth-email-password/handlers/invite-accept.write.js +3 -4
  4. package/dist/auth-email-password/handlers/invite-info.query.js +4 -1
  5. package/dist/auth-email-password/handlers/invite-signup-complete.write.js +3 -4
  6. package/dist/auth-email-password/handlers/signup-confirm.write.js +2 -1
  7. package/dist/auth-email-password/handlers/signup-request.write.js +2 -2
  8. package/dist/auth-email-password/web/auth-gate.d.ts +12 -2
  9. package/dist/auth-email-password/web/auth-gate.js +25 -1
  10. package/dist/auth-email-password/web/auth-redirect.d.ts +5 -0
  11. package/dist/auth-email-password/web/auth-redirect.js +58 -0
  12. package/dist/auth-email-password/web/client-plugin.d.ts +8 -1
  13. package/dist/auth-email-password/web/client-plugin.js +2 -0
  14. package/dist/auth-email-password/web/index.d.ts +2 -1
  15. package/dist/auth-email-password/web/index.js +1 -0
  16. package/dist/auth-email-password/web/session.d.ts +3 -1
  17. package/dist/auth-email-password/web/session.js +8 -2
  18. package/dist/auth-mfa/feature.js +1 -1
  19. package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +1 -1
  20. package/dist/auth-mfa/handlers/enable-start-preauth.write.js +2 -1
  21. package/dist/auth-mfa/handlers/verify.write.js +1 -1
  22. package/dist/auth-mfa/mfa-code-verifier.js +1 -1
  23. package/dist/auth-mfa/mfa-status-checker.js +1 -1
  24. package/dist/billing-foundation/aggregate-id.d.ts +2 -0
  25. package/dist/billing-foundation/aggregate-id.js +8 -0
  26. package/dist/billing-foundation/checkout-core.d.ts +3 -0
  27. package/dist/billing-foundation/checkout-core.js +3 -0
  28. package/dist/billing-foundation/constants.d.ts +38 -0
  29. package/dist/billing-foundation/constants.js +43 -0
  30. package/dist/billing-foundation/consumer-protection/confirmation-mail.d.ts +21 -0
  31. package/dist/billing-foundation/consumer-protection/confirmation-mail.js +125 -0
  32. package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +32 -0
  33. package/dist/billing-foundation/consumer-protection/consent-gate.js +96 -0
  34. package/dist/billing-foundation/consumer-protection/consent-locale.d.ts +5 -0
  35. package/dist/billing-foundation/consumer-protection/consent-locale.js +17 -0
  36. package/dist/billing-foundation/consumer-protection/consent-text.d.ts +11 -0
  37. package/dist/billing-foundation/consumer-protection/consent-text.js +28 -0
  38. package/dist/billing-foundation/consumer-protection/issue-confirmation.d.ts +15 -0
  39. package/dist/billing-foundation/consumer-protection/issue-confirmation.js +149 -0
  40. package/dist/billing-foundation/consumer-protection/register-consent.d.ts +3 -0
  41. package/dist/billing-foundation/consumer-protection/register-consent.js +63 -0
  42. package/dist/billing-foundation/consumer-protection/register-termination.d.ts +3 -0
  43. package/dist/billing-foundation/consumer-protection/register-termination.js +40 -0
  44. package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +45 -0
  45. package/dist/billing-foundation/consumer-protection/termination-mail.js +76 -0
  46. package/dist/billing-foundation/consumer-protection/termination-notify.d.ts +18 -0
  47. package/dist/billing-foundation/consumer-protection/termination-notify.js +21 -0
  48. package/dist/billing-foundation/consumer-protection/termination-pages.d.ts +8 -0
  49. package/dist/billing-foundation/consumer-protection/termination-pages.js +268 -0
  50. package/dist/billing-foundation/consumer-protection/termination-record.d.ts +73 -0
  51. package/dist/billing-foundation/consumer-protection/termination-record.js +230 -0
  52. package/dist/billing-foundation/consumer-protection/termination-request.d.ts +45 -0
  53. package/dist/billing-foundation/consumer-protection/termination-request.js +231 -0
  54. package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +62 -0
  55. package/dist/billing-foundation/consumer-protection/termination-texts.js +168 -0
  56. package/dist/billing-foundation/events.d.ts +105 -2
  57. package/dist/billing-foundation/events.js +88 -2
  58. package/dist/billing-foundation/feature.js +6 -2
  59. package/dist/billing-foundation/handlers/billing-plans.query.js +19 -1
  60. package/dist/billing-foundation/handlers/create-checkout-session.write.js +33 -2
  61. package/dist/billing-foundation/handlers/process-event.write.d.ts +2 -2
  62. package/dist/billing-foundation/handlers/process-event.write.js +43 -15
  63. package/dist/billing-foundation/handlers/process-payment-event.write.d.ts +2 -1
  64. package/dist/billing-foundation/handlers/process-payment-event.write.js +6 -2
  65. package/dist/billing-foundation/handlers/start-plan-checkout.write.js +17 -1
  66. package/dist/billing-foundation/handlers/sync-subscription.write.js +0 -1
  67. package/dist/billing-foundation/i18n.js +61 -0
  68. package/dist/billing-foundation/index.d.ts +3 -2
  69. package/dist/billing-foundation/index.js +2 -1
  70. package/dist/billing-foundation/tenant-destroy-hook.js +4 -4
  71. package/dist/billing-foundation/types.d.ts +54 -9
  72. package/dist/billing-foundation/validate-options.js +51 -10
  73. package/dist/billing-foundation/web/billing-plans-panel.js +40 -3
  74. package/dist/billing-foundation/web/cancel-contract-dialog.d.ts +7 -0
  75. package/dist/billing-foundation/web/cancel-contract-dialog.js +57 -0
  76. package/dist/billing-foundation/web/checkout-consent-dialog.d.ts +28 -0
  77. package/dist/billing-foundation/web/checkout-consent-dialog.js +38 -0
  78. package/dist/billing-foundation/webhook-handler.js +5 -2
  79. package/dist/cap-counter/with-cap-enforcement.js +7 -3
  80. package/dist/cap-overview/access.d.ts +2 -1
  81. package/dist/cap-overview/access.js +26 -5
  82. package/dist/cap-overview/feature.d.ts +4 -0
  83. package/dist/cap-overview/feature.js +8 -6
  84. package/dist/cap-overview/handlers/caps-usage.query.d.ts +1 -1
  85. package/dist/cap-overview/handlers/caps-usage.query.js +6 -4
  86. package/dist/cap-overview/handlers/tenant-caps-list.query.js +12 -7
  87. package/dist/cap-overview/index.d.ts +2 -2
  88. package/dist/cap-overview/index.js +2 -2
  89. package/dist/cap-overview/screens.d.ts +1 -1
  90. package/dist/cap-overview/screens.js +17 -16
  91. package/dist/channel-discord/discord-channel.d.ts +6 -0
  92. package/dist/channel-discord/discord-channel.js +19 -0
  93. package/dist/channel-discord/feature.d.ts +3 -0
  94. package/dist/channel-discord/feature.js +30 -0
  95. package/dist/channel-discord/index.d.ts +2 -0
  96. package/dist/channel-discord/index.js +2 -0
  97. package/dist/channel-slack/feature.d.ts +3 -0
  98. package/dist/channel-slack/feature.js +30 -0
  99. package/dist/channel-slack/index.d.ts +2 -0
  100. package/dist/channel-slack/index.js +2 -0
  101. package/dist/channel-slack/slack-channel.d.ts +5 -0
  102. package/dist/channel-slack/slack-channel.js +16 -0
  103. package/dist/channel-teams/feature.d.ts +3 -0
  104. package/dist/channel-teams/feature.js +34 -0
  105. package/dist/channel-teams/index.d.ts +2 -0
  106. package/dist/channel-teams/index.js +2 -0
  107. package/dist/channel-teams/teams-channel.d.ts +5 -0
  108. package/dist/channel-teams/teams-channel.js +38 -0
  109. package/dist/channel-telegram/feature.d.ts +3 -0
  110. package/dist/channel-telegram/feature.js +28 -0
  111. package/dist/channel-telegram/index.d.ts +2 -0
  112. package/dist/channel-telegram/index.js +2 -0
  113. package/dist/channel-telegram/telegram-channel.d.ts +8 -0
  114. package/dist/channel-telegram/telegram-channel.js +54 -0
  115. package/dist/config/handlers/set.write.js +18 -2
  116. package/dist/crypto-shredding/feature.js +3 -2
  117. package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
  118. package/dist/crypto-shredding/handlers/forget-subject.write.js +30 -19
  119. package/dist/custom-fields/feature.js +9 -6
  120. package/dist/custom-fields/handlers/clear-custom-field.write.js +2 -1
  121. package/dist/custom-fields/handlers/define-tenant-field.write.js +2 -1
  122. package/dist/custom-fields/handlers/set-custom-field.write.js +2 -1
  123. package/dist/custom-fields/wire-user-data-rights.js +1 -1
  124. package/dist/data-retention/feature.js +1 -1
  125. package/dist/delivery/channel-context.d.ts +2 -1
  126. package/dist/delivery/channel-context.js +2 -2
  127. package/dist/delivery/chat-webhook-channel.d.ts +19 -0
  128. package/dist/delivery/chat-webhook-channel.js +48 -0
  129. package/dist/delivery/chat-webhook-sender.d.ts +21 -0
  130. package/dist/delivery/chat-webhook-sender.js +102 -0
  131. package/dist/delivery/delivery-service.js +12 -7
  132. package/dist/delivery/index.d.ts +2 -0
  133. package/dist/delivery/index.js +2 -0
  134. package/dist/delivery/jobs.js +1 -1
  135. package/dist/delivery/testing.d.ts +2 -0
  136. package/dist/delivery/types.d.ts +3 -1
  137. package/dist/delivery/types.js +1 -2
  138. package/dist/document-ingest-foundation/feature.js +3 -2
  139. package/dist/file-derivatives/handlers/public-variant-by-file-ref.query.js +1 -1
  140. package/dist/files-tenant-data/index.js +1 -1
  141. package/dist/form-draft/feature.js +1 -1
  142. package/dist/form-draft/handlers/discard.write.js +2 -1
  143. package/dist/form-draft/handlers/save.write.js +2 -1
  144. package/dist/inbound-mail-foundation/connect-routes.d.ts +7 -0
  145. package/dist/inbound-mail-foundation/connect-routes.js +13 -8
  146. package/dist/inbound-mail-foundation/feature.js +1 -1
  147. package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +2 -1
  148. package/dist/inbound-mail-foundation/index.d.ts +1 -0
  149. package/dist/inbound-mail-foundation/index.js +1 -0
  150. package/dist/inbound-mail-foundation/oauth-access-token.d.ts +28 -0
  151. package/dist/inbound-mail-foundation/oauth-access-token.js +131 -0
  152. package/dist/inbound-mail-foundation/tenant-destroy-hook.js +1 -1
  153. package/dist/inbound-mail-foundation/types.d.ts +18 -1
  154. package/dist/inbound-mail-foundation/watch-supervisor.d.ts +5 -0
  155. package/dist/inbound-mail-foundation/watch-supervisor.js +42 -14
  156. package/dist/managed-pages/handlers/set.write.js +2 -1
  157. package/dist/notes-history/executor.d.ts +2 -0
  158. package/dist/notes-history/executor.js +9 -1
  159. package/dist/notes-history/feature.d.ts +2 -3
  160. package/dist/notes-history/feature.js +2 -1
  161. package/dist/notes-history/handlers/add-note.write.d.ts +2 -1
  162. package/dist/notes-history/handlers/add-note.write.js +21 -4
  163. package/dist/notes-history-user-data/hooks.js +26 -8
  164. package/dist/renderer-simple/feature.d.ts +8 -2
  165. package/dist/renderer-simple/feature.js +8 -5
  166. package/dist/renderer-simple/index.d.ts +2 -2
  167. package/dist/renderer-simple/index.js +1 -1
  168. package/dist/renderer-simple/simple-renderer.d.ts +15 -0
  169. package/dist/renderer-simple/simple-renderer.js +74 -27
  170. package/dist/secrets/feature.js +1 -1
  171. package/dist/sessions/feature.js +1 -1
  172. package/dist/sessions/handlers/revoke-all-for-user.write.js +2 -1
  173. package/dist/subscription-mollie/plugin-methods.js +1 -0
  174. package/dist/subscription-mollie/verify-webhook.js +5 -3
  175. package/dist/subscription-stripe/plugin-methods.d.ts +3 -1
  176. package/dist/subscription-stripe/plugin-methods.js +78 -13
  177. package/dist/subscription-stripe/verify-webhook.js +7 -3
  178. package/dist/tags/executor.d.ts +2 -0
  179. package/dist/tags/executor.js +9 -1
  180. package/dist/tags/feature.d.ts +4 -3
  181. package/dist/tags/feature.js +4 -2
  182. package/dist/tags/handlers/assign-tag.write.d.ts +2 -1
  183. package/dist/tags/handlers/assign-tag.write.js +4 -4
  184. package/dist/tags/handlers/remove-tag.write.d.ts +2 -1
  185. package/dist/tags/handlers/remove-tag.write.js +3 -3
  186. package/dist/tenant-handover/handlers/claim.write.js +2 -2
  187. package/dist/tenant-lifecycle/feature.js +1 -1
  188. package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +4 -1
  189. package/dist/tenant-lifecycle/handlers/request-destruction.write.js +2 -1
  190. package/dist/tier-engine/feature.js +1 -1
  191. package/dist/tier-engine/handlers/get-tenant-tier.query.js +2 -1
  192. package/dist/tier-engine/handlers/set-tenant-tier.write.js +2 -1
  193. package/dist/user/handlers/detail.query.js +1 -0
  194. package/dist/user/handlers/list.query.js +1 -0
  195. package/dist/user-data-rights/feature.js +2 -2
  196. package/dist/user-data-rights/handlers/cancel-deletion.write.js +2 -1
  197. package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +1 -1
  198. package/dist/user-data-rights/handlers/download-attempt-list.query.js +1 -0
  199. package/dist/user-data-rights/handlers/download-by-job.query.js +2 -1
  200. package/dist/user-data-rights/handlers/download-by-token.query.js +2 -1
  201. package/dist/user-data-rights/handlers/export-status.query.js +2 -1
  202. package/dist/user-data-rights/handlers/lift-restriction.write.js +3 -4
  203. package/dist/user-data-rights/handlers/my-audit-log.query.js +2 -1
  204. package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +2 -1
  205. package/dist/user-data-rights/handlers/request-deletion.write.js +1 -1
  206. package/dist/user-data-rights/handlers/request-export.write.js +2 -1
  207. package/dist/user-data-rights/handlers/restrict-account.write.js +2 -2
  208. package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +2 -1
  209. package/dist/user-data-rights/lib/update-user-lifecycle.js +1 -1
  210. package/dist/user-data-rights/run-forget-cleanup.js +30 -15
  211. package/dist/workflow-runner/event-subscriber.js +6 -2
  212. package/dist/workflow-runner/event-trigger.js +5 -0
  213. package/dist/workflow-runner/handlers/resume-run.write.js +147 -59
  214. package/dist/workflow-runner/pending-projection.js +1 -0
  215. package/dist/workflow-runner/runner.d.ts +7 -1
  216. package/dist/workflow-runner/runner.js +1 -1
  217. package/dist/workflow-runner/tables.js +8 -1
  218. package/package.json +41 -9
  219. package/src/agent-tools/changes.json +14 -0
  220. package/src/auth-email-password/changes.json +7 -0
  221. package/src/billing-foundation/changes.json +83 -0
  222. package/src/cap-counter/changes.json +7 -0
  223. package/src/cap-overview/changes.json +14 -0
  224. package/src/config/changes.json +7 -0
  225. package/src/crypto-shredding/changes.json +13 -0
  226. package/src/custom-fields/changes.json +7 -0
  227. package/src/delivery/changes.json +14 -0
  228. package/src/inbound-mail-foundation/changes.json +7 -0
  229. package/src/notes-history/changes.json +7 -0
  230. package/src/renderer-simple/changes.json +9 -1
  231. package/src/subscription-mollie/changes.json +13 -0
  232. package/src/subscription-stripe/changes.json +13 -0
  233. package/src/tags/changes.json +7 -0
  234. package/src/user-data-rights/changes.json +6 -0
  235. package/src/workflow-runner/changes.json +7 -0
@@ -88,6 +88,9 @@ export type OpenCheckoutInput = {
88
88
  readonly cancelUrl: string;
89
89
  readonly providerCustomerId?: string;
90
90
  readonly mode?: "subscription" | "payment";
91
+ readonly consentId?: string;
92
+ readonly locale?: string;
93
+ readonly submitMessage?: string;
91
94
  };
92
95
  /** The one checkout entry-point create-checkout-session and
93
96
  * start-plan-checkout both funnel through. `mode: "payment"` (one-off
@@ -282,6 +282,9 @@ export async function openCheckout(ctx, options, input) {
282
282
  // pre-hardening wire-contract for callers that never sent `mode`
283
283
  // (create-checkout-session's own regression pin).
284
284
  ...(input.mode && { mode: input.mode }),
285
+ ...(input.consentId && { consentId: input.consentId }),
286
+ ...(input.locale && { locale: input.locale }),
287
+ ...(input.submitMessage && { submitMessage: input.submitMessage }),
285
288
  });
286
289
  return { url: result.url, providerName: input.providerName };
287
290
  }
@@ -2,6 +2,12 @@
2
2
  import { Temporal as TemporalPolyfill } from "temporal-polyfill";
3
3
  export declare const BILLING_FOUNDATION_FEATURE: "billing-foundation";
4
4
  export declare const SUBSCRIPTION_PROVIDER_EXTENSION: "subscriptionProvider";
5
+ /** delivery notification type of the § 312f contract confirmation mail. */
6
+ export declare const CONTRACT_CONFIRMATION_NOTIFICATION_TYPE: "billing-foundation:contract-confirmation";
7
+ /** delivery notification types of the § 312k termination receipt and the
8
+ * operator notice. */
9
+ export declare const CONTRACT_TERMINATION_RECEIPT_NOTIFICATION_TYPE: "billing-foundation:contract-termination-receipt";
10
+ export declare const CONTRACT_TERMINATION_OPERATOR_NOTIFICATION_TYPE: "billing-foundation:contract-termination-operator-notice";
5
11
  export declare const SubscriptionFoundationHandlers: {
6
12
  /** Programmatic entry-point für den webhook-handler. Receives the
7
13
  * parsed SubscriptionEvent (vom Plugin schon verifiziert) + macht
@@ -22,6 +28,26 @@ export declare const SubscriptionFoundationHandlers: {
22
28
  * (payment-aggregate), separate from the subscription-aggregate — a
23
29
  * payment is not a subscription-state transition. */
24
30
  readonly processPaymentEvent: "billing-foundation:write:process-payment-event";
31
+ /** System-only: sends the § 312f contract confirmation mail for a recorded
32
+ * checkout consent (consumerProtection only). */
33
+ readonly issueContractConfirmation: "billing-foundation:write:issue-contract-confirmation";
34
+ /** System-only: records a termination/withdrawal on the tenant's
35
+ * subscription stream and asks the provider to cancel (consumerProtection
36
+ * only). */
37
+ readonly recordContractTermination: "billing-foundation:write:record-contract-termination";
38
+ /** System-only: records that a public declaration reached a tenant's
39
+ * subscription stream; the event triggers the provider cancel job
40
+ * (consumerProtection only). */
41
+ readonly declareContractTermination: "billing-foundation:write:declare-contract-termination";
42
+ /** System-only: records a public declaration no single tenant could be
43
+ * matched to, on a system-tenant stream (consumerProtection only). */
44
+ readonly recordUnmatchedContractTermination: "billing-foundation:write:record-unmatched-contract-termination";
45
+ /** Anonymous § 312k cancellation/withdrawal declaration (consumerProtection
46
+ * only). */
47
+ readonly requestContractTermination: "billing-foundation:write:request-contract-termination";
48
+ /** Tenant-Admin terminates or withdraws from the contract of their own
49
+ * tenant (consumerProtection only). */
50
+ readonly terminateContract: "billing-foundation:write:terminate-contract";
25
51
  /** Tenant-Admin/purchase-role picks a plan tier from the catalog with no
26
52
  * existing non-terminal subscription — starts a hosted checkout for the
27
53
  * matching price. Only registered when `createBillingFoundationFeature`
@@ -47,6 +73,9 @@ export declare const SubscriptionFoundationQueries: {
47
73
  * current tier and per-plan action. Only registered when a `catalog` is
48
74
  * configured. */
49
75
  readonly billingPlans: "billing-foundation:query:billing-plans";
76
+ /** System-only probe: does the caller tenant hold a non-terminal
77
+ * subscription (consumerProtection only). */
78
+ readonly terminableSubscription: "billing-foundation:query:terminable-subscription";
50
79
  };
51
80
  export declare const SubscriptionEventTypes: {
52
81
  readonly created: "subscription.created";
@@ -69,6 +98,12 @@ export declare const BillingEventKinds: {
69
98
  readonly payment: "payment";
70
99
  };
71
100
  export type BillingEventKind = (typeof BillingEventKinds)[keyof typeof BillingEventKinds];
101
+ export declare const SubscriptionCancelTimings: {
102
+ readonly periodEnd: "period-end";
103
+ readonly immediately: "immediately";
104
+ };
105
+ export type SubscriptionCancelTiming = (typeof SubscriptionCancelTimings)[keyof typeof SubscriptionCancelTimings];
106
+ export declare function parseProviderConsentId(raw: unknown): string | undefined;
72
107
  export declare const BILLING_PLANS_SCREEN_ID: "billing-plans";
73
108
  export declare const BILLING_PLANS_PANEL_COMPONENT: "BillingPlansPanel";
74
109
  export declare const BillingPlanActions: {
@@ -97,3 +132,6 @@ export declare function isSubscriptionBlockingCheckout(sub: {
97
132
  export declare const SWITCHABLE_SUBSCRIPTION_STATUSES: ReadonlySet<string>;
98
133
  export declare function isSwitchableSubscriptionStatus(status: string): boolean;
99
134
  export declare const DEFAULT_PURCHASE_ROLES: readonly ["TenantAdmin", "SystemAdmin"];
135
+ export declare const TERMINATION_SCOPES: readonly ["tenant-host", "platform"];
136
+ export type TerminationScope = (typeof TERMINATION_SCOPES)[number];
137
+ export declare function isTerminationScope(value: unknown): value is TerminationScope;
@@ -12,6 +12,12 @@ export const BILLING_FOUNDATION_FEATURE = "billing-foundation";
12
12
  // Extension-point name fuer Provider-Plugins (subscription-stripe,
13
13
  // subscription-mollie, ...).
14
14
  export const SUBSCRIPTION_PROVIDER_EXTENSION = "subscriptionProvider";
15
+ /** delivery notification type of the § 312f contract confirmation mail. */
16
+ export const CONTRACT_CONFIRMATION_NOTIFICATION_TYPE = "billing-foundation:contract-confirmation";
17
+ /** delivery notification types of the § 312k termination receipt and the
18
+ * operator notice. */
19
+ export const CONTRACT_TERMINATION_RECEIPT_NOTIFICATION_TYPE = "billing-foundation:contract-termination-receipt";
20
+ export const CONTRACT_TERMINATION_OPERATOR_NOTIFICATION_TYPE = "billing-foundation:contract-termination-operator-notice";
15
21
  // Qualified write handler names (QN format: scope:type:name).
16
22
  export const SubscriptionFoundationHandlers = {
17
23
  /** Programmatic entry-point für den webhook-handler. Receives the
@@ -33,6 +39,26 @@ export const SubscriptionFoundationHandlers = {
33
39
  * (payment-aggregate), separate from the subscription-aggregate — a
34
40
  * payment is not a subscription-state transition. */
35
41
  processPaymentEvent: "billing-foundation:write:process-payment-event",
42
+ /** System-only: sends the § 312f contract confirmation mail for a recorded
43
+ * checkout consent (consumerProtection only). */
44
+ issueContractConfirmation: "billing-foundation:write:issue-contract-confirmation",
45
+ /** System-only: records a termination/withdrawal on the tenant's
46
+ * subscription stream and asks the provider to cancel (consumerProtection
47
+ * only). */
48
+ recordContractTermination: "billing-foundation:write:record-contract-termination",
49
+ /** System-only: records that a public declaration reached a tenant's
50
+ * subscription stream; the event triggers the provider cancel job
51
+ * (consumerProtection only). */
52
+ declareContractTermination: "billing-foundation:write:declare-contract-termination",
53
+ /** System-only: records a public declaration no single tenant could be
54
+ * matched to, on a system-tenant stream (consumerProtection only). */
55
+ recordUnmatchedContractTermination: "billing-foundation:write:record-unmatched-contract-termination",
56
+ /** Anonymous § 312k cancellation/withdrawal declaration (consumerProtection
57
+ * only). */
58
+ requestContractTermination: "billing-foundation:write:request-contract-termination",
59
+ /** Tenant-Admin terminates or withdraws from the contract of their own
60
+ * tenant (consumerProtection only). */
61
+ terminateContract: "billing-foundation:write:terminate-contract",
36
62
  /** Tenant-Admin/purchase-role picks a plan tier from the catalog with no
37
63
  * existing non-terminal subscription — starts a hosted checkout for the
38
64
  * matching price. Only registered when `createBillingFoundationFeature`
@@ -59,6 +85,9 @@ export const SubscriptionFoundationQueries = {
59
85
  * current tier and per-plan action. Only registered when a `catalog` is
60
86
  * configured. */
61
87
  billingPlans: "billing-foundation:query:billing-plans",
88
+ /** System-only probe: does the caller tenant hold a non-terminal
89
+ * subscription (consumerProtection only). */
90
+ terminableSubscription: "billing-foundation:query:terminable-subscription",
62
91
  };
63
92
  // Normalized subscription-event types — provider-agnostic.
64
93
  // Alle Provider-Plugins normalisieren ihre eigenen event-types auf einen
@@ -90,6 +119,16 @@ export const BillingEventKinds = {
90
119
  subscription: "subscription",
91
120
  payment: "payment",
92
121
  };
122
+ export const SubscriptionCancelTimings = {
123
+ periodEnd: "period-end",
124
+ immediately: "immediately",
125
+ };
126
+ // consentId is echoed back by the provider (checkout metadata), so it is
127
+ // untrusted input: bounded length, id-ish charset only.
128
+ const CONSENT_ID_PATTERN = /^[A-Za-z0-9_-]{1,100}$/;
129
+ export function parseProviderConsentId(raw) {
130
+ return typeof raw === "string" && CONSENT_ID_PATTERN.test(raw) ? raw : undefined;
131
+ }
93
132
  // Billing-plans screen/panel identifiers — the dormant dashboard app-builders
94
133
  // mount their own catalog-derived nav entry onto.
95
134
  export const BILLING_PLANS_SCREEN_ID = "billing-plans";
@@ -163,3 +202,7 @@ export const DEFAULT_PURCHASE_ROLES = ["TenantAdmin", "SystemAdmin"];
163
202
  // Stripe-priceIds vs PayPal-plan-ids vs Apple-product-ids
164
203
  // unterschiedliche IDs sind. Jeder Plugin definiert seinen eigenen
165
204
  // `<plugin-name>:config:price-to-tier`-Key.
205
+ export const TERMINATION_SCOPES = ["tenant-host", "platform"];
206
+ export function isTerminationScope(value) {
207
+ return TERMINATION_SCOPES.includes(value);
208
+ }
@@ -0,0 +1,21 @@
1
+ import type { CheckoutConsentRecordedPayload } from "../events.js";
2
+ export type ContractConfirmationContent = {
3
+ readonly subject: string;
4
+ readonly header: string;
5
+ readonly sections: readonly {
6
+ readonly text: string;
7
+ }[];
8
+ readonly footer: string;
9
+ };
10
+ export type RenderContractConfirmationArgs = {
11
+ readonly consent: CheckoutConsentRecordedPayload;
12
+ readonly consentGivenAtIso: string;
13
+ readonly contractStartIso: string;
14
+ readonly currentPeriodEndIso?: string;
15
+ readonly vatNote: Readonly<Record<string, string>>;
16
+ readonly operatorEmail: string;
17
+ readonly termsContent: string;
18
+ };
19
+ /** Structured mail content (header/sections/footer) for the email channel's
20
+ * renderer; the renderer owns HTML escaping, this only builds plain text. */
21
+ export declare function renderContractConfirmation(args: RenderContractConfirmationArgs): ContractConfirmationContent;
@@ -0,0 +1,125 @@
1
+ import { Temporal } from "temporal-polyfill";
2
+ import { CONSENT_TEXTS, resolveConsentLocale } from "./consent-text.js";
3
+ const LABELS = {
4
+ de: {
5
+ subject: "Vertragsbestätigung",
6
+ intro: "vielen Dank für deine Bestellung. Hiermit bestätigen wir den Vertragsschluss mit folgendem Inhalt.",
7
+ plan: "Tarif",
8
+ oneOffPayment: "Einmalzahlung",
9
+ price: "Preis",
10
+ oneOff: "einmalig",
11
+ contractStart: "Vertragsbeginn",
12
+ currentPeriodEnd: "Aktueller Abrechnungszeitraum bis",
13
+ consentHeading: "Deine Zustimmungen",
14
+ consentGivenAt: "Erteilt am",
15
+ termsHeading: "Allgemeine Geschäftsbedingungen",
16
+ contact: "Fragen zu deinem Vertrag richtest du bitte an",
17
+ every: "alle",
18
+ intervals: {
19
+ day: { one: "Tag", many: "Tage" },
20
+ week: { one: "Woche", many: "Wochen" },
21
+ month: { one: "Monat", many: "Monate" },
22
+ year: { one: "Jahr", many: "Jahre" },
23
+ },
24
+ },
25
+ en: {
26
+ subject: "Contract confirmation",
27
+ intro: "thank you for your order. We hereby confirm the conclusion of the contract with the following content.",
28
+ plan: "Plan",
29
+ oneOffPayment: "One-off payment",
30
+ price: "Price",
31
+ oneOff: "one-off",
32
+ contractStart: "Contract start",
33
+ currentPeriodEnd: "Current billing period ends",
34
+ consentHeading: "Your consents",
35
+ consentGivenAt: "Given on",
36
+ termsHeading: "Terms and conditions",
37
+ contact: "Questions about your contract go to",
38
+ every: "every",
39
+ intervals: {
40
+ day: { one: "day", many: "days" },
41
+ week: { one: "week", many: "weeks" },
42
+ month: { one: "month", many: "months" },
43
+ year: { one: "year", many: "years" },
44
+ },
45
+ },
46
+ };
47
+ function formatPrice(consent, locale) {
48
+ const labels = LABELS[locale];
49
+ if (consent.unitAmount === null || consent.currency === null)
50
+ return "-";
51
+ let amount;
52
+ try {
53
+ const format = new Intl.NumberFormat(locale, { style: "currency", currency: consent.currency });
54
+ const fractionDigits = format.resolvedOptions().maximumFractionDigits ?? 2;
55
+ amount = format.format(consent.unitAmount / 10 ** fractionDigits);
56
+ }
57
+ catch {
58
+ // Currency codes come from the provider; an unknown one must not block the mail.
59
+ amount = `${consent.unitAmount} ${consent.currency.toUpperCase()}`;
60
+ }
61
+ if (consent.interval === null)
62
+ return `${amount} (${labels.oneOff})`;
63
+ const unit = labels.intervals[consent.interval];
64
+ const count = consent.intervalCount ?? 1;
65
+ const intervalText = unit === undefined
66
+ ? consent.interval
67
+ : count === 1
68
+ ? unit.one
69
+ : `${labels.every} ${count} ${unit.many}`;
70
+ return count === 1 ? `${amount} / ${intervalText}` : `${amount}, ${intervalText}`;
71
+ }
72
+ function formatDate(iso, locale) {
73
+ let epochMilliseconds;
74
+ try {
75
+ epochMilliseconds = Temporal.Instant.from(iso).epochMilliseconds;
76
+ }
77
+ catch {
78
+ return iso;
79
+ }
80
+ return new Intl.DateTimeFormat(locale, {
81
+ dateStyle: "long",
82
+ timeStyle: "short",
83
+ timeZone: "UTC",
84
+ }).format(epochMilliseconds);
85
+ }
86
+ function paragraphs(text) {
87
+ return text
88
+ .split(/\n{2,}/)
89
+ .map((part) => part.trim())
90
+ .filter((part) => part.length > 0)
91
+ .map((part) => ({ text: part }));
92
+ }
93
+ /** Structured mail content (header/sections/footer) for the email channel's
94
+ * renderer; the renderer owns HTML escaping, this only builds plain text. */
95
+ export function renderContractConfirmation(args) {
96
+ const locale = resolveConsentLocale(args.consent.locale);
97
+ const labels = LABELS[locale];
98
+ const texts = CONSENT_TEXTS[locale];
99
+ const { consent } = args;
100
+ const contractLines = [
101
+ `${labels.plan}: ${consent.tier ?? labels.oneOffPayment}`,
102
+ `${labels.price}: ${formatPrice(consent, locale)}`,
103
+ `${labels.contractStart}: ${formatDate(args.contractStartIso, locale)}`,
104
+ ...(args.currentPeriodEndIso !== undefined
105
+ ? [`${labels.currentPeriodEnd}: ${formatDate(args.currentPeriodEndIso, locale)}`]
106
+ : []),
107
+ ];
108
+ const vatNote = args.vatNote[locale] ?? args.vatNote[resolveConsentLocale()];
109
+ return {
110
+ subject: labels.subject,
111
+ header: labels.subject,
112
+ sections: [
113
+ { text: labels.intro },
114
+ ...contractLines.map((text) => ({ text })),
115
+ ...(vatNote !== undefined ? [{ text: vatNote }] : []),
116
+ { text: labels.consentHeading },
117
+ { text: texts.earlyPerformance },
118
+ { text: texts.withdrawalLoss },
119
+ { text: `${labels.consentGivenAt}: ${formatDate(args.consentGivenAtIso, locale)}` },
120
+ { text: labels.termsHeading },
121
+ ...paragraphs(args.termsContent),
122
+ ],
123
+ footer: `${labels.contact} ${args.operatorEmail}`,
124
+ };
125
+ }
@@ -0,0 +1,32 @@
1
+ import type { HandlerContext } from "@cosmicdrift/kumiko-framework/engine";
2
+ import * as z from "zod";
3
+ import type { ConsumerProtectionOptions, ProviderPrice } from "../types.js";
4
+ export type CheckoutMode = "subscription" | "payment";
5
+ export declare const consentPayloadSchema: z.ZodObject<{
6
+ earlyPerformanceRequested: z.ZodBoolean;
7
+ withdrawalLossAcknowledged: z.ZodBoolean;
8
+ consentTextVersion: z.ZodString;
9
+ locale: z.ZodString;
10
+ }, z.core.$strict>;
11
+ export type ConsentPayload = z.infer<typeof consentPayloadSchema>;
12
+ export type PreparedConsent = {
13
+ readonly consentId: string;
14
+ readonly mode: CheckoutMode;
15
+ readonly locale: string;
16
+ readonly submitMessage: string;
17
+ readonly consentTextVersion: string;
18
+ readonly termsHash: string;
19
+ readonly termsTemplateVersion: number;
20
+ };
21
+ export type ConsentPriceDetails = {
22
+ readonly tier: string | null;
23
+ readonly priceId: string;
24
+ readonly price: Pick<ProviderPrice, "unitAmount" | "currency" | "interval" | "intervalCount"> | null;
25
+ };
26
+ export declare function prepareConsent(ctx: HandlerContext, options: ConsumerProtectionOptions, consent: ConsentPayload | undefined, mode: CheckoutMode): Promise<PreparedConsent>;
27
+ export declare function consentCheckoutFields(prepared: PreparedConsent): {
28
+ readonly consentId: string;
29
+ readonly locale: string;
30
+ readonly submitMessage: string;
31
+ };
32
+ export declare function recordConsent(ctx: HandlerContext, prepared: PreparedConsent, details: ConsentPriceDetails): Promise<void>;
@@ -0,0 +1,96 @@
1
+ // Consent gate shared by start-plan-checkout and create-checkout-session:
2
+ // validate + prepare before the provider call, record only after it succeeded.
3
+ import { createHash } from "node:crypto";
4
+ import { UnprocessableError } from "@cosmicdrift/kumiko-framework/errors";
5
+ import { generateId } from "@cosmicdrift/kumiko-framework/utils";
6
+ import * as z from "zod";
7
+ import { requireTemplateResolver, TEXT_BLOCK_KIND, TemplateNotFoundError, } from "../../template-resolver/index.js";
8
+ import { paymentAggregateId, subscriptionAggregateId } from "../aggregate-id.js";
9
+ import { CHECKOUT_CONSENT_RECORDED_EVENT_QN, PAYMENT_AGGREGATE_TYPE, SUBSCRIPTION_AGGREGATE_TYPE, } from "../events.js";
10
+ import { consentTextVersion, resolveConsentLocale, submitMessageFor } from "./consent-text.js";
11
+ // Optional at the schema level so a missing/unchecked consent reaches
12
+ // prepareConsent and fails as 422 consent_required, not as a zod 400.
13
+ export const consentPayloadSchema = z
14
+ .object({
15
+ earlyPerformanceRequested: z.boolean(),
16
+ withdrawalLossAcknowledged: z.boolean(),
17
+ consentTextVersion: z.string().min(1).max(64),
18
+ locale: z.string().min(2).max(35),
19
+ })
20
+ .strict();
21
+ export async function prepareConsent(ctx, options, consent, mode) {
22
+ if (!consent?.earlyPerformanceRequested || !consent.withdrawalLossAcknowledged) {
23
+ throw new UnprocessableError("consent_required", {
24
+ i18nKey: "billing-foundation.errors.consentRequired",
25
+ message: "billing-foundation: both consent flags must be true to start a checkout",
26
+ });
27
+ }
28
+ const locale = resolveConsentLocale(consent.locale);
29
+ if (consent.consentTextVersion !== consentTextVersion(locale)) {
30
+ throw new UnprocessableError("consent_text_outdated", {
31
+ i18nKey: "billing-foundation.errors.consentTextOutdated",
32
+ message: `billing-foundation: consent text version "${consent.consentTextVersion}" is not current for locale "${locale}"`,
33
+ });
34
+ }
35
+ const resolver = requireTemplateResolver(ctx, "billing-foundation");
36
+ let terms;
37
+ try {
38
+ terms = await resolver.resolveTemplate({
39
+ tenantId: ctx.user.tenantId,
40
+ slug: options.termsTextBlock,
41
+ kind: TEXT_BLOCK_KIND,
42
+ locale,
43
+ });
44
+ }
45
+ catch (error) {
46
+ if (error instanceof TemplateNotFoundError) {
47
+ throw new UnprocessableError("terms_unavailable", {
48
+ i18nKey: "billing-foundation.errors.termsUnavailable",
49
+ message: `billing-foundation: terms text block "${options.termsTextBlock}" could not be resolved`,
50
+ cause: error,
51
+ });
52
+ }
53
+ throw error;
54
+ }
55
+ return {
56
+ consentId: generateId(),
57
+ mode,
58
+ locale,
59
+ submitMessage: submitMessageFor(locale, mode),
60
+ consentTextVersion: consent.consentTextVersion,
61
+ termsHash: createHash("sha256").update(terms.content).digest("hex"),
62
+ termsTemplateVersion: terms.version,
63
+ };
64
+ }
65
+ export function consentCheckoutFields(prepared) {
66
+ return {
67
+ consentId: prepared.consentId,
68
+ locale: prepared.locale,
69
+ submitMessage: prepared.submitMessage,
70
+ };
71
+ }
72
+ export async function recordConsent(ctx, prepared, details) {
73
+ const payload = {
74
+ consentId: prepared.consentId,
75
+ mode: prepared.mode,
76
+ tier: details.tier,
77
+ priceId: details.priceId,
78
+ unitAmount: details.price?.unitAmount ?? null,
79
+ currency: details.price?.currency ?? null,
80
+ interval: details.price?.interval ?? null,
81
+ intervalCount: details.price?.intervalCount ?? null,
82
+ consentTextVersion: prepared.consentTextVersion,
83
+ termsHash: prepared.termsHash,
84
+ termsTemplateVersion: prepared.termsTemplateVersion,
85
+ locale: prepared.locale,
86
+ actorUserId: String(ctx.user.id),
87
+ };
88
+ const tenantId = ctx.user.tenantId;
89
+ const isSubscription = prepared.mode === "subscription";
90
+ await ctx.unsafeAppendEvent({
91
+ aggregateId: isSubscription ? subscriptionAggregateId(tenantId) : paymentAggregateId(tenantId),
92
+ aggregateType: isSubscription ? SUBSCRIPTION_AGGREGATE_TYPE : PAYMENT_AGGREGATE_TYPE,
93
+ type: CHECKOUT_CONSENT_RECORDED_EVENT_QN,
94
+ payload,
95
+ });
96
+ }
@@ -0,0 +1,5 @@
1
+ export declare const CONSENT_LOCALES: readonly ["de", "en"];
2
+ export type ConsentLocale = (typeof CONSENT_LOCALES)[number];
3
+ export declare const FALLBACK_CONSENT_LOCALE: ConsentLocale;
4
+ /** Exact match, else the language part ("de-AT" → "de"), else the fallback. */
5
+ export declare function resolveConsentLocale(input?: string): ConsentLocale;
@@ -0,0 +1,17 @@
1
+ // @runtime client
2
+ // Crypto-free so the browser panel can share the locale rule with the server.
3
+ export const CONSENT_LOCALES = ["de", "en"];
4
+ export const FALLBACK_CONSENT_LOCALE = "de";
5
+ function isConsentLocale(value) {
6
+ return CONSENT_LOCALES.some((locale) => locale === value);
7
+ }
8
+ /** Exact match, else the language part ("de-AT" → "de"), else the fallback. */
9
+ export function resolveConsentLocale(input) {
10
+ if (input === undefined)
11
+ return FALLBACK_CONSENT_LOCALE;
12
+ const lower = input.toLowerCase();
13
+ if (isConsentLocale(lower))
14
+ return lower;
15
+ const language = lower.split("-")[0] ?? "";
16
+ return isConsentLocale(language) ? language : FALLBACK_CONSENT_LOCALE;
17
+ }
@@ -0,0 +1,11 @@
1
+ import type { ConsentLocale } from "./consent-locale.js";
2
+ export { CONSENT_LOCALES, type ConsentLocale, FALLBACK_CONSENT_LOCALE, resolveConsentLocale, } from "./consent-locale.js";
3
+ export type ConsentTexts = {
4
+ readonly earlyPerformance: string;
5
+ readonly withdrawalLoss: string;
6
+ readonly subscriptionSubmitMessage: string;
7
+ readonly paymentSubmitMessage: string;
8
+ };
9
+ export declare const CONSENT_TEXTS: Readonly<Record<ConsentLocale, ConsentTexts>>;
10
+ export declare function consentTextVersion(locale: ConsentLocale): string;
11
+ export declare function submitMessageFor(locale: ConsentLocale, mode: "subscription" | "payment"): string;
@@ -0,0 +1,28 @@
1
+ import { createHash } from "node:crypto";
2
+ export { CONSENT_LOCALES, FALLBACK_CONSENT_LOCALE, resolveConsentLocale, } from "./consent-locale.js";
3
+ export const CONSENT_TEXTS = {
4
+ de: {
5
+ earlyPerformance: "Ich verlange ausdrücklich, dass der Anbieter vor Ablauf der Widerrufsfrist mit der Ausführung des Vertrags beginnt.",
6
+ withdrawalLoss: "Ich nehme zur Kenntnis, dass ich mein Widerrufsrecht verliere, sobald der Vertrag von dem Anbieter vollständig erfüllt ist, bei digitalen Inhalten sobald die Ausführung begonnen hat, nachdem ich ausdrücklich zugestimmt habe und bestätigt habe, dass ich dadurch mein Widerrufsrecht verliere.",
7
+ subscriptionSubmitMessage: "Kostenpflichtiges Abonnement: Es verlängert sich zum Ende jedes Abrechnungszeitraums automatisch, bis du es kündigst. Du kannst jederzeit zum Ende des laufenden Zeitraums kündigen.",
8
+ paymentSubmitMessage: "Kostenpflichtige Einmalzahlung: Mit Klick auf den Button verpflichtest du dich zur Zahlung.",
9
+ },
10
+ en: {
11
+ earlyPerformance: "I expressly request that the provider begins performing the contract before the withdrawal period has expired.",
12
+ withdrawalLoss: "I acknowledge that I lose my right of withdrawal once the provider has fully performed the contract, and for digital content once performance has begun, after I have expressly consented and confirmed that I lose my right of withdrawal as a result.",
13
+ subscriptionSubmitMessage: "Paid subscription: it renews automatically at the end of each billing period until you cancel. You can cancel at any time, effective at the end of the current period.",
14
+ paymentSubmitMessage: "Paid one-off payment: by clicking the button you commit to paying.",
15
+ },
16
+ };
17
+ const CONSENT_TEXT_VERSION_LENGTH = 16;
18
+ export function consentTextVersion(locale) {
19
+ const { earlyPerformance, withdrawalLoss } = CONSENT_TEXTS[locale];
20
+ return createHash("sha256")
21
+ .update(`${earlyPerformance}\n${withdrawalLoss}`)
22
+ .digest("hex")
23
+ .slice(0, CONSENT_TEXT_VERSION_LENGTH);
24
+ }
25
+ export function submitMessageFor(locale, mode) {
26
+ const texts = CONSENT_TEXTS[locale];
27
+ return mode === "subscription" ? texts.subscriptionSubmitMessage : texts.paymentSubmitMessage;
28
+ }
@@ -0,0 +1,15 @@
1
+ import { type WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
2
+ import * as z from "zod";
3
+ import type { ConsumerProtectionOptions, ResolvedBillingFoundationOptions } from "../types.js";
4
+ export declare const issueContractConfirmationSchema: z.ZodObject<{
5
+ consentId: z.ZodString;
6
+ sourceAggregateId: z.ZodUUID;
7
+ currentPeriodEndIso: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strict>;
9
+ export type IssueContractConfirmationResult = {
10
+ readonly issued: true;
11
+ } | {
12
+ readonly issued: false;
13
+ readonly reason: "consent_not_found" | "already_issued" | "recipient_not_found";
14
+ };
15
+ export declare function createIssueContractConfirmationHandler(options: ResolvedBillingFoundationOptions, consumerProtection: ConsumerProtectionOptions): WriteHandlerDef;