cabloy 5.1.122 → 5.1.124

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 (244) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +13 -3
  3. package/.claude/skills/cabloy-workflow/SKILL.md +6 -0
  4. package/.claude/skills/cabloy-workflow/evals/evals.json +6 -0
  5. package/CHANGELOG.md +24 -0
  6. package/CLAUDE.md +1 -1
  7. package/cabloy-docs/.vitepress/config.mjs +4 -0
  8. package/cabloy-docs/frontend/form-layout-guide.md +29 -7
  9. package/cabloy-docs/fullstack/parallel-worktree-environment.md +87 -0
  10. package/e2e/scripts/e2e.ts +13 -1
  11. package/e2e/scripts/startE2eVona.ts +4 -1
  12. package/e2e/specs/a-commerce/commerce.spec.ts +129 -32
  13. package/package.json +2 -1
  14. package/playwright-report/data/24ac790ec112339c76cfb0c8e2c2539c2f953a0b.zip +0 -0
  15. package/playwright-report/data/2fddcabcdbd0b695061a565acfff243cb3a4f5e3.zip +0 -0
  16. package/playwright-report/data/4738038d9e861d63fd25937c9a3570934786a62f.zip +0 -0
  17. package/playwright-report/data/72f99c7da67c7f6ea53cab7d8681798b70d79a10.md +222 -0
  18. package/playwright-report/data/7b9684a7c793e9798b898f8ff50177b660a7a733.md +222 -0
  19. package/playwright-report/data/8b31595f5f1cc9d23863af51a592e6b90dcbc4ce.zip +0 -0
  20. package/playwright-report/data/95b578407a68806202dda917a61c50c750bd285b.md +222 -0
  21. package/playwright-report/data/ad960b3a58f224f5b4cad04a24067600064ffb7b.md +222 -0
  22. package/playwright-report/index.html +90 -0
  23. package/playwright-report/trace/assets/codeMirrorModule-LEHpjmcn.js +32 -0
  24. package/playwright-report/trace/assets/defaultSettingsView-BNmKHKpQ.js +264 -0
  25. package/playwright-report/trace/assets/urlMatch-BYQrIQwR.js +1 -0
  26. package/playwright-report/trace/codeMirrorModule.DYBRYzYX.css +1 -0
  27. package/playwright-report/trace/codicon.DCmgc-ay.ttf +0 -0
  28. package/playwright-report/trace/defaultSettingsView.CjdS-WJx.css +1 -0
  29. package/playwright-report/trace/index.CzXZzn5A.css +1 -0
  30. package/playwright-report/trace/index.DMMX1gXU.js +2 -0
  31. package/playwright-report/trace/index.html +44 -0
  32. package/playwright-report/trace/manifest.webmanifest +16 -0
  33. package/playwright-report/trace/playwright-logo.svg +9 -0
  34. package/playwright-report/trace/snapshot.html +10 -0
  35. package/playwright-report/trace/snapshot.v8KI4P3m.js +2 -0
  36. package/playwright-report/trace/sw.bundle.js +5 -0
  37. package/playwright-report/trace/uiMode.BZQ54Kgt.css +1 -0
  38. package/playwright-report/trace/uiMode.Ut8wwJNp.js +6 -0
  39. package/playwright-report/trace/uiMode.html +18 -0
  40. package/playwright-report/trace/xtermModule.DYP7pi_n.css +32 -0
  41. package/vona/env/.env +12 -0
  42. package/vona/env/.env.test +7 -0
  43. package/vona/packages-cli/cli/package.json +1 -1
  44. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}Create.tsx_ +19 -1
  45. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}Update.tsx_ +19 -1
  46. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}View.tsx_ +19 -1
  47. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/3-en-us.ts +6 -1
  48. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/4-zh-cn.ts +6 -1
  49. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}Create.tsx_ +19 -1
  50. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}Update.tsx_ +19 -1
  51. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}View.tsx_ +19 -1
  52. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/3-en-us.ts +6 -1
  53. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/4-zh-cn.ts +6 -1
  54. package/vona/packages-cli/cli-set-api/package.json +1 -1
  55. package/vona/pnpm-lock.yaml +255 -12
  56. package/vona/pnpm-workspace.yaml +1 -1
  57. package/vona/src/suite/a-commerce/modules/commerce-payment/package.json +4 -1
  58. package/vona/src/suite/a-commerce/modules/commerce-payment/src/.metadata/index.ts +45 -3
  59. package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/meta.index.ts +1 -0
  60. package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/meta.version.ts +25 -1
  61. package/vona/src/suite/a-commerce/modules/commerce-payment/src/bean/payScene.commerceOrder.ts +20 -0
  62. package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/paymentAttempt.tsx +9 -0
  63. package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/paymentAudit.tsx +5 -2
  64. package/vona/src/suite/a-commerce/modules/commerce-payment/src/entity/refundAttempt.tsx +6 -0
  65. package/vona/src/suite/a-commerce/modules/commerce-payment/src/index.ts +2 -0
  66. package/vona/src/suite/a-commerce/modules/commerce-payment/src/lib/index.ts +1 -0
  67. package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/commercePayScene.ts +54 -0
  68. package/vona/src/suite/a-commerce/modules/commerce-payment/src/service/paymentAttempt.ts +7 -1
  69. package/vona/src/suite/a-commerce/modules/commerce-payment/src/types/index.ts +1 -0
  70. package/vona/src/suite/a-commerce/modules/commerce-payment/test/paymentAttempt.test.ts +21 -0
  71. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/.metadata/index.ts +30 -0
  72. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/config/locale/en-us.ts +4 -0
  73. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/config/locale/zh-cn.ts +4 -0
  74. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/dto/couponTemplateCreate.tsx +88 -1
  75. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/dto/couponTemplateUpdate.tsx +44 -1
  76. package/vona/src/suite/a-commerce/modules/commerce-promotion/src/dto/couponTemplateView.tsx +72 -1
  77. package/vona/src/suite/a-commerce/modules/commerce-promotion/test/couponTemplateFormLayout.test.ts +121 -0
  78. package/vona/src/suite/a-commerce/modules/commerce-siteweb/src/bean/ssrMenu.home.ts +4 -1
  79. package/vona/src/suite/a-commerce/modules/commerce-trade/package.json +4 -1
  80. package/vona/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +0 -21
  81. package/vona/src/suite/a-commerce/modules/commerce-trade/src/dto/checkoutResult.tsx +3 -0
  82. package/vona/src/suite/a-commerce/modules/commerce-trade/src/service/order.ts +79 -0
  83. package/vona/src/suite/a-commerce/modules/commerce-trade/test/orderSnapshot.test.ts +227 -0
  84. package/vona/src/suite/a-commerce/modules/commerce-trade/test/paymentOutcome.test.ts +121 -13
  85. package/vona/src/suite/a-commerce/modules/commerce-trade/test/refundLifecycle.test.ts +312 -0
  86. package/vona/src/suite-vendor/a-pay/modules/a-pay/package.json +76 -0
  87. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/index.ts +842 -0
  88. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/this.ts +2 -0
  89. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/bean.payProvider.ts +92 -0
  90. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/bean.payScene.ts +105 -0
  91. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.index.ts +28 -0
  92. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.redlock.ts +11 -0
  93. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/meta.version.ts +113 -0
  94. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/queue.outboxDispatch.ts +31 -0
  95. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/bean/schedule.outboxDispatch.ts +11 -0
  96. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/controller/paymentSession.ts +55 -0
  97. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/controller/webhook.ts +47 -0
  98. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/dto/paymentSessionStart.tsx +8 -0
  99. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/dto/paymentSessionView.tsx +69 -0
  100. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/dto/webhookReceipt.tsx +12 -0
  101. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/outboxEvent.tsx +46 -0
  102. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/paymentAudit.tsx +36 -0
  103. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/paymentSession.tsx +72 -0
  104. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/providerOperation.tsx +54 -0
  105. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/refundOperation.tsx +37 -0
  106. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/entity/webhookInbox.tsx +59 -0
  107. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/index.ts +3 -0
  108. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/lib/index.ts +2 -0
  109. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/lib/payProvider.ts +7 -0
  110. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/lib/payScene.ts +7 -0
  111. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/outboxEvent.ts +10 -0
  112. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/paymentAudit.ts +10 -0
  113. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/paymentSession.ts +10 -0
  114. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/providerOperation.ts +10 -0
  115. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/refundOperation.ts +10 -0
  116. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/model/webhookInbox.ts +10 -0
  117. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/outbox.ts +156 -0
  118. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentSession.ts +278 -0
  119. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/providerOperation.ts +5 -0
  120. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/service/webhook.ts +127 -0
  121. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/index.ts +3 -0
  122. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payProvider.ts +74 -0
  123. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payScene.ts +63 -0
  124. package/vona/src/suite-vendor/a-pay/modules/a-pay/src/types/payment.ts +151 -0
  125. package/vona/src/suite-vendor/a-pay/modules/a-pay/test/outbox.test.ts +209 -0
  126. package/vona/src/suite-vendor/a-pay/modules/a-pay/test/paymentSession.test.ts +153 -0
  127. package/vona/src/suite-vendor/a-pay/modules/a-pay/test/webhook.test.ts +278 -0
  128. package/vona/src/suite-vendor/a-pay/modules/a-pay/tsconfig.build.json +11 -0
  129. package/vona/src/suite-vendor/a-pay/modules/a-pay/tsconfig.json +7 -0
  130. package/vona/src/suite-vendor/a-pay/modules/pay-mock/package.json +55 -0
  131. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/.metadata/index.ts +182 -0
  132. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/.metadata/this.ts +2 -0
  133. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/bean/payProvider.mock.ts +149 -0
  134. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/controller/mockPayment.ts +25 -0
  135. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/dto/mockPaymentComplete.tsx +13 -0
  136. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/dto/mockPaymentReceipt.tsx +16 -0
  137. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/index.ts +3 -0
  138. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/lib/index.ts +1 -0
  139. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/service/payMock.ts +78 -0
  140. package/vona/src/suite-vendor/a-pay/modules/pay-mock/src/types/index.ts +10 -0
  141. package/vona/src/suite-vendor/a-pay/modules/pay-mock/test/payMock.test.ts +141 -0
  142. package/vona/src/suite-vendor/a-pay/modules/pay-mock/tsconfig.build.json +11 -0
  143. package/vona/src/suite-vendor/a-pay/modules/pay-mock/tsconfig.json +7 -0
  144. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/package.json +55 -0
  145. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/.metadata/index.ts +62 -0
  146. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/.metadata/this.ts +2 -0
  147. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/bean/payProvider.paypal.ts +91 -0
  148. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/index.ts +3 -0
  149. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/lib/index.ts +1 -0
  150. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/src/types/index.ts +9 -0
  151. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/tsconfig.build.json +11 -0
  152. package/vona/src/suite-vendor/a-pay/modules/pay-paypal/tsconfig.json +7 -0
  153. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/package.json +55 -0
  154. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/.metadata/index.ts +62 -0
  155. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/.metadata/this.ts +2 -0
  156. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/bean/payProvider.stripe.ts +88 -0
  157. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/index.ts +2 -0
  158. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/src/types/index.ts +8 -0
  159. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/tsconfig.build.json +11 -0
  160. package/vona/src/suite-vendor/a-pay/modules/pay-stripe/tsconfig.json +7 -0
  161. package/vona/src/suite-vendor/a-pay/package.json +15 -0
  162. package/vona/src/suite-vendor/a-pay/tsconfig.base.json +4 -0
  163. package/vona/src/suite-vendor/a-pay/tsconfig.json +19 -0
  164. package/zova/env/.env +1 -0
  165. package/zova/openapi.config.ts +5 -1
  166. package/zova/package.original.json +1 -1
  167. package/zova/packages-cli/cli/package.json +2 -2
  168. package/zova/packages-cli/cli-set-front/cli/templates/openapi/config/boilerplate/project/openapi.config.ts +5 -1
  169. package/zova/packages-cli/cli-set-front/package.json +1 -1
  170. package/zova/packages-cli/cli-set-front/src/lib/bean/cli.openapi.generate.ts +8 -0
  171. package/zova/packages-cli/cli-set-front/src/lib/common/cliCreatePage.ts +7 -24
  172. package/zova/packages-cli/cli-set-front/src/lib/common/utils.ts +10 -1
  173. package/zova/packages-utils/zova-jsx/package.json +2 -2
  174. package/zova/packages-zova/zova/package.json +3 -3
  175. package/zova/packages-zova/zova-core/package.json +1 -1
  176. package/zova/packages-zova/zova-core/src/types/interface/component.ts +5 -0
  177. package/zova/pnpm-lock.yaml +96 -26
  178. package/zova/src/front/config/config/config.cabloyCommerce.ts +1 -1
  179. package/zova/src/suite/a-commerce/modules/commerce-promotion/src/api/openapi/schemas.ts +110 -12
  180. package/zova/src/suite/a-commerce/modules/commerce-promotion/src/api/openapi/types.ts +1292 -160
  181. package/zova/src/suite/a-commerce/modules/commerce-trade/cli/openapi.config.ts +0 -1
  182. package/zova/src/suite/a-commerce/modules/commerce-trade/package.json +4 -1
  183. package/zova/src/suite/a-commerce/modules/commerce-trade/src/.metadata/index.ts +1 -47
  184. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/schemas.ts +23 -8
  185. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/openapi/types.ts +249 -62
  186. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/checkout/controller.tsx +5 -1
  187. package/zova/src/suite/a-commerce/modules/commerce-trade/src/page/payment/controller.tsx +152 -39
  188. package/zova/src/suite/a-commerce/modules/commerce-trade/src/routes.ts +1 -1
  189. package/zova/src/suite-vendor/a-pay/modules/a-pay/cli/openapi.config.ts +9 -0
  190. package/zova/src/suite-vendor/a-pay/modules/a-pay/package.json +53 -0
  191. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/component/paymentNextAction.ts +31 -0
  192. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/index.ts +217 -0
  193. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/.metadata/this.ts +2 -0
  194. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/baseURL.ts +5 -0
  195. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/index.ts +3 -0
  196. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/schemas.ts +584 -0
  197. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/openapi/types.ts +9324 -0
  198. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/api/paymentSession.ts +52 -0
  199. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/apiSchema/paymentSession.ts +20 -0
  200. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/component/paymentNextAction/controller.tsx +76 -0
  201. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/index.ts +3 -0
  202. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/lib/index.ts +1 -0
  203. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/model/paymentSession.ts +32 -0
  204. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/service/paymentCoordinator.ts +13 -0
  205. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/types/index.ts +1 -0
  206. package/zova/src/suite-vendor/a-pay/modules/a-pay/src/types/payment.ts +25 -0
  207. package/zova/src/suite-vendor/a-pay/modules/a-pay/tsconfig.build.json +13 -0
  208. package/zova/src/suite-vendor/a-pay/modules/a-pay/tsconfig.json +5 -0
  209. package/zova/src/suite-vendor/a-pay/modules/pay-mock/cli/openapi.config.ts +9 -0
  210. package/zova/src/suite-vendor/a-pay/modules/pay-mock/package.json +53 -0
  211. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/.metadata/index.ts +140 -0
  212. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/.metadata/this.ts +2 -0
  213. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/openapi/baseURL.ts +5 -0
  214. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/openapi/index.ts +3 -0
  215. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/openapi/schemas.ts +584 -0
  216. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/openapi/types.ts +9324 -0
  217. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/api/payMockPayment.ts +34 -0
  218. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/apiSchema/payMockPayment.ts +13 -0
  219. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/index.ts +1 -0
  220. package/zova/src/suite-vendor/a-pay/modules/pay-mock/src/model/payMockPayment.ts +20 -0
  221. package/zova/src/suite-vendor/a-pay/modules/pay-mock/tsconfig.build.json +13 -0
  222. package/zova/src/suite-vendor/a-pay/modules/pay-mock/tsconfig.json +5 -0
  223. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/package.json +52 -0
  224. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/src/.metadata/index.ts +26 -0
  225. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/src/.metadata/this.ts +2 -0
  226. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/src/index.ts +1 -0
  227. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/tsconfig.build.json +13 -0
  228. package/zova/src/suite-vendor/a-pay/modules/pay-paypal/tsconfig.json +5 -0
  229. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/package.json +52 -0
  230. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/src/.metadata/index.ts +26 -0
  231. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/src/.metadata/this.ts +2 -0
  232. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/src/index.ts +1 -0
  233. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/tsconfig.build.json +13 -0
  234. package/zova/src/suite-vendor/a-pay/modules/pay-stripe/tsconfig.json +5 -0
  235. package/zova/src/suite-vendor/a-pay/package.json +15 -0
  236. package/zova/src/suite-vendor/a-pay/tsconfig.base.json +4 -0
  237. package/zova/src/suite-vendor/a-pay/tsconfig.json +4 -0
  238. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
  239. package/zova/src/suite-vendor/a-zova/package.json +2 -2
  240. package/vona/src/suite/a-commerce/modules/commerce-trade/src/controller/payment.ts +0 -23
  241. package/zova/src/suite/a-commerce/modules/commerce-trade/src/api/commerceTradePayment.ts +0 -36
  242. package/zova/src/suite/a-commerce/modules/commerce-trade/src/apiSchema/commerceTradePayment.ts +0 -13
  243. package/zova/src/suite/a-commerce/modules/commerce-trade/src/model/payment.ts +0 -25
  244. /package/vona/patches/{zova-core@5.1.79.patch → zova-core@5.1.81.patch} +0 -0
