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,18 +1,27 @@
1
- import { sessionMiddleware } from '@blocklet/sdk/lib/middlewares/session';
2
- import { Router } from 'express';
1
+ // Phase 3 (express→hono) hono fork of routes/customers.ts. Sub-app with
2
+ // routes relative to /api/customers (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
+ import { Hono } from 'hono';
3
6
  import Joi from 'joi';
4
7
  import pick from 'lodash/pick';
5
8
  import isEmail from 'validator/es/lib/isEmail';
6
9
 
7
10
  import { Op, type WhereOptions } from 'sequelize';
8
11
  import { BN } from '@ocap/util';
9
- import { getStakeSummaryByDid, getTokenSummaryByDid, getTokenByAddress } from '../integrations/arcblock/stake';
10
- import { createListParamSchema, getOrder, getWhereFromKvQuery, getWhereFromQuery, MetadataSchema } from '../libs/api';
11
- import { authenticate } from '../libs/security';
12
- import { formatMetadata } from '../libs/util';
13
- import { Customer } from '../store/models/customer';
14
- import { blocklet } from '../libs/auth';
15
- import logger from '../libs/logger';
12
+ import { getStakeSummaryByDid, getTokenSummaryByDid, getTokenByAddress } from '../../integrations/arcblock/stake';
13
+ import {
14
+ createListParamSchema,
15
+ getOrder,
16
+ getWhereFromKvQuery,
17
+ getWhereFromQuery,
18
+ MetadataSchema,
19
+ } from '../../libs/api';
20
+ import { authenticate } from '../../middlewares/hono/security';
21
+ import { formatMetadata } from '../../libs/util';
22
+ import { Customer } from '../../store/models/customer';
23
+ import { blocklet } from '../../libs/auth';
24
+ import logger from '../../libs/logger';
16
25
  import {
17
26
  Invoice,
18
27
  PaymentCurrency,
@@ -21,14 +30,13 @@ import {
21
30
  Product,
22
31
  Subscription,
23
32
  SubscriptionItem,
24
- } from '../store/models';
25
- import { getSubscriptionPaymentAddress, calculateRecommendedRechargeAmount } from '../libs/subscription';
26
- import { expandLineItems } from '../libs/session';
27
- import { handleNotificationPreferenceChange } from '../queues/notification';
28
- import { getEndpointAndSpaceDid } from '../libs/did-space';
29
- import { spaceQueue } from '../queues/space';
30
-
31
- const router = Router();
33
+ } from '../../store/models';
34
+ import { getSubscriptionPaymentAddress, calculateRecommendedRechargeAmount } from '../../libs/subscription';
35
+ import { expandLineItems } from '../../libs/session';
36
+ import { handleNotificationPreferenceChange } from '../../queues/notification';
37
+ import { sessionMiddleware } from '../../middlewares/hono/session';
38
+
39
+ const app = new Hono();
32
40
  const auth = authenticate<Customer>({ component: true, roles: ['owner', 'admin'] });
