payment-kit 1.29.1 → 1.29.2

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 (310) 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 +36 -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 +27 -24
  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/index.ts +22 -161
  12. package/api/src/integrations/app-store/client.ts +3 -4
  13. package/api/src/integrations/app-store/handlers/subscription.ts +7 -7
  14. package/api/src/integrations/app-store/signed-data-verifier.ts +3 -2
  15. package/api/src/integrations/arcblock/token.ts +21 -7
  16. package/api/src/integrations/google-play/handlers/subscription.ts +6 -6
  17. package/api/src/integrations/google-play/handlers/voided.ts +2 -2
  18. package/api/src/integrations/google-play/verify.ts +3 -2
  19. package/api/src/integrations/iap-reconcile.ts +3 -5
  20. package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
  21. package/api/src/integrations/stripe/handlers/subscription.ts +3 -3
  22. package/api/src/libs/archive/query.ts +19 -0
  23. package/api/src/libs/audit.ts +61 -4
  24. package/api/src/libs/auth.ts +99 -38
  25. package/api/src/libs/context.ts +78 -1
  26. package/api/src/libs/currency.ts +2 -2
  27. package/api/src/libs/dayjs.ts +8 -2
  28. package/api/src/libs/drivers/auth-storage.ts +118 -0
  29. package/api/src/libs/drivers/cron.ts +264 -0
  30. package/api/src/libs/drivers/db.ts +170 -0
  31. package/api/src/libs/drivers/identity.ts +81 -0
  32. package/api/src/libs/drivers/index.ts +40 -0
  33. package/api/src/libs/drivers/locks.ts +226 -0
  34. package/api/src/libs/drivers/migrate-runner.ts +70 -0
  35. package/api/src/libs/drivers/queue.ts +104 -0
  36. package/api/src/libs/drivers/secrets.ts +194 -0
  37. package/api/src/libs/env.ts +170 -54
  38. package/api/src/libs/exchange-rate/service.ts +7 -6
  39. package/api/src/libs/http-fetch-adapter.ts +50 -0
  40. package/api/src/libs/invoice.ts +1 -1
  41. package/api/src/libs/lock.ts +51 -47
  42. package/api/src/libs/logger.ts +48 -8
  43. package/api/src/libs/notification/index.ts +1 -1
  44. package/api/src/libs/notification/template/customer-credit-low-balance.ts +2 -1
  45. package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -1
  46. package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -1
  47. package/api/src/libs/overdraft-protection.ts +1 -1
  48. package/api/src/libs/payout.ts +1 -1
  49. package/api/src/libs/queue/index.ts +259 -52
  50. package/api/src/libs/queue/runtime.ts +175 -0
  51. package/api/src/libs/resource.ts +3 -3
  52. package/api/src/libs/secrets.ts +38 -0
  53. package/api/src/libs/session.ts +3 -2
  54. package/api/src/libs/subscription.ts +5 -5
  55. package/api/src/libs/tenant.ts +92 -0
  56. package/api/src/libs/url.ts +3 -3
  57. package/api/src/libs/util.ts +21 -13
  58. package/api/src/middlewares/hono/cdn.ts +63 -0
  59. package/api/src/middlewares/hono/context.ts +73 -0
  60. package/api/src/middlewares/hono/csrf.ts +72 -0
  61. package/api/src/middlewares/hono/fallback.ts +194 -0
  62. package/api/src/middlewares/hono/pipeline.ts +73 -0
  63. package/api/src/middlewares/hono/resource-mount.ts +42 -0
  64. package/api/src/middlewares/hono/resource.ts +63 -0
  65. package/api/src/middlewares/hono/security.ts +214 -0
  66. package/api/src/middlewares/hono/session.ts +114 -0
  67. package/api/src/middlewares/hono/xss.ts +61 -0
  68. package/api/src/queues/auto-recharge.ts +12 -10
  69. package/api/src/queues/checkout-session.ts +17 -12
  70. package/api/src/queues/credit-consume.ts +40 -36
  71. package/api/src/queues/credit-grant.ts +25 -18
  72. package/api/src/queues/credit-reconciliation.ts +7 -5
  73. package/api/src/queues/discount-status.ts +9 -6
  74. package/api/src/queues/event.ts +12 -4
  75. package/api/src/queues/exchange-rate-health.ts +49 -30
  76. package/api/src/queues/invoice.ts +18 -15
  77. package/api/src/queues/notification.ts +14 -7
  78. package/api/src/queues/payment.ts +41 -28
  79. package/api/src/queues/payout.ts +9 -5
  80. package/api/src/queues/refund.ts +18 -12
  81. package/api/src/queues/subscription.ts +83 -53
  82. package/api/src/queues/token-transfer.ts +15 -10
  83. package/api/src/queues/usage-record.ts +8 -5
  84. package/api/src/queues/vendors/commission.ts +7 -5
  85. package/api/src/queues/vendors/fulfillment-coordinator.ts +17 -13
  86. package/api/src/queues/vendors/fulfillment.ts +4 -2
  87. package/api/src/queues/vendors/return-processor.ts +5 -3
  88. package/api/src/queues/vendors/return-scanner.ts +5 -4
  89. package/api/src/queues/vendors/status-check.ts +10 -7
  90. package/api/src/queues/webhook.ts +60 -32
  91. package/api/src/routes/connect/shared.ts +1 -2
  92. package/api/src/routes/connect/subscribe.ts +3 -3
  93. package/api/src/routes/{archive.ts → hono/archive.ts} +69 -64
  94. package/api/src/routes/{auto-recharge-configs.ts → hono/auto-recharge-configs.ts} +39 -28
  95. package/api/src/routes/{checkout-sessions.ts → hono/checkout-sessions.ts} +790 -923
  96. package/api/src/routes/{coupons.ts → hono/coupons.ts} +93 -76
  97. package/api/src/routes/{credit-grants.ts → hono/credit-grants.ts} +140 -126
  98. package/api/src/routes/hono/credit-tokens.ts +43 -0
  99. package/api/src/routes/{credit-transactions.ts → hono/credit-transactions.ts} +37 -29
  100. package/api/src/routes/{customers.ts → hono/customers.ts} +193 -223
  101. package/api/src/routes/{donations.ts → hono/donations.ts} +41 -32
  102. package/api/src/routes/{entitlements.ts → hono/entitlements.ts} +28 -25
  103. package/api/src/routes/{events.ts → hono/events.ts} +107 -71
  104. package/api/src/routes/{exchange-rate-providers.ts → hono/exchange-rate-providers.ts} +138 -126
  105. package/api/src/routes/hono/exchange-rates.ts +77 -0
  106. package/api/src/routes/hono/index.ts +115 -0
  107. package/api/src/routes/{integrations → hono/integrations}/app-store.ts +68 -48
  108. package/api/src/routes/{integrations → hono/integrations}/google-play.ts +78 -58
  109. package/api/src/routes/hono/integrations/stripe.ts +74 -0
  110. package/api/src/routes/{invoices.ts → hono/invoices.ts} +253 -244
  111. package/api/src/routes/{meter-events.ts → hono/meter-events.ts} +120 -110
  112. package/api/src/routes/hono/meters.ts +288 -0
  113. package/api/src/routes/hono/passports.ts +73 -0
  114. package/api/src/routes/{payment-currencies.ts → hono/payment-currencies.ts} +219 -197
  115. package/api/src/routes/{payment-intents.ts → hono/payment-intents.ts} +136 -132
  116. package/api/src/routes/{payment-links.ts → hono/payment-links.ts} +145 -128
  117. package/api/src/routes/{payment-methods.ts → hono/payment-methods.ts} +125 -93
  118. package/api/src/routes/{payment-stats.ts → hono/payment-stats.ts} +30 -25
  119. package/api/src/routes/{payouts.ts → hono/payouts.ts} +55 -47
  120. package/api/src/routes/{prices.ts → hono/prices.ts} +265 -242
  121. package/api/src/routes/{pricing-table.ts → hono/pricing-table.ts} +94 -87
  122. package/api/src/routes/{products.ts → hono/products.ts} +172 -159
  123. package/api/src/routes/{promotion-codes.ts → hono/promotion-codes.ts} +207 -185
  124. package/api/src/routes/hono/redirect.ts +24 -0
  125. package/api/src/routes/{refunds.ts → hono/refunds.ts} +96 -80
  126. package/api/src/routes/{settings.ts → hono/settings.ts} +64 -55
  127. package/api/src/routes/{subscription-items.ts → hono/subscription-items.ts} +64 -57
  128. package/api/src/routes/{subscriptions.ts → hono/subscriptions.ts} +475 -528
  129. package/api/src/routes/{tax-rates.ts → hono/tax-rates.ts} +71 -70
  130. package/api/src/routes/hono/tool.ts +69 -0
  131. package/api/src/routes/{usage-records.ts → hono/usage-records.ts} +47 -42
  132. package/api/src/routes/{vendor.ts → hono/vendor.ts} +315 -167
  133. package/api/src/routes/{webhook-attempts.ts → hono/webhook-attempts.ts} +17 -13
  134. package/api/src/routes/hono/webhook-endpoints.ts +126 -0
  135. package/api/src/service.ts +667 -0
  136. package/api/src/store/migrations/20230911-seeding.ts +2 -1
  137. package/api/src/store/migrations/20260609-remove-did-space-jobs.ts +23 -0
  138. package/api/src/store/migrations/20260610-tenant-columns.ts +40 -0
  139. package/api/src/store/migrations/20260611-tenant-backfill.ts +33 -0
  140. package/api/src/store/models/auto-recharge-config.ts +22 -10
  141. package/api/src/store/models/checkout-session.ts +15 -14
  142. package/api/src/store/models/coupon.ts +29 -20
  143. package/api/src/store/models/credit-grant.ts +38 -29
  144. package/api/src/store/models/credit-transaction.ts +32 -21
  145. package/api/src/store/models/customer.ts +19 -17
  146. package/api/src/store/models/discount.ts +11 -2
  147. package/api/src/store/models/entitlement-grant.ts +21 -9
  148. package/api/src/store/models/entitlement-product.ts +21 -9
  149. package/api/src/store/models/entitlement.ts +19 -10
  150. package/api/src/store/models/event.ts +18 -9
  151. package/api/src/store/models/exchange-rate-provider.ts +17 -4
  152. package/api/src/store/models/invoice-item.ts +18 -9
  153. package/api/src/store/models/invoice.ts +16 -8
  154. package/api/src/store/models/meter-event.ts +27 -9
  155. package/api/src/store/models/meter.ts +31 -22
  156. package/api/src/store/models/payment-currency.ts +25 -8
  157. package/api/src/store/models/payment-intent.ts +15 -6
  158. package/api/src/store/models/payment-link.ts +15 -6
  159. package/api/src/store/models/payment-method.ts +38 -22
  160. package/api/src/store/models/payment-stat.ts +18 -9
  161. package/api/src/store/models/payout.ts +15 -6
  162. package/api/src/store/models/price-quote.ts +17 -8
  163. package/api/src/store/models/price.ts +24 -12
  164. package/api/src/store/models/pricing-table.ts +29 -20
  165. package/api/src/store/models/product-vendor.ts +20 -10
  166. package/api/src/store/models/product.ts +15 -6
  167. package/api/src/store/models/promotion-code.ts +14 -6
  168. package/api/src/store/models/refund.ts +15 -6
  169. package/api/src/store/models/revenue-snapshot.ts +21 -9
  170. package/api/src/store/models/setting.ts +18 -9
  171. package/api/src/store/models/setup-intent.ts +36 -27
  172. package/api/src/store/models/subscription-item.ts +21 -9
  173. package/api/src/store/models/subscription-schedule.ts +21 -9
  174. package/api/src/store/models/subscription.ts +21 -10
  175. package/api/src/store/models/tax-rate.ts +29 -21
  176. package/api/src/store/models/usage-record.ts +11 -2
  177. package/api/src/store/models/webhook-attempt.ts +18 -9
  178. package/api/src/store/models/webhook-endpoint.ts +18 -9
  179. package/api/src/store/scoped-core.ts +55 -0
  180. package/api/src/store/scoped.ts +247 -0
  181. package/api/src/store/sequelize.ts +66 -22
  182. package/api/src/store/sql-migrations.ts +20 -0
  183. package/api/src/store/tenant-backfill.ts +260 -0
  184. package/api/src/store/tenant-model.ts +124 -0
  185. package/api/src/store/tenant-tables.ts +50 -0
  186. package/api/tests/embedded/embedded-multi-mode-d3.spec.ts +257 -0
  187. package/api/tests/fixtures/bare-query-violation.ts +13 -0
  188. package/api/tests/fixtures/core-env-violation.ts +10 -0
  189. package/api/tests/fixtures/host-read-violation.ts +19 -0
  190. package/api/tests/fixtures/tenants.ts +4 -0
  191. package/api/tests/integrations/iap-tenant.spec.ts +284 -0
  192. package/api/tests/libs/archive-query.spec.ts +26 -0
  193. package/api/tests/libs/audit-tenant.spec.ts +153 -0
  194. package/api/tests/libs/context.spec.ts +204 -0
  195. package/api/tests/libs/core-config.spec.ts +115 -0
  196. package/api/tests/libs/cron-driver-d2.spec.ts +237 -0
  197. package/api/tests/libs/crons-conservation-d2.spec.ts +52 -0
  198. package/api/tests/libs/lock-tenant.spec.ts +66 -0
  199. package/api/tests/libs/scoped.spec.ts +222 -0
  200. package/api/tests/libs/secrets-facade.spec.ts +52 -0
  201. package/api/tests/libs/tenancy-slot-authority.spec.ts +209 -0
  202. package/api/tests/libs/tenant-middleware.spec.ts +42 -0
  203. package/api/tests/libs/tenant-scanner.spec.ts +120 -0
  204. package/api/tests/middlewares/hono/cdn.spec.ts +70 -0
  205. package/api/tests/middlewares/hono/context.spec.ts +113 -0
  206. package/api/tests/middlewares/hono/csrf.spec.ts +136 -0
  207. package/api/tests/middlewares/hono/fallback.spec.ts +67 -0
  208. package/api/tests/middlewares/hono/pipeline.spec.ts +47 -0
  209. package/api/tests/middlewares/hono/security.spec.ts +181 -0
  210. package/api/tests/middlewares/hono/session.spec.ts +42 -0
  211. package/api/tests/middlewares/hono/xss.spec.ts +81 -0
  212. package/api/tests/models/tenant-backfill.spec.ts +287 -0
  213. package/api/tests/models/tenant-columns-model.spec.ts +46 -0
  214. package/api/tests/models/tenant-columns.spec.ts +161 -0
  215. package/api/tests/queues/credit-consume-batch.spec.ts +8 -1
  216. package/api/tests/queues/credit-consume.spec.ts +8 -1
  217. package/api/tests/queues/event-tenant.spec.ts +236 -0
  218. package/api/tests/queues/exchange-rate-health-tenant-d6.spec.ts +62 -0
  219. package/api/tests/queues/queue-parity.spec.ts +249 -0
  220. package/api/tests/queues/queue-runtime-surface.spec.ts +277 -0
  221. package/api/tests/queues/queue-teardown-d2.spec.ts +127 -0
  222. package/api/tests/queues/tenant-matrix-a.spec.ts +245 -0
  223. package/api/tests/queues/tenant-matrix-b.spec.ts +168 -0
  224. package/api/tests/routes/connect/hono-attach.spec.ts +107 -0
  225. package/api/tests/service/collapse.spec.ts +96 -0
  226. package/api/tests/store/tenant-crosscut.spec.ts +202 -0
  227. package/api/tests/store/tenant-model-spike.spec.ts +177 -0
  228. package/api/tests/store/tenant-model.spec.ts +162 -0
  229. package/api/tests/store/tenant-residual.spec.ts +196 -0
  230. package/api/third.d.ts +4 -0
  231. package/blocklet.yml +1 -1
  232. package/cloudflare/README.md +26 -6
  233. package/cloudflare/build.ts +28 -13
  234. package/cloudflare/did-connect-auth.ts +0 -217
  235. package/cloudflare/migrations/0006_tenant_columns.sql +46 -0
  236. package/cloudflare/migrations/0007_tenant_backfill_indexes.sql +65 -0
  237. package/cloudflare/migrations/0008_schema_parity.sql +16 -0
  238. package/cloudflare/migrations/0009_remove_did_space_jobs.sql +5 -0
  239. package/cloudflare/queue-runtime-mode.ts +13 -0
  240. package/cloudflare/run-build.js +10 -56
  241. package/cloudflare/shims/blocklet-sdk/asset-host-transformer.ts +20 -0
  242. package/cloudflare/shims/blocklet-sdk/config.ts +8 -1
  243. package/cloudflare/shims/blocklet-sdk/login.ts +12 -0
  244. package/cloudflare/shims/blocklet-sdk/service-api.ts +14 -0
  245. package/cloudflare/shims/blocklet-sdk/session.ts +4 -2
  246. package/cloudflare/shims/blocklet-sdk/util-constants.ts +8 -0
  247. package/cloudflare/shims/blocklet-sdk/util-csrf.ts +13 -0
  248. package/cloudflare/shims/blocklet-sdk/util-wallet.ts +8 -0
  249. package/cloudflare/shims/cron.ts +38 -158
  250. package/cloudflare/shims/events.ts +124 -0
  251. package/cloudflare/shims/fastq.ts +15 -1
  252. package/cloudflare/shims/nedb-storage.ts +16 -8
  253. package/cloudflare/shims/xss.ts +8 -0
  254. package/cloudflare/tenant-middleware.ts +36 -0
  255. package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
  256. package/cloudflare/tests/worker-handler-gate.spec.ts +44 -0
  257. package/cloudflare/worker.ts +204 -433
  258. package/cloudflare/wrangler.local-e2e.jsonc +26 -0
  259. package/jest.config.js +3 -1
  260. package/package.json +33 -38
  261. package/scripts/core-env-whitelist.json +1 -0
  262. package/scripts/e2e-12b-runtime.ts +149 -0
  263. package/scripts/e2e-core-config.ts +125 -0
  264. package/scripts/e2e-d1-tenancy.ts +116 -0
  265. package/scripts/e2e-d2-cron-queue.ts +139 -0
  266. package/scripts/e2e-d3-embedded-multi.ts +171 -0
  267. package/scripts/e2e-hono-s2.ts +125 -0
  268. package/scripts/e2e-hono-s3e.ts +135 -0
  269. package/scripts/e2e-hono-s4.ts +114 -0
  270. package/scripts/e2e-migration-contract.ts +100 -0
  271. package/scripts/e2e-s0.ts +61 -0
  272. package/scripts/e2e-s1.ts +107 -0
  273. package/scripts/e2e-s2.ts +178 -0
  274. package/scripts/e2e-s3.ts +110 -0
  275. package/scripts/e2e-s4.ts +191 -0
  276. package/scripts/e2e-s5.ts +139 -0
  277. package/scripts/e2e-s6.ts +127 -0
  278. package/scripts/e2e-tenant-model.ts +119 -0
  279. package/scripts/e2e-tenant-worker.ts +199 -0
  280. package/scripts/gen-sql-migrations.js +46 -0
  281. package/scripts/phase8-codemod.js +219 -0
  282. package/scripts/phase9a-env-getters-codemod.js +82 -0
  283. package/scripts/scan-core-env.js +109 -0
  284. package/scripts/scan-tenant-queries.js +235 -0
  285. package/scripts/schema-drift-guard.ts +210 -0
  286. package/scripts/tenant-scan-whitelist.json +1 -0
  287. package/src/env.d.ts +13 -1
  288. package/tsconfig.json +1 -1
  289. package/api/src/libs/did-space.ts +0 -235
  290. package/api/src/libs/middleware.ts +0 -50
  291. package/api/src/libs/security.ts +0 -192
  292. package/api/src/queues/space.ts +0 -662
  293. package/api/src/routes/credit-tokens.ts +0 -38
  294. package/api/src/routes/exchange-rates.ts +0 -87
  295. package/api/src/routes/index.ts +0 -142
  296. package/api/src/routes/integrations/stripe.ts +0 -61
  297. package/api/src/routes/meters.ts +0 -274
  298. package/api/src/routes/passports.ts +0 -68
  299. package/api/src/routes/redirect.ts +0 -20
  300. package/api/src/routes/tool.ts +0 -65
  301. package/api/src/routes/webhook-endpoints.ts +0 -126
  302. package/api/tests/routes/credit-grants.spec.ts +0 -1261
  303. package/cloudflare/shims/did-space-js.ts +0 -17
  304. package/cloudflare/shims/did-space.ts +0 -11
  305. package/cloudflare/shims/express-compat/index.ts +0 -80
  306. package/cloudflare/shims/express-compat/types.ts +0 -41
  307. package/cloudflare/shims/lock.ts +0 -115
  308. package/cloudflare/shims/queue.ts +0 -611
  309. package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +0 -87
  310. package/cloudflare/tests/shims/queue-scheduled.spec.ts +0 -186