package/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.122
1
+ 5.1.124
@@ -36,6 +36,14 @@ Then classify the request:
36
36
 
37
37
  Default to frontend-first. Only escalate mentally to a broader fullstack workflow when the frontend task obviously crosses the contract boundary.
38
38
 
39
+ ### Decide a form layout or ask
40
+
41
+ When the task changes a schema-driven form, inspect the generated baseline, operation-specific DTO/schema, nearby Cabloy forms, the active edition and UI adapter, field relationships, and the user's stated workflow before choosing a layout.
42
+
43
+ Proceed autonomously when that evidence establishes the form purpose and field relationships. Choose the smallest fitting structure: no explicit layout for a short conventional form, flow for compact filters, Grid for responsive related fields, a group for one meaningful business boundary, or tabs for genuinely independent domains or workflows. State the consequential business assumptions with the implementation.
44
+
45
+ Ask one focused business question only when the layout would encode an unresolved semantic or authority decision: whether areas are independent or one workflow, which audience or task has priority, whether a form is a compact filter or a full entry workflow, or whether staged responsibilities are intended. Do not ask merely because several visual arrangements are technically valid.
46
+
39
47
  If the user is still deciding a new business-domain boundary or suite/module naming, use the root `cabloy-domain-planning` skill before scaffolding.
