@rebuy/rebuy 3.55.0 → 3.56.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 (31) hide show
  1. package/dist/index.cjs +317 -273
  2. package/dist/index.mjs +317 -273
  3. package/dist/schema/index.d.ts +1 -0
  4. package/dist/schema/postPurchaseAdmission.d.ts +5 -5
  5. package/dist/schema/postPurchaseLedger.d.ts +56 -0
  6. package/dist/schema/postPurchaseProduct.d.ts +2 -0
  7. package/dist/schema/postPurchaseProductCollectionView.d.ts +3 -0
  8. package/dist/schema/postPurchaseProductView.d.ts +5 -1
  9. package/dist/schema/postPurchaseSession.d.ts +14 -0
  10. package/dist/schema/postPurchaseSubscription.d.ts +1 -0
  11. package/dist/schema/postPurchaseSubscriptionProduct.d.ts +2 -0
  12. package/dist/server/index.cjs +69 -23
  13. package/dist/server/index.mjs +69 -23
  14. package/dist/server/normalizePostPurchaseProduct.d.ts +16 -1
  15. package/dist/server/postPurchaseCardSections.d.ts +2 -2
  16. package/dist/server/postPurchaseSts.d.ts +6 -0
  17. package/dist/server/prepareNormalizedSubscription.d.ts +2 -1
  18. package/dist/server/preparePostPurchaseAdmissionView.d.ts +3 -0
  19. package/dist/server/preparePostPurchaseCollectionGroup.d.ts +3 -0
  20. package/dist/server/preparePostPurchaseCollectionView.d.ts +3 -0
  21. package/dist/server/preparePostPurchaseGroupAdmissionView.d.ts +3 -0
  22. package/dist/server/preparePostPurchaseGroupDefault.d.ts +3 -0
  23. package/dist/server/preparePostPurchaseGroupView.d.ts +3 -0
  24. package/dist/server/preparePostPurchaseMemberView.d.ts +12 -0
  25. package/dist/server/preparePostPurchaseQuantityAdmissionView.d.ts +3 -0
  26. package/dist/server/preparePostPurchaseQuantityView.d.ts +3 -0
  27. package/dist/server/preparePostPurchaseStsMember.d.ts +3 -0
  28. package/dist/server/preparePostPurchaseSubscription.d.ts +1 -0
  29. package/dist/transforms/index.cjs +17 -11
  30. package/dist/transforms/index.mjs +17 -11
  31. package/package.json +1 -1
@@ -21,6 +21,7 @@ export * from './postPurchaseHealth';
21
21
  export * from './postPurchaseHealthProgress';
22
22
  export * from './postPurchaseHealthSchedule';
23
23
  export * from './postPurchaseIdentity';
24
+ export * from './postPurchaseLedger';
24
25
  export * from './postPurchaseLifecycle';
25
26
  export * from './postPurchaseMemberIdentity';
26
27
  export * from './postPurchaseObservation';
@@ -4,8 +4,8 @@ import { z } from 'zod';
4
4
  * refunds the buyer's original one-time line of the same variant; the server binds that line, never the client.
5
5
  */
6
6
  export declare const PurchaseAdmissionModeSchema: z.ZodEnum<{
7
- purchase: "purchase";
8
7
  sts: "sts";
8
+ purchase: "purchase";
9
9
  }>;
10
10
  /** The buyer's SDK-calculated total for the affirmed terms (`calculateChangeset`), never proof of payment. */
