@comfyorg/account-core 1.0.0-alpha.1 → 1.0.0-alpha.3

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 (76) hide show
  1. package/dist/core/billing/index.d.ts +4 -3
  2. package/dist/core/billing/index.js +2 -1
  3. package/dist/core/billing/operationLifecycle.d.ts +26 -4
  4. package/dist/core/billing/operationLifecycle.js +113 -14
  5. package/dist/core/billing/operationPointer.d.ts +24 -0
  6. package/dist/core/billing/operationPointer.js +14 -1
  7. package/dist/core/billing/operationState.d.ts +135 -1
  8. package/dist/core/billing/operationState.js +15 -2
  9. package/dist/core/billing/paymentFriction.d.ts +17 -0
  10. package/dist/core/billing/paymentFriction.js +41 -0
  11. package/dist/core/billing/subscriptionCommands.d.ts +64 -2
  12. package/dist/core/billing/subscriptionCommands.js +31 -7
  13. package/dist/core/billing/subscriptionDiscount.d.ts +26 -0
  14. package/dist/core/billing/subscriptionDiscount.js +6 -0
  15. package/dist/core/billing/telemetry/billingTelemetryEvent.d.ts +25 -0
  16. package/dist/core/billing/telemetry/billingTelemetryEvent.js +3 -0
  17. package/dist/core/billing/telemetry/cancelEvent.d.ts +19 -0
  18. package/dist/core/billing/telemetry/cancelEvent.js +1 -0
  19. package/dist/core/billing/telemetry/capabilityReadEvent.d.ts +4 -0
  20. package/dist/core/billing/telemetry/capabilityReadEvent.js +1 -0
  21. package/dist/core/billing/telemetry/checkoutChallengeEvent.d.ts +25 -0
  22. package/dist/core/billing/telemetry/checkoutChallengeEvent.js +1 -0
  23. package/dist/core/billing/telemetry/checkoutJourney.d.ts +176 -0
  24. package/dist/core/billing/telemetry/checkoutJourney.js +115 -0
  25. package/dist/core/billing/telemetry/checkoutRedirectEvent.d.ts +30 -0
  26. package/dist/core/billing/telemetry/checkoutRedirectEvent.js +1 -0
  27. package/dist/core/billing/telemetry/downgradeToPersonalEvent.d.ts +14 -0
  28. package/dist/core/billing/telemetry/downgradeToPersonalEvent.js +1 -0
  29. package/dist/core/billing/telemetry/entryEvent.d.ts +16 -0
  30. package/dist/core/billing/telemetry/entryEvent.js +1 -0
  31. package/dist/core/billing/telemetry/eventNames.d.ts +36 -0
  32. package/dist/core/billing/telemetry/eventNames.js +36 -0
  33. package/dist/core/billing/telemetry/index.d.ts +17 -0
  34. package/dist/core/billing/telemetry/index.js +6 -0
  35. package/dist/core/billing/telemetry/operationEvent.d.ts +25 -0
  36. package/dist/core/billing/telemetry/operationEvent.js +1 -0
  37. package/dist/core/billing/telemetry/operationLifecycleEvent.d.ts +9 -0
  38. package/dist/core/billing/telemetry/operationLifecycleEvent.js +79 -0
  39. package/dist/core/billing/telemetry/payload.d.ts +55 -0
  40. package/dist/core/billing/telemetry/payload.js +75 -0
  41. package/dist/core/billing/telemetry/portalEvent.d.ts +23 -0
  42. package/dist/core/billing/telemetry/portalEvent.js +1 -0
  43. package/dist/core/billing/telemetry/resubscribeEvent.d.ts +13 -0
  44. package/dist/core/billing/telemetry/resubscribeEvent.js +1 -0
  45. package/dist/core/billing/telemetry/stages.d.ts +45 -0
  46. package/dist/core/billing/telemetry/stages.js +1 -0
  47. package/dist/core/billing/telemetry/subscriptionCheckoutEvent.d.ts +23 -0
  48. package/dist/core/billing/telemetry/subscriptionCheckoutEvent.js +1 -0
  49. package/dist/core/billing/telemetry/topupEvent.d.ts +30 -0
  50. package/dist/core/billing/telemetry/topupEvent.js +6 -0
  51. package/dist/core/billing/telemetry/vocabulary.d.ts +9 -0
  52. package/dist/core/billing/telemetry/vocabulary.js +1 -0
  53. package/dist/core/billing/telemetry/webEntryEvent.d.ts +27 -0
  54. package/dist/core/billing/telemetry/webEntryEvent.js +1 -0
  55. package/dist/core/billing/telemetry/webHandoffEvent.d.ts +11 -0
  56. package/dist/core/billing/telemetry/webHandoffEvent.js +1 -0
  57. package/dist/core/billing/telemetry/webReturnEvent.d.ts +8 -0
  58. package/dist/core/billing/telemetry/webReturnEvent.js +1 -0
  59. package/dist/core/billing/telemetry/webSessionEvent.d.ts +17 -0
  60. package/dist/core/billing/telemetry/webSessionEvent.js +1 -0
  61. package/dist/core/requestTimeout.d.ts +7 -0
  62. package/dist/core/requestTimeout.js +13 -0
  63. package/dist/core/sessionTokenMint.d.ts +3 -1
  64. package/dist/core/sessionTokenMint.js +28 -11
  65. package/dist/core/webSession.d.ts +6 -3
  66. package/dist/core/webSession.js +31 -23
  67. package/dist/core/webSessionFlag.d.ts +1 -2
  68. package/dist/core/webSessionFlag.js +1 -2
  69. package/dist/core/webSessionIdentity.d.ts +12 -6
  70. package/dist/core/webSessionIdentity.js +17 -4
  71. package/dist/signInSchemas.d.ts +1 -1
  72. package/dist/telemetry.d.ts +26 -0
  73. package/dist/telemetry.js +28 -7
  74. package/dist/testing.d.ts +2 -1
  75. package/dist/testing.js +5 -4
  76. package/package.json +3 -1