40
48
 
41
49
  If the task is really a broad cross-stack workflow, consider whether the root `cabloy-workflow` skill is the better primary router.
@@ -205,6 +213,7 @@ Especially relevant pages include:
205
213
  - `cabloy-docs/frontend/route-alias-guide.md`
206
214
  - `cabloy-docs/frontend/navigation-guards-guide.md`
207
215
  - `cabloy-docs/frontend/component-guide.md`
216
+ - `cabloy-docs/frontend/form-layout-guide.md` for schema-driven field placement, Grid/flow selection, groups, tabs, or embedded filter actions
208
217
  - `cabloy-docs/frontend/component-props-guide.md`
209
218
  - `cabloy-docs/frontend/component-v-model-guide.md`
210
219
  - `cabloy-docs/frontend/generic-component-guide.md`
@@ -249,8 +258,9 @@ When helpful, structure the response around these points:
249
258
  1. detected edition
250
259
  2. frontend-first or clearly fullstack-sensitive classification
251
260
  3. recommended Zova CLI path
252
- 4. required frontend follow-up layers to check
253
- 5. optional backend-contract reminder if applicable
254
- 6. verification steps
261
+ 4. form-layout decision, consequential business assumptions, and any user-confirmed boundary when the task is layout-sensitive
262
+ 5. required frontend follow-up layers to check
263
+ 6. optional backend-contract reminder if applicable
264
+ 7. verification steps
255
265
 
