@wix/himalaya-cli 0.1324.0 → 0.1325.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 (177) hide show
  1. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/checkout/src/index.d.ts +6 -10
  2. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/order-refs.d.ts +1 -0
  3. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/iap-checkout/src/purchase-fields.d.ts +1 -0
  4. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/flows/screens/src/index.d.ts +9 -35
  5. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/_types/stdlib/wix-sdk/stores-v1/src/display.d.ts +1 -0
  6. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/checkout.mjs +30 -13
  7. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/iap-checkout.mjs +20 -2
  8. package/dist/authoring-runtime/node_modules/@wix/himalaya/dist/screens.mjs +2890 -54
  9. package/dist/cli.mjs +4 -4
  10. package/fixtures/wix-canned.json +61 -6
  11. package/guides/iap-pricing.md +9 -0
  12. package/guides/iap-screen-adoption.md +77 -0
  13. package/guides/importable-sdks.md +2 -0
  14. package/guides/index.json +6 -0
  15. package/guides/sdk-reference.json +339 -1
  16. package/package.json +2 -2
  17. package/templates/beauty-salon/config.ts +1 -1
  18. package/templates/beauty-salon/dev/index.ts +46 -18
  19. package/templates/beauty-salon/tier5-src/_shared/collection-contract.ts +1 -1
  20. package/templates/beauty-salon/tier5-src/_shared/collection-service.ts +3 -2
  21. package/templates/beauty-salon/tier5-src/_shared/himi-store-collection.ts +1 -1
  22. package/templates/beauty-salon/tier5-src/_shared/home-service.ts +2 -1
  23. package/templates/beauty-salon/tier5-src/_shared/iap-presentation.ts +53 -0
  24. package/templates/beauty-salon/tier5-src/_shared/iap-worker.ts +42 -0
  25. package/templates/beauty-salon/tier5-src/himi-branded-lite-cart/index.ts +48 -1
  26. package/templates/beauty-salon/tier5-src/himi-branded-lite-home/index.ts +16 -2
  27. package/templates/beauty-salon/tier5-src/himi-branded-lite-order/index.ts +24 -3
  28. package/templates/beauty-salon/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  29. package/templates/beauty-salon/tier5-src/himi-store-product/index.ts +24 -3
  30. package/templates/beauty-salon/tier5-src/himi-store-saved/index.ts +8 -3
  31. package/templates/beauty-salon/tier5-src/himi-store-shop/index.ts +10 -0
  32. package/templates/beauty-salon/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  33. package/templates/beauty-salon-owner/tier5-src/_shared/collection-contract.ts +1 -1
  34. package/templates/beauty-salon-owner/tier5-src/_shared/collection-service.ts +3 -2
  35. package/templates/beauty-salon-owner/tier5-src/_shared/home-service.ts +2 -1
  36. package/templates/branded-lite/config.ts +1 -1
  37. package/templates/branded-lite/dev/index.ts +46 -18
  38. package/templates/branded-lite/tier5-src/_shared/collection-contract.ts +1 -1
  39. package/templates/branded-lite/tier5-src/_shared/collection-service.ts +3 -2
  40. package/templates/branded-lite/tier5-src/_shared/himi-store-collection.ts +1 -1
  41. package/templates/branded-lite/tier5-src/_shared/home-service.ts +2 -1
  42. package/templates/branded-lite/tier5-src/_shared/iap-presentation.ts +53 -0
  43. package/templates/branded-lite/tier5-src/himi-branded-lite-cart/index.ts +48 -1
  44. package/templates/branded-lite/tier5-src/himi-branded-lite-home/index.ts +16 -2
  45. package/templates/branded-lite/tier5-src/himi-branded-lite-order/index.ts +24 -3
  46. package/templates/branded-lite/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  47. package/templates/branded-lite/tier5-src/himi-store-product/index.ts +24 -3
  48. package/templates/branded-lite/tier5-src/himi-store-saved/index.ts +8 -3
  49. package/templates/branded-lite/tier5-src/himi-store-shop/index.ts +10 -0
  50. package/templates/branded-lite/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  51. package/templates/community-club/config.ts +1 -1
  52. package/templates/community-club/dev/index.ts +3 -1
  53. package/templates/community-club/tier5-src/_shared/collection-contract.ts +1 -1
  54. package/templates/community-club/tier5-src/_shared/collection-service.ts +3 -2
  55. package/templates/community-club/tier5-src/_shared/home-service.ts +2 -1
  56. package/templates/community-club/tier5-src/_shared/iap-presentation.ts +53 -0
  57. package/templates/community-club/tier5-src/_shared/iap-worker.ts +42 -0
  58. package/templates/community-club/tier5-src/himi-branded-lite-home/index.ts +16 -2
  59. package/templates/community-club-owner/tier5-src/_shared/collection-contract.ts +1 -1
  60. package/templates/community-club-owner/tier5-src/_shared/collection-service.ts +3 -2
  61. package/templates/community-club-owner/tier5-src/_shared/home-service.ts +2 -1
  62. package/templates/events-meetups/config.ts +1 -1
  63. package/templates/events-meetups/dev/index.ts +3 -1
  64. package/templates/events-meetups/tier5-src/_shared/collection-contract.ts +1 -1
  65. package/templates/events-meetups/tier5-src/_shared/collection-service.ts +3 -2
  66. package/templates/events-meetups/tier5-src/_shared/home-service.ts +2 -1
  67. package/templates/events-meetups/tier5-src/_shared/iap-presentation.ts +53 -0
  68. package/templates/events-meetups/tier5-src/_shared/iap-worker.ts +42 -0
  69. package/templates/events-meetups/tier5-src/himi-branded-lite-home/index.ts +16 -2
  70. package/templates/events-meetups-owner/tier5-src/_shared/collection-contract.ts +1 -1
  71. package/templates/events-meetups-owner/tier5-src/_shared/collection-service.ts +3 -2
  72. package/templates/events-meetups-owner/tier5-src/_shared/home-service.ts +2 -1
  73. package/templates/gym-fitness/config.ts +1 -1
  74. package/templates/gym-fitness/dev/index.ts +46 -18
  75. package/templates/gym-fitness/tier5-src/_shared/collection-contract.ts +1 -1
  76. package/templates/gym-fitness/tier5-src/_shared/collection-service.ts +3 -2
  77. package/templates/gym-fitness/tier5-src/_shared/himi-store-collection.ts +1 -1
  78. package/templates/gym-fitness/tier5-src/_shared/home-service.ts +2 -1
  79. package/templates/gym-fitness/tier5-src/_shared/iap-presentation.ts +53 -0
  80. package/templates/gym-fitness/tier5-src/_shared/iap-worker.ts +42 -0
  81. package/templates/gym-fitness/tier5-src/himi-branded-lite-cart/index.ts +48 -1
  82. package/templates/gym-fitness/tier5-src/himi-branded-lite-home/index.ts +16 -2
  83. package/templates/gym-fitness/tier5-src/himi-branded-lite-order/index.ts +24 -3
  84. package/templates/gym-fitness/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  85. package/templates/gym-fitness/tier5-src/himi-store-product/index.ts +24 -3
  86. package/templates/gym-fitness/tier5-src/himi-store-saved/index.ts +8 -3
  87. package/templates/gym-fitness/tier5-src/himi-store-shop/index.ts +10 -0
  88. package/templates/gym-fitness/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  89. package/templates/gym-fitness-owner/tier5-src/_shared/collection-contract.ts +1 -1
  90. package/templates/gym-fitness-owner/tier5-src/_shared/collection-service.ts +3 -2
  91. package/templates/gym-fitness-owner/tier5-src/_shared/home-service.ts +2 -1
  92. package/templates/nonprofit-cause/config.ts +1 -1
  93. package/templates/nonprofit-cause/dev/index.ts +29 -13
  94. package/templates/nonprofit-cause/tier5-src/_shared/collection-contract.ts +1 -1
  95. package/templates/nonprofit-cause/tier5-src/_shared/collection-service.ts +3 -2
  96. package/templates/nonprofit-cause/tier5-src/_shared/home-service.ts +2 -1
  97. package/templates/nonprofit-cause/tier5-src/_shared/iap-presentation.ts +53 -0
  98. package/templates/nonprofit-cause/tier5-src/_shared/iap-worker.ts +42 -0
  99. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-home/index.ts +16 -2
  100. package/templates/nonprofit-cause/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  101. package/templates/nonprofit-cause/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  102. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-contract.ts +1 -1
  103. package/templates/nonprofit-cause-owner/tier5-src/_shared/collection-service.ts +3 -2
  104. package/templates/nonprofit-cause-owner/tier5-src/_shared/home-service.ts +2 -1
  105. package/templates/owner-lite/tier5-src/_shared/collection-contract.ts +1 -1
  106. package/templates/owner-lite/tier5-src/_shared/collection-service.ts +3 -2
  107. package/templates/owner-lite/tier5-src/_shared/home-service.ts +2 -1
  108. package/templates/podcast-creator/config.ts +1 -1
  109. package/templates/podcast-creator/dev/index.ts +29 -13
  110. package/templates/podcast-creator/tier5-src/_shared/collection-contract.ts +1 -1
  111. package/templates/podcast-creator/tier5-src/_shared/collection-service.ts +3 -2
  112. package/templates/podcast-creator/tier5-src/_shared/home-service.ts +2 -1
  113. package/templates/podcast-creator/tier5-src/_shared/iap-presentation.ts +53 -0
  114. package/templates/podcast-creator/tier5-src/_shared/iap-worker.ts +42 -0
  115. package/templates/podcast-creator/tier5-src/himi-branded-lite-home/index.ts +16 -2
  116. package/templates/podcast-creator/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  117. package/templates/podcast-creator/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  118. package/templates/podcast-creator-owner/tier5-src/_shared/collection-contract.ts +1 -1
  119. package/templates/podcast-creator-owner/tier5-src/_shared/collection-service.ts +3 -2
  120. package/templates/podcast-creator-owner/tier5-src/_shared/home-service.ts +2 -1
  121. package/templates/portfolio-creative/config.ts +1 -1
  122. package/templates/portfolio-creative/dev/index.ts +20 -6
  123. package/templates/portfolio-creative/tier5-src/_shared/collection-contract.ts +1 -1
  124. package/templates/portfolio-creative/tier5-src/_shared/collection-service.ts +3 -2
  125. package/templates/portfolio-creative/tier5-src/_shared/himi-store-collection.ts +1 -1
  126. package/templates/portfolio-creative/tier5-src/_shared/home-service.ts +2 -1
  127. package/templates/portfolio-creative/tier5-src/_shared/iap-presentation.ts +53 -0
  128. package/templates/portfolio-creative/tier5-src/_shared/iap-worker.ts +42 -0
  129. package/templates/portfolio-creative/tier5-src/himi-branded-lite-cart/index.ts +48 -1
  130. package/templates/portfolio-creative/tier5-src/himi-branded-lite-home/index.ts +16 -2
  131. package/templates/portfolio-creative/tier5-src/himi-branded-lite-order/index.ts +24 -3
  132. package/templates/portfolio-creative/tier5-src/himi-store-product/index.ts +24 -3
  133. package/templates/portfolio-creative/tier5-src/himi-store-saved/index.ts +8 -3
  134. package/templates/portfolio-creative/tier5-src/himi-store-shop/index.ts +10 -0
  135. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-contract.ts +1 -1
  136. package/templates/portfolio-creative-owner/tier5-src/_shared/collection-service.ts +3 -2
  137. package/templates/portfolio-creative-owner/tier5-src/_shared/home-service.ts +2 -1
  138. package/templates/restaurant/config.ts +1 -1
  139. package/templates/restaurant/dev/index.ts +3 -1
  140. package/templates/restaurant/tier5-src/_shared/collection-contract.ts +1 -1
  141. package/templates/restaurant/tier5-src/_shared/collection-service.ts +3 -2
  142. package/templates/restaurant/tier5-src/_shared/home-service.ts +2 -1
  143. package/templates/restaurant/tier5-src/_shared/iap-presentation.ts +53 -0
  144. package/templates/restaurant/tier5-src/_shared/iap-worker.ts +42 -0
  145. package/templates/restaurant/tier5-src/himi-branded-lite-home/index.ts +16 -2
  146. package/templates/restaurant-owner/tier5-src/_shared/collection-contract.ts +1 -1
  147. package/templates/restaurant-owner/tier5-src/_shared/collection-service.ts +3 -2
  148. package/templates/restaurant-owner/tier5-src/_shared/home-service.ts +2 -1
  149. package/templates/retail-store/config.ts +1 -1
  150. package/templates/retail-store/dev/index.ts +20 -6
  151. package/templates/retail-store/tier5-src/_shared/collection-contract.ts +1 -1
  152. package/templates/retail-store/tier5-src/_shared/collection-service.ts +3 -2
  153. package/templates/retail-store/tier5-src/_shared/himi-store-collection.ts +1 -1
  154. package/templates/retail-store/tier5-src/_shared/home-service.ts +2 -1
  155. package/templates/retail-store/tier5-src/_shared/iap-presentation.ts +53 -0
  156. package/templates/retail-store/tier5-src/_shared/iap-worker.ts +42 -0
  157. package/templates/retail-store/tier5-src/himi-branded-lite-cart/index.ts +48 -1
  158. package/templates/retail-store/tier5-src/himi-branded-lite-home/index.ts +16 -2
  159. package/templates/retail-store/tier5-src/himi-branded-lite-order/index.ts +24 -3
  160. package/templates/retail-store/tier5-src/himi-store-product/index.ts +24 -3
  161. package/templates/retail-store/tier5-src/himi-store-saved/index.ts +8 -3
  162. package/templates/retail-store/tier5-src/himi-store-shop/index.ts +10 -0
  163. package/templates/retail-store-owner/tier5-src/_shared/collection-contract.ts +1 -1
  164. package/templates/retail-store-owner/tier5-src/_shared/collection-service.ts +3 -2
  165. package/templates/retail-store-owner/tier5-src/_shared/home-service.ts +2 -1
  166. package/templates/yoga-studio/config.ts +1 -1
  167. package/templates/yoga-studio/dev/index.ts +29 -13
  168. package/templates/yoga-studio/tier5-src/_shared/collection-contract.ts +1 -1
  169. package/templates/yoga-studio/tier5-src/_shared/collection-service.ts +3 -2
  170. package/templates/yoga-studio/tier5-src/_shared/home-service.ts +2 -1
  171. package/templates/yoga-studio/tier5-src/_shared/iap-presentation.ts +53 -0
  172. package/templates/yoga-studio/tier5-src/himi-branded-lite-home/index.ts +16 -2
  173. package/templates/yoga-studio/tier5-src/himi-branded-lite-plans/index.ts +153 -64
  174. package/templates/yoga-studio/tier5-src/wix-spaces-plan-detail/index.ts +8 -3
  175. package/templates/yoga-studio-owner/tier5-src/_shared/collection-contract.ts +1 -1
  176. package/templates/yoga-studio-owner/tier5-src/_shared/collection-service.ts +3 -2
  177. package/templates/yoga-studio-owner/tier5-src/_shared/home-service.ts +2 -1