@@ -1,23 +1,30 @@
1
- import { middleware } from '@blocklet/payment-vendor';
1
+ // Phase 3 (express→hono) hono fork of routes/vendor.ts. Sub-app with
2
+ // routes relative to /api/vendor (mounted via mountResourceGroup). The
3
+ // business logic is unchanged; only the express plumbing becomes hono:
4
+ // req.body → c.get('sanitizedBody') ?? {}; res.status(n).json(x) → c.json(x, n);
5
+ // res.redirect(u) → return c.redirect(u).
6
+ import { VendorAuth } from '@blocklet/payment-vendor';
7
+ import { toBase58 } from '@ocap/util';
2
8
  import { getUrl } from '@blocklet/sdk/lib/component';
3
- import { Router } from 'express';
9
+ import { Hono } from 'hono';
10
+ import type { MiddlewareHandler } from 'hono';
4
11
  import Joi from 'joi';
5
12
 
6
13
  // eslint-disable-next-line import/no-extraneous-dependencies
7
14
  import { gte } from 'semver';
8
15
  import { Op } from 'sequelize';
9
- import { MetadataSchema } from '../libs/api';
10
- import { wallet } from '../libs/auth';
11
- import dayjs from '../libs/dayjs';
12
- import env from '../libs/env';
13
- import logger from '../libs/logger';
14
- import { authenticate } from '../libs/security';
15
- import { formatToShortUrl } from '../libs/url';
16
- import { getBlockletJson } from '../libs/util';
17
- import { VendorFulfillmentService } from '../libs/vendor-util/fulfillment';
18
- import { buildRefundInfoFromPayout } from '../libs/vendor-util/tool';
19
- import { CheckoutSession, Invoice, Payout, PaymentCurrency, Subscription } from '../store/models';
20
- import { ProductVendor } from '../store/models/product-vendor';
16
+ import { MetadataSchema } from '../../libs/api';
17
+ import { wallet } from '../../libs/auth';
18
+ import dayjs from '../../libs/dayjs';
19
+ import env, { isDevelopmentEnv } from '../../libs/env';
20
+ import logger from '../../libs/logger';
21
+ import { authenticate } from '../../middlewares/hono/security';
22
+ import { formatToShortUrl } from '../../libs/url';
23
+ import { getBlockletJson } from '../../libs/util';
24
+ import { VendorFulfillmentService } from '../../libs/vendor-util/fulfillment';
25
+ import { buildRefundInfoFromPayout } from '../../libs/vendor-util/tool';
26
+ import { CheckoutSession, Invoice, Payout, PaymentCurrency, Subscription } from '../../store/models';
27
+ import { ProductVendor } from '../../store/models/product-vendor';
21
28
 