256
266
  Keep the response practical. The value of this skill is turning Cabloy frontend requests into the right generation + refactor + verification workflow, not writing more prose than necessary.
@@ -98,6 +98,12 @@ For deeper reference material, read:
98
98
 
99
99
  The reason is simple: these files are where Cabloy already encodes its real workflows.
100
100
 
101
+ ### Parallel worktree environment setup
102
+
103
+ When a request involves a second worktree, concurrent Vona/Zova development, isolated ordinary tests, or managed clean E2E, classify it as fullstack workflow setup. Read [Parallel Worktree Environment](../../../cabloy-docs/fullstack/parallel-worktree-environment.md) for the canonical Cabloy Basic recipe.
104
+
105
+ Before applying the broad `.env.local` recipe, check for more-specific `.env.*.local` overrides. Keep Vona and Zova `APP_NAME` values aligned, use the required matching API target, and add database, Redis, external-service, mock, or SSR-preview isolation only when the work actually uses those shared resources. For Cabloy Start, inspect the active Start repository rather than reusing the Basic recipe unchanged.
106
+
101
107
  ## Step 4: Prefer CLI-first workflows
102
108
 
103
109
  Whenever the task maps to an existing generator, initializer, refactor, or metadata command, prefer the CLI.
@@ -30,6 +30,12 @@
30
30
  "prompt": "I want to update the public guidance for Cabloy contributors about using CLI generators first, but I also think Claude should follow the same rule automatically. Where should each part of that knowledge live?",
31
31
  "expected_output": "Splits the guidance across public docs and Claude rules/skills appropriately, keeping maintainer rationale out of public docs unless needed.",
32
32
  "files": []
33
+ },
34
+ {
35
+ "id": 6,
36
+ "prompt": "I created a second Cabloy Basic worktree and need to run Vona, Zova, and test:e2e:basic:clean in parallel with the first worktree. Which environment files and exact variables should I configure without changing shared defaults?",
37
+ "expected_output": "Classifies this as fullstack parallel-worktree environment setup and routes to the canonical Parallel Worktree Environment guide. Checks for more-specific local overrides, uses Vona vona/env/.env.local with APP_NAME and SERVER_LISTEN_PORT, and Zova zova/env/.env.local with the same APP_NAME plus API_BASE_URL pointing to that Vona port, DEV_SERVER_PORT, and DEV_SERVER_HMR_PORT. Keeps SSR_API_BASE_URL inherited from API_BASE_URL unless a more-specific override changes it, and adds database, Redis, external-service, mock, or SSR-preview settings only when those resources are actually used.",
38
+ "files": []
33
39
  }
34
40
  ]
35
41
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.124
4
+
5
+ ### Improvements
6
+
7
+ - Refresh the Zova lockfile and core patch dependencies.
8
+
9
+ ## 5.1.123
10
+
11
+ ### Features
12
+
13
+ - Add payment payload handling and A-Pay integration updates.
14
+ - Add OpenAPI configuration support.
15
+ - Add configurable E2E test port support.
16
+ - Update payment scene and session view capabilities.
17
+
18
+ ### Bug Fixes
19
+
20
+ - Import `VNode` as a type in `zova-core`.
21
+
22
+ ### Improvements
23
+
24
+ - Align commerce documentation with the acceptance gate.
25
+ - Update application components, SSR menu configuration, package metadata, and dependencies.
26
+
3
27
  ## 5.1.122
4
28
 
5
29
  ### Features
package/CLAUDE.md CHANGED
@@ -58,7 +58,7 @@ Before inventing a custom implementation path:
58
58
  - Default to establishing such query state during render. Use `disableSuspenseOnInit: true` only for relatively stable query-backed state when you want to skip the init-time `query.suspense()` kick; it does not prevent query creation, fetches, or hydration-time rendering. If strict readiness is needed later, wait explicitly at the interaction boundary.
59
59
  - In SSR, keep server HTML and the client's hydration-time initial render equivalent. When server rendering intentionally omits private, cookie-unavailable, or browser-only state, keep the same neutral shell or placeholder through hydration and defer its query/load/render branch to an explicit post-hydration, admission, mounted, or interaction boundary.
60
60
  - Keep repo-wide AI rules in `CLAUDE.md` short and durable; put branching Zova analysis workflows in `.claude/skills/`.
61
- - Do not modify `APP_NAME` in environment files unless the user explicitly requests it. When another worktree holds shared test resources, wait and retry later rather than changing application identity or related environment settings.
61
+ - Do not modify shared environment identity or ports merely to bypass a busy resource. For intentional parallel worktree setup, follow `cabloy-docs/fullstack/parallel-worktree-environment.md`; otherwise wait for the shared resource or ask the user.
62
62
  - For SSR theme-sensitive frontend work, detect the active edition marker and UI library before making assumptions. Cabloy Basic currently means DaisyUI + Tailwind CSS assumptions; Cabloy Start currently means Vuetify assumptions.
63
63
  - In Web SSR without cookie-backed theme resolution, do not treat server reads of `$theme.dark`, `$theme.darkMode`, or `$token` as final browser truth. Keep theme-sensitive SSR branching hydration-tolerant or defer final theme-sensitive decisions to the client.
64
64
  - Do not assume Cabloy Basic and Cabloy Start use the same adapter-level SSR theme handoff. Verify the active theme handler and client hydration path before changing SSR theme behavior.
@@ -75,6 +75,10 @@ const fullstackGroups = [
75
75
  text: 'Tooling & Workflow',
76
76
  items: [
77
77
  { text: 'CLI', link: '/fullstack/cli' },
78
+ {
79
+ text: 'Parallel Worktree Environment',
80
+ link: '/fullstack/parallel-worktree-environment',
81
+ },
78
82
  { text: 'VS Code Extensions', link: '/fullstack/vscode-extensions' },
79
83
  ],
80
84
  },
@@ -7,6 +7,27 @@ This guide covers the **structural** layout contract authored in DTO metadata an
7
7
  > [!TIP]