@@ -2473,7 +2473,9 @@ const SCREENS = runtimeSetting({
2473
2473
  "integrity": "",
2474
2474
  "contentType": "application/javascript+ts-bundle",
2475
2475
  "declaredCapabilities": [
2476
- "wix_api"
2476
+ "wix_api",
2477
+ "iap",
2478
+ "app_info"
2477
2479
  ],
2478
2480
  "minBinaryVersion": "0.2.0",
2479
2481
  "bundleVersion": "0.0.1"
@@ -8052,7 +8054,9 @@ const SCREENS = runtimeSetting({
8052
8054
  "contentType": "application/javascript+ts-bundle",
8053
8055
  "declaredCapabilities": [
8054
8056
  "wix_api",
8055
- "local_database"
8057
+ "local_database",
8058
+ "iap",
8059
+ "app_info"
8056
8060
  ],
8057
8061
  "minBinaryVersion": "0.1.0",
8058
8062
  "bundleVersion": "0.0.1"
@@ -8520,7 +8524,11 @@ const SCREENS = runtimeSetting({
8520
8524
  "url": "http://localhost:8190/v1/bundles/himi-store-saved",
8521
8525
  "integrity": "",
8522
8526
  "contentType": "application/javascript+ts-bundle",
8523
- "declaredCapabilities": [],
8527
+ "declaredCapabilities": [
8528
+ "wix_api",
8529
+ "iap",
8530
+ "app_info"
8531
+ ],
8524
8532
  "minBinaryVersion": "0.1.0",
8525
8533
  "bundleVersion": "0.0.1"
8526
8534
  }
@@ -10046,7 +10054,9 @@ const SCREENS = runtimeSetting({
10046
10054
  "wix_cashier",
10047
10055
  "open_url",
10048
10056
  "local_database",
10049
- "wix_platform"
10057
+ "wix_platform",
10058
+ "iap",
10059
+ "app_info"
10050
10060
  ],
10051
10061
  "minBinaryVersion": "0.1.0",
10052
10062
  "bundleVersion": "0.0.1"
@@ -19169,7 +19179,8 @@ const SCREENS = runtimeSetting({
19169
19179
  "package": "himalaya",
19170
19180
  "type": "Button",
19171
19181
  "props": {
19172
- "title": "Join on website",
19182
+ "title": "$item.joinLabel",
19183
+ "disabledPath": "$item.joinDisabled",
19173
19184
  "style": "primary",
19174
19185
  "fullWidth": true,
19175
19186
  "action": {
@@ -19190,7 +19201,8 @@ const SCREENS = runtimeSetting({
19190
19201
  "package": "himalaya",
19191
19202
  "type": "Button",
19192
19203
  "props": {
19193
- "title": "Join on website",
19204
+ "title": "$item.joinLabel",
19205
+ "disabledPath": "$item.joinDisabled",
19194
19206
  "style": "secondary",
19195
19207
  "fullWidth": true,
19196
19208
  "action": {
@@ -19371,7 +19383,8 @@ const SCREENS = runtimeSetting({
19371
19383
  "package": "himalaya",
19372
19384
  "type": "Button",
19373
19385
  "props": {
19374
- "title": "Join on website",
19386
+ "title": "$item.joinLabel",
19387
+ "disabledPath": "$item.joinDisabled",
19375
19388
  "style": "primary",
19376
19389
  "fullWidth": true,
19377
19390
  "action": {
@@ -19392,7 +19405,8 @@ const SCREENS = runtimeSetting({
19392
19405
  "package": "himalaya",
19393
19406
  "type": "Button",
19394
19407
  "props": {
19395
- "title": "Join on website",
19408
+ "title": "$item.joinLabel",
19409
+ "disabledPath": "$item.joinDisabled",
19396
19410
  "style": "secondary",
19397
19411
  "fullWidth": true,
19398
19412
  "action": {
@@ -19797,7 +19811,8 @@ const SCREENS = runtimeSetting({
19797
19811
  "package": "himalaya",
19798
19812
  "type": "Button",
19799
19813
  "props": {
19800
- "title": "Join on website",
19814
+ "title": "$item.joinLabel",
19815
+ "disabledPath": "$item.joinDisabled",
19801
19816
  "style": "primary",
19802
19817
  "fullWidth": true,
19803
19818
  "action": {
@@ -19818,7 +19833,8 @@ const SCREENS = runtimeSetting({
19818
19833
  "package": "himalaya",
19819
19834
  "type": "Button",
19820
19835
  "props": {
19821
- "title": "Join on website",
19836
+ "title": "$item.joinLabel",
19837
+ "disabledPath": "$item.joinDisabled",
19822
19838
  "style": "secondary",
19823
19839
  "fullWidth": true,
19824
19840
  "action": {
@@ -19999,7 +20015,8 @@ const SCREENS = runtimeSetting({
19999
20015
  "package": "himalaya",
20000
20016
  "type": "Button",
20001
20017
  "props": {
20002
- "title": "Join on website",
20018
+ "title": "$item.joinLabel",
20019
+ "disabledPath": "$item.joinDisabled",
20003
20020
  "style": "primary",
20004
20021
  "fullWidth": true,
20005
20022
  "action": {
@@ -20020,7 +20037,8 @@ const SCREENS = runtimeSetting({
20020
20037
  "package": "himalaya",
20021
20038
  "type": "Button",
20022
20039
  "props": {
20023
- "title": "Join on website",
20040
+ "title": "$item.joinLabel",
20041
+ "disabledPath": "$item.joinDisabled",
20024
20042
  "style": "secondary",
20025
20043
  "fullWidth": true,
20026
20044
  "action": {
@@ -20598,7 +20616,8 @@ const SCREENS = runtimeSetting({
20598
20616
  "package": "himalaya",
20599
20617
  "type": "Button",
20600
20618
  "props": {
20601
- "title": "Join on website",
20619
+ "title": "$item.joinLabel",
20620
+ "disabledPath": "$item.joinDisabled",
20602
20621
  "style": "primary",
20603
20622
  "fullWidth": true,
20604
20623
  "action": {
@@ -20619,7 +20638,8 @@ const SCREENS = runtimeSetting({
20619
20638
  "package": "himalaya",
20620
20639
  "type": "Button",
20621
20640
  "props": {
20622
- "title": "Join on website",
20641
+ "title": "$item.joinLabel",
20642
+ "disabledPath": "$item.joinDisabled",
20623
20643
  "style": "secondary",
20624
20644
  "fullWidth": true,
20625
20645
  "action": {
@@ -20897,7 +20917,9 @@ const SCREENS = runtimeSetting({
20897
20917
  "wix_api",
20898
20918
  "wix_cashier",
20899
20919
  "open_url",
20900
- "wix_platform"
20920
+ "wix_platform",
20921
+ "iap",
20922
+ "app_info"
20901
20923
  ],
20902
20924
  "minBinaryVersion": "0.1.0",
20903
20925
  "bundleVersion": "0.0.1"
@@ -21153,7 +21175,9 @@ const SCREENS = runtimeSetting({
21153
21175
  "integrity": "",
21154
21176
  "contentType": "application/javascript+ts-bundle",
21155
21177
  "declaredCapabilities": [
21156
- "wix_api"
21178
+ "wix_api",
21179
+ "iap",
21180
+ "app_info"
21157
21181
  ],
21158
21182
  "minBinaryVersion": "0.1.0",
21159
21183
  "bundleVersion": "0.0.1"
@@ -30284,7 +30308,9 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
30284
30308
  "declaredCapabilities": [
30285
30309
  "wix_api",
30286
30310
  "local_database",
30287
- "live_activity"
30311
+ "live_activity",
30312
+ "iap",
30313
+ "app_info"
30288
30314
  ],
30289
30315
  "minBinaryVersion": "0.1.0",
30290
30316
  "bundleVersion": "0.0.1"
@@ -31166,7 +31192,9 @@ const TEMPLATED: { pattern: string; display: string; liveSeed?: LiveSeed; static
31166
31192
  "wix_api",
31167
31193
  "share",
31168
31194
  "local_database",
31169
- "open_url"
31195
+ "open_url",
31196
+ "iap",
31197
+ "app_info"
31170
31198
  ],
31171
31199
  "minBinaryVersion": "0.1.0",
31172
31200
  "bundleVersion": "0.0.1"
@@ -87,7 +87,7 @@ export const COLLECTIONS: Record<string, CollectionConfig> = {
87
87
  himiBrandedOrders: { rows: 'rows', title: 'title', subtitle: 'subtitle', view: 'list', backendQuery: true,
88
88
  searchPlaceholder: 'Search by order number', countNoun: ['order', 'orders'], filterValues: ['Not fulfilled', 'Partially fulfilled', 'Fulfilled'],
89
89
  sortLabels: { default: 'Newest', az: 'Oldest', za: 'Order # ↓' } },
90
- himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
90
+ himiBrandedPlans: { rows: 'rows', title: 'name', subtitle: 'description', metadata: ['badge', 'priceLine', 'periodLine', 'trialBadge', 'perk1', 'perk2', 'perk3', 'joinNote'], view: 'grid', gridColumns: 1, metaMaxLines: 8, countNoun: ['plan', 'plans'] },
91
91
  himiBrandedPrograms: { rows: 'rows', title: 'name', subtitle: 'description', image: 'image', view: 'grid' },
92
92
  himiBrandedCoupons: { rows: 'coupons', title: 'name', subtitle: 'subtitle', view: 'list', primaryVerb: { label: 'Get code' } },
93
93
  himiBrandedNotifications: { rows: 'items', title: 'name', subtitle: 'subtitle', image: 'photo', metadata: ['unreadDot'], view: 'list' },
@@ -323,6 +323,7 @@ export function decorateCollection(name: string, def: { state: State; actions?:
323
323
  /** Domain-owned page source with shared query controls. Publishing uses a normal
324
324
  * reducer and reads the private controller snapshot, never an SDUI payload. */
325
325
  export function defineBackendCollection<S extends object, Row extends object>(name: string, options: {
326
+ register?: typeof register;
326
327
  /** Revalidate already opened pages on navigation return; explicit refresh resets. */
327
328
  restorePagesOnLoad?: boolean;
328
329
  /** The load action's argument (onAppear params beyond `copy`), before the query runs — for a
@@ -342,7 +343,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
342
343
  controller: controller as unknown as BackendCollection['controller'],
343
344
  apply: (state, page) => options.apply(state as S, page as unknown as CollectionQueryState<Row>) as State,
344
345
  });
345
- const handle = register(name, definition);
346
+ const handle = (options.register ?? register)(name, definition);
346
347
  publish = () => handle.collectionDataChanged();
347
348
  return handle;
348
349
  }
@@ -350,7 +351,7 @@ export function defineBackendCollection<S extends object, Row extends object>(na
350
351
  /** Keep the domain service's existing TypeScript API. Extra collection verbs are
351
352
  * dispatched through SDUI and do not widen the native capability surface. */
352
353
  export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
353
- register(name, decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }))) as typeof register;
354
+ register(name, { ...def, ...decorateCollection(name, def as unknown as { state: State; actions?: Record<string, Reducer> }) } as ServiceDefinition<State>)) as typeof register;
354
355
 
355
356
  /** Domains can supply their existing saved-items store without putting domain
356
357
  * data or mutations in the shared collection layer. */
@@ -8,7 +8,7 @@ export const storeCollectionSaving: CollectionSaving = {
8
8
  for (const row of rows) {
9
9
  if (typeof row.id !== 'string' || typeof row.name !== 'string') continue;
10
10
  products.set(row.id, { id: row.id, name: row.name,
11
- price: typeof row.subtitle === 'string' ? row.subtitle : '',
11
+ price: typeof row.commercePrice === 'string' ? row.commercePrice : typeof row.subtitle === 'string' ? row.subtitle : '',
12
12
  image: typeof row.image === 'string' ? row.image : '' });
13
13
  }
14
14
  const selected = ids.map(id => products.get(id));
@@ -87,4 +87,5 @@ export function decorateHome(def: { state: State; actions?: Record<string, Actio
87
87
  actions.homeMoveDown = change(id => { const index = prefs.order.indexOf(id); if (index >= 0 && index < prefs.order.length - 1) [prefs.order[index], prefs.order[index + 1]] = [prefs.order[index + 1], prefs.order[index]]; });
88
88
  return { state: derive(def.state), actions };
89
89
  }
90
- export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) => register(name, decorateHome(def as unknown as { state: State; actions?: Record<string, Action> }))) as typeof register;
90
+ export const defineService: typeof register = ((name: string, def: ServiceDefinition<unknown>) =>
91
+ register(name, { ...def, ...decorateHome(def as unknown as { state: State; actions?: Record<string, Action> }) })) as typeof register;
@@ -0,0 +1,53 @@
1
+ import { defineService, type ServiceDefinition } from "@himalaya/state";
2
+ import { createIapCommerce, type IapCommerce } from "@himalaya/iap-checkout";
3
+ import { iapPriceFields, type IapCatalogItem } from "@himalaya/iap";
4
+ import { iapPriceCopy, resetUnhydratedKeysOnOlderRuntime } from "./iap-worker.js";
5
+
6
+ // Presentation only. Checkout, mapping, cache, session and refresh policy stay in the shared flow.
7
+ export function withIapPresentation<S extends object>(options: {
8
+ register?: typeof defineService;
9
+ onCreate?(commerce: IapCommerce): void;
10
+ onContent?(state: S): void;
11
+ items(state: S): IapCatalogItem[];
12
+ present(state: S, price: (id: string, commercePrice?: string) => string, allowed: (id: string) => boolean): S;
13
+ contentActions: string[];
14
+ transientKeys: (keyof S & string)[];
15
+ prefix: string;
16
+ }): typeof defineService {
17
+ return ((name: string, definition: ServiceDefinition<S>) => {
18
+ const commerce = createIapCommerce({ onChange: () => { void service.iapPublish(); } });
19
+ options.onCreate?.(commerce);
20
+ let items: IapCatalogItem[] = [];
21
+ let contentVersion = 0;
22
+ const project = (state: S): S => {
23
+ const price = (id: string, commercePrice?: string) => {
24
+ const item = items.find(item => item.reference.id === id);
25
+ if (!item) return "";
26
+ const view = commerce.pricing.view(item.reference);
27
+ return view.route === "disabled" || view.route === "regular" ? commercePrice ?? item.commerceDisplayPrice ?? "" : iapPriceFields(view, iapPriceCopy(options.prefix)).priceText;
28
+ };
29
+ const allowed = (id: string) => commerce.pricing.checkoutGuard({ kind: "storesProduct", id }).allowed;
30
+ const poll = commerce.pollDirective(`${name}.iapPoll`);
31
+ return { ...options.present(state, price, allowed), ...(poll ? { _poll: poll } : {}) };
32
+ };
33
+ const actions = Object.fromEntries(Object.entries(definition.actions ?? {}).map(([action, reduce]) => [action, async (state: S, ...args: never[]) => {
34
+ const content = options.contentActions.includes(action);
35
+ const version = action === "load" ? ++contentVersion : contentVersion;
36
+ const next = await reduce(state, ...args);
37
+ if (!next || (content && version !== contentVersion)) return undefined;
38
+ if (content) {
39
+ options.onContent?.(next);
40
+ items = options.items(next);
41
+ void commerce.loadPrices(items);
42
+ }
43
+ return project(next);
44
+ }]));
45
+ const service = (options.register ?? defineService)(name, {
46
+ ...definition, notHydrated: [...(definition.notHydrated ?? []), ...options.transientKeys],
47
+ actions: { ...actions, iapPublish: project, iapPoll: async () => { await commerce.poll(); return undefined; },
48
+ iapRefresh: async () => { await commerce.loadPrices(items); return undefined; } },
49
+ });
50
+ resetUnhydratedKeysOnOlderRuntime(service, definition.state, options.transientKeys);
51
+ return service;
52
+ }) as unknown as typeof defineService;
53
+ }
@@ -0,0 +1,42 @@
1
+ // Worker-side glue for the shared IAP layer (`@himalaya/iap` catalog pricing and
2
+ // `@himalaya/iap-checkout` → createIapCommerce), shared by every screen that shows or sells an
3
+ // IAP-backed item. Mapping, Apple prices, payment guards, the host scope and the sale live in the
4
+ // SDK; this only supplies the copy and the hydration reset.
5
+ import type { IapPriceCopy } from "@himalaya/iap";
6
+ import type { IapPurchaseCopy } from "@himalaya/iap-checkout";
7
+ import { copyParam } from "./himi-copy.js";
8
+
9
+ /** Price labels from the screen's copy pack (`<prefix>.priceLoadingText`, …). */
10
+ export function iapPriceCopy(prefix: string): IapPriceCopy {
11
+ return {
12
+ loading: copyParam(`${prefix}.priceLoadingText`, "Loading price…"),
13
+ unavailable: copyParam(`${prefix}.priceUnavailableText`, "Price unavailable"),
14
+ oneTimePurchase: copyParam(`${prefix}.oneTimePurchaseLabel`, "One-time purchase"),
15
+ purchaseUnavailable: copyParam(`${prefix}.purchaseUnavailableText`, "Purchasing is not available yet."),
16
+ };
17
+ }
18
+
19
+ /** App Store purchase labels from the screen's copy pack (`<prefix>.buyCta`, …), for
20
+ * `iapPurchaseFields`. Customer copy only: no backend names, ids or diagnostics. */
21
+ export function iapPurchaseCopy(prefix: string): IapPurchaseCopy {
22
+ return {
23
+ buy: copyParam(`${prefix}.buyCta`, "Get access"),
24
+ processing: copyParam(`${prefix}.purchaseProcessingText`, "Processing your purchase…"),
25
+ awaitingApproval: copyParam(`${prefix}.purchaseAwaitingApprovalText`, "Waiting for approval"),
26
+ unavailable: copyParam(`${prefix}.purchaseTemporarilyUnavailableText`, "Purchasing is temporarily unavailable."),
27
+ signInRequired: copyParam(`${prefix}.purchaseSignInText`, "Sign in to buy."),
28
+ retry: copyParam(`${prefix}.purchaseRetryText`, "Something went wrong. Try again."),
29
+ priceChanged: copyParam(`${prefix}.purchasePriceChangedText`, "The price changed. Check it and try again."),
30
+ support: copyParam(`${prefix}.purchaseSupportText`, "We couldn't finish this purchase. Contact support."),
31
+ };
32
+ }
33
+
34
+ /** What `notHydrated` does, for a runtime that ignores the option (a binary older than it, still
35
+ * served new bundles): put the listed keys back to their declared values before any action runs,
36
+ * so a persisted price or an enabled purchase action never paints as this run's. */
37
+ export function resetUnhydratedKeysOnOlderRuntime<S extends object>(svc: { getState(): S }, declared: S, keys: readonly (keyof S & string)[]): void {
38
+ if ((globalThis as { himalaya?: { hydrationOptOut?: unknown } }).himalaya?.hydrationOptOut === true) return;
39
+ const booted = svc.getState();
40
+ if (!booted || typeof booted !== "object") return;
41
+ for (const key of keys) booted[key] = declared[key];
42
+ }
@@ -1,4 +1,7 @@
1
- import { defineService } from "@himalaya/state";
1
+ import { getStoreProduct } from "../_shared/himi-store.js";
2
+ import { iapPriceCopy } from "../_shared/iap-worker.js";
3
+ import { withIapPresentation } from "../_shared/iap-presentation.js";
4
+ import { type IapCommerce, classifyCart } from "@himalaya/iap-checkout";
2
5
  import { createCheckoutFromCurrentCart, createOrder, getCurrentCart, removeLineItemsFromCurrentCart, updateLineItemsQuantity, type WixCartLineItemDetail } from "@himalaya/wix-ecom";
3
6
  import { createRedirectSession } from "@himalaya/wix-redirects";
4
7
  import { session as wixPlatformSession } from "@himalaya/wix-platform";
@@ -6,6 +9,7 @@ import { payCashierOrder } from "@himalaya/checkout";
6
9
 
7
10
  /** Wix Stores appDefId — the vertical that owns a cart/ecom order, and therefore the
8
11
  * `appId` its cashier order is paid under. (Same constant Wix's own tooling uses.) */
12
+ const WIX_STORES_CATALOG_ID = "215238eb-22a5-4c36-9e7b-e7c08025e04e";
9
13
  const WIX_STORES_APP_ID = "1380b703-ce81-ff05-f115-39571d94dfcd";
10
14
 
11
15
  /** The site's metaSiteId for the cashier payment config. Best-effort — the native
@@ -40,6 +44,7 @@ async function cacheDrop(id: string): Promise<void> { try { await dbRemove(ENTIT
40
44
  async function queueWrite(actionId: string, params: Record<string, string>): Promise<void> { try { await enqueue(actionId, params); } catch { /* capability not granted */ } }
41
45
 
42
46
  interface Row {
47
+ catalogReference?: { appId: string; catalogItemId: string; options?: Record<string, unknown> };
43
48
  id: string;
44
49
  name: string;
45
50
  title: string;
@@ -50,6 +55,7 @@ interface Row {
50
55
  /** The "−" control at qty 1 removes the line, so it never dead-ends. */
51
56
  atMinQuantity: boolean;
52
57
  total: string;
58
+ commerceTotal: string;
53
59
  image: string;
54
60
  initial: string;
55
61
  hasImage: boolean;
@@ -59,6 +65,7 @@ interface State {
59
65
  status: "loading" | "ready" | "error";
60
66
  items: Row[];
61
67
  subtotal: string;
68
+ commerceSubtotal?: string;
62
69
  emptyText: string;
63
70
  checkoutHint: string;
64
71
  isLoading: boolean;
@@ -118,6 +125,7 @@ function row(item: WixCartLineItemDetail, index: number): Row {
118
125
  const name = item.productName?.original || copyParam("cart.itemFallbackName", "Cart item");
119
126
  const image = mediaUrl(item);
120
127
  return {
128
+ catalogReference: (item as WixCartLineItemDetail & { catalogReference?: Row["catalogReference"] }).catalogReference,
121
129
  id: item.id || `line-${index}`,
122
130
  name,
123
131
  title: name,
@@ -126,6 +134,7 @@ function row(item: WixCartLineItemDetail, index: number): Row {
126
134
  quantityLabel: `${quantity}`,
127
135
  atMinQuantity: quantity <= 1,
128
136
  total: item.price?.formattedAmount ?? "",
137
+ commerceTotal: item.price?.formattedAmount ?? "",
129
138
  image,
130
139
  initial: (name.trim()[0] || "C").toUpperCase(),
131
140
  hasImage: Boolean(image),
@@ -163,6 +172,7 @@ async function refresh(state: State, checkoutHint = ""): Promise<State> {
163
172
  status: "ready",
164
173
  items,
165
174
  subtotal: cart.subtotal?.formattedAmount ?? "",
175
+ commerceSubtotal: cart.subtotal?.formattedAmount ?? "",
166
176
  emptyText: items.length ? "" : copyParam("cart.emptyText", "Your cart is empty."),
167
177
  checkoutHint,
168
178
  });
@@ -190,6 +200,16 @@ function cartConsumedByOrder(): Pick<State, "items" | "subtotal" | "emptyText" |
190
200
  };
191
201
  }
192
202
 
203
+ let cartPricing: IapCommerce;
204
+ const defineService = withIapPresentation<State>({
205
+ onCreate: commerce => { cartPricing = commerce; },
206
+ prefix: "cart", transientKeys: ["items", "subtotal"], contentActions: ["load", "remove", "increment", "decrement"],
207
+ items: state => state.items.flatMap(row => row.catalogReference?.catalogItemId ? [{ reference: { kind: "storesProduct", id: row.catalogReference.catalogItemId }, commerceDisplayPrice: row.commerceTotal ?? row.total }] : []),
208
+ present: (state, price, allowed) => ({ ...state,
209
+ items: state.items.map(row => ({ ...row, total: row.catalogReference?.catalogItemId ? price(row.catalogReference.catalogItemId, row.commerceTotal ?? row.total) : row.total })),
210
+ subtotal: state.items.length > 0 && state.items.every(row => row.catalogReference?.catalogItemId && allowed(row.catalogReference.catalogItemId)) ? state.commerceSubtotal ?? state.subtotal : "" }),
211
+ });
212
+
193
213
  const svc = defineService<State, {
194
214
  load: (state: State, arg?: unknown) => Promise<State>;
195
215
  remove: (state: State, arg?: unknown) => Promise<State>;
@@ -272,6 +292,25 @@ const svc = defineService<State, {
272
292
  // `createOrder` returning no paymentGatewayOrderId means there is nothing to
273
293
  // charge (fully covered / zero total) — the order already stands.
274
294
  checkout: async (state) => {
295
+ let validatedLines: WixCartLineItemDetail[] = [];
296
+ const signature = (lines: WixCartLineItemDetail[]) => JSON.stringify(lines.map(line => [line.id, line.quantity, (line as WixCartLineItemDetail & { catalogReference?: Row["catalogReference"] }).catalogReference]));
297
+ try {
298
+ const cart = await getCurrentCart();
299
+ const lines = cart.lineItems ?? [];
300
+ if (!lines.length) return derive({ ...state, checkoutHint: copyParam("cart.checkoutEmptyMessage", "Add an item before checkout.") });
301
+ const commerce = cartPricing;
302
+ const items = lines.map(line => (line as WixCartLineItemDetail & { catalogReference?: Row["catalogReference"] }).catalogReference);
303
+ if (items.some(item => !item?.catalogItemId || !item.appId)) return derive({ ...state, checkoutHint: iapPriceCopy("cart").purchaseUnavailable });
304
+ await commerce.loadPrices(items.map((item, index) => ({ reference: { kind: "storesProduct", id: item!.catalogItemId }, commerceDisplayPrice: lines[index].price?.formattedAmount })));
305
+ const classification = classifyCart(items.map((item, index) => ({ route: commerce.pricing.view({ kind: "storesProduct", id: item!.catalogItemId }).route, quantity: lines[index].quantity ?? 1 })));
306
+ if (classification !== "regular") return derive({ ...state, checkoutHint: classification === "mixedRefused" ? copyParam("cart.iapSingleItemOnly", "Buy digital items individually, with quantity 1 and no options.") : iapPriceCopy("cart").purchaseUnavailable });
307
+ const products = await Promise.all(items.map(async item => item!.appId === WIX_STORES_CATALOG_ID ? (await getStoreProduct(item!.catalogItemId)).product : undefined));
308
+ if (products.some(product => product?.productType?.toLowerCase() !== "physical")) return derive({ ...state, checkoutHint: iapPriceCopy("cart").purchaseUnavailable });
309
+ const latest = await getCurrentCart();
310
+ if (signature(latest.lineItems ?? []) !== signature(lines)) return derive({ ...state, checkoutHint: copyParam("cart.changedBeforeCheckout", "Your cart changed. Review it before checking out.") });
311
+ validatedLines = lines;
312
+ } catch { return derive({ ...state, checkoutHint: iapPriceCopy("cart").purchaseUnavailable }); }
313
+
275
314
  if (!state.items.length) return derive({ ...state, checkoutHint: copyParam("cart.checkoutEmptyMessage", "Add an item before checkout.") });
276
315
  try {
277
316
  const checkout = await createCheckoutFromCurrentCart();
@@ -288,6 +327,14 @@ const svc = defineService<State, {
288
327
  appId: WIX_STORES_APP_ID,
289
328
  snapshotId,
290
329
  msid: await resolveMsid(),
330
+ mayCharge: async () => {
331
+ const latest = await getCurrentCart();
332
+ if (signature(latest.lineItems ?? []) !== signature(validatedLines)) return false;
333
+ return validatedLines.every(line => {
334
+ const reference = (line as WixCartLineItemDetail & { catalogReference?: Row["catalogReference"] }).catalogReference;
335
+ return !!reference && cartPricing.pricing.continueCheckoutGuard({ kind: "storesProduct", id: reference.catalogItemId }).allowed;
336
+ });
337
+ },
291
338
  cashierUrl: async () => {
292
339
  const redirect = await createRedirectSession({
293
340
  ecomCheckout: { checkoutId },
@@ -1,6 +1,7 @@
1
1
  import { fetchMemberBookings, type MemberBookingRow } from "../_shared/member-bookings.js";
2
2
  import { memberAccess, assertMemberAccess } from "../_shared/member-access.js";
3
- import { defineService } from "../_shared/home-service.js";
3
+ import { defineService as registerHome } from "../_shared/home-service.js";
4
+ import { withIapPresentation } from "../_shared/iap-presentation.js";
4
5
  import { queryServices, type WixService } from "@himalaya/wix-bookings";
5
6
  import { getPost, queryPosts, type WixPost } from "@himalaya/wix-blog";
6
7
  import { queryGroups, type WixGroup } from "@himalaya/wix-groups";
@@ -68,6 +69,7 @@ interface ProductFeature {
68
69
  /** Device-local catalog saves are useful to visitors too; they are never
69
70
  * presented as account-synced data or mixed with member-only bookings. */
70
71
  interface SavedProductCard {
72
+ commercePrice: string;
71
73
  id: string;
72
74
  name: string;
73
75
  price: string;
@@ -573,7 +575,7 @@ function postCard(post: WixPost): PostCard {
573
575
  function savedProductCard(product: SavedProduct): SavedProductCard {
574
576
  const name = cleanTitle(product.name) || copyParam("home.savedProductFallbackName", "Saved item");
575
577
  const image = product.image ? wixMediaUrl(product.image) : "";
576
- return { id: product.id, name, price: product.price, image, initial: initials(name), hasImage: !!image, noImage: !image, detailScreenId: "himi_product" };
578
+ return { id: product.id, name, price: product.price, commercePrice: product.price, image, initial: initials(name), hasImage: !!image, noImage: !image, detailScreenId: "himi_product" };
577
579
  }
578
580
 
579
581
  function savedPostCard(post: WixPost): SavedPostCard {
@@ -1111,6 +1113,18 @@ async function refresh(state: State, arg?: unknown): Promise<State | undefined>
1111
1113
  weeklyClassCount: current.weeklyClassCount, weeklyClassGoalFraction: current.weeklyClassGoalFraction, weeklyGoalLabel: current.weeklyGoalLabel });
1112
1114
  }
1113
1115
 
1116
+ const defineService = withIapPresentation<State>({
1117
+ register: registerHome, prefix: "home", transientKeys: ["featuredProducts", "savedProducts", "heroPrice"], contentActions: ["refresh"],
1118
+ items: state => [
1119
+ ...state.featuredProducts.map(row => ({ reference: { kind: "storesProduct", id: row.id }, commerceDisplayPrice: row.price })),
1120
+ ...state.savedProducts.map(row => ({ reference: { kind: "storesProduct", id: row.id }, commerceDisplayPrice: row.commercePrice })),
1121
+ ],
1122
+ present: (state, price) => derive({ ...state,
1123
+ featuredProducts: state.featuredProducts.map(row => ({ ...row, price: price(row.id) })),
1124
+ savedProducts: state.savedProducts.map(row => ({ ...row, price: price(row.id, row.commercePrice) })),
1125
+ }),
1126
+ });
1127
+
1114
1128
  const svc = defineService<State, {
1115
1129
  // `State | undefined`, matching the implementation: `refresh` returns undefined on a
1116
1130
  // STALE generation (`if (gen !== refreshGen) return undefined`) so the runtime keeps
@@ -1,3 +1,6 @@
1
+ import { createIapCommerce, classifyCart } from "@himalaya/iap-checkout";
2
+ import { getStoreProduct } from "../_shared/himi-store.js";
3
+ import { iapPriceCopy } from "../_shared/iap-worker.js";
1
4
  import { defineService } from "@himalaya/state";
2
5
  import { orderMember, assertOrderMember, ownedOrderCache, orderAccessDenied, orderAccessChanged, type OrderMember } from "../_shared/member-orders.js";
3
6
  import { addToCurrentCart, getOrder, searchOrders, type WixOrder } from "@himalaya/wix-ecom";
@@ -38,6 +41,8 @@ interface LineRow {
38
41
 
39
42
  /** What reorder re-adds: catalog item id + quantity per line (internal). */
40
43
  interface ReorderRef {
44
+ appId?: string;
45
+ options?: Record<string, unknown>;
41
46
  catalogItemId: string;
42
47
  quantity: number;
43
48
  }
@@ -164,10 +169,10 @@ function lineRows(order: WixOrder): LineRow[] {
164
169
  /** Catalog references for the reorder CTA — only store-catalog lines re-add. */
165
170
  function reorderRefs(order: WixOrder): ReorderRef[] {
166
171
  return (order.lineItems ?? []).flatMap((item) => {
167
- const ref = (item as { catalogReference?: { catalogItemId?: string } }).catalogReference;
172
+ const ref = (item as { catalogReference?: { catalogItemId?: string; appId?: string; options?: Record<string, unknown> } }).catalogReference;
168
173
  const catalogItemId = ref?.catalogItemId;
169
174
  return typeof catalogItemId === "string" && catalogItemId
170
- ? [{ catalogItemId, quantity: item.quantity || 1 }]
175
+ ? [{ catalogItemId, appId: ref.appId, ...(ref.options ? { options: ref.options } : {}), quantity: item.quantity || 1 }]
171
176
  : [];
172
177
  });
173
178
  }
@@ -307,6 +312,7 @@ async function load(state: State, arg?: unknown): Promise<State | undefined> {
307
312
  */
308
313
  const FOLLOW_TTL_SECONDS = 24 * 60 * 60;
309
314
 
315
+ const reorderPricing = createIapCommerce();
310
316
  const svc = defineService<State, {
311
317
  load: (state: State, arg?: unknown) => Promise<State | undefined>;
312
318
  retry: (state: State) => Promise<State | undefined>;
@@ -401,8 +407,23 @@ const svc = defineService<State, {
401
407
  catch { return version === loadVersion ? derive({ ...clearOrder(state), status: "error", emptyText: copyParam("order.signInEmptyText", "Sign in to view this order.") }) : undefined; }
402
408
  if (version !== loadVersion) return undefined;
403
409
  if (!state.reorderRefs.length) return derive({ ...state, reorderMessage: copyParam("order.reorderNoneMessage", "Nothing on this order can be re-added.") });
410
+ try {
411
+ const refs = state.reorderRefs;
412
+ if (refs.some(ref => ref.appId !== "215238eb-22a5-4c36-9e7b-e7c08025e04e")) return derive({ ...state, reorderMessage: iapPriceCopy("order").purchaseUnavailable });
413
+ await reorderPricing.loadPrices(refs.map(ref => ({ reference: { kind: "storesProduct", id: ref.catalogItemId } })));
414
+ const route = classifyCart(refs.map(ref => ({ route: reorderPricing.pricing.view({ kind: "storesProduct", id: ref.catalogItemId }).route, quantity: ref.quantity })));
415
+ const products = await Promise.all(refs.map(async ref => (await getStoreProduct(ref.catalogItemId)).product));
416
+ if (route !== "regular" || products.some(product => product?.productType?.toLowerCase() !== "physical")) return derive({ ...state, reorderMessage: iapPriceCopy("order").purchaseUnavailable });
417
+ await assertOrderMember(member);
418
+ if (version !== loadVersion) return undefined;
419
+ } catch {
420
+ if (version !== loadVersion) return undefined;
421
+ try { await assertOrderMember(member); }
422
+ catch { return derive({ ...clearOrder(state), status: "error", emptyText: copyParam("order.signInEmptyText", "Sign in to view this order.") }); }
423
+ return derive({ ...state, reorderMessage: iapPriceCopy("order").purchaseUnavailable });
424
+ }
404
425
  const results = await Promise.allSettled(
405
- state.reorderRefs.map((ref) => addToCurrentCart(ref.catalogItemId, ref.quantity)),
426
+ state.reorderRefs.map((ref) => addToCurrentCart(ref.catalogItemId, ref.quantity, ref.options)),
406
427
  );
407
428
  // A pending mutation must never restore an older order after navigation or sign-out.
408
429
  if (version !== loadVersion) return undefined;