@@ -0,0 +1,41 @@
1
+ /**
2
+ * What one transition of a pending operation put in the customer's way: a
3
+ * challenge first required under a client secret, this tab's verdict on it,
4
+ * or a retryable decline with a reason the operation did not hold before.
5
+ * The first decline the server reports for a challenge this tab already saw
6
+ * fail is the same failure, so it is not reported twice.
7
+ */
8
+ export function paymentFrictionBetween(before, after) {
9
+ if (after.phase !== 'pending')
10
+ return [];
11
+ const earlier = before?.phase === 'pending' ? before : undefined;
12
+ return [
13
+ ...challengeSignals(earlier?.challenge, after.challenge),
14
+ ...declineSignals(earlier, after)
15
+ ];
16
+ }
17
+ function challengeSignals(before, after) {
18
+ if (after === undefined)
19
+ return [];
20
+ if (after.clientSecret !== before?.clientSecret) {
21
+ return after.status === 'required' ? [{ stage: 'challenge_required' }] : [];
22
+ }
23
+ if (after.status === before.status)
24
+ return [];
25
+ if (after.status === 'completed')
26
+ return [{ stage: 'challenge_completed' }];
27
+ if (after.status === 'failed')
28
+ return [{ stage: 'challenge_failed' }];
29
+ return [];
30
+ }
31
+ function declineSignals(before, after) {
32
+ const reason = after.declineReason;
33
+ if (reason === undefined || reason === before?.declineReason)
34
+ return [];
35
+ const echoesFailedChallenge = after.presentation === 'embedded' &&
36
+ after.challenge?.status === 'failed' &&
37
+ before?.declineReason === undefined;
38
+ return echoesFailedChallenge
39
+ ? []
40
+ : [{ stage: 'challenge_failed', declineReason: reason }];
41
+ }
@@ -24,6 +24,8 @@ export declare const RESUBSCRIBE_ROUTE = "/billing/subscription/resubscribe";
24
24
  export declare const CANCEL_SUBSCRIPTION_ROUTE = "/billing/subscription/cancel";
25
25
  export declare const PAYMENT_PORTAL_ROUTE = "/billing/payment-portal";
26
26
  export declare const PREVIEW_SUBSCRIBE_ROUTE = "/billing/preview-subscribe";
27
+ export declare function cancelOperationRoute(operationId: string): string;
28
+ declare const CANCEL_REFUSAL_SERVER_CODES: readonly ["NOT_CANCELABLE", "PAYMENT_IN_FLIGHT"];
27
29
  export type SubscribeInput = z.infer<typeof zSubscribeRequest>;
28
30
  export type TerminalBillingOperation = Exclude<BillingOperationState, PendingBillingOperation>;
29
31
  /** Outcomes a subscription command adds to the shared `BillingErrorCode` set. */
@@ -64,6 +66,23 @@ export type SubscriptionCommandResult = {
64
66
  readonly status: 'ok';
65
67
  readonly value: SubscriptionCommandOutcome;
66
68
  } | SubscriptionCommandFailure;