8
8
  > Cabloy Basic implements the renderer described here with DaisyUI and Tailwind CSS. The `formLayout` contract and its resolver are shared Zova surfaces, but group, Grid, flow, and tab presentation are Basic-specific. Do not assume Cabloy Start uses identical markup or styling.
9
9
 
10
+ ## Choose the layout from the business form
11
+
12
+ Start with the user's task, information hierarchy, and audience—not with the available layout node types or the number of fields. The generated CRUD layout is a valid baseline to review and refine, not a requirement to preserve unchanged.
13
+
14
+ Choose the smallest structure that communicates the business form:
15
+
16
+ | Business shape | Recommended structure |
17
+ | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
18
+ | A short, conventional form whose schema order is already clear | No explicit `formLayout`, or a flat field list when placement must be explicit |
19
+ | One meaningful business subsection | A `group` with a title that explains the boundary |
20
+ | Related fields users complete or compare together and that need responsive density or spans | A Grid `section`, normally inside the relevant group |
21
+ | Compact scan-and-act controls, especially a list filter with Search/Reset | A `section` with `layout: 'flow'` and one embedded filter-actions block |
22
+ | Genuinely separate business domains or workflows | `tabs` with one `tab` per independent area |
23
+ | A nested detail collection | One normal `field`; its field renderer owns the detail UI |
24
+
25
+ Do not select tabs merely because a form has many fields, and do not add a structural tree merely because the API supports one. A `group` communicates a semantic boundary; a `section` solves Grid or flow placement. Field renderer and wrapper requirements remain separate concerns, as explained in the next section.
26
+
27
+ Create, update, and view can reuse one layout when they present the same information hierarchy. When their tasks differ—for example, a focused creation workflow versus a review-oriented view—author scene-specific DTO layouts instead of forcing a shared tree. Likewise, choose entry-form and filter layouts independently: a compact filter is not a smaller entry form.
28
+
29
+ When the business grouping is unknown, keep the simpler layout or clarify the grouping before introducing groups or tabs. See the [complete entry-form example](#complete-entry-form-example) for a justified tabbed business form and the [complete filter-form example](#complete-filter-form-example) for the maintained compact filter convention.
30
+
10
31
  ## The layout layers are different
11
32
 
12
33
  Several APIs contain the word “layout,” but they own different concerns:
@@ -330,13 +351,14 @@ Here `formFieldLayout.inline: true` controls how each field wrapper is presented
330
351
 
331
352
  ## Authoring checklist
332
353
 
333
- 1. Start with DTO or resource metadata; do not hand-patch generated `.zova-rest` artifacts.
334
- 2. Use `formLayout` when the requirement is field placement, Grid or flow structure, groups, or tabs.
335
- 3. Use `layout`, `formFieldLayout`, `options`, or provider behaviors when the requirement is one field's wrapper or renderer.
336
- 4. Keep entry actions in page-entry toolbar blocks. Keep filter action semantics in `basic-page:blockFilterActions`; place that block inside Form Layout when the actions must share structural Grid or flow placement with fields.
337
- 5. For maintained Cabloy Basic list filters, prefer one inline flow section that explicitly lists every real filter-schema field in schema order and ends with one embedded `basic-page:blockFilterActions` block. Do not add virtual request fields, alter filter transforms, or combine it with a sibling action block.
338
- 6. Review field names against the scene-specific schema. Unlisted visible fields are appended; unknown and duplicate declarations are silently pruned from the rendered plan.
339
- 7. Use the smallest layout that communicates the form structure; reserve tabs for genuinely separate field groups.
354
+ 1. Identify the audience, task, and meaningful field groupings for the operation-specific DTO before choosing a structural tree.
355
+ 2. Start with DTO or resource metadata; do not hand-patch generated `.zova-rest` artifacts.
356
+ 3. Use no structural layout when schema order is sufficient. Otherwise use the smallest layout that communicates the business structure; reserve tabs for genuinely separate business domains or workflows.
357
+ 4. Use `formLayout` when the requirement is field placement, Grid or flow structure, groups, or tabs.
358
+ 5. Use `layout`, `formFieldLayout`, `options`, or provider behaviors when the requirement is one field's wrapper or renderer.
359
+ 6. Keep entry actions in page-entry toolbar blocks. Keep filter action semantics in `basic-page:blockFilterActions`; place that block inside Form Layout when the actions must share structural Grid or flow placement with fields.
360
+ 7. For maintained Cabloy Basic list filters, prefer one inline flow section that explicitly lists every real filter-schema field in schema order and ends with one embedded `basic-page:blockFilterActions` block. Do not add virtual request fields, alter filter transforms, or combine it with a sibling action block.
361
+ 8. Review field names against the scene-specific schema. Unlisted visible fields are appended; unknown and duplicate declarations are silently pruned from the rendered plan.
340
362
 
341
363
  ## Source-reading and verification path
342
364
 
@@ -0,0 +1,87 @@
1
+ # Parallel Worktree Environment
2
+
3
+ Use worktree-local environment overrides when two Cabloy Basic worktrees need to run ordinary local development or managed E2E checks at the same time.
4
+
5
+ This workflow creates an isolated local runtime without changing committed environment defaults. It covers normal Vona and Zova development, `npm run test`, and `npm run test:e2e:XXX:clean`. It does not automatically isolate every external dependency.
6
+
7
+ ## Before creating local overrides
8
+
9
+ 1. Confirm that the checkout is a separate worktree.
10
+ 2. Check `vona/env/` and `zova/env/` for applicable, more-specific `.env.*.local` files. A more-specific local override can take precedence over `.env.local`.
11
+ 3. Choose one unique worktree name and unused Vona, Zova, and HMR ports.
12
+
13
+ The repository ignores `**/env/.env*.local`, so these files remain local to the worktree and must not be committed.
14
+
15
+ ## Minimum configuration
16
+
17
+ Create these files in the new worktree.
18
+
19
+ ### Vona
20
+
21
+ `vona/env/.env.local`
22
+
23
+ ```dotenv
24
+ APP_NAME = cabloy-basic-my-worktree
25
+ SERVER_LISTEN_PORT = 7113
26
+ ```
27
+
28
+ ### Zova
29
+
30
+ `zova/env/.env.local`
31
+
32
+ ```dotenv
33
+ APP_NAME = cabloy-basic-my-worktree
34
+ API_BASE_URL = http://localhost:7113
35
+ DEV_SERVER_PORT = 9013
36
+ DEV_SERVER_HMR_PORT = 24693
37
+ ```
38
+
39
+ Keep these invariants:
40
+
41
+ - Vona and Zova use the same unique `APP_NAME`.
42
+ - `API_BASE_URL` points to the selected Vona `SERVER_LISTEN_PORT`.
43
+ - `SERVER_LISTEN_PORT`, `DEV_SERVER_PORT`, and `DEV_SERVER_HMR_PORT` are unique among concurrently running worktrees.
44
+
45
+ The base Zova environment defines `SSR_API_BASE_URL = $API_BASE_URL`, so SSR uses the same Vona target in the normal baseline. Add an explicit local `SSR_API_BASE_URL` only if an applicable, more-specific environment file changes that relationship.
46
+
47
+ ## What this isolates
48
+
49
+ A unique `APP_NAME` separates the application identity used by ordinary framework-managed local resources, including managed test database names and Redis-related key prefixes. Worktree-local generated files, runtime files, build output, and coverage output are already separated by their worktree paths.
50
+
51
+ The selected Vona, Zova, and HMR ports let the worktrees run their ordinary local processes concurrently. The matching Zova API URL prevents the frontend in one worktree from calling the Vona process in another.
52
+
53
+ This is sufficient for the normal local workflows:
54
+
55
+ ```bash
56
+ npm run dev
57
+ npm run dev:zova:admin
58
+ npm run dev:zova:web
59
+ npm run test
60
+ npm run test:e2e:basic:clean
61
+ ```
62
+
63
+ The managed clean E2E workflow reads Vona's effective local `SERVER_LISTEN_PORT`, then resets, starts, and targets that local runtime.
64
+
65
+ ## Add configuration only when the work uses it
66
+
67
+ Do not add isolation settings preemptively. Extend the local overrides only when the selected workflow actually shares one of these resources:
68
+
69
+ - an explicitly named database or a separately configured Redis target
70
+ - mail, payment, webhook, object-storage, or other external providers
71
+ - a standalone mock-build process, using `MOCK_BUILD_PORT`
72
+ - an SSR preview or production process, using `SSR_PROD_PORT`
73
+
74
+ For example, `APP_NAME` namespaces ordinary framework Redis keys, but it does not create a separate Redis server or automatically isolate custom, unprefixed keys. Likewise, an explicitly configured external database remains shared until it is configured separately.
75
+
76
+ ## Edition-aware note
77
+
78
+ This page describes the current Cabloy Basic workflow. For Cabloy Start, detect the active edition first and inspect the Start repository's current scripts, flavors, environment files, and generated-output paths before applying this recipe.
79
+
80
+ ## Read together with
81
+
82
+ - [Quick Start](/fullstack/quickstart)
83
+ - [Vona + Zova Integration](/fullstack/vona-zova-integration)
84
+ - [Runtime Environments and Flavors](/backend/runtime-and-flavors)
85
+ - [Environment and Config Guide](/frontend/environment-config-guide)
86
+ - [SSR Environment Variables](/frontend/ssr-env)
87
+ - [Mock Guide](/frontend/mock-guide)
@@ -1,8 +1,20 @@
1
+ import * as dotenv from '@cabloy/dotenv';
1
2
  import { dirname, resolve } from 'node:path';
2
3
  import { fileURLToPath } from 'node:url';
3
4
 
4
5
  export const E2E_ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
5
- export const E2E_PORT = 7102;
6
+
7
+ const vonaEnv = dotenv.loadEnvs(
8
+ { flavor: 'normal', mode: 'dev', local: 'local' },
9
+ resolve(E2E_ROOT_DIR, 'vona', 'env'),
10
+ '.env',
11
+ );
12
+ const e2ePort = Number(vonaEnv?.SERVER_LISTEN_PORT);
13
+ if (!Number.isInteger(e2ePort) || e2ePort <= 0 || e2ePort > 65535) {
14
+ throw new Error('Missing or invalid SERVER_LISTEN_PORT for local E2E target');
15
+ }
16
+
17
+ export const E2E_PORT = e2ePort;
6
18
  export const E2E_LOCAL_BASE_URL = `http://127.0.0.1:${E2E_PORT}`;
7
19
 
8
20
  const E2E_CONFIG_DIR = resolve(E2E_ROOT_DIR, 'e2e', 'config');
@@ -1,11 +1,14 @@
1
+ import type { ChildProcess } from 'node:child_process';
2
+
1
3
  import { spawn } from 'node:child_process';
2
4
 
3
5
  import { E2E_ROOT_DIR } from './e2e.ts';
4
6
 
5
- const child = spawn('npm', ['run', 'dev:one'], {
7
+ const child: ChildProcess = spawn('npm', ['run', 'dev:one'], {
6
8
  cwd: E2E_ROOT_DIR,
7
9
  detached: process.platform !== 'win32',
8
10
  stdio: 'inherit',
11
+ env: process.env,
9
12
  });
10
13
 
11
14
  const gracefulShutdownTimeout = 7000;
@@ -1,4 +1,4 @@
1
- import type { APIRequestContext, Browser, Page, TestInfo } from '@playwright/test';
1
+ import type { APIRequestContext, Browser, Locator, Page, TestInfo } from '@playwright/test';
2
2
 
3
3
  import { expect, test } from '@playwright/test';
4
4
 
@@ -35,6 +35,10 @@ function waitForApiResponse(page: Page, method: string, path: string | RegExp) {
35
35
  });
36
36
  }
37
37
 
38
+ function expectTableIdentity(value: unknown) {
39
+ expect(['string', 'number']).toContain(typeof value);
40
+ }
41
+
38
42
  function waitForAddressResponse(page: Page, method: string, path: string | RegExp) {
39
43
  return waitForApiResponse(page, method, path);
40
44
  }
@@ -43,6 +47,12 @@ function waitForAddressMine(page: Page) {
43
47
  return waitForAddressResponse(page, 'GET', addressMinePath);
44
48
  }
45
49
 
50
+ async function getAdminOrderRow(page: Page, orderId: number): Promise<Locator> {
51
+ const row = page.locator(`tr:has(td:first-child:text-is("${orderId}"))`);
52
+ await expect(row).toHaveCount(1);
53
+ return row;
54
+ }
55
+
46
56
  async function login(
47
57
  page: Page,
48
58
  path: string,
@@ -63,7 +73,14 @@ async function login(
63
73
  await expect(usernameInput).toHaveValue(username);
64
74
  await expect(passwordInput).toHaveValue(password);
65
75
  await expect(page.getByPlaceholder('Please input captcha')).not.toHaveValue('');
76
+ const loginResponse = waitForApiResponse(page, 'POST', '/api/home/user/passport/login');
66
77
  await page.getByRole('button', { name: 'Login', exact: true }).click();
78
+ const loginResponseValue = await loginResponse;
79
+ if (!loginResponseValue.ok()) {
80
+ throw new Error(
81
+ `login failed: ${loginResponseValue.status()} ${await loginResponseValue.text()}`,
82
+ );
83
+ }
67
84
  await expect(page).not.toHaveURL(/\/login(?:\?|$)/);
68
85
  }
69
86
  }
@@ -383,38 +400,43 @@ test(
383
400
  const checkoutResponseValue = await checkoutResponse;
384
401
  expect(checkoutResponseValue.ok()).toBeTruthy();
385
402
  const checkout = (await checkoutResponseValue.json()).data;
386
- expect(checkout.orderId).toEqual(expect.any(Number));
387
- expect(checkout.paymentAttemptId).toEqual(expect.any(Number));
403
+ expectTableIdentity(checkout.orderId);
404
+ expectTableIdentity(checkout.paymentAttemptId);
405
+ expectTableIdentity(checkout.paymentSessionId);
388
406
  expect(checkout.state).toBe('awaiting_payment');
389
407
  expect(checkout.paymentAttemptState).toBe('created');
390
408
  expect(checkout.currency).toBe('USD');
391
409
  expect(checkout.payableTotalCents).toBe(4599);
392
410
  await expect(page).toHaveURL(
393
- new RegExp(`/commerce/payment/${checkout.paymentAttemptId}(?:/|$)`),
411
+ new RegExp(`/commerce/payment/${checkout.paymentSessionId}/${checkout.orderId}(?:/|$)`),
394
412
  );
395
- await expect(page.getByRole('heading', { name: 'Mock payment' })).toBeVisible();
413
+ await expect(page.getByRole('heading', { name: 'Payment', exact: true })).toBeVisible();
396
414
 
397
- const paymentResponse = waitForApiResponse(
415
+ const startResponse = waitForApiResponse(
416
+ page,
417
+ 'POST',
418
+ new RegExp(`/api/payment-session/${checkout.paymentSessionId}/start$`),
419
+ );
420
+ await page.getByRole('button', { name: 'Start payment', exact: true }).click();
421
+ expect((await startResponse).ok()).toBeTruthy();
422
+ await expect(page.getByText('Payment is being prepared.')).toBeVisible();
423
+ await expect(page.getByRole('heading', { name: 'Mock payment simulator' })).toBeVisible();
424
+ const completeResponse = waitForApiResponse(
398
425
  page,
399
426
  'POST',
400
- new RegExp(`/api/commerce/trade/payment/${checkout.paymentAttemptId}/outcome$`),
427
+ new RegExp(`/api/pay/mock/payment-session/${checkout.paymentSessionId}/complete$`),
401
428
  );
429
+ await page.getByRole('button', { name: 'Payment succeeded', exact: true }).click();
430
+ expect((await completeResponse).ok()).toBeTruthy();
431
+ await expect(page).toHaveURL(new RegExp(`/commerce/order/${checkout.orderId}(?:/|$)`), {
432
+ timeout: 40_000,
433
+ });
402
434
  const orderDetailResponse = waitForApiResponse(
403
435
  page,
404
436
  'GET',
405
- new RegExp('/api/commerce/trade/order/viewMine/\\d+$'),
437
+ new RegExp(`/api/commerce/trade/order/viewMine/${checkout.orderId}$`),
406
438
  );
407
- await page.getByRole('button', { name: 'Payment succeeded', exact: true }).click();
408
- const paymentResponseValue = await paymentResponse;
409
- expect(paymentResponseValue.ok()).toBeTruthy();
410
- const payment = (await paymentResponseValue.json()).data;
411
- expect(payment.orderId).toBe(checkout.orderId);
412
- expect(payment.paymentAttemptId).toBe(checkout.paymentAttemptId);
413
- expect(payment.orderState).toBe('paid');
414
- expect(payment.paymentAttemptState).toBe('succeeded');
415
- expect(payment.currency).toBe('USD');
416
- expect(payment.payableTotalCents).toBe(4599);
417
- await expect(page).toHaveURL(new RegExp(`/commerce/order/${checkout.orderId}(?:/|$)`));
439
+ await page.reload({ waitUntil: 'load' });
418
440
  const orderDetailResponseValue = await orderDetailResponse;
419
441
  expect(orderDetailResponseValue.ok()).toBeTruthy();
420
442
  const order = (await orderDetailResponseValue.json()).data;
@@ -459,9 +481,7 @@ test(
459
481
  'data-zova-hydrated',
460
482
  'commerceAdmin',
461
483
  );
462
- const orderRow = adminPage
463
- .getByRole('cell', { name: String(checkout.orderId), exact: true })
464
- .locator('..');
484
+ const orderRow = await getAdminOrderRow(adminPage, checkout.orderId);
465
485
  await expect(orderRow).toBeVisible();
466
486
  const carrierInput = orderRow.getByPlaceholder('Carrier');
467
487
  const trackingNumberInput = orderRow.getByPlaceholder('Tracking number');
@@ -525,6 +545,70 @@ test(
525
545
  },
526
546
  );
527
547
 
548
+ test(
549
+ 'Payment cancellation: customer observes the cancelled order after verified provider confirmation',
550
+ { tag: ['@web', '@flow', '@payment'] },
551
+ async ({ browser, request }, testInfo) => {
552
+ test.setTimeout(60_000);
553
+ const { context, fixture, page, pageErrors } = await createAddressThroughCustomerPage(
554
+ browser,
555
+ request,
556
+ testInfo,
557
+ );
558
+ try {
559
+ await page.goto('/commerce', { waitUntil: 'load' });
560
+ await expect(page.locator('html')).toHaveAttribute('data-zova-hydrated', 'commerce');
561
+ const productLink = page.getByRole('link', { name: 'Pour-Over Coffee Set', exact: true });
562
+ await expect(productLink).toBeVisible();
563
+ await productLink.click();
564
+ await expect(page).toHaveURL(/\/commerce\/product\/\d+(?:\/|$)/);
565
+ await expect(page.getByRole('heading', { name: 'Pour-Over Coffee Set' })).toBeVisible();
566
+ const addResponse = waitForApiResponse(page, 'POST', '/api/commerce/trade/cart/items');
567
+ await page.getByRole('button', { name: 'Add to cart', exact: true }).click();
568
+ expect((await addResponse).ok()).toBeTruthy();
569
+ await page.getByRole('link', { name: /^Cart/ }).click();
570
+ await expect(page).toHaveURL(/\/commerce\/cart(?:\/|$)/);
571
+ await page.getByRole('link', { name: 'Checkout', exact: true }).click();
572
+ await expect(page).toHaveURL(/\/commerce\/checkout(?:\/|$)/);
573
+ const addressChoice = page.getByRole('radio', { name: new RegExp(fixture.recipientName) });
574
+ await expect(addressChoice).toBeVisible();
575
+ await addressChoice.check();
576
+ await page.getByRole('radio', { name: 'No coupon', exact: true }).check();
577
+ const checkoutResponse = waitForApiResponse(page, 'POST', '/api/commerce/trade/checkout');
578
+ await page.getByRole('button', { name: 'Create order', exact: true }).click();
579
+ const checkoutResponseValue = await checkoutResponse;
580
+ expect(checkoutResponseValue.ok()).toBeTruthy();
581
+ const checkout = (await checkoutResponseValue.json()).data;
582
+ expectTableIdentity(checkout.orderId);
583
+ expectTableIdentity(checkout.paymentSessionId);
584
+ const startResponse = waitForApiResponse(
585
+ page,
586
+ 'POST',
587
+ new RegExp(`/api/payment-session/${checkout.paymentSessionId}/start$`),
588
+ );
589
+ await page.getByRole('button', { name: 'Start payment', exact: true }).click();
590
+ expect((await startResponse).ok()).toBeTruthy();
591
+ await expect(page.getByText('Payment is being prepared.')).toBeVisible();
592
+ await expect(page.getByRole('heading', { name: 'Mock payment simulator' })).toBeVisible();
593
+ const cancelResponse = waitForApiResponse(
594
+ page,
595
+ 'POST',
596
+ new RegExp(`/api/pay/mock/payment-session/${checkout.paymentSessionId}/complete$`),
597
+ );
598
+ await page.getByRole('button', { name: 'Cancel payment', exact: true }).click();
599
+ expect((await cancelResponse).ok()).toBeTruthy();
600
+ await expect(page).toHaveURL(new RegExp(`/commerce/order/${checkout.orderId}(?:/|$)`), {
601
+ timeout: 40_000,
602
+ });
603
+ await expect(page.getByText('cancelled · $45.99')).toBeVisible();
604
+ await expect(page.getByRole('alert')).toHaveCount(0);
605
+ expect(pageErrors).toEqual([]);
606
+ } finally {
607
+ await context.close().catch(() => {});
608
+ }
609
+ },
610
+ );
611
+
528
612
  test(
529
613
  'Phase 60: customer requests and operator executes a whole-order refund',
530
614
  { tag: ['@web', '@admin', '@flow', '@payment', '@refund'] },
@@ -558,15 +642,30 @@ test(
558
642
  await page.getByRole('radio', { name: 'No coupon', exact: true }).check();
559
643
  const checkoutResponse = waitForApiResponse(page, 'POST', '/api/commerce/trade/checkout');
560
644
  await page.getByRole('button', { name: 'Create order', exact: true }).click();
561
- const checkout = (await (await checkoutResponse).json()).data;
562
- const paymentResponse = waitForApiResponse(
645
+ const checkoutResponseValue = await checkoutResponse;
646
+ expect(checkoutResponseValue.ok()).toBeTruthy();
647
+ const checkout = (await checkoutResponseValue.json()).data;
648
+ expectTableIdentity(checkout.orderId);
649
+ expectTableIdentity(checkout.paymentAttemptId);
650
+ expectTableIdentity(checkout.paymentSessionId);
651
+ await expect(page.getByRole('heading', { name: 'Payment', exact: true })).toBeVisible();
652
+ const startResponse = waitForApiResponse(
563
653
  page,
564
654
  'POST',
565
- new RegExp(`/api/commerce/trade/payment/${checkout.paymentAttemptId}/outcome$`),
655
+ new RegExp(`/api/payment-session/${checkout.paymentSessionId}/start$`),
656
+ );
657
+ await page.getByRole('button', { name: 'Start payment', exact: true }).click();
658
+ expect((await startResponse).ok()).toBeTruthy();
659
+ await expect(page.getByText('Payment is being prepared.')).toBeVisible();
660
+ await expect(page.getByRole('heading', { name: 'Mock payment simulator' })).toBeVisible();
661
+ const completeResponse = waitForApiResponse(
662
+ page,
663
+ 'POST',
664
+ new RegExp(`/api/pay/mock/payment-session/${checkout.paymentSessionId}/complete$`),
566
665
  );
567
666
  await page.getByRole('button', { name: 'Payment succeeded', exact: true }).click();
568
- expect((await paymentResponse).ok()).toBeTruthy();
569
- await expect(page.getByText('paid · $45.99')).toBeVisible();
667
+ expect((await completeResponse).ok()).toBeTruthy();
668
+ await expect(page.getByText('paid · $45.99')).toBeVisible({ timeout: 40_000 });
570
669
 
571
670
  const requestResponse = waitForApiResponse(
572
671
  page,
@@ -591,9 +690,7 @@ test(
591
690
  'data-zova-hydrated',
592
691
  'commerceAdmin',
593
692
  );
594
- const orderRow = adminPage
595
- .getByRole('cell', { name: String(checkout.orderId), exact: true })
596
- .locator('..');
693
+ const orderRow = await getAdminOrderRow(adminPage, checkout.orderId);
597
694
  await expect(orderRow).toBeVisible();
598
695
  await orderRow.getByPlaceholder('Decision reason').fill('E2E approval');
599
696
  await orderRow.getByRole('checkbox').check();
@@ -722,7 +819,7 @@ test(
722
819
  async ({ page, request }) => {
723
820
  const routes = [
724
821
  ['/commerce/checkout', '/checkout'],
725
- ['/commerce/payment/1', '/payment/1'],
822
+ ['/commerce/payment/1/1', '/payment/1/1'],
726
823
  ['/commerce/orders', '/orders'],
727
824
  ['/commerce/order/1', '/order/1'],
728
825
  ] as const;
@@ -736,7 +833,7 @@ test(
736
833
  expect(html, path).not.toContain('Payment succeeded');
737
834
  expect(html, path).not.toContain('Order #');
738
835
  expect(html, path).not.toContain('Checkout');
739
- expect(html, path).not.toContain('Mock payment');
836
+ expect(html, path).not.toContain('Mock payment simulator');
740
837
  expect(html, path).not.toContain('My orders');
741
838
 
742
839
  const pageErrors = collectPageErrors(page);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.122",
3
+ "version": "5.1.124",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -77,6 +77,7 @@
77
77
  "prepare": "husky"
78
78
  },
79
79
  "devDependencies": {
80
+ "@cabloy/dotenv": "^1.2.8",
80
81
  "@cabloy/lint": "^5.1.27",
81
82
  "@cabloy/process-helper": "^3.1.3",
82
83
  "@playwright/test": "^1.61.1",