payment-kit 1.29.0 → 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 (312) 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/docs/2026-06-10-bundle-size-analysis.md +288 -0
  236. package/cloudflare/migrations/0006_tenant_columns.sql +46 -0
  237. package/cloudflare/migrations/0007_tenant_backfill_indexes.sql +65 -0
  238. package/cloudflare/migrations/0008_schema_parity.sql +16 -0
  239. package/cloudflare/migrations/0009_remove_did_space_jobs.sql +5 -0
  240. package/cloudflare/queue-runtime-mode.ts +13 -0
  241. package/cloudflare/run-build.js +31 -56
  242. package/cloudflare/shims/blocklet-sdk/asset-host-transformer.ts +20 -0
  243. package/cloudflare/shims/blocklet-sdk/config.ts +8 -1
  244. package/cloudflare/shims/blocklet-sdk/login.ts +12 -0
  245. package/cloudflare/shims/blocklet-sdk/service-api.ts +14 -0
  246. package/cloudflare/shims/blocklet-sdk/session.ts +4 -2
  247. package/cloudflare/shims/blocklet-sdk/util-constants.ts +8 -0
  248. package/cloudflare/shims/blocklet-sdk/util-csrf.ts +13 -0
  249. package/cloudflare/shims/blocklet-sdk/util-wallet.ts +8 -0
  250. package/cloudflare/shims/cron.ts +38 -158
  251. package/cloudflare/shims/events.ts +124 -0
  252. package/cloudflare/shims/fastq.ts +15 -1
  253. package/cloudflare/shims/nedb-storage.ts +16 -8
  254. package/cloudflare/shims/node-fetch.ts +35 -0
  255. package/cloudflare/shims/xss.ts +8 -0
  256. package/cloudflare/tenant-middleware.ts +36 -0
  257. package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
  258. package/cloudflare/tests/worker-handler-gate.spec.ts +44 -0
  259. package/cloudflare/worker.ts +204 -433
  260. package/cloudflare/wrangler.local-e2e.jsonc +26 -0
  261. package/jest.config.js +3 -1
  262. package/package.json +33 -38
  263. package/scripts/core-env-whitelist.json +1 -0
  264. package/scripts/e2e-12b-runtime.ts +149 -0
  265. package/scripts/e2e-core-config.ts +125 -0
  266. package/scripts/e2e-d1-tenancy.ts +116 -0
  267. package/scripts/e2e-d2-cron-queue.ts +139 -0
  268. package/scripts/e2e-d3-embedded-multi.ts +171 -0
  269. package/scripts/e2e-hono-s2.ts +125 -0
  270. package/scripts/e2e-hono-s3e.ts +135 -0
  271. package/scripts/e2e-hono-s4.ts +114 -0
  272. package/scripts/e2e-migration-contract.ts +100 -0
  273. package/scripts/e2e-s0.ts +61 -0
  274. package/scripts/e2e-s1.ts +107 -0
  275. package/scripts/e2e-s2.ts +178 -0
  276. package/scripts/e2e-s3.ts +110 -0
  277. package/scripts/e2e-s4.ts +191 -0
  278. package/scripts/e2e-s5.ts +139 -0
  279. package/scripts/e2e-s6.ts +127 -0
  280. package/scripts/e2e-tenant-model.ts +119 -0
  281. package/scripts/e2e-tenant-worker.ts +199 -0
  282. package/scripts/gen-sql-migrations.js +46 -0
  283. package/scripts/phase8-codemod.js +219 -0
  284. package/scripts/phase9a-env-getters-codemod.js +82 -0
  285. package/scripts/scan-core-env.js +109 -0
  286. package/scripts/scan-tenant-queries.js +235 -0
  287. package/scripts/schema-drift-guard.ts +210 -0
  288. package/scripts/tenant-scan-whitelist.json +1 -0
  289. package/src/env.d.ts +13 -1
  290. package/tsconfig.json +1 -1
  291. package/api/src/libs/did-space.ts +0 -235
  292. package/api/src/libs/middleware.ts +0 -50
  293. package/api/src/libs/security.ts +0 -192
  294. package/api/src/queues/space.ts +0 -662
  295. package/api/src/routes/credit-tokens.ts +0 -38
  296. package/api/src/routes/exchange-rates.ts +0 -87
  297. package/api/src/routes/index.ts +0 -142
  298. package/api/src/routes/integrations/stripe.ts +0 -61
  299. package/api/src/routes/meters.ts +0 -274
  300. package/api/src/routes/passports.ts +0 -68
  301. package/api/src/routes/redirect.ts +0 -20
  302. package/api/src/routes/tool.ts +0 -65
  303. package/api/src/routes/webhook-endpoints.ts +0 -126
  304. package/api/tests/routes/credit-grants.spec.ts +0 -1261
  305. package/cloudflare/shims/did-space-js.ts +0 -17
  306. package/cloudflare/shims/did-space.ts +0 -11
  307. package/cloudflare/shims/express-compat/index.ts +0 -80
  308. package/cloudflare/shims/express-compat/types.ts +0 -41
  309. package/cloudflare/shims/lock.ts +0 -115
  310. package/cloudflare/shims/queue.ts +0 -611
  311. package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +0 -87
  312. package/cloudflare/tests/shims/queue-scheduled.spec.ts +0 -186