33
41
  const authPortal = authenticate<Customer>({
34
42
  component: true,
@@ -42,8 +50,8 @@ const authPortal = authenticate<Customer>({
42
50
  });
43
51
 
44
52
  const schema = createListParamSchema<{ did?: string }>({ did: Joi.string().empty('') });
45
- router.get('/', auth, async (req, res) => {
46
- const { page, pageSize, ...query } = await schema.validateAsync(req.query, { stripUnknown: true });
53
+ app.get('/', auth, async (c) => {
54
+ const { page, pageSize, ...query } = await schema.validateAsync(c.req.query(), { stripUnknown: true });
47
55
  const where = getWhereFromKvQuery(query.q);
48
56
 
49
57
  if (query.did) {
@@ -59,21 +67,23 @@ router.get('/', auth, async (req, res) => {
59
67
  include: [],
60
68
  });
61
69
 
62
- res.json({ count, list, paging: { page, pageSize } });
70
+ return c.json({ count, list, paging: { page, pageSize } });
63
71
  } catch (err) {
64
72
  logger.error(err);
65
- res.json({ count: 0, list: [], paging: { page, pageSize } });
73
+ return c.json({ count: 0, list: [], paging: { page, pageSize } });
66
74
  }
67
75
  });
68
76
 
69
77
  // search customers
78
+ // Static path — registered before /:id so hono matches it first.
70
79
  const searchSchema = createListParamSchema<{
71
80
  query: string;
72
81
  }>({
73
82
  query: Joi.string(),
74
83
  });
75
- router.get('/search', auth, async (req, res) => {
76
- const { page, pageSize, query, livemode, q, o } = await searchSchema.validateAsync(req.query, {
84
+ app.get('/search', auth, async (c) => {
85
+ const rawQuery = c.req.query();
86
+ const { page, pageSize, query, livemode, q, o } = await searchSchema.validateAsync(rawQuery, {
77
87
  stripUnknown: false,
78
88
  allowUnknown: true,
79
89
  });
@@ -84,35 +94,35 @@ router.get('/search', auth, async (req, res) => {
84
94
  }
85
95
  const { rows: list, count } = await Customer.findAndCountAll({
86
96
  where,
87
- order: getOrder(req.query, [['created_at', o === 'asc' ? 'ASC' : 'DESC']]),
97
+ order: getOrder(rawQuery, [['created_at', o === 'asc' ? 'ASC' : 'DESC']]),
88
98
  offset: (page - 1) * pageSize,
89
99
  limit: pageSize,
90
100
  include: [],
91
101
  });
92
102
 
93
- res.json({ count, list, paging: { page, pageSize } });
103
+ return c.json({ count, list, paging: { page, pageSize } });
94
104
  });
95
105
 
96
- // eslint-disable-next-line consistent-return
97
- router.get('/me', sessionMiddleware({ accessKey: true }), async (req, res) => {
98
- if (!req.user) {
99
- return res.status(403).json({ error: 'Unauthorized' });
106
+ // Static path — registered before /:id so hono matches it first.
107
+ app.get('/me', sessionMiddleware({ accessKey: true }), async (c) => {
108
+ if (!c.get('user')) {
109
+ return c.json({ error: 'Unauthorized' }, 403);
100
110
  }
101
111
 
102
112
  try {
103
- let doc = await Customer.findByPkOrDid(req.user.did as string);
104
- const livemode = req.query.livemode ? !!req?.livemode : !!doc?.livemode;
113
+ let doc = await Customer.findByPkOrDid(c.get('user').did as string);
114
+ const livemode = c.req.query('livemode') ? !!c.get('livemode') : !!doc?.livemode;
105
115
  if (!doc) {
106
- if (req.query.fallback) {
107
- const result = await blocklet.getUser(req.user.did);
108
- return res.json({ ...result.user, address: Customer.formatAddressFromUser(result.user), livemode });
116
+ if (c.req.query('fallback')) {
117
+ const result = await blocklet.getUser(c.get('user').did);
118
+ return c.json({ ...result.user, address: Customer.formatAddressFromUser(result.user), livemode });
109
119
  }
110
- if (req.query.create) {
120
+ if (c.req.query('create')) {
111
121
  // create customer
112
- const { user } = await blocklet.getUser(req.user.did);
122
+ const { user } = await blocklet.getUser(c.get('user').did);
113
123
  const customer = await Customer.create({
114
124
  livemode: true,
115
- did: req.user.did,
125
+ did: c.get('user').did,
116
126
  name: user.fullName,
117
127
  email: user.email,
118
128
  phone: user.phone,
@@ -130,11 +140,11 @@ router.get('/me', sessionMiddleware({ accessKey: true }), async (req, res) => {
130
140
  });
131
141
  doc = customer;
132
142
  } else {
133
- return res.json({ error: 'Customer not found' });
143
+ return c.json({ error: 'Customer not found' });
134
144
  }
135
145
  }
136
- if (req.query.skipSummary) {
137
- return res.json({ ...doc.toJSON(), livemode });
146
+ if (c.req.query('skipSummary')) {
147
+ return c.json({ ...doc.toJSON(), livemode });
138
148
  }
139
149
  try {
140
150
  const [summary, stake, token] = await Promise.all([
@@ -142,11 +152,11 @@ router.get('/me', sessionMiddleware({ accessKey: true }), async (req, res) => {
142
152
  getStakeSummaryByDid(doc.did, livemode),
143
153
  getTokenSummaryByDid(doc.did, livemode),
144
154
  ]);
145
- res.json({ ...doc.toJSON(), summary: { ...summary, stake, token }, livemode });
155
+ return c.json({ ...doc.toJSON(), summary: { ...summary, stake, token }, livemode });
146
156
  } catch (summaryErr) {
147
157
  logger.error('get customer summary failed', summaryErr);
148
- if (req.query.skipError) {
149
- return res.json({
158
+ if (c.req.query('skipError')) {
159
+ return c.json({
150
160
  ...doc.toJSON(),
151
161
  summary: { stake: {}, token: {} },
152
162
  livemode,
@@ -158,147 +168,42 @@ router.get('/me', sessionMiddleware({ accessKey: true }), async (req, res) => {
158
168
  }
159
169
  } catch (err) {
160
170
  logger.error('get customer failed', err);
161
- if (req.query.skipError) {
162
- return res.json({
171
+ if (c.req.query('skipError')) {
172
+ return c.json({
163
173
  error: `Failed to get customer: ${err.message}`,
164
- did: req.user?.did,
165
- name: req.user?.fullName,
174
+ did: c.get('user')?.did,
175
+ name: c.get('user')?.fullName,
166
176
  address: {},
167
- livemode: !!req.query.livemode,
177
+ livemode: !!c.req.query('livemode'),
168
178
  summary: { stake: {}, token: {} },
169
179
  });
170
180
  }
171
- return res.status(500).json({ error: `Failed to get customer: ${err.message}` });
181
+ return c.json({ error: `Failed to get customer: ${err.message}` }, 500);
172
182
  }
173
183
  });
174
184
 
175
- router.post('/sync-to-space', sessionMiddleware(), async (req, res) => {
176
- if (!req.user) {
177
- return res.status(403).json({ error: 'Unauthorized' });
185
+ // Static path registered before /:id so hono matches it first.
186
+ app.get('/recharge', sessionMiddleware({ accessKey: true }), async (c) => {
187
+ if (!c.get('user')) {
188
+ return c.json({ error: 'Unauthorized' }, 403);
178
189
  }
179
- try {
180
- const userDid = req.user.did;
181
- const jobId = `space-${userDid}`;
182
- const { endpoint } = await getEndpointAndSpaceDid(userDid);
183
- if (endpoint) {
184
- const mainTask = await spaceQueue.get(jobId);
185
- if (mainTask) {
186
- return res.json({
187
- success: true,
188
- message: 'Billing data sync already in progress',
189
- });
190
- }
191
- spaceQueue.push({
192
- id: jobId,
193
- job: {
194
- type: 'customer',
195
- data: {
196
- id: userDid,
197
- },
198
- },
199
- delay: 60, // delay 1min
200
- });
201
- logger.info('Queued billing sync to DID Space for user:', { did: req.user.did });
202
-
203
- return res.json({
204
- success: true,
205
- message: 'Billing data sync will start soon',
206
- });
207
- }
208
- return res.json({
209
- success: false,
210
- message: 'No endpoint found for the user',
211
- });
212
- } catch (error) {
213
- return res.json({
214
- success: false,
215
- message: error.message,
216
- });
190
+ if (!c.req.query('currencyId')) {
191
+ return c.json({ error: 'Currency ID is required' }, 400);
217
192
  }
218
- });
219
-
220
- // get overdue invoices
221
- router.get('/:id/overdue/invoices', authPortal, async (req, res) => {
222
193
  try {
223
- const doc = await Customer.findByPkOrDid(req.params.id as string);
224
- if (!doc) {
225
- return res.status(404).json({ error: 'Customer not found' });
226
- }
227
- const where: WhereOptions<Invoice> = {
228
- customer_id: doc.id,
229
- status: ['uncollectible'],
230
- amount_remaining: { [Op.gt]: '0' },
231
- };
232
- if (typeof req.livemode === 'boolean') {
233
- where.livemode = !!req.livemode;
234
- }
235
- const { rows: invoices, count } = await Invoice.findAndCountAll({
236
- where,
237
- include: [
238
- { model: PaymentCurrency, as: 'paymentCurrency' },
239
- { model: PaymentMethod, as: 'paymentMethod' },
240
- ],
241
- });
242
- if (count === 0) {
243
- return res.json({
244
- summary: null,
245
- invoices: [],
246
- subscriptionCount: 0,
247
- });
248
- }
249
- const summary: Record<string, { amount: string; currency: PaymentCurrency; method: PaymentMethod }> = {};
250
- invoices.forEach((invoice) => {
251
- const key = invoice.currency_id;
252
- if (!summary[key]) {
253
- summary[key] = {
254
- amount: '0',
255
- // @ts-ignore
256
- currency: invoice.paymentCurrency,
257
- // @ts-ignore
258
- method: invoice.paymentMethod,
259
- };
260
- }
261
- if (invoice && summary[key]) {
262
- // @ts-ignore
263
- summary[key].amount = new BN(summary[key]?.amount || '0')
264
- .add(new BN(invoice.amount_remaining || '0'))
265
- .toString();
266
- }
267
- });
268
- const subscriptionCount = new Set(invoices.map((x) => x.subscription_id)).size;
269
- return res.json({
270
- summary,
271
- invoices,
272
- subscriptionCount,
273
- customer: doc,
274
- });
275
- } catch (err) {
276
- logger.error(err);
277
- return res.status(500).json({ error: `Failed to get overdue invoices: ${err.message}` });
278
- }
279
- });
280
-
281
- router.get('/recharge', sessionMiddleware({ accessKey: true }), async (req, res) => {
282
- if (!req.user) {
283
- return res.status(403).json({ error: 'Unauthorized' });
284
- }
285
- if (!req.query.currencyId) {
286
- return res.status(400).json({ error: 'Currency ID is required' });
287
- }
288
- try {
289
- const customer = await Customer.findByPkOrDid(req.user.did as string);
194
+ const customer = await Customer.findByPkOrDid(c.get('user').did as string);
290
195
  if (!customer) {
291
- return res.status(404).json({ error: 'Customer not found' });
196
+ return c.json({ error: 'Customer not found' }, 404);
292
197
  }
293
198
 
294
- const paymentCurrency = await PaymentCurrency.findByPk(req.query.currencyId as string);
199
+ const paymentCurrency = await PaymentCurrency.findByPk(c.req.query('currencyId') as string);
295
200
  if (!paymentCurrency) {
296
- return res.status(404).json({ error: 'Currency not found' });
201
+ return c.json({ error: 'Currency not found' }, 404);
297
202
  }
298
203
 
299
204
  const paymentMethod = await PaymentMethod.findByPk(paymentCurrency.payment_method_id);
300
205
  if (!paymentMethod) {
301
- return res.status(404).json({ error: 'Payment method not found' });
206
+ return c.json({ error: 'Payment method not found' }, 404);
302
207
  }
303
208
 
304
209
  let subscriptions = await Subscription.findAll({
@@ -338,7 +243,7 @@ router.get('/recharge', sessionMiddleware({ accessKey: true }), async (req, res)
338
243
  // Calculate recommended recharge cycle and amount
339
244
  const recommendedRecharge = calculateRecommendedRechargeAmount(relatedSubscriptions, paymentCurrency.id);
340
245
 
341
- return res.json({
246
+ return c.json({
342
247
  currency: {
343
248
  ...paymentCurrency.toJSON(),
344
249
  paymentMethod,
@@ -348,79 +253,142 @@ router.get('/recharge', sessionMiddleware({ accessKey: true }), async (req, res)
348
253
  });
349
254
  } catch (err) {
350
255
  logger.error('Error getting balance recharge info', err);
351
- return res.status(500).json({ error: err.message });
256
+ return c.json({ error: err.message }, 500);
352
257
  }
353
258
  });
354
259
 
355
260
  // get address token
356
- router.get('/payer-token', sessionMiddleware({ accessKey: true }), async (req, res) => {
357
- if (!req.user) {
358
- return res.status(403).json({ error: 'Unauthorized' });
261
+ // Static path registered before /:id so hono matches it first.
262
+ app.get('/payer-token', sessionMiddleware({ accessKey: true }), async (c) => {
263
+ if (!c.get('user')) {
264
+ return c.json({ error: 'Unauthorized' }, 403);
359
265
  }
360
- if (!req.query.currencyId) {
361
- return res.status(400).json({ error: 'Currency ID is required' });
266
+ if (!c.req.query('currencyId')) {
267
+ return c.json({ error: 'Currency ID is required' }, 400);
362
268
  }
363
269
  try {
364
- let paymentAddress = req.query.payerAddress as string;
270
+ let paymentAddress = c.req.query('payerAddress') as string;
365
271
  let customer: Customer | null = null;
366
272
  if (!paymentAddress) {
367
- customer = await Customer.findByPkOrDid(req.user.did as string);
273
+ customer = await Customer.findByPkOrDid(c.get('user').did as string);
368
274
  if (!customer) {
369
- return res.status(404).json({ error: 'Customer not found' });
275
+ return c.json({ error: 'Customer not found' }, 404);
370
276
  }
371
277
  paymentAddress = customer.did;
372
278
  }
373
279
 
374
- const paymentCurrency = await PaymentCurrency.findByPk(req.query.currencyId as string);
280
+ const paymentCurrency = await PaymentCurrency.findByPk(c.req.query('currencyId') as string);
375
281
  if (!paymentCurrency) {
376
- return res.status(404).json({ error: 'Currency not found' });
282
+ return c.json({ error: 'Currency not found' }, 404);
377
283
  }
378
284
 
379
285
  const paymentMethod = await PaymentMethod.findByPk(paymentCurrency.payment_method_id);
380
286
  if (!paymentMethod) {
381
- return res.status(404).json({ error: 'Payment method not found' });
287
+ return c.json({ error: 'Payment method not found' }, 404);
382
288
  }
383
289
 
384
290
  if (!['arcblock', 'ethereum', 'base'].includes(paymentMethod.type)) {
385
- return res.status(400).json({ error: `Payment method not supported: ${paymentMethod.type}` });
291
+ return c.json({ error: `Payment method not supported: ${paymentMethod.type}` }, 400);
386
292
  }
387
293
 
388
294
  if (!paymentAddress) {
389
- return res.status(400).json({ error: `Payment address not found for customer: ${customer?.id}` });
295
+ return c.json({ error: `Payment address not found for customer: ${customer?.id}` }, 400);
390
296
  }
391
297
 
392
298
  const token = await getTokenByAddress(paymentAddress, paymentMethod, paymentCurrency);
393
- return res.json({ token, paymentAddress });
299
+ return c.json({ token, paymentAddress });
394
300
  } catch (err) {
395
301
  logger.error('Error getting customer payer token', err);
396
- return res.status(500).json({ error: err.message });
302
+ return c.json({ error: err.message }, 500);
303
+ }
304
+ });
305
+
306
+ // get overdue invoices — /:id/overdue/invoices registered before plain /:id
307
+ app.get('/:id/overdue/invoices', authPortal, async (c) => {
308
+ try {
309
+ const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
310
+ if (!doc) {
311
+ return c.json({ error: 'Customer not found' }, 404);
312
+ }
313
+ const where: WhereOptions<Invoice> = {
314
+ customer_id: doc.id,
315
+ status: ['uncollectible'],
316
+ amount_remaining: { [Op.gt]: '0' },
317
+ };
318
+ if (typeof c.get('livemode') === 'boolean') {
319
+ where.livemode = !!c.get('livemode');
320
+ }
321
+ const { rows: invoices, count } = await Invoice.findAndCountAll({
322
+ where,
323
+ include: [
324
+ { model: PaymentCurrency, as: 'paymentCurrency' },
325
+ { model: PaymentMethod, as: 'paymentMethod' },
326
+ ],
327
+ });
328
+ if (count === 0) {
329
+ return c.json({
330
+ summary: null,
331
+ invoices: [],
332
+ subscriptionCount: 0,
333
+ });
334
+ }
335
+ const summary: Record<string, { amount: string; currency: PaymentCurrency; method: PaymentMethod }> = {};
336
+ invoices.forEach((invoice) => {
337
+ const key = invoice.currency_id;
338
+ if (!summary[key]) {
339
+ summary[key] = {
340
+ amount: '0',
341
+ // @ts-ignore
342
+ currency: invoice.paymentCurrency,
343
+ // @ts-ignore
344
+ method: invoice.paymentMethod,
345
+ };
346
+ }
347
+ if (invoice && summary[key]) {
348
+ // @ts-ignore
349
+ summary[key].amount = new BN(summary[key]?.amount || '0')
350
+ .add(new BN(invoice.amount_remaining || '0'))
351
+ .toString();
352
+ }
353
+ });
354
+ const subscriptionCount = new Set(invoices.map((x) => x.subscription_id)).size;
355
+ return c.json({
356
+ summary,
357
+ invoices,
358
+ subscriptionCount,
359
+ customer: doc,
360
+ });
361
+ } catch (err) {
362
+ logger.error(err);
363
+ return c.json({ error: `Failed to get overdue invoices: ${err.message}` }, 500);
397
364
  }
398
365
  });
399
366
 
400
- router.get('/:id', auth, async (req, res) => {
401
- if (!req.params.id) {
402
- return res.status(400).json({ error: 'Customer ID is required' });
367
+ app.get('/:id', auth, async (c) => {
368
+ if (!c.req.param('id')) {
369
+ return c.json({ error: 'Customer ID is required' }, 400);
403
370
  }
404
371
  try {
405
- const doc = await Customer.findByPkOrDid(req.params.id as string);
372
+ const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
406
373
  if (doc) {
407
- return res.json(doc);
374
+ return c.json(doc);
408
375
  }
409
- if (req.body.create) {
410
- if (!req.user) {
411
- return res.status(403).json({ error: 'Unauthorized' });
376
+ const body = c.get('sanitizedBody') ?? {};
377
+ if (body.create) {
378
+ if (!c.get('user')) {
379
+ return c.json({ error: 'Unauthorized' }, 403);
412
380
  }
413
- if (req.params.id.startsWith('cus_')) {
414
- return res.status(404).json({ error: 'Customer not found' });
381
+ if (c.req.param('id').startsWith('cus_')) {
382
+ return c.json({ error: 'Customer not found' }, 404);
415
383
  }
416
- const { user } = await blocklet.getUser(req.params.id);
384
+ const { user } = await blocklet.getUser(c.req.param('id'));
417
385
  if (!user) {
418
- return res.status(404).json({ error: 'User not found' });
386
+ return c.json({ error: 'User not found' }, 404);
419
387
  }
420
388
  const customer = await Customer.create({
421
389
  livemode: true,
422
- did: user?.did || req.params.id,
423
- name: user?.fullName ?? req.params.id,
390
+ did: user?.did || c.req.param('id'),
391
+ name: user?.fullName ?? c.req.param('id'),
424
392
  email: user?.email ?? '',
425
393
  phone: user?.phone ?? '',
426
394
  address: Customer.formatAddressFromUser(user),
@@ -435,21 +403,20 @@ router.get('/:id', auth, async (req, res) => {
435
403
  customerId: customer.id,
436
404
  did: customer.did,
437
405
  });
438
- return res.json(customer);
406
+ return c.json(customer);
439
407
  }
440
- return res.status(404).json(null);
408
+ return c.json(null, 404);
441
409
  } catch (err) {
442
410
  logger.error(err);
443
- return res.status(500).json({ error: `Failed to get customer: ${err.message}` });
411
+ return c.json({ error: `Failed to get customer: ${err.message}` }, 500);
444
412
  }
445
413
  });
446
414
 
447
- router.get('/:id/summary', auth, async (req, res) => {
415
+ app.get('/:id/summary', auth, async (c) => {
448
416
  try {
449
- const doc = await Customer.findByPkOrDid(req.params.id as string);
417
+ const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
450
418
  if (!doc) {
451
- res.status(404).json({ error: 'Customer not found' });
452
- return;
419
+ return c.json({ error: 'Customer not found' }, 404);
453
420
  }
454
421
 
455
422
  const [summary, stake, token] = await Promise.all([
@@ -457,10 +424,10 @@ router.get('/:id/summary', auth, async (req, res) => {
457
424
  getStakeSummaryByDid(doc.did, doc.livemode),
458
425
  getTokenSummaryByDid(doc.did, doc.livemode),
459
426
  ]);
460
- res.json({ ...summary, stake, token });
427
+ return c.json({ ...summary, stake, token });
461
428
  } catch (err) {
462
429
  logger.error(err);
463
- res.json(null);
430
+ return c.json(null);
464
431
  }
465
432
  });
466
433
 
@@ -484,20 +451,22 @@ const updatePreferenceSchema = Joi.object({
484
451
  }).optional(),
485
452
  }).unknown(false);
486
453
 
487
- router.put('/preference', sessionMiddleware({ accessKey: true }), async (req, res) => {
454
+ // Static path registered before PUT /:id so hono matches it first.
455
+ app.put('/preference', sessionMiddleware({ accessKey: true }), async (c) => {
488
456
  try {
489
- if (!req.user) {
490
- return res.status(403).json({ error: 'Unauthorized' });
457
+ if (!c.get('user')) {
458
+ return c.json({ error: 'Unauthorized' }, 403);
491
459
  }
492
460
 
493
- const doc = await Customer.findByPkOrDid(req.user.did as string);
461
+ const doc = await Customer.findByPkOrDid(c.get('user').did as string);
494
462
  if (!doc) {
495
- return res.status(404).json({ error: 'Customer not found' });
463
+ return c.json({ error: 'Customer not found' }, 404);
496
464
  }
497
465
 
498
- const { error, value } = updatePreferenceSchema.validate(req.body);
466
+ const body = c.get('sanitizedBody') ?? {};
467
+ const { error, value } = updatePreferenceSchema.validate(body);
499
468
  if (error) {
500
- return res.status(400).json({ error: error.message });
469
+ return c.json({ error: error.message }, 400);
501
470
  }
502
471
 
503
472
  // Get old preference before update
@@ -514,10 +483,10 @@ router.put('/preference', sessionMiddleware({ accessKey: true }), async (req, re
514
483
  await handleNotificationPreferenceChange(doc.id, value.notification);
515
484
  }
516
485
 
517
- return res.json(doc);
486
+ return c.json(doc);
518
487
  } catch (err) {
519
488
  logger.error('Failed to update customer preference', err);
520
- return res.status(400).json({ error: `Failed to update preference: ${err.message}` });
489
+ return c.json({ error: `Failed to update preference: ${err.message}` }, 400);
521
490
  }
522
491
  });
523
492
 
@@ -542,29 +511,30 @@ const updateCustomerSchema = Joi.object({
542
511
  postal_code: Joi.string().max(20).empty(''),
543
512
  }).empty(''),
544
513
  }).unknown(true);
545
- // eslint-disable-next-line consistent-return
546
- router.put('/:id', authPortal, async (req, res) => {
514
+
515
+ app.put('/:id', authPortal, async (c) => {
547
516
  try {
548
- const doc = await Customer.findByPkOrDid(req.params.id as string);
517
+ const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
549
518
  if (!doc) {
550
- return res.status(404).json({ error: 'Customer not found' });
519
+ return c.json({ error: 'Customer not found' }, 404);
551
520
  }
552
521
 
553
- const raw = pick(req.body, ['metadata', 'name', 'email', 'phone', 'address']);
522
+ const body = c.get('sanitizedBody') ?? {};
523
+ const raw = pick(body, ['metadata', 'name', 'email', 'phone', 'address']);
554
524
  const { error } = updateCustomerSchema.validate(raw);
555
525
  if (error) {
556
- return res.status(400).json({ error: error.message });
526
+ return c.json({ error: error.message }, 400);
557
527
  }
558
528
  if (raw.metadata) {
559
529
  raw.metadata = formatMetadata(raw.metadata);
560
530
  }
561
531
 
562
532
  await doc.update(raw);
563
- res.json(doc);
533
+ return c.json(doc);
564
534
  } catch (err) {
565
535
  logger.error(err);
566
- res.json(null);
536
+ return c.json(null);
567
537
  }
568
538
  });
569
539
 
570
- export default router;
540
+ export default app;