22
29
  const VENDOR_DID = {
23
30
  launcher: 'z8iZkFBbrVQxZHvcWWB3Sa2TrfGmSeFz9MSU7',
@@ -67,45 +74,60 @@ const vendorRedirectQuerySchema = Joi.object({
67
74
  target: Joi.string().valid('home', 'dashboard').default('home').allow(''),
68
75
  });
69
76
 
70
- function validateParams(schema: Joi.ObjectSchema) {
71
- return (req: any, res: any, next: any) => {
72
- const { error } = schema.validate(req.params);
77
+ function validateParams(schema: Joi.ObjectSchema): MiddlewareHandler {
78
+ // eslint-disable-next-line require-await -- MiddlewareHandler contract is async; body is sync
79
+ return async (c, next) => {
80
+ const params: Record<string, string> = {};
81
+ // Collect all named params from the matched route
82
+ try {
83
+ const raw = c.req.param();
84
+ Object.assign(params, raw);
85
+ } catch (_) {
86
+ // no params
87
+ }
88
+ const { error } = schema.validate(params);
73
89
  if (error) {
74
90
  logger.error('Invalid parameters', {
75
91
  error,
76
- params: req.params,
77
- });
78
- return res.status(400).json({
79
- error: 'Invalid parameters',
80
- message: error.message,
92
+ params,
81
93
  });
94
+ return c.json(
95
+ {
96
+ error: 'Invalid parameters',
97
+ message: error.message,
98
+ },
99
+ 400
100
+ );
82
101
  }
83
102
  return next();
84
103
  };
85
104
  }
86
105
 
87
- function validateQuery(schema: Joi.ObjectSchema) {
88
- return (req: any, res: any, next: any) => {
89
- const { error, value } = schema.validate(req.query);
106
+ function validateQuery(schema: Joi.ObjectSchema): MiddlewareHandler {
107
+ // eslint-disable-next-line require-await -- MiddlewareHandler contract is async; body is sync
108
+ return async (c, next) => {
109
+ const query = c.req.query();
110
+ const { error, value } = schema.validate(query);
90
111
  if (error) {
91
112
  logger.error('Invalid query parameters', {
92
113
  error,
93
- query: req.query,
114
+ query,
94
115
  });
95
- return res.redirect('/404');
116
+ return c.redirect('/404');
96
117
  }
97
- req.query = value;
118
+ // Store validated/defaulted query in context for downstream use
119
+ c.set('validatedQuery' as any, value);
98
120
  return next();
99
121
  };
100
122
  }
101
123
 
102
- async function getAllVendors(_req: any, res: any) {
124
+ async function getAllVendors(c: any) {
103
125
  try {
104
126
  const vendors = await ProductVendor.findAll({
105
127
  order: [['created_at', 'DESC']],
106
128
  });
107
129
 
108
- return res.json({
130
+ return c.json({
109
131
  data: vendors,
110
132
  pk: wallet.publicKey,
111
133
  total: vendors.length,
@@ -115,24 +137,24 @@ async function getAllVendors(_req: any, res: any) {
115
137
  error,
116
138
  stack: error.stack,
117
139
  });
118
- return res.status(500).json({ error: 'Internal server error. Failed to get vendors' });
140
+ return c.json({ error: 'Internal server error. Failed to get vendors' }, 500);
119
141
  }
120
142
  }
121
143
 
122
- async function getVendorInfo(req: any, res: any) {
144
+ async function getVendorInfo(c: any) {
123
145
  try {
124
- const vendor = await ProductVendor.findByPk(req.params.id);
146
+ const vendor = await ProductVendor.findByPk(c.req.param('id'));
125
147
  if (!vendor) {
126
- return res.status(404).json({ error: 'Vendor not found' });
148
+ return c.json({ error: 'Vendor not found' }, 404);
127
149
  }
128
150
 
129
- return res.json({ data: vendor });
151
+ return c.json({ data: vendor });
130
152
  } catch (error: any) {
131
153
  logger.error('Failed to get vendor', {
132
154
  error,
133
- id: req.params.id,
155
+ id: c.req.param('id'),
134
156
  });
135
- return res.status(500).json({ error: 'Internal server error. Failed to get vendor' });
157
+ return c.json({ error: 'Internal server error. Failed to get vendor' }, 500);
136
158
  }
137
159
  }
138
160
 
@@ -181,14 +203,18 @@ async function prepareVendorData(appUrlInput: string, vendorType: 'launcher' | '
181
203
  };
182
204
  }
183
205
 
184
- async function createVendor(req: any, res: any) {
206
+ async function createVendor(c: any) {
185
207
  try {
186
- const { error, value } = createVendorSchema.validate(req.body);
208
+ const body = c.get('sanitizedBody') ?? {};
209
+ const { error, value } = createVendorSchema.validate(body);
187
210
  if (error) {
188
- return res.status(400).json({
189
- error: 'Validation failed',
190
- message: error.message,
191
- });
211
+ return c.json(
212
+ {
213
+ error: 'Validation failed',
214
+ message: error.message,
215
+ },
216
+ 400
217
+ );
192
218
  }
193
219
 
194
220
  const { vendor_key: vendorKey, vendor_type: type, name, description, metadata, status } = value;
@@ -199,12 +225,12 @@ async function createVendor(req: any, res: any) {
199
225
  where: { vendor_key: vendorKey },
200
226
  });
201
227
  if (existingVendor) {
202
- return res.status(400).json({ error: 'Vendor key already exists' });
228
+ return c.json({ error: 'Vendor key already exists' }, 400);
203
229
  }
204
230
 
205
231
  const preparedData = await prepareVendorData(value.app_url, vendorType, metadata);
206
232
  if ('error' in preparedData) {
207
- return res.status(400).json({ error: preparedData.error });
233
+ return c.json({ error: preparedData.error }, 400);
208
234
  }
209
235
 
210
236
  const vendor = await ProductVendor.create({
@@ -214,32 +240,36 @@ async function createVendor(req: any, res: any) {
214
240
  name,
215
241
  description,
216
242
  status: status || 'active',
217
- created_by: req.user?.did || 'admin',
243
+ created_by: c.get('user')?.did || 'admin',
218
244
  });
219
245
 
220
246
  logger.info('Vendor created', { vendorId: vendor.id, vendorKey: vendor.vendor_key });
221
- return res.status(201).json({ data: vendor });
247
+ return c.json({ data: vendor }, 201);
222
248
  } catch (error: any) {
223
249
  logger.error('Failed to create vendor', {
224
250
  error,
225
251
  });
226
- return res.status(500).json({ error: 'Internal server error' });
252
+ return c.json({ error: 'Internal server error' }, 500);
227
253
  }
228
254
  }
229
255
 
230
- async function updateVendor(req: any, res: any) {
256
+ async function updateVendor(c: any) {
231
257
  try {
232
- const vendor = await ProductVendor.findByPk(req.params.id);
258
+ const vendor = await ProductVendor.findByPk(c.req.param('id'));
233
259
  if (!vendor) {
234
- return res.status(404).json({ error: 'Vendor not found' });
260
+ return c.json({ error: 'Vendor not found' }, 404);
235
261
  }
236
262
 
237
- const { error, value } = updateVendorSchema.validate(req.body);
263
+ const body = c.get('sanitizedBody') ?? {};
264
+ const { error, value } = updateVendorSchema.validate(body);
238
265
  if (error) {
239
- return res.status(400).json({
240
- error: 'Validation failed',
241
- message: error.message,
242
- });
266
+ return c.json(
267
+ {
268
+ error: 'Validation failed',
269
+ message: error.message,
270
+ },
271
+ 400
272
+ );
243
273
  }
244
274
 
245
275
  const { vendor_type: type, vendor_key: vendorKey, name, description, status, metadata } = value;
@@ -249,14 +279,14 @@ async function updateVendor(req: any, res: any) {
249
279
  where: { vendor_key: vendorKey },
250
280
  });
251
281
  if (existingVendor) {
252
- return res.status(400).json({ error: 'Vendor key already exists' });
282
+ return c.json({ error: 'Vendor key already exists' }, 400);
253
283
  }
254
284
  }
255
285
 
256
286
  const vendorType = (type || 'launcher') as 'launcher' | 'didnames';
257
287
  const preparedData = await prepareVendorData(value.app_url, vendorType, metadata);
258
288
  if ('error' in preparedData) {
259
- return res.status(400).json({ error: preparedData.error });
289
+ return c.json({ error: preparedData.error }, 400);
260
290
  }
261
291
 
262
292
  const updates = {
@@ -271,38 +301,41 @@ async function updateVendor(req: any, res: any) {
271
301
  await vendor.update(Object.fromEntries(Object.entries(updates).filter(([, v]) => v !== undefined)));
272
302
 
273
303
  logger.info('Vendor updated', { vendorId: vendor.id });
274
- return res.json({ data: vendor });
304
+ return c.json({ data: vendor });
275
305
  } catch (error: any) {
276
- logger.error('Failed to update vendor', { error, id: req.params.id });
277
- return res.status(500).json({ error: 'Internal server error' });
306
+ logger.error('Failed to update vendor', { error, id: c.req.param('id') });
307
+ return c.json({ error: 'Internal server error' }, 500);
278
308
  }
279
309
  }
280
310
 
281
- async function deleteVendor(req: any, res: any) {
311
+ async function deleteVendor(c: any) {
282
312
  try {
283
- const vendor = await ProductVendor.findByPk(req.params.id);
313
+ const vendor = await ProductVendor.findByPk(c.req.param('id'));
284
314
  if (!vendor) {
285
- return res.status(404).json({ error: 'Vendor not found' });
315
+ return c.json({ error: 'Vendor not found' }, 404);
286
316
  }
287
317
 
288
318
  await vendor.destroy();
289
319
 
290
320
  logger.info('Vendor deleted', { vendorId: vendor.id });
291
- return res.json({ success: true });
321
+ return c.json({ success: true });
292
322
  } catch (error: any) {
293
- logger.error('Failed to delete vendor', { error, id: req.params.id });
294
- return res.status(500).json({ error: 'Internal server error' });
323
+ logger.error('Failed to delete vendor', { error, id: c.req.param('id') });
324
+ return c.json({ error: 'Internal server error' }, 500);
295
325
  }
296
326
  }
297
327
 
298
- async function testVendorConnection(req: any, res: any) {
328
+ async function testVendorConnection(c: any) {
299
329
  try {
300
- const vendor = await ProductVendor.findByPk(req.params.id);
330
+ const vendor = await ProductVendor.findByPk(c.req.param('id'));
301
331
  if (!vendor) {
302
- return res.status(404).json({
303
- code: 'VENDOR_NOT_FOUND',
304
- error: 'Vendor not found',
305
- });
332
+ return c.json(
333
+ {
334
+ code: 'VENDOR_NOT_FOUND',
335
+ error: 'Vendor not found',
336
+ },
337
+ 404
338
+ );
306
339
  }
307
340
 
308
341
  const vendorAdapter = await VendorFulfillmentService.getVendorAdapter(vendor.vendor_key);
@@ -310,32 +343,41 @@ async function testVendorConnection(req: any, res: any) {
310
343
 
311
344
  if (result.connected) {
312
345
  if (env.appId !== result.did) {
313
- return res.status(400).json({
314
- code: 'VENDOR_CONNECT_TEST_FAILED',
315
- error: 'Vendor connection test failed! Please check the DID in the vendor preferences config',
316
- });
346
+ return c.json(
347
+ {
348
+ code: 'VENDOR_CONNECT_TEST_FAILED',
349
+ error: 'Vendor connection test failed! Please check the DID in the vendor preferences config',
350
+ },
351
+ 400
352
+ );
317
353
  }
318
- return res.status(200).json({ ...result });
354
+ return c.json({ ...result }, 200);
319
355
  }
320
356
 
321
357
  if (result.error) {
322
- return res.status(400).json({
323
- code: 'VENDOR_CONNECT_TEST_FAILED',
324
- error: result.error,
325
- });
358
+ return c.json(
359
+ {
360
+ code: 'VENDOR_CONNECT_TEST_FAILED',
361
+ error: result.error,
362
+ },
363
+ 400
364
+ );
326
365
  }
327
366
 
328
367
  if (!result.sdkVersion) {
329
- return res.status(400).json({
330
- code: 'VENDOR_CONNECT_TEST_FAILED',
331
- error: 'Vendor SDK version is too low. Please upgrade to the latest version (>=1.21.4)',
332
- });
368
+ return c.json(
369
+ {
370
+ code: 'VENDOR_CONNECT_TEST_FAILED',
371
+ error: 'Vendor SDK version is too low. Please upgrade to the latest version (>=1.21.4)',
372
+ },
373
+ 400
374
+ );
333
375
  }
334
376
 
335
- return res.status(400).json({ ...result });
377
+ return c.json({ ...result }, 400);
336
378
  } catch (error: any) {
337
- logger.error('Failed to test vendor connection', { error, id: req.params.id });
338
- return res.status(500).json({ error: 'Internal server error' });
379
+ logger.error('Failed to test vendor connection', { error, id: c.req.param('id') });
380
+ return c.json({ error: 'Internal server error' }, 500);
339
381
  }
340
382
  }
341
383
 
@@ -503,45 +545,50 @@ async function getVendorStatus(sessionId: string) {
503
545
  return result;
504
546
  }
505
547
 
506
- async function getVendorFulfillmentStatus(req: any, res: any) {
507
- const { sessionId } = req.params;
548
+ async function getVendorFulfillmentStatus(c: any) {
549
+ const { sessionId } = c.req.param();
508
550
 
509
551
  try {
510
552
  const status = await getVendorStatus(sessionId);
511
553
 
512
554
  if (status.code) {
513
- return res.status(status.code).json({ error: status.error });
555
+ return c.json({ error: status.error }, status.code);
514
556
  }
515
- return res.json(status);
557
+ return c.json(status);
516
558
  } catch (error: any) {
517
- return res.status(500).json({ error: error.message });
559
+ return c.json({ error: error.message }, 500);
518
560
  }
519
561
  }
520
562
 
521
- async function getVendorFulfillmentDetail(req: any, res: any) {
522
- const { sessionId } = req.params;
523
- const { shortUrl, ownerDid, appDid } = req.query;
563
+ async function getVendorFulfillmentDetail(c: any) {
564
+ const { sessionId } = c.req.param();
565
+ const shortUrl = c.req.query('shortUrl');
566
+ const ownerDid = c.req.query('ownerDid');
567
+ const appDid = c.req.query('appDid');
524
568
 
525
569
  try {
526
570
  const detail = await processVendorOrders(sessionId, 'getOrder', shortUrl === 'true', ownerDid, appDid);
527
571
  if (detail.code) {
528
- return res.status(detail.code).json({ error: detail.error });
572
+ return c.json({ error: detail.error }, detail.code);
529
573
  }
530
- return res.json(detail);
574
+ return c.json(detail);
531
575
  } catch (error: any) {
532
- return res.status(500).json({ error: error.message });
576
+ return c.json({ error: error.message }, 500);
533
577
  }
534
578
  }
535
579
 
536
- async function redirectToVendor(req: any, res: any) {
537
- const { subscriptionId } = req.params;
538
- const { vendorId, target } = req.query;
580
+ async function redirectToVendor(c: any) {
581
+ const { subscriptionId } = c.req.param();
582
+ // validated query values are stored by validateQuery middleware
583
+ const validatedQuery = c.get('validatedQuery' as any) ?? {};
584
+ const vendorId = validatedQuery.vendorId ?? c.req.query('vendorId');
585
+ const target = validatedQuery.target ?? c.req.query('target');
539
586
 
540
587
  try {
541
588
  const checkoutSession = await CheckoutSession.findBySubscriptionId(subscriptionId);
542
589
  if (!checkoutSession) {
543
590
  logger.warn('CheckoutSession not found for subscription[redirect to vendor]', { subscriptionId });
544
- return res.redirect('/404');
591
+ return c.redirect('/404');
545
592
  }
546
593
 
547
594
  const order = checkoutSession?.vendor_info?.find((item) => item.vendor_id === vendorId);
@@ -551,16 +598,16 @@ async function redirectToVendor(req: any, res: any) {
551
598
  vendorId,
552
599
  availableVendors: checkoutSession.vendor_info?.map((v) => v.vendor_key) || [],
553
600
  });
554
- return res.redirect('/404');
601
+ return c.redirect('/404');
555
602
  }
556
603
 
557
- const isOwner = req.user.did === checkoutSession.customer_did;
604
+ const isOwner = c.get('user').did === checkoutSession.customer_did;
558
605
 
559
606
  if (!isOwner) {
560
607
  if (order.app_url) {
561
- return res.redirect(order.app_url);
608
+ return c.redirect(order.app_url);
562
609
  }
563
- return res.redirect('/404');
610
+ return c.redirect('/404');
564
611
  }
565
612
 
566
613
  const result = await executeVendorOperation({
@@ -578,11 +625,11 @@ async function redirectToVendor(req: any, res: any) {
578
625
  error: result.error,
579
626
  message: result.message,
580
627
  });
581
- return res.redirect('/404');
628
+ return c.redirect('/404');
582
629
  }
583
630
 
584
631
  const redirectUrl = target === 'dashboard' ? result.data.dashboardUrl : result.data.homeUrl;
585
- return res.redirect(redirectUrl);
632
+ return c.redirect(redirectUrl);
586
633
  } catch (error: any) {
587
634
  logger.error('Failed to redirect to vendor service', {
588
635
  error,
@@ -590,17 +637,18 @@ async function redirectToVendor(req: any, res: any) {
590
637
  vendorId,
591
638
  target,
592
639
  });
593
- return res.redirect('/404');
640
+ return c.redirect('/404');
594
641
  }
595
642
  }
596
643
 
597
- async function getCancelledSessions(req: any, res: any) {
598
- const { error } = sessionIdsParamSchema.validate(req.body);
644
+ async function getCancelledSessions(c: any) {
645
+ const body = c.get('sanitizedBody') ?? {};
646
+ const { error } = sessionIdsParamSchema.validate(body);
599
647
  if (error) {
600
- return res.status(400).json({ error: error.message });
648
+ return c.json({ error: error.message }, 400);
601
649
  }
602
650
 
603
- const { sessionIds = [] } = req.body;
651
+ const { sessionIds = [] } = body;
604
652
 
605
653
  const allCheckoutSessions = await CheckoutSession.findAll({
606
654
  where: { id: { [Op.in]: sessionIds } },
@@ -621,22 +669,22 @@ async function getCancelledSessions(req: any, res: any) {
621
669
  const cancelledSessions = allCheckoutSessions.filter(
622
670
  (item) => item.subscription_id && cancelledSubIds.includes(item.subscription_id)
623
671
  );
624
- return res.json({ cancelledSessions });
672
+ return c.json({ cancelledSessions });
625
673
  }
626
674
 
627
- async function getVendorSubscription(req: any, res: any) {
628
- const { sessionId } = req.params;
675
+ async function getVendorSubscription(c: any) {
676
+ const { sessionId } = c.req.param();
629
677
 
630
678
  const checkoutSession = await CheckoutSession.findByPk(sessionId);
631
679
 
632
680
  if (!checkoutSession) {
633
- return res.status(404).json({ error: 'Checkout session not found' });
681
+ return c.json({ error: 'Checkout session not found' }, 404);
634
682
  }
635
683
 
636
684
  const subscription = await Subscription.findByPk(checkoutSession.subscription_id);
637
685
 
638
686
  if (!subscription) {
639
- return res.status(404).json({ error: 'Subscription not found' });
687
+ return c.json({ error: 'Subscription not found' }, 404);
640
688
  }
641
689
 
642
690
  const invoices = await Invoice.findAll({
@@ -667,56 +715,57 @@ async function getVendorSubscription(req: any, res: any) {
667
715
  limit: 20,
668
716
  });
669
717
 
670
- return res.json({
718
+ return c.json({
671
719
  subscription: subscription.toJSON(),
672
720
  billing_history: invoices,
673
721
  });
674
722
  }
675
723
 
676
- async function handleSubscriptionRedirect(req: any, res: any) {
677
- const { sessionId } = req.params;
724
+ async function handleSubscriptionRedirect(c: any) {
725
+ const { sessionId } = c.req.param();
678
726
 
679
727
  const checkoutSession = await CheckoutSession.findByPk(sessionId);
680
728
  if (!checkoutSession) {
681
- return res.status(404).json({ error: 'Checkout session not found' });
729
+ return c.json({ error: 'Checkout session not found' }, 404);
682
730
  }
683
731
 
684
- return res.redirect(getUrl(`/customer/subscription/${checkoutSession.subscription_id}`));
732
+ return c.redirect(getUrl(`/customer/subscription/${checkoutSession.subscription_id}`));
685
733
  }
686
734
 
687
- async function vendorRefund(req: any, res: any) {
688
- const { id } = req.params;
689
- const { reason } = req.body;
735
+ async function vendorRefund(c: any) {
736
+ const id = c.req.param('id');
737
+ const body = c.get('sanitizedBody') ?? {};
738
+ const { reason } = body;
690
739
  try {
691
740
  const payout = await Payout.findByPk(id);
692
741
 
693
742
  if (!payout) {
694
- return res.status(404).json({ error: 'Payout not found' });
743
+ return c.json({ error: 'Payout not found' }, 404);
695
744
  }
696
745
 
697
746
  if (payout.status !== 'paid') {
698
- return res.status(400).json({ error: 'Only paid payouts can be refunded' });
747
+ return c.json({ error: 'Only paid payouts can be refunded' }, 400);
699
748
  }
700
749
 
701
750
  if (!payout.vendor_info?.vendor_id || !payout.vendor_info?.order_id) {
702
- return res.status(400).json({ error: 'Payout has no vendor information' });
751
+ return c.json({ error: 'Payout has no vendor information' }, 400);
703
752
  }
704
753
 
705
754
  const vendor = await ProductVendor.findByPk(payout.vendor_info.vendor_id);
706
755
  if (!vendor) {
707
- return res.status(404).json({ error: 'Vendor not found' });
756
+ return c.json({ error: 'Vendor not found' }, 404);
708
757
  }
709
758
 
710
759
  let refundInfo;
711
760
  try {
712
761
  refundInfo = await buildRefundInfoFromPayout(payout, reason);
713
762
  } catch (err: any) {
714
- return res.status(400).json({ error: err.message });
763
+ return c.json({ error: err.message }, 400);
715
764
  }
716
765
 
717
766
  const vendorAdapter = await VendorFulfillmentService.getVendorAdapter(vendor.vendor_key);
718
767
  if (!vendorAdapter) {
719
- return res.status(404).json({ error: `No adapter found for vendor: ${vendor.vendor_key}` });
768
+ return c.json({ error: `No adapter found for vendor: ${vendor.vendor_key}` }, 404);
720
769
  }
721
770
 
722
771
  logger.info('Requesting refund from vendor', {
@@ -724,7 +773,7 @@ async function vendorRefund(req: any, res: any) {
724
773
  vendorId: vendor.id,
725
774
  orderId: payout.vendor_info.order_id,
726
775
  refundInfo,
727
- requestedBy: req.user?.did,
776
+ requestedBy: c.get('user')?.did,
728
777
  });
729
778
 
730
779
  const result = await vendorAdapter.requestRefund({
@@ -739,7 +788,7 @@ async function vendorRefund(req: any, res: any) {
739
788
  refundResult: result.data?.refundResult,
740
789
  });
741
790
  await payout.update({ status: 'reverted', description: reason || 'Refund requested by admin' });
742
- return res.json({ data: result.data?.refundResult });
791
+ return c.json({ data: result.data?.refundResult });
743
792
  }
744
793
  logger.error('Vendor payout refund failed', {
745
794
  payoutId: payout.id,
@@ -747,49 +796,148 @@ async function vendorRefund(req: any, res: any) {
747
796
  refundInfo,
748
797
  refundResult: result.data?.refundResult,
749
798
  });
750
- return res.status(400).json({
751
- error: result.message || 'Vendor payout refund failed',
752
- data: result.data?.refundResult,
753
- requestedBy: req.user?.did,
754
- });
799
+ return c.json(
800
+ {
801
+ error: result.message || 'Vendor payout refund failed',
802
+ data: result.data?.refundResult,
803
+ requestedBy: c.get('user')?.did,
804
+ },
805
+ 400
806
+ );
755
807
  } catch (err: any) {
756
808
  logger.error('Request refund failed', {
757
809
  error: err,
758
- payoutId: req.params.id,
759
- requestedBy: req.user?.did,
810
+ payoutId: c.req.param('id'),
811
+ requestedBy: c.get('user')?.did,
760
812
  });
761
- return res.status(500).json({ error: err.message || 'Failed to request refund' });
813
+ return c.json({ error: err.message || 'Failed to request refund' }, 500);
762
814
  }
763
815
  }
764
816
 
765
- function getVendorConnectTest(req: any, res: any) {
766
- const sdkVersion = req.headers['x-broker-vendor-version'];
817
+ function getVendorConnectTest(c: any) {
818
+ const sdkVersion = c.req.header('x-broker-vendor-version');
767
819
  if (sdkVersion && gte(sdkVersion, '1.21.4')) {
768
- return res.json({ connected: true, sdkVersion });
820
+ return c.json({ connected: true, sdkVersion });
769
821
  }
770
- return res.json({
822
+ return c.json({
771
823
  connected: false,
772
824
  sdkVersion,
773
825
  error: 'Vendor SDK version is too low, please upgrade to the latest version (>=1.21.4)',
774
826
  });
775
827
  }
776
828
 
777
- const router = Router();
829
+ const app = new Hono();
778
830
 
779
- const ensureVendorAuth = middleware.ensureVendorAuth((vendorPk: string) =>
780
- ProductVendor.findOne({ where: { 'extends.appPk': vendorPk } }).then((v) => v as any)
781
- );
831
+ // Hono-native version of middleware.ensureVendorAuth inlined because the
832
+ // upstream middleware is express-specific (req.vendor, req.vendorRequest).
833
+ const ensureVendorAuth: MiddlewareHandler = async (c, next) => {
834
+ try {
835
+ let vendorPk = c.req.header('x-broker-vendor-did') as string;
836
+ const sig = c.req.header('x-broker-vendor-sig') as string;
837
+
838
+ if (!vendorPk) {
839
+ logger.warn('Authentication failed: Missing vendor ID header', { url: c.req.url });
840
+ return c.json(
841
+ {
842
+ error: 'Vendor public key is required',
843
+ code: 'MISSING_VENDOR_PUBLIC_KEY',
844
+ },
845
+ 400
846
+ );
847
+ }
848
+
849
+ if (!sig) {
850
+ logger.warn('Authentication failed: Missing signature header', { url: c.req.url });
851
+ return c.json(
852
+ {
853
+ error: 'Vendor signature is required',
854
+ code: 'MISSING_SIGNATURE',
855
+ },
856
+ 400
857
+ );
858
+ }
859
+
860
+ vendorPk = vendorPk.startsWith('0x') ? toBase58(vendorPk) : vendorPk;
861
+ const isGetRequest = c.req.method === 'GET';
862
+ const requestBody = isGetRequest ? (c.req.query() as any) : ((c.get('sanitizedBody') ?? {}) as any);
863
+
864
+ if (!requestBody || typeof requestBody !== 'object') {
865
+ return c.json(
866
+ {
867
+ error: 'Valid request body is required',
868
+ code: 'INVALID_REQUEST_BODY',
869
+ },
870
+ 400
871
+ );
872
+ }
873
+
874
+ const vendor = await ProductVendor.findOne({ where: { 'extends.appPk': vendorPk } }).then((v) => v as any);
875
+
876
+ if (!vendor) {
877
+ logger.warn('Authorization failed: Vendor not found or inactive', {
878
+ url: c.req.url,
879
+ vendorId: `${vendorPk.substring(0, 10)}...`,
880
+ });
881
+ return c.json(
882
+ {
883
+ error: 'Vendor not found or inactive',
884
+ code: 'VENDOR_NOT_ALLOWED',
885
+ },
886
+ 403
887
+ );
888
+ }
889
+
890
+ const signedRequest = { ...requestBody, signature: sig };
891
+ const verified = await VendorAuth.verifyRequest(signedRequest, vendor.extends.appPk);
892
+
893
+ if (!verified) {
894
+ logger.warn('Authentication failed: Invalid request signature', {
895
+ url: c.req.url,
896
+ vendorId: vendor.id,
897
+ appId: vendor.extends.appId,
898
+ });
899
+ return c.json(
900
+ {
901
+ error: 'Invalid request signature',
902
+ code: 'INVALID_SIGNATURE',
903
+ },
904
+ 401
905
+ );
906
+ }
907
+
908
+ c.set('vendor' as any, vendor);
909
+ c.set('vendorRequest' as any, signedRequest);
910
+
911
+ // eslint-disable-next-line @typescript-eslint/return-await -- do NOT await: downstream errors must not be caught by this auth catch
912
+ return next();
913
+ } catch (error: any) {
914
+ logger.error('Vendor authentication middleware error', {
915
+ url: c.req.url,
916
+ error: error.message,
917
+ stack: error.stack,
918
+ });
919
+
920
+ return c.json(
921
+ {
922
+ error: 'Authentication failed',
923
+ code: 'AUTHENTICATION_ERROR',
924
+ details: isDevelopmentEnv() ? error.message : undefined,
925
+ },
926
+ 500
927
+ );
928
+ }
929
+ };
782
930
 
783
- router.get('/order/:sessionId/status', loginAuth, validateParams(sessionIdParamSchema), getVendorFulfillmentStatus);
784
- router.get('/order/:sessionId/detail', loginAuth, validateParams(sessionIdParamSchema), getVendorFulfillmentDetail);
931
+ app.get('/order/:sessionId/status', loginAuth, validateParams(sessionIdParamSchema), getVendorFulfillmentStatus);
932
+ app.get('/order/:sessionId/detail', loginAuth, validateParams(sessionIdParamSchema), getVendorFulfillmentDetail);
785
933
 
786
934
  // Those for Vendor Call
787
- router.get('/connectTest', ensureVendorAuth, getVendorConnectTest);
788
- router.post('/subscription/cancelled', ensureVendorAuth, getCancelledSessions);
789
- router.get('/subscription/:sessionId/redirect', handleSubscriptionRedirect);
790
- router.get('/subscription/:sessionId', ensureVendorAuth, getVendorSubscription);
935
+ app.get('/connectTest', ensureVendorAuth, getVendorConnectTest);
936
+ app.post('/subscription/cancelled', ensureVendorAuth, getCancelledSessions);
937
+ app.get('/subscription/:sessionId/redirect', handleSubscriptionRedirect);
938
+ app.get('/subscription/:sessionId', ensureVendorAuth, getVendorSubscription);
791
939
 
792
- router.get(
940
+ app.get(
793
941
  '/open/:subscriptionId',
794
942
  loginAuth,
795
943
  validateParams(subscriptionIdParamSchema),
@@ -797,14 +945,14 @@ router.get(
797
945
  redirectToVendor
798
946
  );
799
947
 
800
- router.post('/refund/:id', authAdmin, vendorRefund);
948
+ app.post('/refund/:id', authAdmin, vendorRefund);
801
949
 
802
- router.get('/', getAllVendors);
803
- router.get('/:id', authAdmin, validateParams(vendorIdParamSchema), getVendorInfo);
804
- router.post('/', authAdmin, createVendor);
805
- router.put('/:id', authAdmin, validateParams(vendorIdParamSchema), updateVendor);
806
- router.delete('/:id', authAdmin, validateParams(vendorIdParamSchema), deleteVendor);
950
+ app.get('/', getAllVendors);
951
+ app.get('/:id', authAdmin, validateParams(vendorIdParamSchema), getVendorInfo);
952
+ app.post('/', authAdmin, createVendor);
953
+ app.put('/:id', authAdmin, validateParams(vendorIdParamSchema), updateVendor);
954
+ app.delete('/:id', authAdmin, validateParams(vendorIdParamSchema), deleteVendor);
807
955
 
808
- router.post('/:id/test-connection', authAdmin, validateParams(vendorIdParamSchema), testVendorConnection);
956
+ app.post('/:id/test-connection', authAdmin, validateParams(vendorIdParamSchema), testVendorConnection);
809
957
 
810
- export default router;
958
+ export default app;