11
11
  export declare const EventCalculationSchema: z.ZodObject<{
@@ -66,8 +66,8 @@ export declare const PurchaseExpectedSchema: z.ZodObject<{
66
66
  productIndex: z.ZodNumber;
67
67
  }, z.core.$strict>>;
68
68
  mode: z.ZodOptional<z.ZodEnum<{
69
- purchase: "purchase";
70
69
  sts: "sts";
70
+ purchase: "purchase";
71
71
  }>>;
72
72
  progressRevision: z.ZodOptional<z.ZodNumber>;
73
73
  reportedCalculation: z.ZodOptional<z.ZodObject<{
@@ -138,8 +138,8 @@ export declare const PurchaseRequestSchema: z.ZodObject<{
138
138
  productIndex: z.ZodNumber;
139
139
  }, z.core.$strict>>;
140
140
  mode: z.ZodOptional<z.ZodEnum<{
141
- purchase: "purchase";
142
141
  sts: "sts";
142
+ purchase: "purchase";
143
143
  }>>;
144
144
  progressRevision: z.ZodOptional<z.ZodNumber>;
145
145
  reportedCalculation: z.ZodOptional<z.ZodObject<{
@@ -159,8 +159,8 @@ export declare const PurchaseReceiptSchema: z.ZodObject<{
159
159
  admittedAt: z.ZodNumber;
160
160
  changesetToken: z.ZodString;
161
161
  mode: z.ZodOptional<z.ZodEnum<{
162
- purchase: "purchase";
163
162
  sts: "sts";
163
+ purchase: "purchase";
164
164
  }>>;
165
165
  operationId: z.ZodUUID;
166
166
  originalLine: z.ZodOptional<z.ZodObject<{
@@ -180,8 +180,8 @@ export declare const PurchaseResponseSchema: z.ZodObject<{
180
180
  admittedAt: z.ZodNumber;
181
181
  changesetToken: z.ZodString;
182
182
  mode: z.ZodOptional<z.ZodEnum<{
183
- purchase: "purchase";
184
183
  sts: "sts";
184
+ purchase: "purchase";
185
185
  }>>;
186
186
  operationId: z.ZodUUID;
187
187
  originalLine: z.ZodOptional<z.ZodObject<{
@@ -0,0 +1,56 @@
1
+ import { z } from 'zod';
2
+ /** At most this many rows per `/cab/post-purchase/ledger` request; the client sends one per observation. */
3
+ export declare const CLIENT_LEDGER_MAX_EVENTS = 16;
4
+ /**
5
+ * The ledger types cab-post-purchase writes, each registered in apiv2's
6
+ * `LogEventValidationService::EVENT_REGISTRY`. rebuy-api refuses any other type, so the route can never
7
+ * write a server-owned row (`changeset.*`, `health.*`, `decision.shadow`, …).
8
+ */
9
+ export declare const CLIENT_LEDGER_TYPES: readonly ["decision.verdict", "error.flow", "error.schema", "offer.decline", "render.gate", "shouldRender.selectedFlow"];
10
+ /** One client-observed ledger row. */
11
+ export declare const ClientLedgerEventSchema: z.ZodObject<{
12
+ instance: z.ZodString;
13
+ payload: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
14
+ phase: z.ZodEnum<{
15
+ render: "render";
16
+ shouldRender: "shouldRender";
17
+ }>;
18
+ seq: z.ZodNumber;
19
+ ts: z.ZodISODateTime;
20
+ type: z.ZodEnum<{
21
+ "decision.verdict": "decision.verdict";
22
+ "error.flow": "error.flow";
23
+ "error.schema": "error.schema";
24
+ "offer.decline": "offer.decline";
25
+ "render.gate": "render.gate";
26
+ "shouldRender.selectedFlow": "shouldRender.selectedFlow";
27
+ }>;
28
+ }, z.core.$strict>;
29
+ /**
30
+ * A batch of client-observed ledger rows for `/cab/post-purchase/ledger`. Identity (checkout, shop, upstream)
31
+ * is never read from the body: rebuy-api takes it from the verified post-purchase token, so any echoed
32
+ * `shop`/`checkoutToken` is stripped.
33
+ */
34
+ export declare const ClientLedgerRequestSchema: z.ZodObject<{
35
+ events: z.ZodArray<z.ZodObject<{
36
+ instance: z.ZodString;
37
+ payload: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
38
+ phase: z.ZodEnum<{
39
+ render: "render";
40
+ shouldRender: "shouldRender";
41
+ }>;
42
+ seq: z.ZodNumber;
43
+ ts: z.ZodISODateTime;
44
+ type: z.ZodEnum<{
45
+ "decision.verdict": "decision.verdict";
46
+ "error.flow": "error.flow";
47
+ "error.schema": "error.schema";
48
+ "offer.decline": "offer.decline";
49
+ "render.gate": "render.gate";
50
+ "shouldRender.selectedFlow": "shouldRender.selectedFlow";
51
+ }>;
52
+ }, z.core.$strict>>;
53
+ }, z.core.$strip>;
54
+ export type ClientLedgerEvent = z.infer<typeof ClientLedgerEventSchema>;
55
+ export type ClientLedgerRequest = z.infer<typeof ClientLedgerRequestSchema>;
56
+ export type ClientLedgerType = (typeof CLIENT_LEDGER_TYPES)[number];
@@ -168,6 +168,7 @@ export declare const PreparedPostPurchaseProduct: z.ZodObject<{
168
168
  presentmentTitle: z.ZodString;
169
169
  title: z.ZodString;
170
170
  }, z.core.$strip>;
171
+ sts: z.ZodOptional<z.ZodLiteral<true>>;
171
172
  }, z.core.$strip>>;
172
173
  }, z.core.$strip>;
173
174
  }, z.core.$strip>;
@@ -333,6 +334,7 @@ export declare const PostPurchasePreparation: z.ZodDiscriminatedUnion<[z.ZodObje
333
334
  presentmentTitle: z.ZodString;
334
335
  title: z.ZodString;
335
336
  }, z.core.$strip>;
337
+ sts: z.ZodOptional<z.ZodLiteral<true>>;
336
338
  }, z.core.$strip>>;
337
339
  }, z.core.$strip>;
338
340
  }, z.core.$strip>, z.ZodObject<{
@@ -185,6 +185,7 @@ export declare const PostPurchaseProductCollectionView: z.ZodObject<{
185
185
  presentmentTitle: z.ZodString;
186
186
  title: z.ZodString;
187
187
  }, z.core.$strip>;
188
+ sts: z.ZodOptional<z.ZodLiteral<true>>;
188
189
  }, z.core.$strip>>;
189
190
  }, z.core.$strip>;