@@ -1,20 +1,24 @@
1
+ // Phase 3 (express→hono) — hono fork of routes/payment-links.ts. Sub-app with
2
+ // routes relative to /api/payment-links (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';
1
6
  import Joi from 'joi';
2
- import { Router } from 'express';
3
7
  import pick from 'lodash/pick';
4
8
  import { Op } from 'sequelize';
5
9
  import type { WhereOptions } from 'sequelize';
6
10
 
7
- import { createListParamSchema, getOrder, MetadataSchema } from '../libs/api';
8
- import logger from '../libs/logger';
9
- import { authenticate } from '../libs/security';
10
- import { isLineItemAligned } from '../libs/session';
11
- import { formatMetadata } from '../libs/util';
12
- import { PaymentLink } from '../store/models/payment-link';
13
- import { Price } from '../store/models/price';
14
- import { Product } from '../store/models/product';
15
- import { getDonationBenefits } from '../libs/payment';
16
-
17
- const router = Router();
11
+ import { createListParamSchema, getOrder, MetadataSchema } from '../../libs/api';
12
+ import logger from '../../libs/logger';
13
+ import { authenticate } from '../../middlewares/hono/security';
14
+ import { isLineItemAligned } from '../../libs/session';
15
+ import { formatMetadata } from '../../libs/util';
16
+ import { PaymentLink } from '../../store/models/payment-link';
17
+ import { Price } from '../../store/models/price';
18
+ import { Product } from '../../store/models/product';
19
+ import { getDonationBenefits } from '../../libs/payment';
20
+
21
+ const app = new Hono();
18
22
  const auth = authenticate<PaymentLink>({ component: true, roles: ['owner', 'admin'] });
19
23
 