69
+ /**
70
+ * Why the server kept a payment it was asked to cancel. `PAYMENT_IN_FLIGHT`:
71
+ * the payment won the race and the operation will settle on its own.
72
+ * `NOT_CANCELABLE`: the operation is not one waiting on authentication.
73
+ */
74
+ export type CancelRefusalCode = (typeof CANCEL_REFUSAL_SERVER_CODES)[number];
75
+ /**
76
+ * The server's answer to cancelling a pending operation. `canceled`: dropped,
77
+ * nothing charged, also for one already discarded. `cancel_requested`: the
78
+ * cancel was delivered but has not settled; asking again is safe.
79
+ */
80
+ export type CancelOperationResult = {
81
+ readonly status: 'canceled' | 'cancel_requested';
82
+ } | {
83
+ readonly status: 'not_canceled';
84
+ readonly code: CancelRefusalCode;
85
+ } | BillingFailure;
67
86
  export type PaymentPortalResult = {
68
87
  readonly status: 'ok';
69
88
  readonly value: {
@@ -90,9 +109,13 @@ declare const PreviewSchema: z.ZodObject<{
90
109
  cost_today_cents: z.ZodNumber;
91
110
  credits_next_period_cents: z.ZodNumber;
92
111
  credits_today_cents: z.ZodNumber;
112
+ credits_today: z.ZodOptional<z.ZodNumber>;
113
+ credits_next_period: z.ZodOptional<z.ZodNumber>;
93
114
  renewal_amount_cents: z.ZodOptional<z.ZodNumber>;
94
115
  subtotal_cents: z.ZodOptional<z.ZodNumber>;
95
116
  balance_applied_cents: z.ZodOptional<z.ZodNumber>;
117
+ proration_remaining_cents: z.ZodOptional<z.ZodNumber>;
118
+ proration_unused_cents: z.ZodOptional<z.ZodNumber>;
96
119
  current_plan: z.ZodOptional<z.ZodObject<{
97
120
  duration: z.ZodEnum<["MONTHLY", "ANNUAL"]>;
98
121
  period_end: z.ZodOptional<z.ZodString>;
@@ -103,6 +126,8 @@ declare const PreviewSchema: z.ZodObject<{
103
126
  credits_cents: z.ZodNumber;
104
127
  price_cents: z.ZodNumber;
105
128
  list_price_cents: z.ZodOptional<z.ZodNumber>;
129
+ monthly_list_price_cents: z.ZodOptional<z.ZodNumber>;
130
+ monthly_price_cents: z.ZodOptional<z.ZodNumber>;
106
131
  seat_summary: z.ZodObject<{
107
132
  seat_count: z.ZodNumber;
108
133
  } & {
@@ -131,6 +156,8 @@ declare const PreviewSchema: z.ZodObject<{
131
156
  period_end?: string | undefined;
132
157
  period_start?: string | undefined;
133
158
  list_price_cents?: number | undefined;
159
+ monthly_list_price_cents?: number | undefined;
160
+ monthly_price_cents?: number | undefined;
134
161
  }, {
135
162
  price_cents: number;
136
163
  duration: "MONTHLY" | "ANNUAL";
@@ -145,6 +172,8 @@ declare const PreviewSchema: z.ZodObject<{
145
172
  period_end?: string | undefined;
146
173
  period_start?: string | undefined;
147
174
  list_price_cents?: number | undefined;
175
+ monthly_list_price_cents?: number | undefined;
176
+ monthly_price_cents?: number | undefined;
148
177
  }>>;
149
178
  new_plan: z.ZodObject<{
150
179
  duration: z.ZodEnum<["MONTHLY", "ANNUAL"]>;
@@ -156,6 +185,8 @@ declare const PreviewSchema: z.ZodObject<{
156
185
  credits_cents: z.ZodNumber;
157
186
  price_cents: z.ZodNumber;
158
187
  list_price_cents: z.ZodOptional<z.ZodNumber>;
188
+ monthly_list_price_cents: z.ZodOptional<z.ZodNumber>;
189
+ monthly_price_cents: z.ZodOptional<z.ZodNumber>;
159
190
  seat_summary: z.ZodObject<{
160
191
  seat_count: z.ZodNumber;
161
192
  } & {
@@ -184,6 +215,8 @@ declare const PreviewSchema: z.ZodObject<{
184
215
  period_end?: string | undefined;
185
216
  period_start?: string | undefined;
186
217
  list_price_cents?: number | undefined;
218
+ monthly_list_price_cents?: number | undefined;
219
+ monthly_price_cents?: number | undefined;
187
220
  }, {
188
221
  price_cents: number;
189
222
  duration: "MONTHLY" | "ANNUAL";
@@ -198,12 +231,15 @@ declare const PreviewSchema: z.ZodObject<{
198
231
  period_end?: string | undefined;
199
232
  period_start?: string | undefined;
200
233
  list_price_cents?: number | undefined;
234
+ monthly_list_price_cents?: number | undefined;
235
+ monthly_price_cents?: number | undefined;
201
236
  }>;
202
237
  discounts: z.ZodOptional<z.ZodArray<z.ZodObject<{
203
238
  code: z.ZodString;
204
239
  duration: z.ZodOptional<z.ZodEnum<["once", "repeating", "forever"]>>;
205
240
  kind: z.ZodEnum<["plan", "promotion"]>;
206
241
  name: z.ZodOptional<z.ZodString>;
242
+ term: z.ZodOptional<z.ZodEnum<["this_payment", "first_month", "first_year", "months", "ongoing"]>>;
207
243
  } & {
208
244
  amount_off_cents: z.ZodOptional<z.ZodNumber>;
209
245
  duration_in_months: z.ZodOptional<z.ZodNumber>;
@@ -214,6 +250,7 @@ declare const PreviewSchema: z.ZodObject<{
214
250
  amount_off_cents?: number | undefined;
215
251
  duration?: "once" | "repeating" | "forever" | undefined;
216
252
  duration_in_months?: number | undefined;
253
+ term?: "this_payment" | "first_month" | "first_year" | "months" | "ongoing" | undefined;
217
254
  }, {
218
255
  code: string;
219
256
  kind: "plan" | "promotion";
@@ -221,6 +258,7 @@ declare const PreviewSchema: z.ZodObject<{
221
258
  amount_off_cents?: number | undefined;
222
259
  duration?: "once" | "repeating" | "forever" | undefined;
223
260
  duration_in_months?: number | undefined;
261
+ term?: "this_payment" | "first_month" | "first_year" | "months" | "ongoing" | undefined;
224
262
  }>, "many">>;
225
263
  }, "strip", z.ZodTypeAny, {
226
264
  effective_at: string;
@@ -244,9 +282,12 @@ declare const PreviewSchema: z.ZodObject<{
244
282
  period_end?: string | undefined;
245
283
  period_start?: string | undefined;
246
284
  list_price_cents?: number | undefined;
285
+ monthly_list_price_cents?: number | undefined;
286
+ monthly_price_cents?: number | undefined;
247
287
  };
248
288
  transition_type: "new_subscription" | "upgrade" | "downgrade" | "duration_change";
249
289
  currency?: string | undefined;
290
+ reason?: string | undefined;
250
291
  promotion_code?: string | undefined;
251
292
  proration_at?: string | undefined;
252
293
  quote_id?: string | undefined;
@@ -267,6 +308,8 @@ declare const PreviewSchema: z.ZodObject<{
267
308
  period_end?: string | undefined;
268
309
  period_start?: string | undefined;
269
310
  list_price_cents?: number | undefined;
311
+ monthly_list_price_cents?: number | undefined;
312
+ monthly_price_cents?: number | undefined;
270
313
  } | undefined;
271
314
  discounts?: {
272
315
  code: string;
@@ -275,13 +318,17 @@ declare const PreviewSchema: z.ZodObject<{
275
318
  amount_off_cents?: number | undefined;
276
319
  duration?: "once" | "repeating" | "forever" | undefined;
277
320
  duration_in_months?: number | undefined;
321
+ term?: "this_payment" | "first_month" | "first_year" | "months" | "ongoing" | undefined;
278
322
  }[] | undefined;
279
323
  payment_method_configuration_id?: string | undefined;
280
- reason?: string | undefined;
324
+ proration_remaining_cents?: number | undefined;
325
+ proration_unused_cents?: number | undefined;
281
326
  renewal_amount_cents?: number | undefined;
282
327
  renewal_at?: string | undefined;
283
328
  requires_reactivation_confirmation?: boolean | undefined;
284
329
  subtotal_cents?: number | undefined;
330
+ credits_today?: number | undefined;
331
+ credits_next_period?: number | undefined;
285
332
  }, {
286
333
  effective_at: string;
287
334
  allowed: boolean;
@@ -304,9 +351,12 @@ declare const PreviewSchema: z.ZodObject<{
304
351
  period_end?: string | undefined;
305
352
  period_start?: string | undefined;
306
353
  list_price_cents?: number | undefined;
354
+ monthly_list_price_cents?: number | undefined;
355
+ monthly_price_cents?: number | undefined;
307
356
  };
308
357
  transition_type: "new_subscription" | "upgrade" | "downgrade" | "duration_change";
309
358
  currency?: string | undefined;
359
+ reason?: string | undefined;
310
360
  promotion_code?: string | undefined;
311
361
  proration_at?: string | undefined;
312
362
  quote_id?: string | undefined;
@@ -327,6 +377,8 @@ declare const PreviewSchema: z.ZodObject<{
327
377
  period_end?: string | undefined;
328
378
  period_start?: string | undefined;
329
379
  list_price_cents?: number | undefined;
380
+ monthly_list_price_cents?: number | undefined;
381
+ monthly_price_cents?: number | undefined;
330
382
  } | undefined;
331
383
  discounts?: {
332
384
  code: string;
@@ -335,13 +387,17 @@ declare const PreviewSchema: z.ZodObject<{
335
387
  amount_off_cents?: number | undefined;
336
388
  duration?: "once" | "repeating" | "forever" | undefined;
337
389
  duration_in_months?: number | undefined;
390
+ term?: "this_payment" | "first_month" | "first_year" | "months" | "ongoing" | undefined;
338
391
  }[] | undefined;
339
392
  payment_method_configuration_id?: string | undefined;
340
- reason?: string | undefined;
393
+ proration_remaining_cents?: number | undefined;
394
+ proration_unused_cents?: number | undefined;
341
395
  renewal_amount_cents?: number | undefined;
342
396
  renewal_at?: string | undefined;
343
397
  requires_reactivation_confirmation?: boolean | undefined;
344
398
  subtotal_cents?: number | undefined;
399
+ credits_today?: number | undefined;
400
+ credits_next_period?: number | undefined;
345
401
  }>;
346
402
  /**
347
403
  * The quote the server returns for a plan change, in the generated field names.
@@ -386,6 +442,12 @@ export interface BillingCommands {
386
442
  previewSubscribe: (input: PreviewSubscribeInput, options?: PreviewSubscribeOptions) => Promise<PreviewSubscribeResult>;
387
443
  resubscribe: () => Promise<SubscriptionCommandResult>;
388
444
  cancelSubscription: () => Promise<SubscriptionCommandResult>;
445
+ /**
446
+ * Asks the server to drop an operation still waiting on the customer's
447
+ * authentication. Any answer that names the operation's fate wakes the
448
+ * lifecycle, so the operation it follows catches up with the server.
449
+ */
450
+ cancelOperation: (operationId: string) => Promise<CancelOperationResult>;
389
451
  /** Returns the portal URL; the host decides how to open it. */
390
452
  openPaymentPortal: (input: {
391
453
  readonly returnUrl?: string;
@@ -11,17 +11,22 @@
11
11
  * against the closed set named here; a code outside it reaches the caller only
12
12
  * as the transport's coded failure.
13
13
  */
14
- import { zCancelSubscriptionResponse2, zPaymentPortalResponse, zPreviewPlanInfo, zPreviewSubscribeRequest, zPreviewSubscribeResponse, zResubscribeResponse, zSubscribeRequest, zSubscribeResponse, zSubscriptionDiscount } from '@comfyorg/ingest-types/zod';
14
+ import { zCancelBillingOpResponse, zCancelSubscriptionResponse2, zPaymentPortalResponse, zPreviewPlanInfo, zPreviewSubscribeRequest, zPreviewSubscribeResponse, zResubscribeResponse, zSubscribeRequest, zSubscribeResponse } from '@comfyorg/ingest-types/zod';
15
15
  import { z } from 'zod';
16
16
  import { matchesServerCode } from './billingContracts.js';
17
+ import { operationRoute } from './operationLifecycle.js';
17
18
  import { validateActionUrl } from './operationState.js';
18
19
  import { readValidatedBillingResponse } from './sharedRead.js';
20
+ import { SubscriptionDiscountSchema } from './subscriptionDiscount.js';
19
21
  import { wireCents } from './wireCents.js';
20
22
  export const SUBSCRIBE_ROUTE = '/billing/subscribe';
21
23
  export const RESUBSCRIBE_ROUTE = '/billing/subscription/resubscribe';
22
24
  export const CANCEL_SUBSCRIPTION_ROUTE = '/billing/subscription/cancel';
23
25
  export const PAYMENT_PORTAL_ROUTE = '/billing/payment-portal';
24
26
  export const PREVIEW_SUBSCRIBE_ROUTE = '/billing/preview-subscribe';
27
+ export function cancelOperationRoute(operationId) {
28
+ return `${operationRoute(operationId)}/cancel`;
29
+ }
25
30
  /** The closed set of `serverCode` values these commands act on. */
26
31
  const NO_ACTIVE_SUBSCRIPTION_SERVER_CODE = 'NO_ACTIVE_SUBSCRIPTION';
27
32
  const REACTIVATION_CONFIRMATION_REQUIRED_SERVER_CODE = 'REACTIVATION_CONFIRMATION_REQUIRED';
@@ -29,10 +34,16 @@ const NOT_SCHEDULED_FOR_CANCELLATION_SERVER_CODE = 'NOT_SCHEDULED_FOR_CANCELLATI
29
34
  const ALREADY_CANCELED_SERVER_CODE = 'ALREADY_CANCELED';
30
35
  const SUBSCRIPTION_QUOTE_STALE_SERVER_CODE = 'SUBSCRIPTION_QUOTE_STALE';
31
36
  const SUBSCRIPTION_CHANGE_IN_PROGRESS_SERVER_CODE = 'SUBSCRIPTION_CHANGE_IN_PROGRESS';
37
+ const CANCEL_REFUSAL_SERVER_CODES = [
38
+ 'NOT_CANCELABLE',
39
+ 'PAYMENT_IN_FLIGHT'
40
+ ];
32
41
  const PlanInfoSchema = zPreviewPlanInfo.extend({
33
42
  credits_cents: wireCents,
34
43
  price_cents: wireCents,
35
44
  list_price_cents: wireCents.optional(),
45
+ monthly_list_price_cents: wireCents.optional(),
46
+ monthly_price_cents: wireCents.optional(),
36
47
  seat_summary: zPreviewPlanInfo.shape.seat_summary.extend({
37
48
  total_cost_cents: wireCents,
38
49
  total_credits_cents: wireCents
@@ -44,17 +55,17 @@ const PreviewSchema = zPreviewSubscribeResponse.extend({
44
55
  cost_today_cents: wireCents,
45
56
  credits_next_period_cents: wireCents,
46
57
  credits_today_cents: wireCents,
58
+ /** Whole credits as granted. Not in ingest-types until cloud PR 11905 syncs. */
59
+ credits_today: wireCents.nonnegative().optional(),
60
+ credits_next_period: wireCents.nonnegative().optional(),
47
61
  renewal_amount_cents: wireCents.optional(),
48
62
  subtotal_cents: wireCents.optional(),
49
63
  balance_applied_cents: wireCents.optional(),
64
+ proration_remaining_cents: wireCents.optional(),
65
+ proration_unused_cents: wireCents.optional(),
50
66
  current_plan: PlanInfoSchema.optional(),
51
67
  new_plan: PlanInfoSchema,
52
- discounts: z
53
- .array(zSubscriptionDiscount.extend({
54
- amount_off_cents: wireCents.optional(),
55
- duration_in_months: wireCents.optional()
56
- }))
57
- .optional()
68
+ discounts: z.array(SubscriptionDiscountSchema).optional()
58
69
  });
59
70
  const ALREADY_HELD = {
60
71
  status: 'ok',
@@ -265,11 +276,24 @@ export function createBillingCommands(options) {
265
276
  }
266
277
  return { status: 'ok', value: { url } };
267
278
  }
279
+ async function cancelOperation(operationId) {
280
+ const response = await post(cancelOperationRoute(operationId), undefined, (body) => zCancelBillingOpResponse.safeParse(body));
281
+ if (response.status === 'error') {
282
+ const refusal = CANCEL_REFUSAL_SERVER_CODES.find((code) => refusedWith(response, code));
283
+ if (refusal === undefined)
284
+ return response;
285
+ lifecycle.wake();
286
+ return { status: 'not_canceled', code: refusal };
287
+ }
288
+ lifecycle.wake();
289
+ return { status: response.value.data.status };
290
+ }
268
291
  return {
269
292
  subscribe,
270
293
  previewSubscribe,
271
294
  resubscribe,
272
295
  cancelSubscription: () => settle('cancel', issueCancel),
296
+ cancelOperation,
273
297
  openPaymentPortal
274
298
  };
275
299
  }
@@ -0,0 +1,26 @@
1
+ export declare const SubscriptionDiscountSchema: import("zod").ZodObject<{
2
+ code: import("zod").ZodString;
3
+ duration: import("zod").ZodOptional<import("zod").ZodEnum<["once", "repeating", "forever"]>>;
4
+ kind: import("zod").ZodEnum<["plan", "promotion"]>;
5
+ name: import("zod").ZodOptional<import("zod").ZodString>;
6
+ term: import("zod").ZodOptional<import("zod").ZodEnum<["this_payment", "first_month", "first_year", "months", "ongoing"]>>;
7
+ } & {
8
+ amount_off_cents: import("zod").ZodOptional<import("zod").ZodNumber>;
9
+ duration_in_months: import("zod").ZodOptional<import("zod").ZodNumber>;
10
+ }, "strip", import("zod").ZodTypeAny, {
11
+ code: string;
12
+ kind: "plan" | "promotion";
13
+ name?: string | undefined;
14
+ amount_off_cents?: number | undefined;
15
+ duration?: "once" | "repeating" | "forever" | undefined;
16
+ duration_in_months?: number | undefined;
17
+ term?: "this_payment" | "first_month" | "first_year" | "months" | "ongoing" | undefined;
18
+ }, {
19
+ code: string;
20
+ kind: "plan" | "promotion";
21
+ name?: string | undefined;
22
+ amount_off_cents?: number | undefined;
23
+ duration?: "once" | "repeating" | "forever" | undefined;
24
+ duration_in_months?: number | undefined;
25
+ term?: "this_payment" | "first_month" | "first_year" | "months" | "ongoing" | undefined;
26
+ }>;
@@ -0,0 +1,6 @@
1
+ import { zSubscriptionDiscount } from '@comfyorg/ingest-types/zod';
2
+ import { wireCents } from './wireCents.js';
3
+ export const SubscriptionDiscountSchema = zSubscriptionDiscount.extend({
4
+ amount_off_cents: wireCents.optional(),
5
+ duration_in_months: wireCents.optional()
6
+ });
@@ -0,0 +1,25 @@
1
+ import type { CancelBillingEvent } from './cancelEvent.js';
2
+ import type { CapabilityReadBillingEvent } from './capabilityReadEvent.js';
3
+ import type { CheckoutChallengeBillingEvent } from './checkoutChallengeEvent.js';
4
+ import type { CheckoutRedirectBillingEvent } from './checkoutRedirectEvent.js';
5
+ import type { DowngradeToPersonalBillingEvent } from './downgradeToPersonalEvent.js';
6
+ import type { EntryBillingEvent } from './entryEvent.js';
7
+ import type { BillingOperationBillingEvent } from './operationEvent.js';
8
+ import type { PortalBillingEvent } from './portalEvent.js';
9
+ import type { ResubscribeBillingEvent } from './resubscribeEvent.js';
10
+ import type { SubscriptionCheckoutBillingEvent } from './subscriptionCheckoutEvent.js';
11
+ import type { TopupBillingEvent } from './topupEvent.js';
12
+ import type { WebEntryBillingEvent } from './webEntryEvent.js';
13
+ import type { WebHandoffBillingEvent } from './webHandoffEvent.js';
14
+ import type { WebReturnBillingEvent } from './webReturnEvent.js';
15
+ import type { WebSessionBillingEvent } from './webSessionEvent.js';
16
+ export type BillingSurface = 'cloud_app' | 'billing_web';
17
+ export type BillingClient = 'sdk' | 'legacy';
18
+ export type BillingTelemetryEvent = {
19
+ /** The rail of the code that emitted the event; absent when the emitter does not know it. */
20
+ billing_client?: BillingClient;
21
+ } & (CapabilityReadBillingEvent | SubscriptionCheckoutBillingEvent | BillingOperationBillingEvent | ResubscribeBillingEvent | TopupBillingEvent | DowngradeToPersonalBillingEvent | WebEntryBillingEvent | WebHandoffBillingEvent | WebReturnBillingEvent | WebSessionBillingEvent | EntryBillingEvent | CancelBillingEvent | PortalBillingEvent | CheckoutChallengeBillingEvent | CheckoutRedirectBillingEvent);
22
+ type BillingTelemetryEventNameFor<T extends BillingTelemetryEvent> = T extends BillingTelemetryEvent ? `billing.${T['operation']}.${T['stage']}` : never;
23
+ export type BillingTelemetryEventName = BillingTelemetryEventNameFor<BillingTelemetryEvent>;
24
+ export declare function getBillingTelemetryEventName(event: BillingTelemetryEvent): BillingTelemetryEventName;
25
+ export {};
@@ -0,0 +1,3 @@
1
+ export function getBillingTelemetryEventName(event) {
2
+ return `billing.${event.operation}.${event.stage}`;
3
+ }
@@ -0,0 +1,19 @@
1
+ import type { BillingFailed, BillingIntent } from './stages.js';
2
+ import type { BillingCycle, SubscriptionCheckoutTier } from './vocabulary.js';
3
+ type CancelAbandoned = {
4
+ stage: 'abandoned';
5
+ outcome: 'pending';
6
+ };
7
+ /**
8
+ * The customer's journey through a cancel flow, on any surface. The cancel
9
+ * request itself is `operation` events with `operation_type: 'cancel'`, so a
10
+ * flow ends in exactly one of those terminals, `abandoned`, or `failed` for a
11
+ * refusal before any operation exists.
12
+ */
13
+ export type CancelBillingEvent = {
14
+ operation: 'cancel';
15
+ /** The plan being cancelled; absent when unknown. */
16
+ current_tier?: SubscriptionCheckoutTier;
17
+ cycle?: BillingCycle;
18
+ } & (BillingIntent | CancelAbandoned | BillingFailed);
19
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { BillingFailed, BillingSucceeded } from './stages.js';
2
+ export type CapabilityReadBillingEvent = {
3
+ operation: 'capability_read';
4
+ } & (BillingSucceeded | Pick<BillingFailed, 'stage' | 'outcome'>);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import type { BillingDeclineReason, BillingOperationKind, BillingPresentation } from '../operationState.js';
2
+ /**
3
+ * Payment friction inside one operation: a bank challenge the customer must
4
+ * complete, the verdict this tab saw, and each retryable decline the server
5
+ * reports while the operation stays open. None of these settle the
6
+ * operation, so every stage stays `pending`. A failure with a
7
+ * `decline_reason` is the server's retryable decline; one without is this
8
+ * tab's own challenge verdict.
9
+ */
10
+ export type CheckoutChallengeBillingEvent = {
11
+ operation: 'checkout';
12
+ outcome: 'pending';
13
+ billing_op_id: string;
14
+ operation_type: BillingOperationKind;
15
+ presentation: BillingPresentation;
16
+ /** True when this tab reattached to an operation it did not issue. */
17
+ resumed?: boolean;
18
+ } & ({
19
+ stage: 'challenge_required';
20
+ } | {
21
+ stage: 'challenge_completed';
22
+ } | {
23
+ stage: 'challenge_failed';
24
+ decline_reason?: BillingDeclineReason;
25
+ });
@@ -0,0 +1,176 @@
1
+ import type { CapabilityDenialReason } from '../capabilityDenials.js';
2
+ import type { BillingSurface } from './billingTelemetryEvent.js';
3
+ import type { BillingTelemetryErrorCode, BillingTelemetryFailureCategory } from './stages.js';
4
+ import type { PaymentIntentSource } from './vocabulary.js';
5
+ /**
6
+ * Checkout-journey lifecycle events for the embedded-checkout rollout.
7
+ *
8
+ * These intermediate stages are kept deliberately separate from the terminal
9
+ * billing taxonomy above (`billing.<operation>.<stage>`): entry, preview, and
10
+ * Payment Element observations are client observations of progress, never
11
+ * business success/failure/timeout. They share one frozen journey context so
12
+ * the two rollout arms can be compared on the same denominator.
13
+ */
14
+ export declare const CHECKOUT_JOURNEY_SCHEMA_VERSION = 1;
15
+ export type CheckoutJourneyArm = 'control' | 'treatment';
16
+ export type CheckoutAssignmentStatus = 'resolved' | 'unavailable';
17
+ export type CheckoutUiMode = 'embedded' | 'full_page' | 'hosted' | 'unknown';
18
+ export type CheckoutEntryFlow = 'initial_subscription' | 'paid_upgrade' | 'topup' | 'other' | 'unknown';
19
+ export type CheckoutEntrySource = 'pricing' | 'deep_link' | 'recovery' | 'settings_billing' | 'other' | 'unknown' | 'agent_paywall';
20
+ type CheckoutElementPhase = 'init' | 'mount' | 'update';
21
+ /** Which Stripe element in the shared group the observation came from. */
22
+ type CheckoutElementKind = 'payment' | 'address';
23
+ type CheckoutSubmitPhase = 'validation' | 'token_creation';
24
+ /**
25
+ * The frozen arm assignment. A resolved assignment always carries an arm; an
26
+ * unavailable one never does, so an unknown assignment cannot masquerade as a
27
+ * resolved `control`. Encoded as a discriminated union so the invariant is a
28
+ * compile-time guarantee rather than a convention.
29
+ */
30
+ type CheckoutJourneyAssignment = {
31
+ assignment_status: 'resolved';
32
+ assigned_arm: CheckoutJourneyArm;
33
+ } | {
34
+ assignment_status: 'unavailable';
35
+ assigned_arm?: never;
36
+ };
37
+ /**
38
+ * Non-sensitive entry context frozen at journey creation and replayed on every
39
+ * journey event.
40
+ */
41
+ export type CheckoutJourneyContext = {
42
+ checkout_journey_id: string;
43
+ /** UTC ISO-8601 timestamp captured at common intent, preserved across reload. */
44
+ checkout_entered_at: string;
45
+ ui_mode?: CheckoutUiMode;
46
+ entry_flow: CheckoutEntryFlow;
47
+ entry_source: CheckoutEntrySource;
48
+ billing_op_id?: string;
49
+ } & CheckoutJourneyAssignment;
50
+ type CheckoutJourneyEntered = {
51
+ phase: 'entered';
52
+ /** The click-time entry the link carried, at the shared source grain. */
53
+ payment_intent_source?: PaymentIntentSource;
54
+ };
55
+ type CheckoutJourneyPreviewReady = {
56
+ phase: 'preview_ready';
57
+ preview_revision?: string;
58
+ };
59
+ /** Refusals the checkout names itself, beside the codes the billing events share. */
60
+ type CheckoutPreviewErrorCode = 'quote_not_allowed' | 'plan_unavailable';
61
+ type CheckoutJourneyPreviewFailed = {
62
+ phase: 'preview_failed';
63
+ failure_category: BillingTelemetryFailureCategory;
64
+ error_code?: BillingTelemetryErrorCode | CheckoutPreviewErrorCode;
65
+ /** Why the capabilities read refused this checkout. */
66
+ denial_reason?: CapabilityDenialReason;
67
+ preview_revision?: string;
68
+ };
69
+ type CheckoutJourneyPaymentElementReady = {
70
+ phase: 'payment_element_ready';
71
+ element: CheckoutElementKind;
72
+ };
73
+ type CheckoutJourneyPaymentElementFailed = {
74
+ phase: 'payment_element_failed';
75
+ element: CheckoutElementKind;
76
+ element_phase: CheckoutElementPhase;
77
+ error_code?: string;
78
+ };
79
+ type CheckoutJourneyPaymentSubmitAttempted = {
80
+ phase: 'payment_submit_attempted';
81
+ };
82
+ type CheckoutJourneyPaymentSubmitFailed = {
83
+ phase: 'payment_submit_failed';
84
+ submit_phase: CheckoutSubmitPhase;
85
+ error_code?: string;
86
+ };
87
+ type CheckoutPaymentRail = 'saved' | 'new' | 'on_file';
88
+ export type CheckoutMethodKind = 'card' | 'alipay' | 'other';
89
+ type CheckoutPromoResult = 'applied' | 'rejected' | 'removed' | 'expired';
90
+ type CheckoutPayBlockedReason = 'reactivation_unconfirmed' | 'promo_unapplied';
91
+ type CheckoutJourneyMethodSelected = {
92
+ phase: 'method_selected';
93
+ rail: CheckoutPaymentRail;
94
+ method_kind?: CheckoutMethodKind;
95
+ };
96
+ type CheckoutJourneyPromo = {
97
+ phase: 'promo';
98
+ result: CheckoutPromoResult;
99
+ /** Whether the entry link carried the code; the code itself is never reported. */
100
+ prefilled: boolean;
101
+ };
102
+ type CheckoutJourneyPayBlocked = {
103
+ phase: 'pay_blocked';
104
+ reason: CheckoutPayBlockedReason;
105
+ };
106
+ type CheckoutJourneySubmitted = {
107
+ phase: 'submitted';
108
+ };
109
+ type CheckoutJourneyOperationLinked = {
110
+ phase: 'operation_linked';
111
+ billing_op_id: string;
112
+ };
113
+ type CheckoutJourneyProgressEvent = CheckoutJourneyEntered | CheckoutJourneyPreviewReady | CheckoutJourneyPreviewFailed | CheckoutJourneyPaymentElementReady | CheckoutJourneyPaymentElementFailed | CheckoutJourneyPaymentSubmitAttempted | CheckoutJourneyPaymentSubmitFailed | CheckoutJourneyMethodSelected | CheckoutJourneyPromo | CheckoutJourneyPayBlocked | CheckoutJourneySubmitted | CheckoutJourneyOperationLinked;
114
+ /** How the customer left: the page went away, a control of this page led out, or the cloud app's checkout dialog closed. */
115
+ export type CheckoutExit = 'page_exit' | 'back' | 'close' | 'host_link' | 'dialog_close';
116
+ /** The ending screens of the full-page checkout. */
117
+ export type CheckoutEndingKind = 'success' | 'completed' | 'already_completed' | 'in_progress' | 'received' | 'unconfirmed' | 'refused' | 'plan_unavailable' | 'link_invalid' | 'load_failed';
118
+ /** Whose payment an ending shows: this page's Pay, a return, one it followed, or one already settled. */
119
+ export type CheckoutEndingAttribution = 'started' | 'returned' | 'followed' | 'settled';
120
+ /** The customer left before the checkout reached an ending. */
121
+ type CheckoutJourneyAbandoned = {
122
+ phase: 'abandoned';
123
+ last_phase: CheckoutJourneyProgressEvent['phase'];
124
+ exit: CheckoutExit;
125
+ };
126
+ type CheckoutJourneyEnded = {
127
+ phase: 'ended';
128
+ ending_kind: CheckoutEndingKind;
129
+ attribution?: CheckoutEndingAttribution;
130
+ };
131
+ export type CheckoutJourneyPhaseEvent = CheckoutJourneyProgressEvent | CheckoutJourneyAbandoned | CheckoutJourneyEnded;
132
+ export type CheckoutJourneyPhase = CheckoutJourneyPhaseEvent['phase'];
133
+ export type CheckoutJourneyTelemetryEvent = CheckoutJourneyContext & CheckoutJourneyPhaseEvent;
134
+ export type CheckoutJourneyTelemetryEventName = `billing.checkout.${CheckoutJourneyPhase}`;
135
+ /**
136
+ * The wire name for every phase. Typed as a total `Record` over the phase
137
+ * union, so a phase added to the union without a name here fails to compile —
138
+ * and so the runtime list below can never drift from the emitted names.
139
+ */
140
+ export declare const CHECKOUT_JOURNEY_EVENT_NAME_BY_PHASE: Record<CheckoutJourneyPhase, CheckoutJourneyTelemetryEventName>;
141
+ export declare function getCheckoutJourneyTelemetryEventName(event: CheckoutJourneyTelemetryEvent): CheckoutJourneyTelemetryEventName;
142
+ export declare function getCheckoutJourneyTelemetryEventPayload(event: CheckoutJourneyTelemetryEvent): {
143
+ attribution?: CheckoutEndingAttribution | undefined;
144
+ ending_kind?: CheckoutEndingKind | undefined;
145
+ exit?: CheckoutExit | undefined;
146
+ last_phase?: "entered" | "preview_ready" | "preview_failed" | "payment_element_ready" | "payment_element_failed" | "payment_submit_attempted" | "payment_submit_failed" | "method_selected" | "promo" | "pay_blocked" | "submitted" | "operation_linked" | undefined;
147
+ reason?: CheckoutPayBlockedReason | undefined;
148
+ prefilled?: boolean | undefined;
149
+ result?: CheckoutPromoResult | undefined;
150
+ method_kind?: CheckoutMethodKind | undefined;
151
+ rail?: CheckoutPaymentRail | undefined;
152
+ payment_intent_source?: PaymentIntentSource | undefined;
153
+ submit_phase?: CheckoutSubmitPhase | undefined;
154
+ element_phase?: CheckoutElementPhase | undefined;
155
+ element?: CheckoutElementKind | undefined;
156
+ denial_reason?: CapabilityDenialReason | undefined;
157
+ error_code?: string | undefined;
158
+ failure_category?: BillingTelemetryFailureCategory | undefined;
159
+ preview_revision?: string | undefined;
160
+ billing_op_id?: string | undefined;
161
+ ui_mode?: CheckoutUiMode | undefined;
162
+ assigned_arm?: CheckoutJourneyArm | undefined;
163
+ schema_version: number;
164
+ phase: "ended" | "abandoned" | "entered" | "preview_ready" | "preview_failed" | "payment_element_ready" | "payment_element_failed" | "payment_submit_attempted" | "payment_submit_failed" | "method_selected" | "promo" | "pay_blocked" | "submitted" | "operation_linked";
165
+ checkout_journey_id: string;
166
+ checkout_entered_at: string;
167
+ assignment_status: "unavailable" | "resolved";
168
+ entry_flow: CheckoutEntryFlow;
169
+ entry_source: CheckoutEntrySource;
170
+ };
171
+ export type CheckoutJourneyTelemetryEventPayload = ReturnType<typeof getCheckoutJourneyTelemetryEventPayload>;
172
+ /** Only the cloud build registers the sinks that call this. */
173
+ export declare function getCloudAppCheckoutJourneyTelemetryEventPayload(event: CheckoutJourneyTelemetryEvent): CheckoutJourneyTelemetryEventPayload & {
174
+ billing_surface: BillingSurface;
175
+ };
176
+ export {};