payment-kit 1.29.1 → 1.29.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/api/dev.ts +41 -2
  2. package/api/hono.d.ts +42 -0
  3. package/api/node-sqlite.d.ts +12 -0
  4. package/api/src/bootstrap.ts +47 -0
  5. package/api/src/crons/base.ts +3 -3
  6. package/api/src/crons/currency.ts +1 -1
  7. package/api/src/crons/index.ts +41 -37
  8. package/api/src/crons/metering-subscription-detection.ts +1 -1
  9. package/api/src/crons/overdue-detection.ts +2 -2
  10. package/api/src/crons/retry-pending-events.ts +6 -0
  11. package/api/src/crons/tenant-fanout.ts +82 -0
  12. package/api/src/host-node/did-connect-runtime-node.ts +33 -0
  13. package/api/src/host-node/serve-static-arc.ts +68 -0
  14. package/api/src/host-node/serve-static.ts +41 -0
  15. package/api/src/index.ts +22 -161
  16. package/api/src/integrations/app-store/client.ts +3 -4
  17. package/api/src/integrations/app-store/handlers/subscription.ts +7 -7
  18. package/api/src/integrations/app-store/signed-data-verifier.ts +3 -2
  19. package/api/src/integrations/arcblock/token.ts +21 -7
  20. package/api/src/integrations/google-play/handlers/subscription.ts +6 -6
  21. package/api/src/integrations/google-play/handlers/voided.ts +2 -2
  22. package/api/src/integrations/google-play/verify.ts +3 -2
  23. package/api/src/integrations/iap-reconcile.ts +3 -5
  24. package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
  25. package/api/src/integrations/stripe/handlers/subscription.ts +3 -3
  26. package/api/src/libs/archive/query.ts +19 -0
  27. package/api/src/libs/audit.ts +61 -4
  28. package/api/src/libs/auth.ts +247 -47
  29. package/api/src/libs/context.ts +89 -1
  30. package/api/src/libs/currency.ts +2 -2
  31. package/api/src/libs/dayjs.ts +8 -2
  32. package/api/src/libs/did-connect/runtime-did-connect-js.ts +88 -0
  33. package/api/src/libs/did-connect/tenant-identity.ts +221 -0
  34. package/api/src/libs/drivers/auth-storage.ts +118 -0
  35. package/api/src/libs/drivers/cron.ts +264 -0
  36. package/api/src/libs/drivers/db.ts +170 -0
  37. package/api/src/libs/drivers/identity.ts +142 -0
  38. package/api/src/libs/drivers/index.ts +40 -0
  39. package/api/src/libs/drivers/locks.ts +226 -0
  40. package/api/src/libs/drivers/migrate-runner.ts +70 -0
  41. package/api/src/libs/drivers/queue.ts +104 -0
  42. package/api/src/libs/drivers/secrets.ts +194 -0
  43. package/api/src/libs/env.ts +170 -54
  44. package/api/src/libs/exchange-rate/service.ts +7 -6
  45. package/api/src/libs/http-fetch-adapter.ts +60 -0
  46. package/api/src/libs/invoice.ts +1 -1
  47. package/api/src/libs/lock.ts +51 -47
  48. package/api/src/libs/logger.ts +48 -8
  49. package/api/src/libs/notification/index.ts +1 -1
  50. package/api/src/libs/notification/template/customer-credit-low-balance.ts +2 -1
  51. package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -1
  52. package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -1
  53. package/api/src/libs/overdraft-protection.ts +1 -1
  54. package/api/src/libs/payout.ts +1 -1
  55. package/api/src/libs/queue/index.ts +271 -52
  56. package/api/src/libs/queue/runtime.ts +175 -0
  57. package/api/src/libs/resource.ts +3 -3
  58. package/api/src/libs/secrets.ts +38 -0
  59. package/api/src/libs/session.ts +3 -2
  60. package/api/src/libs/subscription.ts +5 -5
  61. package/api/src/libs/tenant.ts +92 -0
  62. package/api/src/libs/url.ts +3 -3
  63. package/api/src/libs/util.ts +21 -13
  64. package/api/src/middlewares/hono/cdn.ts +63 -0
  65. package/api/src/middlewares/hono/context.ts +80 -0
  66. package/api/src/middlewares/hono/csrf.ts +83 -0
  67. package/api/src/middlewares/hono/fallback.ts +194 -0
  68. package/api/src/middlewares/hono/pipeline.ts +73 -0
  69. package/api/src/middlewares/hono/resource-mount.ts +42 -0
  70. package/api/src/middlewares/hono/resource.ts +63 -0
  71. package/api/src/middlewares/hono/security.ts +209 -0
  72. package/api/src/middlewares/hono/session.ts +114 -0
  73. package/api/src/middlewares/hono/xss.ts +61 -0
  74. package/api/src/queues/auto-recharge.ts +12 -10
  75. package/api/src/queues/checkout-session.ts +38 -21
  76. package/api/src/queues/credit-consume.ts +40 -36
  77. package/api/src/queues/credit-grant.ts +25 -18
  78. package/api/src/queues/credit-reconciliation.ts +7 -5
  79. package/api/src/queues/discount-status.ts +9 -6
  80. package/api/src/queues/event.ts +41 -11
  81. package/api/src/queues/exchange-rate-health.ts +49 -30
  82. package/api/src/queues/invoice.ts +18 -15
  83. package/api/src/queues/notification.ts +14 -7
  84. package/api/src/queues/payment.ts +64 -37
  85. package/api/src/queues/payout.ts +37 -21
  86. package/api/src/queues/refund.ts +36 -18
  87. package/api/src/queues/subscription.ts +83 -53
  88. package/api/src/queues/token-transfer.ts +15 -10
  89. package/api/src/queues/usage-record.ts +8 -5
  90. package/api/src/queues/vendors/commission.ts +7 -5
  91. package/api/src/queues/vendors/fulfillment-coordinator.ts +17 -13
  92. package/api/src/queues/vendors/fulfillment.ts +4 -2
  93. package/api/src/queues/vendors/return-processor.ts +5 -3
  94. package/api/src/queues/vendors/return-scanner.ts +5 -4
  95. package/api/src/queues/vendors/status-check.ts +10 -7
  96. package/api/src/queues/webhook.ts +60 -32
  97. package/api/src/routes/connect/shared.ts +1 -2
  98. package/api/src/routes/connect/subscribe.ts +3 -3
  99. package/api/src/routes/{archive.ts → hono/archive.ts} +69 -64
  100. package/api/src/routes/{auto-recharge-configs.ts → hono/auto-recharge-configs.ts} +39 -28
  101. package/api/src/routes/{checkout-sessions.ts → hono/checkout-sessions.ts} +790 -923
  102. package/api/src/routes/{coupons.ts → hono/coupons.ts} +93 -76
  103. package/api/src/routes/{credit-grants.ts → hono/credit-grants.ts} +140 -126
  104. package/api/src/routes/hono/credit-tokens.ts +43 -0
  105. package/api/src/routes/{credit-transactions.ts → hono/credit-transactions.ts} +37 -29
  106. package/api/src/routes/{customers.ts → hono/customers.ts} +199 -224
  107. package/api/src/routes/{donations.ts → hono/donations.ts} +41 -32
  108. package/api/src/routes/{entitlements.ts → hono/entitlements.ts} +28 -25
  109. package/api/src/routes/{events.ts → hono/events.ts} +107 -71
  110. package/api/src/routes/{exchange-rate-providers.ts → hono/exchange-rate-providers.ts} +138 -126
  111. package/api/src/routes/hono/exchange-rates.ts +77 -0
  112. package/api/src/routes/hono/index.ts +115 -0
  113. package/api/src/routes/{integrations → hono/integrations}/app-store.ts +68 -48
  114. package/api/src/routes/{integrations → hono/integrations}/google-play.ts +78 -58
  115. package/api/src/routes/hono/integrations/stripe.ts +74 -0
  116. package/api/src/routes/{invoices.ts → hono/invoices.ts} +253 -244
  117. package/api/src/routes/{meter-events.ts → hono/meter-events.ts} +120 -110
  118. package/api/src/routes/hono/meters.ts +288 -0
  119. package/api/src/routes/hono/passports.ts +73 -0
  120. package/api/src/routes/{payment-currencies.ts → hono/payment-currencies.ts} +219 -197
  121. package/api/src/routes/{payment-intents.ts → hono/payment-intents.ts} +136 -132
  122. package/api/src/routes/{payment-links.ts → hono/payment-links.ts} +145 -128
  123. package/api/src/routes/{payment-methods.ts → hono/payment-methods.ts} +125 -93
  124. package/api/src/routes/{payment-stats.ts → hono/payment-stats.ts} +30 -25
  125. package/api/src/routes/{payouts.ts → hono/payouts.ts} +55 -47
  126. package/api/src/routes/{prices.ts → hono/prices.ts} +265 -242
  127. package/api/src/routes/{pricing-table.ts → hono/pricing-table.ts} +94 -87
  128. package/api/src/routes/{products.ts → hono/products.ts} +172 -159
  129. package/api/src/routes/{promotion-codes.ts → hono/promotion-codes.ts} +207 -185
  130. package/api/src/routes/hono/redirect.ts +24 -0
  131. package/api/src/routes/{refunds.ts → hono/refunds.ts} +98 -83
  132. package/api/src/routes/{settings.ts → hono/settings.ts} +64 -55
  133. package/api/src/routes/{subscription-items.ts → hono/subscription-items.ts} +64 -57
  134. package/api/src/routes/{subscriptions.ts → hono/subscriptions.ts} +475 -528
  135. package/api/src/routes/{tax-rates.ts → hono/tax-rates.ts} +71 -70
  136. package/api/src/routes/hono/tool.ts +69 -0
  137. package/api/src/routes/{usage-records.ts → hono/usage-records.ts} +47 -42
  138. package/api/src/routes/{vendor.ts → hono/vendor.ts} +315 -167
  139. package/api/src/routes/{webhook-attempts.ts → hono/webhook-attempts.ts} +17 -13
  140. package/api/src/routes/hono/webhook-endpoints.ts +126 -0
  141. package/api/src/service.ts +814 -0
  142. package/api/src/store/migrations/20230911-seeding.ts +2 -1
  143. package/api/src/store/migrations/20260609-remove-did-space-jobs.ts +23 -0
  144. package/api/src/store/migrations/20260610-tenant-columns.ts +40 -0
  145. package/api/src/store/migrations/20260611-tenant-backfill.ts +33 -0
  146. package/api/src/store/models/auto-recharge-config.ts +22 -10
  147. package/api/src/store/models/checkout-session.ts +15 -14
  148. package/api/src/store/models/coupon.ts +29 -20
  149. package/api/src/store/models/credit-grant.ts +38 -29
  150. package/api/src/store/models/credit-transaction.ts +32 -21
  151. package/api/src/store/models/customer.ts +19 -17
  152. package/api/src/store/models/discount.ts +11 -2
  153. package/api/src/store/models/entitlement-grant.ts +21 -9
  154. package/api/src/store/models/entitlement-product.ts +21 -9
  155. package/api/src/store/models/entitlement.ts +19 -10
  156. package/api/src/store/models/event.ts +18 -9
  157. package/api/src/store/models/exchange-rate-provider.ts +17 -4
  158. package/api/src/store/models/invoice-item.ts +18 -9
  159. package/api/src/store/models/invoice.ts +16 -8
  160. package/api/src/store/models/meter-event.ts +27 -9
  161. package/api/src/store/models/meter.ts +31 -22
  162. package/api/src/store/models/payment-currency.ts +25 -8
  163. package/api/src/store/models/payment-intent.ts +15 -6
  164. package/api/src/store/models/payment-link.ts +15 -6
  165. package/api/src/store/models/payment-method.ts +38 -22
  166. package/api/src/store/models/payment-stat.ts +18 -9
  167. package/api/src/store/models/payout.ts +15 -6
  168. package/api/src/store/models/price-quote.ts +17 -8
  169. package/api/src/store/models/price.ts +24 -12
  170. package/api/src/store/models/pricing-table.ts +29 -20
  171. package/api/src/store/models/product-vendor.ts +20 -10
  172. package/api/src/store/models/product.ts +15 -6
  173. package/api/src/store/models/promotion-code.ts +14 -6
  174. package/api/src/store/models/refund.ts +15 -6
  175. package/api/src/store/models/revenue-snapshot.ts +21 -9
  176. package/api/src/store/models/setting.ts +18 -9
  177. package/api/src/store/models/setup-intent.ts +36 -27
  178. package/api/src/store/models/subscription-item.ts +21 -9
  179. package/api/src/store/models/subscription-schedule.ts +21 -9
  180. package/api/src/store/models/subscription.ts +21 -10
  181. package/api/src/store/models/tax-rate.ts +29 -21
  182. package/api/src/store/models/usage-record.ts +11 -2
  183. package/api/src/store/models/webhook-attempt.ts +18 -9
  184. package/api/src/store/models/webhook-endpoint.ts +18 -9
  185. package/api/src/store/scoped-core.ts +55 -0
  186. package/api/src/store/scoped.ts +247 -0
  187. package/api/src/store/sequelize.ts +82 -23
  188. package/api/src/store/sql-migrations.ts +20 -0
  189. package/api/src/store/tenant-backfill.ts +260 -0
  190. package/api/src/store/tenant-model.ts +124 -0
  191. package/api/src/store/tenant-tables.ts +50 -0
  192. package/api/tests/bootstrap/bootstrap.spec.ts +162 -0
  193. package/api/tests/crons/tenant-fanout.spec.ts +158 -0
  194. package/api/tests/embedded/embedded-multi-mode-d3.spec.ts +257 -0
  195. package/api/tests/fixtures/bare-query-violation.ts +13 -0
  196. package/api/tests/fixtures/core-env-violation.ts +10 -0
  197. package/api/tests/fixtures/host-read-violation.ts +19 -0
  198. package/api/tests/fixtures/tenants.ts +4 -0
  199. package/api/tests/integrations/iap-tenant.spec.ts +284 -0
  200. package/api/tests/libs/archive-query.spec.ts +26 -0
  201. package/api/tests/libs/audit-tenant.spec.ts +153 -0
  202. package/api/tests/libs/context.spec.ts +204 -0
  203. package/api/tests/libs/core-config.spec.ts +115 -0
  204. package/api/tests/libs/cron-driver-d2.spec.ts +237 -0
  205. package/api/tests/libs/crons-conservation-d2.spec.ts +52 -0
  206. package/api/tests/libs/did-connect-runtime-js.spec.ts +98 -0
  207. package/api/tests/libs/did-connect-tenant-identity.spec.ts +159 -0
  208. package/api/tests/libs/lock-tenant.spec.ts +66 -0
  209. package/api/tests/libs/scoped.spec.ts +222 -0
  210. package/api/tests/libs/secrets-facade.spec.ts +52 -0
  211. package/api/tests/libs/service-host.spec.ts +37 -0
  212. package/api/tests/libs/tenancy-slot-authority.spec.ts +209 -0
  213. package/api/tests/libs/tenant-middleware.spec.ts +42 -0
  214. package/api/tests/libs/tenant-scanner.spec.ts +120 -0
  215. package/api/tests/middlewares/hono/cdn.spec.ts +70 -0
  216. package/api/tests/middlewares/hono/context.spec.ts +113 -0
  217. package/api/tests/middlewares/hono/csrf.spec.ts +136 -0
  218. package/api/tests/middlewares/hono/fallback.spec.ts +67 -0
  219. package/api/tests/middlewares/hono/pipeline.spec.ts +47 -0
  220. package/api/tests/middlewares/hono/security.spec.ts +181 -0
  221. package/api/tests/middlewares/hono/session.spec.ts +42 -0
  222. package/api/tests/middlewares/hono/xss.spec.ts +81 -0
  223. package/api/tests/models/tenant-backfill.spec.ts +287 -0
  224. package/api/tests/models/tenant-columns-model.spec.ts +46 -0
  225. package/api/tests/models/tenant-columns.spec.ts +161 -0
  226. package/api/tests/queues/credit-consume-batch.spec.ts +8 -1
  227. package/api/tests/queues/credit-consume.spec.ts +8 -1
  228. package/api/tests/queues/event-tenant.spec.ts +292 -0
  229. package/api/tests/queues/exchange-rate-health-tenant-d6.spec.ts +62 -0
  230. package/api/tests/queues/queue-parity.spec.ts +249 -0
  231. package/api/tests/queues/queue-runtime-surface.spec.ts +277 -0
  232. package/api/tests/queues/queue-teardown-d2.spec.ts +127 -0
  233. package/api/tests/queues/tenant-matrix-a.spec.ts +245 -0
  234. package/api/tests/queues/tenant-matrix-b.spec.ts +168 -0
  235. package/api/tests/routes/connect/hono-attach.spec.ts +107 -0
  236. package/api/tests/service/collapse.spec.ts +96 -0
  237. package/api/tests/service/didconnect-storage-slot.spec.ts +60 -0
  238. package/api/tests/service/fail-closed-http.spec.ts +79 -0
  239. package/api/tests/service/static-arc-handler.spec.ts +101 -0
  240. package/api/tests/service/static-externalized.spec.ts +48 -0
  241. package/api/tests/store/tenant-crosscut.spec.ts +202 -0
  242. package/api/tests/store/tenant-model-spike.spec.ts +177 -0
  243. package/api/tests/store/tenant-model.spec.ts +162 -0
  244. package/api/tests/store/tenant-residual.spec.ts +196 -0
  245. package/api/third.d.ts +4 -0
  246. package/blocklet.yml +1 -1
  247. package/cloudflare/MIGRATION-RUNBOOK.md +3 -8
  248. package/cloudflare/README.md +34 -27
  249. package/cloudflare/STAGING-MIGRATION-GUIDE.md +3 -15
  250. package/cloudflare/build.ts +33 -13
  251. package/cloudflare/cf-adapter.ts +419 -0
  252. package/cloudflare/did-connect-runtime.ts +96 -0
  253. package/cloudflare/did-connect-token-storage.ts +151 -0
  254. package/cloudflare/esbuild-cf-config.cjs +407 -0
  255. package/cloudflare/migrations/0006_tenant_columns.sql +46 -0
  256. package/cloudflare/migrations/0007_tenant_backfill_indexes.sql +65 -0
  257. package/cloudflare/migrations/0008_schema_parity.sql +16 -0
  258. package/cloudflare/migrations/0009_remove_did_space_jobs.sql +5 -0
  259. package/cloudflare/queue-runtime-mode.ts +13 -0
  260. package/cloudflare/run-build.js +33 -403
  261. package/cloudflare/scripts/cf-package-import-probe.mjs +90 -0
  262. package/cloudflare/scripts/didconnect-mock-smoke.mjs +140 -0
  263. package/cloudflare/shims/blocklet-sdk/asset-host-transformer.ts +20 -0
  264. package/cloudflare/shims/blocklet-sdk/config.ts +8 -1
  265. package/cloudflare/shims/blocklet-sdk/login.ts +12 -0
  266. package/cloudflare/shims/blocklet-sdk/service-api.ts +14 -0
  267. package/cloudflare/shims/blocklet-sdk/session.ts +4 -2
  268. package/cloudflare/shims/blocklet-sdk/util-constants.ts +8 -0
  269. package/cloudflare/shims/blocklet-sdk/wallet-authenticator.ts +16 -1
  270. package/cloudflare/shims/blocklet-sdk/wallet-handler.ts +18 -3
  271. package/cloudflare/shims/cron.ts +38 -158
  272. package/cloudflare/shims/events.ts +124 -0
  273. package/cloudflare/shims/fastq.ts +15 -1
  274. package/cloudflare/shims/nedb-storage.ts +16 -8
  275. package/cloudflare/shims/xss.ts +8 -0
  276. package/cloudflare/tenant-middleware.ts +36 -0
  277. package/cloudflare/tests/cf-adapter.spec.ts +244 -0
  278. package/cloudflare/tests/did-connect-token-storage.spec.ts +105 -0
  279. package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
  280. package/cloudflare/tests/worker-handler-gate.spec.ts +69 -0
  281. package/cloudflare/vite.config.ts +53 -45
  282. package/cloudflare/worker.ts +261 -448
  283. package/cloudflare/wrangler.json +0 -6
  284. package/cloudflare/wrangler.jsonc +0 -6
  285. package/cloudflare/wrangler.local-e2e.jsonc +25 -0
  286. package/cloudflare/wrangler.staging.json +0 -6
  287. package/jest.config.js +3 -1
  288. package/package.json +33 -38
  289. package/scripts/bootstrap-inject.ts +166 -0
  290. package/scripts/core-env-whitelist.json +1 -0
  291. package/scripts/e2e-12b-runtime.ts +149 -0
  292. package/scripts/e2e-core-config.ts +125 -0
  293. package/scripts/e2e-d1-tenancy.ts +116 -0
  294. package/scripts/e2e-d2-cron-queue.ts +139 -0
  295. package/scripts/e2e-d3-embedded-multi.ts +171 -0
  296. package/scripts/e2e-hono-s2.ts +125 -0
  297. package/scripts/e2e-hono-s3e.ts +135 -0
  298. package/scripts/e2e-hono-s4.ts +114 -0
  299. package/scripts/e2e-migration-contract.ts +100 -0
  300. package/scripts/e2e-s0.ts +61 -0
  301. package/scripts/e2e-s1.ts +107 -0
  302. package/scripts/e2e-s2.ts +178 -0
  303. package/scripts/e2e-s3.ts +110 -0
  304. package/scripts/e2e-s4.ts +191 -0
  305. package/scripts/e2e-s5.ts +139 -0
  306. package/scripts/e2e-s6.ts +127 -0
  307. package/scripts/e2e-tenant-model.ts +119 -0
  308. package/scripts/e2e-tenant-worker.ts +199 -0
  309. package/scripts/gen-sql-migrations.js +46 -0
  310. package/scripts/phase8-codemod.js +219 -0
  311. package/scripts/phase9a-env-getters-codemod.js +82 -0
  312. package/scripts/scan-core-env.js +109 -0
  313. package/scripts/scan-tenant-queries.js +235 -0
  314. package/scripts/schema-drift-guard.ts +210 -0
  315. package/scripts/tenant-scan-whitelist.json +1 -0
  316. package/src/app.tsx +2 -1
  317. package/src/env.d.ts +13 -1
  318. package/src/libs/service-host.ts +13 -0
  319. package/tsconfig.json +1 -1
  320. package/vite.arc.config.ts +159 -0
  321. package/api/src/libs/did-space.ts +0 -235
  322. package/api/src/libs/middleware.ts +0 -50
  323. package/api/src/libs/security.ts +0 -192
  324. package/api/src/queues/space.ts +0 -662
  325. package/api/src/routes/credit-tokens.ts +0 -38
  326. package/api/src/routes/exchange-rates.ts +0 -87
  327. package/api/src/routes/index.ts +0 -142
  328. package/api/src/routes/integrations/stripe.ts +0 -61
  329. package/api/src/routes/meters.ts +0 -274
  330. package/api/src/routes/passports.ts +0 -68
  331. package/api/src/routes/redirect.ts +0 -20
  332. package/api/src/routes/tool.ts +0 -65
  333. package/api/src/routes/webhook-endpoints.ts +0 -126
  334. package/api/tests/routes/credit-grants.spec.ts +0 -1261
  335. package/cloudflare/did-connect-auth.ts +0 -527
  336. package/cloudflare/shims/did-space-js.ts +0 -17
  337. package/cloudflare/shims/did-space.ts +0 -11
  338. package/cloudflare/shims/express-compat/index.ts +0 -80
  339. package/cloudflare/shims/express-compat/types.ts +0 -41
  340. package/cloudflare/shims/lock.ts +0 -115
  341. package/cloudflare/shims/queue.ts +0 -611
  342. package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +0 -87
  343. package/cloudflare/tests/shims/queue-scheduled.spec.ts +0 -186