190
191
  display: z.ZodObject<{
@@ -658,6 +659,7 @@ export declare const PostPurchaseProductCollectionView: z.ZodObject<{
658
659
  version: z.ZodDefault<z.ZodLiteral<2>>;
659
660
  widgetId: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
660
661
  }, z.core.$strip>;
662
+ sts: z.ZodOptional<z.ZodLiteral<true>>;
661
663
  variantCatalog: z.ZodOptional<z.ZodObject<{
662
664
  displayMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
663
665
  single: "single";
@@ -1121,6 +1123,7 @@ export declare const PostPurchaseProductCollectionView: z.ZodObject<{
1121
1123
  version: z.ZodDefault<z.ZodLiteral<2>>;
1122
1124
  widgetId: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
1123
1125
  }, z.core.$strip>>;
1126
+ sts: z.ZodOptional<z.ZodLiteral<true>>;
1124
1127
  variantCatalog: z.ZodOptional<z.ZodObject<{
1125
1128
  displayMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
1126
1129
  single: "single";
@@ -5,7 +5,8 @@ import { z } from 'zod';
5
5
  * Only a prepared view may carry `changeset`: the unsigned changeset the server derived from this very
6
6
  * preparation (the one admission later rebuilds and signs), ready for Shopify's `calculateChangeset`, and
7
7
  * `displayPrice`: the display pricing worked out from that same preparation. `preparation.status` discriminates
8
- * the two; an unprepared view may carry a tree too.
8
+ * the two; an unprepared view may carry a tree too. `sts` marks a switch to subscription (STS): the buyer's
9
+ * own one-time line becomes this subscription, so the renderer must expect the switch (`mode: 'sts'`).
9
10
  */
10
11
  export declare const PostPurchaseProductView: z.ZodUnion<readonly [z.ZodObject<{
11
12
  changeset: z.ZodOptional<z.ZodObject<{
@@ -188,6 +189,7 @@ export declare const PostPurchaseProductView: z.ZodUnion<readonly [z.ZodObject<{
188
189
  presentmentTitle: z.ZodString;
189
190
  title: z.ZodString;
190
191
  }, z.core.$strip>;
192
+ sts: z.ZodOptional<z.ZodLiteral<true>>;
191
193
  }, z.core.$strip>>;
192
194
  }, z.core.$strip>;
193
195
  display: z.ZodObject<{
@@ -661,6 +663,7 @@ export declare const PostPurchaseProductView: z.ZodUnion<readonly [z.ZodObject<{
661
663
  version: z.ZodDefault<z.ZodLiteral<2>>;
662
664
  widgetId: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
663
665
  }, z.core.$strip>;
666
+ sts: z.ZodOptional<z.ZodLiteral<true>>;
664
667
  variantCatalog: z.ZodOptional<z.ZodObject<{
665
668
  displayMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
666
669
  single: "single";
@@ -1124,6 +1127,7 @@ export declare const PostPurchaseProductView: z.ZodUnion<readonly [z.ZodObject<{
1124
1127
  version: z.ZodDefault<z.ZodLiteral<2>>;
1125
1128
  widgetId: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
1126
1129
  }, z.core.$strip>>;
1130
+ sts: z.ZodOptional<z.ZodLiteral<true>>;
1127
1131
  variantCatalog: z.ZodOptional<z.ZodObject<{
1128
1132
  displayMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
1129
1133
  single: "single";
@@ -106,6 +106,20 @@ export declare const SessionStartResponseSchema: z.ZodObject<{
106
106
  }, z.core.$strip>;
107
107
  successful: z.ZodLiteral<true>;
108
108
  }, z.core.$strip>;
109
+ /**
110
+ * The `/session/start` 409 (`reason: order-pending`): the order this checkout anchors to is not searchable in
111
+ * Shopify yet, so a later start can succeed once `Retry-After` seconds pass. rebuy-api's error envelope, so
112
+ * `errors` and `message` are required here, unlike on a success.
113
+ */
114
+ export declare const PostPurchaseOrderPendingSchema: z.ZodObject<{
115
+ errors: z.ZodArray<z.ZodObject<{
116
+ message: z.ZodString;
117
+ }, z.core.$strip>>;
118
+ message: z.ZodString;
119
+ reason: z.ZodLiteral<"order-pending">;
120
+ successful: z.ZodLiteral<false>;
121
+ }, z.core.$strip>;
122
+ export type PostPurchaseOrderPending = z.infer<typeof PostPurchaseOrderPendingSchema>;
109
123
  export type ReadySessionTiming = z.infer<typeof ReadySessionTimingSchema>;
110
124
  export type SessionIdentity = z.infer<typeof SessionIdentitySchema>;
111
125
  export type SessionOrderTiming = z.infer<typeof SessionOrderTimingSchema>;
@@ -17,5 +17,6 @@ export declare const PostPurchaseSubscriptionTerms: z.ZodObject<{
17
17
  presentmentTitle: z.ZodString;
18
18
  title: z.ZodString;
19
19
  }, z.core.$strip>;
20
+ sts: z.ZodOptional<z.ZodLiteral<true>>;
20
21
  }, z.core.$strip>;
21
22
  export type PostPurchaseSubscriptionTerms = z.infer<typeof PostPurchaseSubscriptionTerms>;
@@ -161,6 +161,7 @@ export declare const PreparedPostPurchaseSubscription: z.ZodObject<{
161
161
  presentmentTitle: z.ZodString;
162
162
  title: z.ZodString;
163
163
  }, z.core.$strip>;
164
+ sts: z.ZodOptional<z.ZodLiteral<true>>;
164
165
  }, z.core.$strip>;
165
166
  }, z.core.$strip>;
166
167
  }, z.core.$strip>;
@@ -304,6 +305,7 @@ export declare const readPostPurchaseSubscription: (prepared: z.infer<typeof Pre
304
305
  amount: number;
305
306
  type: "price" | "fixed_amount" | "percentage";
306
307
  } | undefined;
308
+ sts?: true | undefined;
307
309
  };
308
310
  };
309
311
  } | undefined;
@@ -4644,9 +4644,11 @@ var HAS_RECURRING_TOTAL = "hasRecurringTotal";
4644
4644
  var HAS_RECURRING_SHIPPING = "hasRecurringShipping";
4645
4645
  var small = { color: "subdued", fontSize: "small" };
4646
4646
  var FOOTNOTES = ["Doesn't include tax, duties, or any applicable discounts.", "Auto-renews, skip or cancel anytime"];
4647
+ var countsRecurringShipping = ({ productType, upgradeToSubscription }) => productType === "subscription" && upgradeToSubscription.allow;
4647
4648
  var recurringSummary = (settings) => {
4648
4649
  if (!sellsSubscription(settings)) return [];
4649
4650
  const { global: global2 } = settings.language;
4651
+ const shipping = countsRecurringShipping(settings);
4650
4652
  return [
4651
4653
  {
4652
4654
  direction: "rows",
@@ -4657,18 +4659,20 @@ var recurringSummary = (settings) => {
4657
4659
  columns: ["fill", "auto", "auto"],
4658
4660
  label: merchantLabel(global2.recurringSubtotal, "Recurring subtotal"),
4659
4661
  spacing: "extraTight",
4660
- token: "{{recurringTotal}}",
4662
+ token: shipping ? "{{recurringTotal}}" : "{{recurringSubtotal}}",
4661
4663
  variable: HAS_RECURRING_TOTAL
4662
4664
  }),
4663
- earnedRow({
4664
- after: "(included)",
4665
- columns: ["auto", "auto", "auto"],
4666
- label: { en: `${merchantLabel(global2.recurringShipping, "Recurring shipping").en}:` },
4667
- look: small,
4668
- spacing: "extraTight",
4669
- token: "{{recurringShipping}}",
4670
- variable: HAS_RECURRING_SHIPPING
4671
- }),
4665
+ ...shipping ? [
4666
+ earnedRow({
4667
+ after: "(included)",
4668
+ columns: ["auto", "auto", "auto"],
4669
+ label: { en: `${merchantLabel(global2.recurringShipping, "Recurring shipping").en}:` },
4670
+ look: small,
4671
+ spacing: "extraTight",
4672
+ token: "{{recurringShipping}}",
4673
+ variable: HAS_RECURRING_SHIPPING
4674
+ })
4675
+ ] : [],
4672
4676
  ...FOOTNOTES.map((text2) => ({
4673
4677
  content: { en: styledCopy(text2, small) },
4674
4678
  sectionType: "text",
@@ -4677,7 +4681,9 @@ var recurringSummary = (settings) => {
4677
4681
  ],
4678
4682
  sectionType: "layout",
4679
4683
  spacing: "tight",
4680
- variables: earnedVariables([HAS_RECURRING_TOTAL, HAS_RECURRING_SHIPPING])
4684
+ variables: earnedVariables(
4685
+ shipping ? [HAS_RECURRING_TOTAL, HAS_RECURRING_SHIPPING] : [HAS_RECURRING_TOTAL]
4686
+ )
4681
4687
  }
4682
4688
  ];
4683
4689
  };
@@ -6209,7 +6215,13 @@ var normalizePostPurchaseSource = (settings, response, report) => {
6209
6215
  const data = products.map(({ product }) => product);
6210
6216
  return { dropped, normalized: { data, effective, metadata, original }, products };
6211
6217
  };
6212
- var normalizePostPurchaseProduct = (settings, response, report) => normalizePostPurchaseSource(settings, response, report).normalized;
6218
+ var underPostPurchaseCap = (entries, cap) => entries.filter(({ productIndex }) => productIndex < cap);
6219
+ var normalizePostPurchaseProduct = (settings, response, report) => {
6220
+ const { normalized, products } = normalizePostPurchaseSource(settings, response, report);
6221
+ const { productLimit } = normalized.effective;
6222
+ const readable = productLimit < 1 || underPostPurchaseCap(products, productLimit).length > 0;
6223
+ return readable ? normalized : { ...normalized, data: [] };
6224
+ };
6213
6225
 
6214
6226
  // src/pricing/amountToCents.ts
6215
6227
  var round = (value, precision = 0) => {
@@ -6482,7 +6494,9 @@ var PostPurchaseSubscriptionTerms = import_zod56.z.object({
6482
6494
  intervalLabel: import_zod56.z.string().min(1),
6483
6495
  name: import_zod56.z.string().min(1),
6484
6496
  recurringShippingPrice: import_zod56.z.number().nonnegative(),
6485
- shippingOption: import_zod56.z.object({ presentmentTitle: import_zod56.z.string(), title: import_zod56.z.string() })
6497
+ shippingOption: import_zod56.z.object({ presentmentTitle: import_zod56.z.string(), title: import_zod56.z.string() }),
6498
+ /** A switch to subscription (STS): the one subscription that charges no initial shipping (L122). */
6499
+ sts: import_zod56.z.literal(true).optional()
6486
6500
  });
6487
6501
 
6488
6502
  // src/schema/postPurchaseProduct.ts
@@ -6541,7 +6555,8 @@ var PreparedPostPurchaseSubscription = PreparedPostPurchaseProduct.extend({
6541
6555
  terms: PreparedPostPurchaseProduct.shape.terms.extend({
6542
6556
  subscription: PostPurchaseSubscriptionTerms
6543
6557
  }).refine(
6544
- ({ discount, shipping, subscription: subscription2 }) => (discount.type !== "none" || discount.amount === 0) && shipping.amount === subscription2.initialShippingPrice && shipping.amount === subscription2.recurringShippingPrice && (shipping.type !== "free" || shipping.amount === 0)
6558
+ ({ discount, shipping, subscription: subscription2 }) => (discount.type !== "none" || discount.amount === 0) && // A switch to subscription charges no initial shipping (L122); any other subscription charges it.
6559
+ subscription2.initialShippingPrice === (subscription2.sts ? 0 : shipping.amount) && shipping.amount === subscription2.recurringShippingPrice && (shipping.type !== "free" || shipping.amount === 0)
6545
6560
  )
6546
6561
  });
6547
6562
 
@@ -6889,6 +6904,7 @@ var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowC
6889
6904
  const projected = projectPostPurchaseProduct(normalized, report, quantity3);
6890
6905
  const { discount, shipping } = PostPurchaseSubscriptionSettings.parse(effective.subscription);
6891
6906
  const shippingPrice = shipping.type === "free" ? 0 : shipping.amount;
6907
+ const sts = quantity3?.sts === true;
6892
6908
  return PreparedPostPurchaseSubscription.parse({
6893
6909
  ...projected,
6894
6910
  display: { ...projected.display, shouldShowSavingText: discount.shouldShowSavingText },
@@ -6903,11 +6919,12 @@ var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowC
6903
6919
  shipping: { amount: shippingPrice, title: "Subscription shipping line", type: shipping.type },
6904
6920
  subscription: {
6905
6921
  ...plan.adjustment ? { adjustment: plan.adjustment } : {},
6906
- initialShippingPrice: shippingPrice,
6922
+ initialShippingPrice: sts ? 0 : shippingPrice,
6907
6923
  intervalLabel: plan.intervalLabel,
6908
6924
  name: plan.name,
6909
6925
  recurringShippingPrice: shippingPrice,
6910
- shippingOption: { presentmentTitle: "Subscription shipping line", title: "Subscription shipping line" }
6926
+ shippingOption: { presentmentTitle: "Subscription shipping line", title: "Subscription shipping line" },
6927
+ ...sts ? { sts } : {}
6911
6928
  }
6912
6929
  }
6913
6930
  });
@@ -7117,6 +7134,7 @@ var PostPurchaseProductChangeset = import_zod72.z.object({
7117
7134
  });
7118
7135
 
7119
7136
  // src/schema/postPurchaseProductView.ts
7137
+ var StsMarker = import_zod73.z.literal(true).optional();
7120
7138
  var PostPurchaseProductView = import_zod73.z.union([
7121
7139
  import_zod73.z.object({
7122
7140
  changeset: PostPurchaseProductChangeset.optional(),
@@ -7132,6 +7150,7 @@ var PostPurchaseProductView = import_zod73.z.union([
7132
7150
  }),
7133
7151
  purchaseOptions: PostPurchaseOptions.optional(),
7134
7152
  sections: CABRootSection,
7153
+ sts: StsMarker,
7135
7154
  variantCatalog: PostPurchaseVariantCatalog.optional()
7136
7155
  }),
7137
7156
  import_zod73.z.object({
@@ -7144,6 +7163,7 @@ var PostPurchaseProductView = import_zod73.z.union([
7144
7163
  purchaseOptions: PostPurchaseOptions.optional(),
7145
7164
  /** A choice-level unavailable product keeps its card (values resolve blank); configuration failures send null. */
7146
7165
  sections: CABRootSection.nullable(),
7166
+ sts: StsMarker,
7147
7167
  variantCatalog: PostPurchaseVariantCatalog.optional()
7148
7168
  })
7149
7169
  ]);
@@ -7175,7 +7195,22 @@ var postPurchaseOfferCopy = (blocks, metadata) => {
7175
7195
  };
7176
7196
 
7177
7197
  // src/server/postPurchaseCardSections.ts
7178
- var postPurchaseCardSections = (widgetId, { effective, metadata }, report) => {
7198
+ var stsCardSettings = ({ language: language3, productOptions, ...effective }) => ({
7199
+ ...effective,
7200
+ language: {
7201
+ ...language3,
7202
+ global: {
7203
+ ...language3.global,
7204
+ payNow: language3.global.payNowUpgradeToSubscription || "Upgrade to Subscription"
7205
+ }
7206
+ },
7207
+ productOptions: { ...productOptions, showShipping: false }
7208
+ });
7209
+ var postPurchaseCardSections = (widgetId, {
7210
+ effective: configured,
7211
+ metadata
7212
+ }, report, sts = false) => {
7213
+ const effective = sts ? stsCardSettings(configured) : configured;
7179
7214
  if (!effective.blocks?.length) return null;
7180
7215
  const sections = convertMessagePreview(
7181
7216
  widgetId,
@@ -7234,21 +7269,32 @@ var prepareNormalizedPostPurchaseView = (widgetId, normalized, report, choice, q
7234
7269
  const selected = selectPostPurchaseChoice(normalized, planCatalog, choice, allowCollection, quantity3);
7235
7270
  const purchaseOptions = selected.options;
7236
7271
  const labels = { notAvailable: normalized.effective.language.global.notAvailable };
7272
+ const sts = quantity3?.sts === true;
7273
+ const marker = sts ? { sts: true } : {};
7237
7274
  const preparation = !selected.choice ? { reason: selected.reason, status: "unsupported" } : selected.choice.mode === "subscription" ? prepareNormalizedSubscription(normalized, report, selected.plan, quantity3, allowCollection) : prepareNormalizedProduct(normalized, report, quantity3, allowCollection);
7238
7275
  if (preparation.status === "unsupported") {
7239
- const sections2 = isChoiceUnavailable(preparation.reason) ? postPurchaseCardSections(widgetId, normalized, report) : null;
7240
- return PostPurchaseProductView.parse({ labels, planCatalog, preparation, purchaseOptions, sections: sections2 });
7276
+ const sections2 = isChoiceUnavailable(preparation.reason) ? postPurchaseCardSections(widgetId, normalized, report, sts) : null;
7277
+ return PostPurchaseProductView.parse({
7278
+ labels,
7279
+ planCatalog,
7280
+ preparation,
7281
+ purchaseOptions,
7282
+ sections: sections2,
7283
+ ...marker
7284
+ });
7241
7285
  }
7242
- const sections = postPurchaseCardSections(widgetId, normalized, report);
7286
+ const sections = postPurchaseCardSections(widgetId, normalized, report, sts);
7243
7287
  if (!sections)
7244
7288
  return {
7245
7289
  labels,
7246
7290
  planCatalog,
7247
7291
  preparation: { reason: "One fixed product information block is required", status: "unsupported" },
7248
7292
  purchaseOptions,
7249
- sections: null
7293
+ sections: null,
7294
+ ...marker
7250
7295
  };
7251
7296
  return PostPurchaseProductView.parse({
7297
+ ...marker,
7252
7298
  planCatalog,
7253
7299
  preparation: {
7254
7300
  ...preparation,
@@ -7449,8 +7495,8 @@ var normalizePostPurchaseCollection = (settings, response, excludedProductIds, r
7449
7495
  const limit = import_zod82.z.number().int().positive();
7450
7496
  limit.parse(normalized.original.productLimit);
7451
7497
  const cap = limit.parse(normalized.effective.productLimit);
7452
- const prefix = products.filter(({ productIndex }) => productIndex < cap);
7453
- const unread = dropped.filter(({ productIndex }) => productIndex < cap);
7498
+ const prefix = underPostPurchaseCap(products, cap);
7499
+ const unread = underPostPurchaseCap(dropped, cap);
7454
7500
  if (unread.length && !prefix.length) throw new import_zod82.ZodError(unread.flatMap(({ issues }) => issues));
7455
7501
  if (new Set(prefix.map(({ product }) => product.id)).size !== prefix.length)
7456
7502
  throw new Error("Ambiguous post-purchase collection");
@@ -4571,9 +4571,11 @@ var HAS_RECURRING_TOTAL = "hasRecurringTotal";
4571
4571
  var HAS_RECURRING_SHIPPING = "hasRecurringShipping";
4572
4572
  var small = { color: "subdued", fontSize: "small" };
4573
4573
  var FOOTNOTES = ["Doesn't include tax, duties, or any applicable discounts.", "Auto-renews, skip or cancel anytime"];
4574
+ var countsRecurringShipping = ({ productType, upgradeToSubscription }) => productType === "subscription" && upgradeToSubscription.allow;
4574
4575
  var recurringSummary = (settings) => {
4575
4576
  if (!sellsSubscription(settings)) return [];
4576
4577
  const { global: global2 } = settings.language;
4578
+ const shipping = countsRecurringShipping(settings);
4577
4579
  return [
4578
4580
  {
4579
4581
  direction: "rows",
@@ -4584,18 +4586,20 @@ var recurringSummary = (settings) => {
4584
4586
  columns: ["fill", "auto", "auto"],
4585
4587
  label: merchantLabel(global2.recurringSubtotal, "Recurring subtotal"),
4586
4588
  spacing: "extraTight",
4587
- token: "{{recurringTotal}}",
4589
+ token: shipping ? "{{recurringTotal}}" : "{{recurringSubtotal}}",
4588
4590
  variable: HAS_RECURRING_TOTAL
4589
4591
  }),
4590
- earnedRow({
4591
- after: "(included)",
4592
- columns: ["auto", "auto", "auto"],
4593
- label: { en: `${merchantLabel(global2.recurringShipping, "Recurring shipping").en}:` },
4594
- look: small,
4595
- spacing: "extraTight",
4596
- token: "{{recurringShipping}}",
4597
- variable: HAS_RECURRING_SHIPPING
4598
- }),
4592
+ ...shipping ? [
4593
+ earnedRow({
4594
+ after: "(included)",
4595
+ columns: ["auto", "auto", "auto"],
4596
+ label: { en: `${merchantLabel(global2.recurringShipping, "Recurring shipping").en}:` },
4597
+ look: small,
4598
+ spacing: "extraTight",
4599
+ token: "{{recurringShipping}}",
4600
+ variable: HAS_RECURRING_SHIPPING
4601
+ })
4602
+ ] : [],
4599
4603
  ...FOOTNOTES.map((text2) => ({
4600
4604
  content: { en: styledCopy(text2, small) },
4601
4605
  sectionType: "text",
@@ -4604,7 +4608,9 @@ var recurringSummary = (settings) => {
4604
4608
  ],
4605
4609
  sectionType: "layout",
4606
4610
  spacing: "tight",
4607
- variables: earnedVariables([HAS_RECURRING_TOTAL, HAS_RECURRING_SHIPPING])
4611
+ variables: earnedVariables(
4612
+ shipping ? [HAS_RECURRING_TOTAL, HAS_RECURRING_SHIPPING] : [HAS_RECURRING_TOTAL]
4613
+ )
4608
4614
  }
4609
4615
  ];
4610
4616
  };
@@ -6136,7 +6142,13 @@ var normalizePostPurchaseSource = (settings, response, report) => {
6136
6142
  const data = products.map(({ product }) => product);
6137
6143
  return { dropped, normalized: { data, effective, metadata, original }, products };
6138
6144
  };
6139
- var normalizePostPurchaseProduct = (settings, response, report) => normalizePostPurchaseSource(settings, response, report).normalized;
6145
+ var underPostPurchaseCap = (entries, cap) => entries.filter(({ productIndex }) => productIndex < cap);
6146
+ var normalizePostPurchaseProduct = (settings, response, report) => {
6147
+ const { normalized, products } = normalizePostPurchaseSource(settings, response, report);
6148
+ const { productLimit } = normalized.effective;
6149
+ const readable = productLimit < 1 || underPostPurchaseCap(products, productLimit).length > 0;
6150
+ return readable ? normalized : { ...normalized, data: [] };
6151
+ };
6140
6152
 
6141
6153
  // src/pricing/amountToCents.ts
6142
6154
  var round = (value, precision = 0) => {
@@ -6409,7 +6421,9 @@ var PostPurchaseSubscriptionTerms = z56.object({
6409
6421
  intervalLabel: z56.string().min(1),
6410
6422
  name: z56.string().min(1),
6411
6423
  recurringShippingPrice: z56.number().nonnegative(),
6412
- shippingOption: z56.object({ presentmentTitle: z56.string(), title: z56.string() })
6424
+ shippingOption: z56.object({ presentmentTitle: z56.string(), title: z56.string() }),
6425
+ /** A switch to subscription (STS): the one subscription that charges no initial shipping (L122). */
6426
+ sts: z56.literal(true).optional()
6413
6427
  });
6414
6428
 
6415
6429
  // src/schema/postPurchaseProduct.ts
@@ -6468,7 +6482,8 @@ var PreparedPostPurchaseSubscription = PreparedPostPurchaseProduct.extend({
6468
6482
  terms: PreparedPostPurchaseProduct.shape.terms.extend({
6469
6483
  subscription: PostPurchaseSubscriptionTerms
6470
6484
  }).refine(
6471
- ({ discount, shipping, subscription: subscription2 }) => (discount.type !== "none" || discount.amount === 0) && shipping.amount === subscription2.initialShippingPrice && shipping.amount === subscription2.recurringShippingPrice && (shipping.type !== "free" || shipping.amount === 0)
6485
+ ({ discount, shipping, subscription: subscription2 }) => (discount.type !== "none" || discount.amount === 0) && // A switch to subscription charges no initial shipping (L122); any other subscription charges it.
6486
+ subscription2.initialShippingPrice === (subscription2.sts ? 0 : shipping.amount) && shipping.amount === subscription2.recurringShippingPrice && (shipping.type !== "free" || shipping.amount === 0)
6472
6487
  )
6473
6488
  });
6474
6489
 
@@ -6816,6 +6831,7 @@ var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowC
6816
6831
  const projected = projectPostPurchaseProduct(normalized, report, quantity3);
6817
6832
  const { discount, shipping } = PostPurchaseSubscriptionSettings.parse(effective.subscription);
6818
6833
  const shippingPrice = shipping.type === "free" ? 0 : shipping.amount;
6834
+ const sts = quantity3?.sts === true;
6819
6835
  return PreparedPostPurchaseSubscription.parse({
6820
6836
  ...projected,
6821
6837
  display: { ...projected.display, shouldShowSavingText: discount.shouldShowSavingText },
@@ -6830,11 +6846,12 @@ var prepareNormalizedSubscription = (normalized, report, plan, quantity3, allowC
6830
6846
  shipping: { amount: shippingPrice, title: "Subscription shipping line", type: shipping.type },
6831
6847
  subscription: {
6832
6848
  ...plan.adjustment ? { adjustment: plan.adjustment } : {},
6833
- initialShippingPrice: shippingPrice,
6849
+ initialShippingPrice: sts ? 0 : shippingPrice,
6834
6850
  intervalLabel: plan.intervalLabel,
6835
6851
  name: plan.name,
6836
6852
  recurringShippingPrice: shippingPrice,
6837
- shippingOption: { presentmentTitle: "Subscription shipping line", title: "Subscription shipping line" }
6853
+ shippingOption: { presentmentTitle: "Subscription shipping line", title: "Subscription shipping line" },
6854
+ ...sts ? { sts } : {}
6838
6855
  }
6839
6856
  }
6840
6857
  });
@@ -7044,6 +7061,7 @@ var PostPurchaseProductChangeset = z72.object({
7044
7061
  });
7045
7062
 
7046
7063
  // src/schema/postPurchaseProductView.ts
7064
+ var StsMarker = z73.literal(true).optional();
7047
7065
  var PostPurchaseProductView = z73.union([
7048
7066
  z73.object({
7049
7067
  changeset: PostPurchaseProductChangeset.optional(),
@@ -7059,6 +7077,7 @@ var PostPurchaseProductView = z73.union([
7059
7077
  }),
7060
7078
  purchaseOptions: PostPurchaseOptions.optional(),
7061
7079
  sections: CABRootSection,
7080
+ sts: StsMarker,
7062
7081
  variantCatalog: PostPurchaseVariantCatalog.optional()
7063
7082
  }),
7064
7083
  z73.object({
@@ -7071,6 +7090,7 @@ var PostPurchaseProductView = z73.union([
7071
7090
  purchaseOptions: PostPurchaseOptions.optional(),
7072
7091
  /** A choice-level unavailable product keeps its card (values resolve blank); configuration failures send null. */
7073
7092
  sections: CABRootSection.nullable(),
7093
+ sts: StsMarker,
7074
7094
  variantCatalog: PostPurchaseVariantCatalog.optional()
7075
7095
  })
7076
7096
  ]);
@@ -7102,7 +7122,22 @@ var postPurchaseOfferCopy = (blocks, metadata) => {
7102
7122
  };
7103
7123
 
7104
7124
  // src/server/postPurchaseCardSections.ts
7105
- var postPurchaseCardSections = (widgetId, { effective, metadata }, report) => {
7125
+ var stsCardSettings = ({ language: language3, productOptions, ...effective }) => ({
7126
+ ...effective,
7127
+ language: {
7128
+ ...language3,
7129
+ global: {
7130
+ ...language3.global,
7131
+ payNow: language3.global.payNowUpgradeToSubscription || "Upgrade to Subscription"
7132
+ }
7133
+ },
7134
+ productOptions: { ...productOptions, showShipping: false }
7135
+ });
7136
+ var postPurchaseCardSections = (widgetId, {
7137
+ effective: configured,
7138
+ metadata
7139
+ }, report, sts = false) => {
7140
+ const effective = sts ? stsCardSettings(configured) : configured;
7106
7141
  if (!effective.blocks?.length) return null;
7107
7142
  const sections = convertMessagePreview(
7108
7143
  widgetId,
@@ -7161,21 +7196,32 @@ var prepareNormalizedPostPurchaseView = (widgetId, normalized, report, choice, q
7161
7196
  const selected = selectPostPurchaseChoice(normalized, planCatalog, choice, allowCollection, quantity3);
7162
7197
  const purchaseOptions = selected.options;
7163
7198
  const labels = { notAvailable: normalized.effective.language.global.notAvailable };
7199
+ const sts = quantity3?.sts === true;
7200
+ const marker = sts ? { sts: true } : {};
7164
7201
  const preparation = !selected.choice ? { reason: selected.reason, status: "unsupported" } : selected.choice.mode === "subscription" ? prepareNormalizedSubscription(normalized, report, selected.plan, quantity3, allowCollection) : prepareNormalizedProduct(normalized, report, quantity3, allowCollection);
7165
7202
  if (preparation.status === "unsupported") {
7166
- const sections2 = isChoiceUnavailable(preparation.reason) ? postPurchaseCardSections(widgetId, normalized, report) : null;
7167
- return PostPurchaseProductView.parse({ labels, planCatalog, preparation, purchaseOptions, sections: sections2 });
7203
+ const sections2 = isChoiceUnavailable(preparation.reason) ? postPurchaseCardSections(widgetId, normalized, report, sts) : null;
7204
+ return PostPurchaseProductView.parse({
7205
+ labels,
7206
+ planCatalog,
7207
+ preparation,
7208
+ purchaseOptions,
7209
+ sections: sections2,
7210
+ ...marker
7211
+ });
7168
7212
  }
7169
- const sections = postPurchaseCardSections(widgetId, normalized, report);
7213
+ const sections = postPurchaseCardSections(widgetId, normalized, report, sts);
7170
7214
  if (!sections)
7171
7215
  return {
7172
7216
  labels,
7173
7217
  planCatalog,
7174
7218
  preparation: { reason: "One fixed product information block is required", status: "unsupported" },
7175
7219
  purchaseOptions,
7176
- sections: null
7220
+ sections: null,
7221
+ ...marker
7177
7222
  };
7178
7223
  return PostPurchaseProductView.parse({
7224
+ ...marker,
7179
7225
  planCatalog,
7180
7226
  preparation: {
7181
7227
  ...preparation,
@@ -7376,8 +7422,8 @@ var normalizePostPurchaseCollection = (settings, response, excludedProductIds, r
7376
7422
  const limit = z82.number().int().positive();
7377
7423
  limit.parse(normalized.original.productLimit);
7378
7424
  const cap = limit.parse(normalized.effective.productLimit);
7379
- const prefix = products.filter(({ productIndex }) => productIndex < cap);
7380
- const unread = dropped.filter(({ productIndex }) => productIndex < cap);
7425
+ const prefix = underPostPurchaseCap(products, cap);
7426
+ const unread = underPostPurchaseCap(dropped, cap);
7381
7427
  if (unread.length && !prefix.length) throw new ZodError(unread.flatMap(({ issues }) => issues));
7382
7428
  if (new Set(prefix.map(({ product }) => product.id)).size !== prefix.length)
7383
7429
  throw new Error("Ambiguous post-purchase collection");