20
24
  const formatBeforeSave = (payload: any) => {
@@ -219,26 +223,59 @@ const PaymentLinkCreateSchema = Joi.object({
219
223
  .min(0)
220
224
  .optional(),
221
225
  }).unknown(true);
222
- router.post('/', auth, async (req, res) => {
226
+
227
+ // static route — registered before /:id to avoid shadowing
228
+ app.post('/stash', auth, async (c) => {
229
+ try {
230
+ const body = c.get('sanitizedBody') ?? {};
231
+ const raw: Partial<PaymentLink> = body;
232
+ raw.id = `plink_${c.get('user')?.did}`;
233
+ raw.active = true;
234
+ raw.livemode = !!c.get('livemode');
235
+ raw.created_via = c.get('user')?.via;
236
+ raw.currency_id = raw.currency_id || c.get('baseCurrency').id;
237
+ // Merge existing metadata with preview flag
238
+ raw.metadata = { ...raw.metadata, preview: '1' };
239
+
240
+ let doc = await PaymentLink.findByPk(raw.id);
241
+ if (doc) {
242
+ await doc.update({ ...formatBeforeSave(body), livemode: raw.livemode });
243
+ logger.info('Stashed payment link updated', { id: raw.id, user: c.get('user')?.did });
244
+ } else {
245
+ doc = await PaymentLink.create(raw as PaymentLink);
246
+ logger.info('New stashed payment link created', { id: raw.id, user: c.get('user')?.did });
247
+ }
248
+ return c.json(doc);
249
+ } catch (err) {
250
+ logger.error('Stash payment link error', { error: err.message, stack: err.stack, body: c.get('sanitizedBody') });
251
+ return c.json({ error: err.message }, 500);
252
+ }
253
+ });
254
+
255
+ app.post('/', auth, async (c) => {
223
256
  try {
224
- const { error } = PaymentLinkCreateSchema.validate(req.body);
257
+ const body = c.get('sanitizedBody') ?? {};
258
+ const { error } = PaymentLinkCreateSchema.validate(body);
225
259
  if (error) {
226
- logger.warn('Payment link create request invalid', { error: error.message, body: req.body });
227
- res.status(400).json({ error: `Payment link create request invalid: ${error.message}` });
228
- return;
260
+ logger.warn('Payment link create request invalid', { error: error.message, body });
261
+ return c.json({ error: `Payment link create request invalid: ${error.message}` }, 400);
229
262
  }
230
263
  const result = await createPaymentLink({
231
- ...req.body,
232
- livemode: !!req.livemode,
233
- created_via: req.user?.via,
234
- currency_id: req.body.currency_id || req.baseCurrency.id,
235
- metadata: formatMetadata(req.body.metadata),
264
+ ...body,
265
+ livemode: !!c.get('livemode'),
266
+ created_via: c.get('user')?.via,
267
+ currency_id: (body as any).currency_id || c.get('baseCurrency').id,
268
+ metadata: formatMetadata((body as any).metadata),
236
269
  });
237
- logger.info('Payment link created successfully', { id: result.id, user: req.user?.did });
238
- res.json(result);
270
+ logger.info('Payment link created successfully', { id: result.id, user: c.get('user')?.did });
271
+ return c.json(result);
239
272
  } catch (err) {
240
- logger.error('Create payment link error', { error: err.message, stack: err.stack, body: req.body });
241
- res.status(400).json({ error: err.message });
273
+ logger.error('Create payment link error', {
274
+ error: err.message,
275
+ stack: err.stack,
276
+ body: c.get('sanitizedBody'),
277
+ });
278
+ return c.json({ error: err.message }, 400);
242
279
  }
243
280
  });
244
281
 
@@ -247,24 +284,25 @@ const paginationSchema = createListParamSchema<{ active?: boolean; donation?: st
247
284
  active: Joi.boolean().empty(''),
248
285
  donation: Joi.string().empty(''),
249
286
  });
250
- router.get('/', auth, async (req, res) => {
251
- const { page, pageSize, ...query } = await paginationSchema.validateAsync(req.query, { stripUnknown: true });
287
+ app.get('/', auth, async (c) => {
288
+ const query = c.req.query();
289
+ const { page, pageSize, ...q } = await paginationSchema.validateAsync(query, { stripUnknown: true });
252
290
  const where: WhereOptions<PaymentLink> = { 'metadata.preview': null };
253
291
 
254
- if (typeof query.active === 'boolean') {
255
- where.active = query.active;
292
+ if (typeof q.active === 'boolean') {
293
+ where.active = q.active;
256
294
  }
257
- if (typeof query.livemode === 'boolean') {
258
- where.livemode = query.livemode;
295
+ if (typeof q.livemode === 'boolean') {
296
+ where.livemode = q.livemode;
259
297
  }
260
- if (query.donation === 'hide') {
298
+ if (q.donation === 'hide') {
261
299
  where.submit_type = { [Op.not]: 'donate' };
262
300
  }
263
301
 
264
302
  try {
265
303
  const { rows: list, count } = await PaymentLink.findAndCountAll({
266
304
  where,
267
- order: getOrder(req.query, [['created_at', 'DESC']]),
305
+ order: getOrder(query, [['created_at', 'DESC']]),
268
306
  offset: (page - 1) * pageSize,
269
307
  limit: pageSize,
270
308
  include: [],
@@ -282,15 +320,32 @@ router.get('/', auth, async (req, res) => {
282
320
  });
283
321
  });
284
322
 
285
- res.json({ count, list, paging: { page, pageSize } });
323
+ return c.json({ count, list, paging: { page, pageSize } });
286
324
  } catch (err) {
287
325
  logger.error(err);
288
- res.json({ count: 0, list: [], paging: { page, pageSize } });
326
+ return c.json({ count: 0, list: [], paging: { page, pageSize } });
289
327
  }
290
328
  });
291
329
 
292
- router.get('/:id', auth, async (req, res) => {
293
- const doc = await PaymentLink.findByPkOrLookupKey(req.params.id || '');
330
+ app.get('/:id/benefits', async (c) => {
331
+ try {
332
+ const doc = await PaymentLink.findByPk(c.req.param('id'), {
333
+ attributes: ['id', 'donation_settings'],
334
+ });
335
+ if (!doc) {
336
+ return c.json({ error: 'payment link not found' }, 404);
337
+ }
338
+ const locale = c.req.query('locale') as string;
339
+ const benefits = await getDonationBenefits(doc, '', locale);
340
+ return c.json(benefits);
341
+ } catch (err) {
342
+ logger.error('Get donation benefits error', { error: err.message, stack: err.stack, id: c.req.param('id') });
343
+ return c.json({ error: err.message }, 400);
344
+ }
345
+ });
346
+
347
+ app.get('/:id', auth, async (c) => {
348
+ const doc = await PaymentLink.findByPkOrLookupKey(c.req.param('id') || '');
294
349
 
295
350
  if (doc) {
296
351
  // @ts-ignore
@@ -298,10 +353,9 @@ router.get('/:id', auth, async (req, res) => {
298
353
  }
299
354
 
300
355
  if (doc) {
301
- res.json(doc);
302
- } else {
303
- res.status(404).json(null);
356
+ return c.json(doc);
304
357
  }
358
+ return c.json(null, 404);
305
359
  });
306
360
 
307
361
  const PaymentLinkUpdateSchema = Joi.object({
@@ -351,126 +405,89 @@ const PaymentLinkUpdateSchema = Joi.object({
351
405
  .min(0)
352
406
  .optional(),
353
407
  }).unknown(true);
354
- // eslint-disable-next-line consistent-return
355
- router.put('/:id', auth, async (req, res) => {
356
- const { error } = PaymentLinkUpdateSchema.validate(req.body);
357
- if (error) {
358
- logger.warn('Payment link update request invalid', { error: error.message, id: req.params.id, body: req.body });
359
- return res.status(400).json({ error: `Payment link update request invalid: ${error.message}` });
360
- }
361
- const doc = await PaymentLink.findByPk(req.params.id);
408
+
409
+ // archive static sub-path, registered before /:id to avoid shadowing
410
+ app.put('/:id/archive', auth, async (c) => {
411
+ const doc = await PaymentLink.findByPk(c.req.param('id'));
362
412
 
363
413
  if (!doc) {
364
- logger.warn('Payment link not found for update', { id: req.params.id });
365
- return res.status(404).json({ error: 'payment link not found' });
414
+ logger.warn('Payment link not found for archiving', { id: c.req.param('id') });
415
+ return c.json({ error: 'payment link not found' }, 404);
366
416
  }
417
+
367
418
  if (doc.active === false) {
368
- logger.warn('Attempt to update archived payment link', { id: req.params.id });
369
- return res.status(403).json({ error: 'payment link archived' });
419
+ logger.warn('Attempt to archive already archived payment link', { id: c.req.param('id') });
420
+ return c.json({ error: 'payment link already archived' }, 403);
370
421
  }
371
422
 
372
423
  try {
373
- await doc.update(formatBeforeSave(Object.assign({}, doc.dataValues, req.body)));
374
- logger.info('Payment link updated successfully', { id: req.params.id, user: req.user?.did });
375
- res.json(doc);
424
+ await doc.update({ active: false });
425
+ logger.info('Payment link archived successfully', { id: c.req.param('id'), user: c.get('user')?.did });
426
+ return c.json(doc);
376
427
  } catch (err) {
377
- logger.error('Update payment link error', {
378
- error: err.message,
379
- stack: err.stack,
380
- id: req.params.id,
381
- body: req.body,
382
- });
383
- res.status(500).json({ error: 'Failed to update payment link' });
428
+ logger.error('Archive payment link error', { error: err.message, stack: err.stack, id: c.req.param('id') });
429
+ return c.json({ error: 'Failed to archive payment link' }, 500);
384
430
  }
385
431
  });
386
432
 
387
- // archive
388
- router.put('/:id/archive', auth, async (req, res) => {
389
- const doc = await PaymentLink.findByPk(req.params.id);
433
+ // eslint-disable-next-line consistent-return
434
+ app.put('/:id', auth, async (c) => {
435
+ const body = c.get('sanitizedBody') ?? {};
436
+ const { error } = PaymentLinkUpdateSchema.validate(body);
437
+ if (error) {
438
+ logger.warn('Payment link update request invalid', {
439
+ error: error.message,
440
+ id: c.req.param('id'),
441
+ body,
442
+ });
443
+ return c.json({ error: `Payment link update request invalid: ${error.message}` }, 400);
444
+ }
445
+ const doc = await PaymentLink.findByPk(c.req.param('id'));
390
446
 
391
447
  if (!doc) {
392
- logger.warn('Payment link not found for archiving', { id: req.params.id });
393
- return res.status(404).json({ error: 'payment link not found' });
448
+ logger.warn('Payment link not found for update', { id: c.req.param('id') });
449
+ return c.json({ error: 'payment link not found' }, 404);
394
450
  }
395
-
396
451
  if (doc.active === false) {
397
- logger.warn('Attempt to archive already archived payment link', { id: req.params.id });
398
- return res.status(403).json({ error: 'payment link already archived' });
452
+ logger.warn('Attempt to update archived payment link', { id: c.req.param('id') });
453
+ return c.json({ error: 'payment link archived' }, 403);
399
454
  }
400
455
 
401
456
  try {
402
- await doc.update({ active: false });
403
- logger.info('Payment link archived successfully', { id: req.params.id, user: req.user?.did });
404
- return res.json(doc);
457
+ await doc.update(formatBeforeSave(Object.assign({}, doc.dataValues, body)));
458
+ logger.info('Payment link updated successfully', { id: c.req.param('id'), user: c.get('user')?.did });
459
+ return c.json(doc);
405
460
  } catch (err) {
406
- logger.error('Archive payment link error', { error: err.message, stack: err.stack, id: req.params.id });
407
- return res.status(500).json({ error: 'Failed to archive payment link' });
461
+ logger.error('Update payment link error', {
462
+ error: err.message,
463
+ stack: err.stack,
464
+ id: c.req.param('id'),
465
+ body,
466
+ });
467
+ return c.json({ error: 'Failed to update payment link' }, 500);
408
468
  }
409
469
  });
410
470
 
411
471
  // delete
412
- router.delete('/:id', auth, async (req, res) => {
413
- const doc = await PaymentLink.findByPk(req.params.id);
472
+ app.delete('/:id', auth, async (c) => {
473
+ const doc = await PaymentLink.findByPk(c.req.param('id'));
414
474
 
415
475
  if (!doc) {
416
- return res.status(404).json({ error: 'payment link not found' });
476
+ return c.json({ error: 'payment link not found' }, 404);
417
477
  }
418
478
 
419
479
  if (doc.active === false) {
420
- return res.status(403).json({ error: 'payment link archived' });
480
+ return c.json({ error: 'payment link archived' }, 403);
421
481
  }
422
482
 
423
483
  try {
424
484
  await doc.destroy();
425
- logger.info('Payment link deleted successfully', { id: req.params.id, user: req.user?.did });
426
- return res.json(doc);
427
- } catch (err) {
428
- logger.error('Delete payment link error', { error: err.message, stack: err.stack, id: req.params.id });
429
- return res.status(500).json({ error: 'Failed to delete payment link' });
430
- }
431
- });
432
-
433
- router.post('/stash', auth, async (req, res) => {
434
- try {
435
- const raw: Partial<PaymentLink> = req.body;
436
- raw.id = `plink_${req.user?.did}`;
437
- raw.active = true;
438
- raw.livemode = !!req.livemode;
439
- raw.created_via = req.user?.via;
440
- raw.currency_id = raw.currency_id || req.baseCurrency.id;
441
- // Merge existing metadata with preview flag
442
- raw.metadata = { ...raw.metadata, preview: '1' };
443
-
444
- let doc = await PaymentLink.findByPk(raw.id);
445
- if (doc) {
446
- await doc.update({ ...formatBeforeSave(req.body), livemode: raw.livemode });
447
- logger.info('Stashed payment link updated', { id: raw.id, user: req.user?.did });
448
- } else {
449
- doc = await PaymentLink.create(raw as PaymentLink);
450
- logger.info('New stashed payment link created', { id: raw.id, user: req.user?.did });
451
- }
452
- res.json(doc);
453
- } catch (err) {
454
- logger.error('Stash payment link error', { error: err.message, stack: err.stack, body: req.body });
455
- res.status(500).json({ error: err.message });
456
- }
457
- });
458
-
459
- router.get('/:id/benefits', async (req, res) => {
460
- try {
461
- const doc = await PaymentLink.findByPk(req.params.id, {
462
- attributes: ['id', 'donation_settings'],
463
- });
464
- if (!doc) {
465
- return res.status(404).json({ error: 'payment link not found' });
466
- }
467
- const locale = req.query.locale as string;
468
- const benefits = await getDonationBenefits(doc, '', locale);
469
- return res.json(benefits);
485
+ logger.info('Payment link deleted successfully', { id: c.req.param('id'), user: c.get('user')?.did });
486
+ return c.json(doc);
470
487
  } catch (err) {
471
- logger.error('Get donation benefits error', { error: err.message, stack: err.stack, id: req.params.id });
472
- return res.status(400).json({ error: err.message });
488
+ logger.error('Delete payment link error', { error: err.message, stack: err.stack, id: c.req.param('id') });
489
+ return c.json({ error: 'Failed to delete payment link' }, 500);
473
490
  }
474
491
  });
475
492
 
476
- export default router;
493
+ export default app;