@@ -8,6 +8,7 @@ import type { LiteralUnion } from 'type-fest';
8
8
  import { withQuery } from 'ufo';
9
9
 
10
10
  import { Op } from 'sequelize';
11
+ import env, { paymentDaysUntilDue, paymentDaysUntilCancel } from './env';
11
12
  import {
12
13
  ChainType,
13
14
  Customer,
@@ -27,9 +28,8 @@ import {
27
28
  TLineItemExpanded,
28
29
  UsageRecord,
29
30
  } from '../store/models';
30
- import { createEvent } from './audit';
31
+ import { createEvent, reportAuditFailure } from './audit';
31
32
  import dayjs from './dayjs';
32
- import env from './env';
33
33
  import logger from './logger';
34
34
  import { getExchangeRateService } from './exchange-rate';
35
35
  import { getExchangeRateSymbol } from './exchange-rate/token-address-mapping';
@@ -107,11 +107,11 @@ export function parseIntegerConfig(alternatives: any[], defaultValue: number) {
107
107
  }
108
108
 
109
109
  export function getDaysUntilDue(query: Record<string, any> = {}) {
110
- return parseIntegerConfig([query.days_until_due, process.env.PAYMENT_DAYS_UNTIL_DUE], 6);
110
+ return parseIntegerConfig([query.days_until_due, paymentDaysUntilDue()], 6);
111
111
  }
112
112
 
113
113
  export function getDaysUntilCancel(query: Record<string, any> = {}) {
114
- return parseIntegerConfig([query.days_until_cancel, process.env.PAYMENT_DAYS_UNTIL_CANCEL], 0);
114
+ return parseIntegerConfig([query.days_until_cancel, paymentDaysUntilCancel()], 0);
115
115
  }
116
116
 
117
117
  export const getDueUnit = (interval: string) => {
@@ -821,7 +821,7 @@ export async function finalizeStripeSubscriptionUpdate({
821
821
  await Lock.acquire(`${subscription.id}-change-plan`, releaseAt);
822
822
  logger.info('subscription plan change lock acquired on finalize', { subscription: subscription.id, releaseAt });
823
823
 
824
- createEvent('Subscription', 'customer.subscription.upgraded', subscription).catch(console.error);
824
+ createEvent('Subscription', 'customer.subscription.upgraded', subscription).catch(reportAuditFailure);
825
825
  }
826
826
 
827
827
  logger.info('subscription update finalized', { subscription: subscription.id, updates, items });
@@ -0,0 +1,92 @@
1
+ import { tenantModeRaw, blockletAppPid } from './env';
2
+
3
+ export type TenantMode = 'single' | 'multi';
4
+
5
+ export const TENANT_CONTEXT_MISSING = 'TENANT_CONTEXT_MISSING';
6
+ export const TENANT_MISMATCH = 'TENANT_MISMATCH';
7
+ // programmer error: a scoped helper was pointed at a non-tenant model —
8
+ // distinct from TENANT_MISMATCH so monitoring can tell data races from bugs
9
+ export const INVALID_TENANT_TABLE = 'INVALID_TENANT_TABLE';
10
+ // multi-tenant fail-closed: a request Host did not resolve to any tenant
11
+ // (Phase 10) — the request is refused 4xx with no default-tenant fallback
12
+ export const TENANT_HOST_UNRESOLVED = 'TENANT_HOST_UNRESOLVED';
13
+
14
+ export type TenantErrorCode =
15
+ | typeof TENANT_CONTEXT_MISSING
16
+ | typeof TENANT_MISMATCH
17
+ | typeof INVALID_TENANT_TABLE
18
+ | typeof TENANT_HOST_UNRESOLVED;
19
+
20
+ export class TenantError extends Error {
21
+ code: TenantErrorCode;
22
+
23
+ constructor(code: TenantErrorCode, message: string) {
24
+ super(message);
25
+ this.name = 'TenantError';
26
+ this.code = code;
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Tenant mode of this deployment:
32
+ * - `single` (default, blocklet server legacy): tenant context falls back to the deployment's own app DID
33
+ * - `multi`: no fallback — missing tenant context is a fail-closed error
34
+ *
35
+ * Source of the mode is the existing env mechanism for now; Phase 12 converges it into the config slot.
36
+ */
37
+ export function getTenantMode(): TenantMode {
38
+ // Phase 8: mode-source reads the injected config (libs/env boundary), falling
39
+ // back to process.env. Not request-tamperable — config is set once at factory
40
+ // init, never from request input.
41
+ return tenantModeRaw() === 'multi' ? 'multi' : 'single';
42
+ }
43
+
44
+ /**
45
+ * Single source of truth for "this deployment's app DID" (single-tenant default tenant).
46
+ * Phase 2 backfill values and Phase 10 `tenancy.instanceDid` must reuse this getter.
47
+ */
48
+ // Phase 10: the single-mode tenancy slot value, when the host supplies one via
49
+ // createEmbeddedPaymentService({ tenancy: { mode:'single', instanceDid } }).
50
+ // Preferred over the env snapshot so a host can declare its single-tenant
51
+ // identity explicitly (and so the slot is not silently ignored).
52
+ let overrideDefaultInstanceDid: string | undefined;
53
+
54
+ /** Wire the single-mode tenancy slot value (factory only). Pass undefined to clear. */
55
+ export function setDefaultInstanceDid(did: string | undefined): void {
56
+ if (did !== undefined) assertValidInstanceDid(did);
57
+ overrideDefaultInstanceDid = did;
58
+ }
59
+
60
+ export function getDefaultInstanceDid(): string {
61
+ // explicit tenancy slot value wins; otherwise the app DID from the injected
62
+ // config (libs/env boundary), which itself falls back to process.env's
63
+ // BLOCKLET_APP_PID (the value @blocklet/sdk's env.appPid wrapped before Phase 8).
64
+ const did = overrideDefaultInstanceDid || blockletAppPid() || '';
65
+ if (!did) {
66
+ throw new TenantError(TENANT_CONTEXT_MISSING, 'app DID is not configured for this deployment');
67
+ }
68
+ return did;
69
+ }
70
+
71
+ // Deliberately loose: deployments use both bare addresses (z8iZ...) and
72
+ // did:abt: URIs, so we only reject values that cannot be a DID at all
73
+ // (non-strings, empty/whitespace, embedded whitespace). Strict format
74
+ // enforcement belongs to the identity slot (Phase 10), which resolves
75
+ // Host -> instanceDid from a trusted source.
76
+ /**
77
+ * Tenant of a loaded row, fail-closed: rows written since Phase 2 always
78
+ * carry instance_did; a NULL means pre-backfill data, which is only legal in
79
+ * single mode (where it can only belong to the default tenant).
80
+ */
81
+ export function resolveRowTenant(row: { instance_did?: string | null } | null | undefined): string {
82
+ const fromRow = row?.instance_did;
83
+ if (fromRow) return fromRow;
84
+ if (getTenantMode() === 'single') return getDefaultInstanceDid();
85
+ throw new TenantError(TENANT_CONTEXT_MISSING, 'row has no tenant and deployment is in multi mode');
86
+ }
87
+
88
+ export function assertValidInstanceDid(instanceDid: unknown): asserts instanceDid is string {
89
+ if (typeof instanceDid !== 'string' || !/^\S{3,}$/.test(instanceDid)) {
90
+ throw new TenantError(TENANT_CONTEXT_MISSING, `invalid instanceDid: ${JSON.stringify(instanceDid)}`);
91
+ }
92
+ }
@@ -30,7 +30,7 @@ export async function formatToShortUrl({
30
30
  validUntil?: string;
31
31
  maxVisits?: number;
32
32
  }): Promise<string> {
33
- const apiKey = shortUrlApiKey;
33
+ const apiKey = shortUrlApiKey();
34
34
 
35
35
  if (!apiKey) {
36
36
  return url;
@@ -43,14 +43,14 @@ export async function formatToShortUrl({
43
43
  maxVisits,
44
44
  tags: [],
45
45
  shortCodeLength: 8,
46
- domain: shortUrlDomain,
46
+ domain: shortUrlDomain(),
47
47
  findIfExists: true,
48
48
  validateUrl: true,
49
49
  forwardQuery: true,
50
50
  crawlable: true,
51
51
  };
52
52
 
53
- const response = await fetch(`https://${shortUrlDomain}/rest/v3/short-urls`, {
53
+ const response = await fetch(`https://${shortUrlDomain()}/rest/v3/short-urls`, {
54
54
  method: 'POST',
55
55
  headers: {
56
56
  'Content-Type': 'application/json',
@@ -10,10 +10,10 @@ import type { LiteralUnion } from 'type-fest';
10
10
  import { joinURL, withQuery, withTrailingSlash } from 'ufo';
11
11
 
12
12
  import axios from 'axios';
13
- import { ethers } from 'ethers';
14
13
  import { fromUnitToToken } from '@ocap/util';
15
14
  import get from 'lodash/get';
16
15
  import trimEnd from 'lodash/trimEnd';
16
+ import { googlePlayWebhookUrl, blockletAppUrl, blockletMountPoints, blockletAppId, blockletAppName } from './env';
17
17
  import dayjs from './dayjs';
18
18
  import { blocklet, wallet } from './auth';
19
19
  import type { PaymentCurrency, PaymentMethod, Subscription } from '../store/models';
@@ -37,7 +37,7 @@ export const STRIPE_ENDPOINT: string = getUrl('/api/integrations/stripe/webhook'
37
37
  // Lazy-eval (function not constant) because dotenv loads env AFTER this module
38
38
  // is imported — a constant captured at module-load would only see BLOCKLET_APP_URL.
39
39
  export const googlePlayEndpoint = (): string =>
40
- process.env.GOOGLE_PLAY_WEBHOOK_URL || getUrl('/api/integrations/google-play/webhook');
40
+ googlePlayWebhookUrl() || getUrl('/api/integrations/google-play/webhook');
41
41
 
42
42
  // Back-compat constant for any caller that captures it at module-load.
43
43
  // Prefer googlePlayEndpoint() going forward.
@@ -262,7 +262,7 @@ const cachedBlockletJsonResult = new Map<string, { data: any; expiry: number }>(
262
262
  const CACHE_TTL = 60 * 60 * 1000; // 1 hour
263
263
 
264
264
  export async function getBlockletJson(url?: string) {
265
- const blockletKey = url || process.env.BLOCKLET_APP_URL || 'default';
265
+ const blockletKey = url || blockletAppUrl() || 'default';
266
266
  const now = Date.now();
267
267
 
268
268
  if (cachedBlockletJsonResult.has(blockletKey)) {
@@ -271,7 +271,7 @@ export async function getBlockletJson(url?: string) {
271
271
  return cached.data;
272
272
  }
273
273
  }
274
- const baseUrl = url || process.env.BLOCKLET_APP_URL;
274
+ const baseUrl = url || blockletAppUrl();
275
275
  if (!baseUrl) {
276
276
  return null;
277
277
  }
@@ -282,14 +282,14 @@ export async function getBlockletJson(url?: string) {
282
282
  return blockletMeta;
283
283
  } catch (err) {
284
284
  logger.error(`getBlockletJson error for ${scriptUrl}`, err);
285
- if (process.env.BLOCKLET_MOUNT_POINTS) {
286
- const BLOCKLET_MOUNT_POINTS = safeJsonParse(process.env.BLOCKLET_MOUNT_POINTS, []);
285
+ if (blockletMountPoints()) {
286
+ const BLOCKLET_MOUNT_POINTS = safeJsonParse(blockletMountPoints(), []);
287
287
  return {
288
288
  componentMountPoints: BLOCKLET_MOUNT_POINTS,
289
- appId: process.env.BLOCKLET_APP_ID,
290
- appName: process.env.BLOCKLET_APP_NAME,
289
+ appId: blockletAppId(),
290
+ appName: blockletAppName(),
291
291
  appLogo: '/.well-known/service/blocklet/logo',
292
- appUrl: process.env.BLOCKLET_APP_URL,
292
+ appUrl: blockletAppUrl(),
293
293
  };
294
294
  }
295
295
  return null;
@@ -313,9 +313,9 @@ export async function getUserOrAppInfo(
313
313
  if (appInfo) {
314
314
  return {
315
315
  name: appInfo.name,
316
- avatar: joinURL(process.env.BLOCKLET_APP_URL!, `.well-known/service/blocklet/logo-bundle/${appInfo.did}`),
316
+ avatar: joinURL(blockletAppUrl()!, `.well-known/service/blocklet/logo-bundle/${appInfo.did}`),
317
317
  type: 'dapp',
318
- url: joinURL(process.env.BLOCKLET_APP_URL!, appInfo.mountPoint),
318
+ url: joinURL(blockletAppUrl()!, appInfo.mountPoint),
319
319
  };
320
320
  }
321
321
  }
@@ -324,7 +324,7 @@ export async function getUserOrAppInfo(
324
324
  const locale = get(user, 'locale', 'en');
325
325
  return {
326
326
  name: user?.fullName,
327
- avatar: joinURL(process.env.BLOCKLET_APP_URL!, user?.avatar),
327
+ avatar: joinURL(blockletAppUrl()!, user?.avatar),
328
328
  type: 'user',
329
329
  url: getCustomerProfileUrl({ userDid: address, locale }),
330
330
  };
@@ -608,7 +608,15 @@ export async function isUserInBlocklist(did: string, paymentMethod: PaymentMetho
608
608
  }
609
609
 
610
610
  export function resolveAddressChainTypes(address: string): LiteralUnion<'ethereum' | 'base' | 'arcblock', string>[] {
611
- if (ethers.isAddress(address)) {
611
+ // Phase 13b2: lazy ethers. An eager top-level `import 'ethers'` loaded ethers
612
+ // during createEmbeddedPaymentService assembly (libs/util is pulled in at factory
613
+ // time), so a host that force-resolves an incompatible @noble/hashes (e.g. arc's
614
+ // `@noble/hashes:^2.2.0` override vs ethers@6.16's declared 1.3.2) crashed ethers
615
+ // at require. Deferring to call time keeps the factory + rpc.entitlements.check
616
+ // ethers-free; this fn runs only on EVM address resolution.
617
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
618
+ const { isAddress } = require('ethers');
619
+ if (isAddress(address)) {
612
620
  return ['ethereum', 'base', 'arcblock'];
613
621
  }
614
622
  return ['arcblock'];
@@ -0,0 +1,63 @@
1
+ // Phase 1 (express→hono) — hono fork of @blocklet/sdk/lib/middlewares/cdn.js.
2
+ //
3
+ // The express version monkeypatches res.send to rewrite asset URLs to the CDN
4
+ // host on outgoing HTML. hono responses are built differently, so this fork
5
+ // rewrites in the RESPONSE phase: run the handler, then if the response is HTML
6
+ // (production GET/HEAD, non-resource, html-accepting), read it once and rebuild
7
+ // the Response with rewritten URLs. The transform core (AssetHostTransformer) is
8
+ // REUSED VERBATIM from the SDK — byte-identical rewriting. Inert for JSON /api
9
+ // routes (content-type is not text/html), which is the only surface in Phases
10
+ // 1-3; it becomes load-bearing when SPA HTML serving moves off the bridge.
11
+ import type { MiddlewareHandler } from 'hono';
12
+ // eslint-disable-next-line import/no-extraneous-dependencies
13
+ import { AssetHostTransformer } from '@blocklet/sdk/lib/util/asset-host-transformer';
14
+ // eslint-disable-next-line import/no-extraneous-dependencies
15
+ import { env } from '@blocklet/sdk/lib/config';
16
+ // eslint-disable-next-line import/no-extraneous-dependencies
17
+ import { BLOCKLET_PROXY_PATH_PREFIX } from '@abtnode/constant';
18
+ // eslint-disable-next-line import/no-extraneous-dependencies
19
+ import { RESOURCE_PATTERN } from '@blocklet/constant';
20
+ import { nodeEnv, readConfig } from '../../libs/env';
21
+
22
+ function isProductionRuntime(): boolean {
23
+ return nodeEnv() === 'production' || readConfig('ABT_NODE_SERVICE_ENV') === 'production';
24
+ }
25
+
26
+ // Parity with express req.accepts(['html', ...]) for the html family.
27
+ function acceptsHtml(accept: string): boolean {
28
+ if (!accept) return true; // express treats a missing Accept as accept-all
29
+ return accept.includes('text/html') || accept.includes('application/xhtml+xml') || accept.includes('*/*');
30
+ }
31
+
32
+ function shouldProcess(method: string, path: string, accept: string): boolean {
33
+ if (!isProductionRuntime()) return false;
34
+ if (method !== 'GET' && method !== 'HEAD') return false;
35
+ if (path.includes('/.well-known/service/')) return false;
36
+ if (RESOURCE_PATTERN.test(path)) return false;
37
+ return acceptsHtml(accept);
38
+ }
39
+
40
+ export function cdn(): MiddlewareHandler {
41
+ // Lazy + skip-if-absent: a bare/embedded host without componentDid never
42
+ // rewrites (the SDK throws "did is required" eagerly; the fork stays inert).
43
+ let transformer: AssetHostTransformer | undefined;
44
+ return async (c, next) => {
45
+ await next();
46
+ const assetHost = (env as any).assetCdnHost;
47
+ const did = (env as any).componentDid;
48
+ if (!assetHost || !did) return;
49
+ if (!shouldProcess(c.req.method.toUpperCase(), c.req.path, c.req.header('accept') || '')) return;
50
+
51
+ const contentType = c.res.headers.get('content-type') || '';
52
+ if (!contentType.includes('text/html')) return;
53
+
54
+ transformer ??= new AssetHostTransformer(`${BLOCKLET_PROXY_PATH_PREFIX}/${did}/`);
55
+ const html = await c.res.text();
56
+ const transformed = transformer.transform(html, assetHost);
57
+ const rebuilt = new Response(transformed, c.res);
58
+ rebuilt.headers.delete('content-length'); // body length changed; let the server derive it
59
+ c.res = rebuilt;
60
+ };
61
+ }
62
+
63
+ export default cdn;
@@ -0,0 +1,80 @@
1
+ // Phase 1 (express→hono) — hono fork of api/src/libs/middleware.ts
2
+ // (ensureI18n + contextMiddleware). Behavior is identical to the express
3
+ // version; only the req/res plumbing changes:
4
+ // - req.query.locale / req.t= → c.req.query('locale') / c.set('t', ...)
5
+ // - req.get('x-component-sig') → c.req.header('x-component-sig')
6
+ // - req.body (component sig verify) → c.get('sanitizedBody') (xss is the single
7
+ // body read-point, already ran upstream)
8
+ // - req.headers.host (tenant) → c.req.header('host') (raw Host only, never
9
+ // a proxy header — single tenant resolution)
10
+ // - res.status(400).json(...) → c.json(..., 400) (fail-closed on unknown
11
+ // host in multi mode)
12
+ // - context.run(..., next) → context.run(..., () => next()) (same ALS)
13
+ import type { MiddlewareHandler } from 'hono';
14
+ // eslint-disable-next-line import/no-extraneous-dependencies
15
+ import { verify } from '@blocklet/sdk/lib/util/verify-sign';
16
+ import { translate } from '../../locales';
17
+ import { context } from '../../libs/context';
18
+ import { TenantError, TENANT_HOST_UNRESOLVED } from '../../libs/tenant';
19
+ import { resolveTenantForHost } from '../../libs/drivers/identity';
20
+ import { warmTenantIdentity } from '../../libs/did-connect/tenant-identity';
21
+
22
+ export function ensureI18n(): MiddlewareHandler {
23
+ return (c, next) => {
24
+ c.set('locale', String(c.req.query('locale') || 'en'));
25
+ c.set('t', translate);
26
+ return next();
27
+ };
28
+ }
29
+
30
+ export function contextMiddleware(): MiddlewareHandler {
31
+ return async (c, next) => {
32
+ const requestId = c.req.header('x-request-id') || `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
33
+ let requestedBy = 'system';
34
+
35
+ // component signature — verify against the SANITIZED body (xss ran first)
36
+ const sig = c.req.header('x-component-sig');
37
+ const componentDid = c.req.header('x-component-did');
38
+ if (sig && componentDid) {
39
+ const data = c.get('sanitizedBody') ?? {};
40
+ const verified = await verify(data, sig);
41
+ if (verified) {
42
+ requestedBy = componentDid;
43
+ }
44
+ }
45
+
46
+ // user DID from headers
47
+ const userDid = c.req.header('x-user-did');
48
+ if (userDid) {
49
+ requestedBy = userDid;
50
+ }
51
+
52
+ // authenticated user (security middleware set this upstream)
53
+ const user = c.get('user');
54
+ if (user?.did) {
55
+ requestedBy = user.did;
56
+ }
57
+
58
+ // Resolve tenant from the raw Host (single point). Multi-mode unknown host →
59
+ // 4xx fail-closed, no default-tenant fallback.
60
+ let instanceDid: string;
61
+ try {
62
+ instanceDid = await resolveTenantForHost(c.req.header('host'));
63
+ } catch (err) {
64
+ if (err instanceof TenantError && err.code === TENANT_HOST_UNRESOLVED) {
65
+ return c.json({ error: { code: err.code, message: err.message } }, 400);
66
+ }
67
+ throw err;
68
+ }
69
+
70
+ return context.run({ requestId, requestedBy, instanceDid }, async () => {
71
+ // Warm the tenant's signing identity (arc/CF dynamic runtime only — no-op on
72
+ // blocklet-server) so the synchronous business wallet proxies (libs/auth.ts:
73
+ // `wallet`/`ethWallet`) resolve to THIS tenant's wallet inside the handler.
74
+ // Best-effort: a request that never touches a wallet is not blocked; one that
75
+ // does fails-closed at getCachedTenantIdentity.
76
+ await warmTenantIdentity(instanceDid);
77
+ await next();
78
+ });
79
+ };
80
+ }
@@ -0,0 +1,83 @@
1
+ // Phase 1 (express→hono) — hono fork of @blocklet/sdk/lib/middlewares/csrf.js.
2
+ //
3
+ // The crypto core (sign / verify / getCsrfSecret) is framework-agnostic and is
4
+ // REUSED VERBATIM from the SDK — tokens are byte-identical and interchangeable
5
+ // across the express and hono engines (proven in spikes/csrf-parity.mjs, §3.2).
6
+ // Only the express plumbing (req.cookies / res.cookie / res.status().send) is
7
+ // replaced with hono/cookie helpers + c.text(403). The shouldGenerateToken
8
+ // (GET) / shouldVerifyToken (mutating + cookie present + non-/mcp + non-didwallet)
9
+ // semantics are preserved faithfully.
10
+ import type { MiddlewareHandler } from 'hono';
11
+ import { getCookie, setCookie } from 'hono/cookie';
12
+ // eslint-disable-next-line import/no-extraneous-dependencies
13
+ import { sign, verify, getCsrfSecret } from '@blocklet/sdk/lib/util/csrf';
14
+ // eslint-disable-next-line import/no-extraneous-dependencies
15
+ import { isDidWalletConnect } from '@blocklet/sdk/lib/util/wallet';
16
+ import { readConfig } from '../../libs/env';
17
+
18
+ const isEmpty = (v: unknown): boolean => v === undefined || v === null || v === '';
19
+
20
+ // The CSRF secret is HOST-GLOBAL, not per-tenant: this middleware runs BEFORE the
21
+ // tenant context is established (pipeline order cors→xss→csrf→…→context), so it
22
+ // has no instanceDid, and it binds the host-global login_token anyway. An embedded
23
+ // host (arc) that has no BLOCKLET_APP_SK env injects a dedicated secret via the
24
+ // config slot (`PAYMENT_CSRF_SECRET`); the standard blocklet server falls back to
25
+ // the SDK's env-based secret. See docs/architecture/payment-credential-sourcing.md.
26
+ function csrfSecret(): string {
27
+ return readConfig('PAYMENT_CSRF_SECRET') || getCsrfSecret();
28
+ }
29
+
30
+ // Express SDK: shouldGenerateToken === GET; shouldVerifyToken === mutating
31
+ // method AND an x-csrf-token cookie already exists AND path is not /mcp AND the
32
+ // caller is not a DID Wallet connect request.
33
+ const MUTATING = ['POST', 'PUT', 'PATCH', 'DELETE'];
34
+
35
+ /**
36
+ * hono csrf middleware. Faithful to the SDK express version:
37
+ * - GET: if a login_token cookie exists, (re)issue x-csrf-token = sign(secret,
38
+ * login_token) when it differs from the current cookie. {sameSite:'Strict',
39
+ * secure:true} matches the express res.cookie attributes.
40
+ * - mutating: only ENFORCED when a login_token AND an x-csrf-token cookie are
41
+ * both present and the path is not /mcp and the caller is not a DID Wallet
42
+ * (parity with the SDK — absent cookie => skip, never reject). The header
43
+ * must equal the cookie and verify() against the login_token, else 403.
44
+ */
45
+ export function csrf(): MiddlewareHandler {
46
+ // async (no await): the SDK crypto core is synchronous, but the handler mixes a
47
+ // sync Response (c.text 403) with next()'s promise — async unifies the return
48
+ // type to the MiddlewareHandler contract.
49
+ // eslint-disable-next-line require-await
50
+ return async (c, next) => {
51
+ const method = c.req.method.toUpperCase();
52
+ const loginToken = getCookie(c, 'login_token');
53
+ const existingCsrf = getCookie(c, 'x-csrf-token');
54
+
55
+ if (method === 'GET') {
56
+ if (loginToken) {
57
+ const newCsrf = sign(csrfSecret(), loginToken);
58
+ if (newCsrf !== existingCsrf) {
59
+ setCookie(c, 'x-csrf-token', newCsrf, { sameSite: 'Strict', secure: true });
60
+ }
61
+ }
62
+ return next();
63
+ }
64
+
65
+ if (MUTATING.includes(method)) {
66
+ // shouldVerifyToken parity: skip (do NOT reject) when the SDK would skip.
67
+ if (c.req.path.includes('/mcp')) return next();
68
+ if (isEmpty(loginToken)) return next();
69
+ if (isEmpty(existingCsrf)) return next();
70
+ if (isDidWalletConnect(c.req.header())) return next();
71
+
72
+ const headerCsrf = c.req.header('x-csrf-token');
73
+ if (existingCsrf === headerCsrf && verify(csrfSecret(), existingCsrf as string, loginToken as string)) {
74
+ return next();
75
+ }
76
+ return c.text('Invalid request: csrf token mismatch, please refresh the page try again', 403);
77
+ }
78
+
79
+ return next();
80
+ };
81
+ }
82
+
83
+ export default csrf;