payment-kit 1.29.1 → 1.29.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/api/dev.ts +41 -2
  2. package/api/hono.d.ts +42 -0
  3. package/api/node-sqlite.d.ts +12 -0
  4. package/api/src/bootstrap.ts +47 -0
  5. package/api/src/crons/base.ts +3 -3
  6. package/api/src/crons/currency.ts +1 -1
  7. package/api/src/crons/index.ts +41 -37
  8. package/api/src/crons/metering-subscription-detection.ts +1 -1
  9. package/api/src/crons/overdue-detection.ts +2 -2
  10. package/api/src/crons/retry-pending-events.ts +6 -0
  11. package/api/src/crons/tenant-fanout.ts +82 -0
  12. package/api/src/host-node/did-connect-runtime-node.ts +33 -0
  13. package/api/src/host-node/serve-static-arc.ts +68 -0
  14. package/api/src/host-node/serve-static.ts +41 -0
  15. package/api/src/index.ts +22 -161
  16. package/api/src/integrations/app-store/client.ts +3 -4
  17. package/api/src/integrations/app-store/handlers/subscription.ts +7 -7
  18. package/api/src/integrations/app-store/signed-data-verifier.ts +3 -2
  19. package/api/src/integrations/arcblock/token.ts +21 -7
  20. package/api/src/integrations/google-play/handlers/subscription.ts +6 -6
  21. package/api/src/integrations/google-play/handlers/voided.ts +2 -2
  22. package/api/src/integrations/google-play/verify.ts +3 -2
  23. package/api/src/integrations/iap-reconcile.ts +3 -5
  24. package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
  25. package/api/src/integrations/stripe/handlers/subscription.ts +3 -3
  26. package/api/src/libs/archive/query.ts +19 -0
  27. package/api/src/libs/audit.ts +61 -4
  28. package/api/src/libs/auth.ts +247 -47
  29. package/api/src/libs/context.ts +89 -1
  30. package/api/src/libs/currency.ts +2 -2
  31. package/api/src/libs/dayjs.ts +8 -2
  32. package/api/src/libs/did-connect/runtime-did-connect-js.ts +88 -0
  33. package/api/src/libs/did-connect/tenant-identity.ts +221 -0
  34. package/api/src/libs/drivers/auth-storage.ts +118 -0
  35. package/api/src/libs/drivers/cron.ts +264 -0
  36. package/api/src/libs/drivers/db.ts +170 -0
  37. package/api/src/libs/drivers/identity.ts +142 -0
  38. package/api/src/libs/drivers/index.ts +40 -0
  39. package/api/src/libs/drivers/locks.ts +226 -0
  40. package/api/src/libs/drivers/migrate-runner.ts +70 -0
  41. package/api/src/libs/drivers/queue.ts +104 -0
  42. package/api/src/libs/drivers/secrets.ts +194 -0
  43. package/api/src/libs/env.ts +170 -54
  44. package/api/src/libs/exchange-rate/service.ts +7 -6
  45. package/api/src/libs/http-fetch-adapter.ts +60 -0
  46. package/api/src/libs/invoice.ts +1 -1
  47. package/api/src/libs/lock.ts +51 -47
  48. package/api/src/libs/logger.ts +48 -8
  49. package/api/src/libs/notification/index.ts +1 -1
  50. package/api/src/libs/notification/template/customer-credit-low-balance.ts +2 -1
  51. package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -1
  52. package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -1
  53. package/api/src/libs/overdraft-protection.ts +1 -1
  54. package/api/src/libs/payout.ts +1 -1
  55. package/api/src/libs/queue/index.ts +271 -52
  56. package/api/src/libs/queue/runtime.ts +175 -0
  57. package/api/src/libs/resource.ts +3 -3
  58. package/api/src/libs/secrets.ts +38 -0
  59. package/api/src/libs/session.ts +3 -2
  60. package/api/src/libs/subscription.ts +5 -5
  61. package/api/src/libs/tenant.ts +92 -0
  62. package/api/src/libs/url.ts +3 -3
  63. package/api/src/libs/util.ts +21 -13
  64. package/api/src/middlewares/hono/cdn.ts +63 -0
  65. package/api/src/middlewares/hono/context.ts +80 -0
  66. package/api/src/middlewares/hono/csrf.ts +83 -0
  67. package/api/src/middlewares/hono/fallback.ts +194 -0
  68. package/api/src/middlewares/hono/pipeline.ts +73 -0
  69. package/api/src/middlewares/hono/resource-mount.ts +42 -0
  70. package/api/src/middlewares/hono/resource.ts +63 -0
  71. package/api/src/middlewares/hono/security.ts +209 -0
  72. package/api/src/middlewares/hono/session.ts +114 -0
  73. package/api/src/middlewares/hono/xss.ts +61 -0
  74. package/api/src/queues/auto-recharge.ts +12 -10
  75. package/api/src/queues/checkout-session.ts +38 -21
  76. package/api/src/queues/credit-consume.ts +40 -36
  77. package/api/src/queues/credit-grant.ts +25 -18
  78. package/api/src/queues/credit-reconciliation.ts +7 -5
  79. package/api/src/queues/discount-status.ts +9 -6
  80. package/api/src/queues/event.ts +41 -11
  81. package/api/src/queues/exchange-rate-health.ts +49 -30
  82. package/api/src/queues/invoice.ts +18 -15
  83. package/api/src/queues/notification.ts +14 -7
  84. package/api/src/queues/payment.ts +64 -37
  85. package/api/src/queues/payout.ts +37 -21
  86. package/api/src/queues/refund.ts +36 -18
  87. package/api/src/queues/subscription.ts +83 -53
  88. package/api/src/queues/token-transfer.ts +15 -10
  89. package/api/src/queues/usage-record.ts +8 -5
  90. package/api/src/queues/vendors/commission.ts +7 -5
  91. package/api/src/queues/vendors/fulfillment-coordinator.ts +17 -13
  92. package/api/src/queues/vendors/fulfillment.ts +4 -2
  93. package/api/src/queues/vendors/return-processor.ts +5 -3
  94. package/api/src/queues/vendors/return-scanner.ts +5 -4
  95. package/api/src/queues/vendors/status-check.ts +10 -7
  96. package/api/src/queues/webhook.ts +60 -32
  97. package/api/src/routes/connect/shared.ts +1 -2
  98. package/api/src/routes/connect/subscribe.ts +3 -3
  99. package/api/src/routes/{archive.ts → hono/archive.ts} +69 -64
  100. package/api/src/routes/{auto-recharge-configs.ts → hono/auto-recharge-configs.ts} +39 -28
  101. package/api/src/routes/{checkout-sessions.ts → hono/checkout-sessions.ts} +790 -923
  102. package/api/src/routes/{coupons.ts → hono/coupons.ts} +93 -76
  103. package/api/src/routes/{credit-grants.ts → hono/credit-grants.ts} +140 -126
  104. package/api/src/routes/hono/credit-tokens.ts +43 -0
  105. package/api/src/routes/{credit-transactions.ts → hono/credit-transactions.ts} +37 -29
  106. package/api/src/routes/{customers.ts → hono/customers.ts} +199 -224
  107. package/api/src/routes/{donations.ts → hono/donations.ts} +41 -32
  108. package/api/src/routes/{entitlements.ts → hono/entitlements.ts} +28 -25
  109. package/api/src/routes/{events.ts → hono/events.ts} +107 -71
  110. package/api/src/routes/{exchange-rate-providers.ts → hono/exchange-rate-providers.ts} +138 -126
  111. package/api/src/routes/hono/exchange-rates.ts +77 -0
  112. package/api/src/routes/hono/index.ts +115 -0
  113. package/api/src/routes/{integrations → hono/integrations}/app-store.ts +68 -48
  114. package/api/src/routes/{integrations → hono/integrations}/google-play.ts +78 -58
  115. package/api/src/routes/hono/integrations/stripe.ts +74 -0
  116. package/api/src/routes/{invoices.ts → hono/invoices.ts} +253 -244
  117. package/api/src/routes/{meter-events.ts → hono/meter-events.ts} +120 -110
  118. package/api/src/routes/hono/meters.ts +288 -0
  119. package/api/src/routes/hono/passports.ts +73 -0
  120. package/api/src/routes/{payment-currencies.ts → hono/payment-currencies.ts} +219 -197
  121. package/api/src/routes/{payment-intents.ts → hono/payment-intents.ts} +136 -132
  122. package/api/src/routes/{payment-links.ts → hono/payment-links.ts} +145 -128
  123. package/api/src/routes/{payment-methods.ts → hono/payment-methods.ts} +125 -93
  124. package/api/src/routes/{payment-stats.ts → hono/payment-stats.ts} +30 -25
  125. package/api/src/routes/{payouts.ts → hono/payouts.ts} +55 -47
  126. package/api/src/routes/{prices.ts → hono/prices.ts} +265 -242
  127. package/api/src/routes/{pricing-table.ts → hono/pricing-table.ts} +94 -87
  128. package/api/src/routes/{products.ts → hono/products.ts} +172 -159
  129. package/api/src/routes/{promotion-codes.ts → hono/promotion-codes.ts} +207 -185
  130. package/api/src/routes/hono/redirect.ts +24 -0
  131. package/api/src/routes/{refunds.ts → hono/refunds.ts} +98 -83
  132. package/api/src/routes/{settings.ts → hono/settings.ts} +64 -55
  133. package/api/src/routes/{subscription-items.ts → hono/subscription-items.ts} +64 -57
  134. package/api/src/routes/{subscriptions.ts → hono/subscriptions.ts} +475 -528
  135. package/api/src/routes/{tax-rates.ts → hono/tax-rates.ts} +71 -70
  136. package/api/src/routes/hono/tool.ts +69 -0
  137. package/api/src/routes/{usage-records.ts → hono/usage-records.ts} +47 -42
  138. package/api/src/routes/{vendor.ts → hono/vendor.ts} +315 -167
  139. package/api/src/routes/{webhook-attempts.ts → hono/webhook-attempts.ts} +17 -13
  140. package/api/src/routes/hono/webhook-endpoints.ts +126 -0
  141. package/api/src/service.ts +814 -0
  142. package/api/src/store/migrations/20230911-seeding.ts +2 -1
  143. package/api/src/store/migrations/20260609-remove-did-space-jobs.ts +23 -0
  144. package/api/src/store/migrations/20260610-tenant-columns.ts +40 -0
  145. package/api/src/store/migrations/20260611-tenant-backfill.ts +33 -0
  146. package/api/src/store/models/auto-recharge-config.ts +22 -10
  147. package/api/src/store/models/checkout-session.ts +15 -14
  148. package/api/src/store/models/coupon.ts +29 -20
  149. package/api/src/store/models/credit-grant.ts +38 -29
  150. package/api/src/store/models/credit-transaction.ts +32 -21
  151. package/api/src/store/models/customer.ts +19 -17
  152. package/api/src/store/models/discount.ts +11 -2
  153. package/api/src/store/models/entitlement-grant.ts +21 -9
  154. package/api/src/store/models/entitlement-product.ts +21 -9
  155. package/api/src/store/models/entitlement.ts +19 -10
  156. package/api/src/store/models/event.ts +18 -9
  157. package/api/src/store/models/exchange-rate-provider.ts +17 -4
  158. package/api/src/store/models/invoice-item.ts +18 -9
  159. package/api/src/store/models/invoice.ts +16 -8
  160. package/api/src/store/models/meter-event.ts +27 -9
  161. package/api/src/store/models/meter.ts +31 -22
  162. package/api/src/store/models/payment-currency.ts +25 -8
  163. package/api/src/store/models/payment-intent.ts +15 -6
  164. package/api/src/store/models/payment-link.ts +15 -6
  165. package/api/src/store/models/payment-method.ts +38 -22
  166. package/api/src/store/models/payment-stat.ts +18 -9
  167. package/api/src/store/models/payout.ts +15 -6
  168. package/api/src/store/models/price-quote.ts +17 -8
  169. package/api/src/store/models/price.ts +24 -12
  170. package/api/src/store/models/pricing-table.ts +29 -20
  171. package/api/src/store/models/product-vendor.ts +20 -10
  172. package/api/src/store/models/product.ts +15 -6
  173. package/api/src/store/models/promotion-code.ts +14 -6
  174. package/api/src/store/models/refund.ts +15 -6
  175. package/api/src/store/models/revenue-snapshot.ts +21 -9
  176. package/api/src/store/models/setting.ts +18 -9
  177. package/api/src/store/models/setup-intent.ts +36 -27
  178. package/api/src/store/models/subscription-item.ts +21 -9
  179. package/api/src/store/models/subscription-schedule.ts +21 -9
  180. package/api/src/store/models/subscription.ts +21 -10
  181. package/api/src/store/models/tax-rate.ts +29 -21
  182. package/api/src/store/models/usage-record.ts +11 -2
  183. package/api/src/store/models/webhook-attempt.ts +18 -9
  184. package/api/src/store/models/webhook-endpoint.ts +18 -9
  185. package/api/src/store/scoped-core.ts +55 -0
  186. package/api/src/store/scoped.ts +247 -0
  187. package/api/src/store/sequelize.ts +82 -23
  188. package/api/src/store/sql-migrations.ts +20 -0
  189. package/api/src/store/tenant-backfill.ts +260 -0
  190. package/api/src/store/tenant-model.ts +124 -0
  191. package/api/src/store/tenant-tables.ts +50 -0
  192. package/api/tests/bootstrap/bootstrap.spec.ts +162 -0
  193. package/api/tests/crons/tenant-fanout.spec.ts +158 -0
  194. package/api/tests/embedded/embedded-multi-mode-d3.spec.ts +257 -0
  195. package/api/tests/fixtures/bare-query-violation.ts +13 -0
  196. package/api/tests/fixtures/core-env-violation.ts +10 -0
  197. package/api/tests/fixtures/host-read-violation.ts +19 -0
  198. package/api/tests/fixtures/tenants.ts +4 -0
  199. package/api/tests/integrations/iap-tenant.spec.ts +284 -0
  200. package/api/tests/libs/archive-query.spec.ts +26 -0
  201. package/api/tests/libs/audit-tenant.spec.ts +153 -0
  202. package/api/tests/libs/context.spec.ts +204 -0
  203. package/api/tests/libs/core-config.spec.ts +115 -0
  204. package/api/tests/libs/cron-driver-d2.spec.ts +237 -0
  205. package/api/tests/libs/crons-conservation-d2.spec.ts +52 -0
  206. package/api/tests/libs/did-connect-runtime-js.spec.ts +98 -0
  207. package/api/tests/libs/did-connect-tenant-identity.spec.ts +159 -0
  208. package/api/tests/libs/lock-tenant.spec.ts +66 -0
  209. package/api/tests/libs/scoped.spec.ts +222 -0
  210. package/api/tests/libs/secrets-facade.spec.ts +52 -0
  211. package/api/tests/libs/service-host.spec.ts +37 -0
  212. package/api/tests/libs/tenancy-slot-authority.spec.ts +209 -0
  213. package/api/tests/libs/tenant-middleware.spec.ts +42 -0
  214. package/api/tests/libs/tenant-scanner.spec.ts +120 -0
  215. package/api/tests/middlewares/hono/cdn.spec.ts +70 -0
  216. package/api/tests/middlewares/hono/context.spec.ts +113 -0
  217. package/api/tests/middlewares/hono/csrf.spec.ts +136 -0
  218. package/api/tests/middlewares/hono/fallback.spec.ts +67 -0
  219. package/api/tests/middlewares/hono/pipeline.spec.ts +47 -0
  220. package/api/tests/middlewares/hono/security.spec.ts +181 -0
  221. package/api/tests/middlewares/hono/session.spec.ts +42 -0
  222. package/api/tests/middlewares/hono/xss.spec.ts +81 -0
  223. package/api/tests/models/tenant-backfill.spec.ts +287 -0
  224. package/api/tests/models/tenant-columns-model.spec.ts +46 -0
  225. package/api/tests/models/tenant-columns.spec.ts +161 -0
  226. package/api/tests/queues/credit-consume-batch.spec.ts +8 -1
  227. package/api/tests/queues/credit-consume.spec.ts +8 -1
  228. package/api/tests/queues/event-tenant.spec.ts +292 -0
  229. package/api/tests/queues/exchange-rate-health-tenant-d6.spec.ts +62 -0
  230. package/api/tests/queues/queue-parity.spec.ts +249 -0
  231. package/api/tests/queues/queue-runtime-surface.spec.ts +277 -0
  232. package/api/tests/queues/queue-teardown-d2.spec.ts +127 -0
  233. package/api/tests/queues/tenant-matrix-a.spec.ts +245 -0
  234. package/api/tests/queues/tenant-matrix-b.spec.ts +168 -0
  235. package/api/tests/routes/connect/hono-attach.spec.ts +107 -0
  236. package/api/tests/service/collapse.spec.ts +96 -0
  237. package/api/tests/service/didconnect-storage-slot.spec.ts +60 -0
  238. package/api/tests/service/fail-closed-http.spec.ts +79 -0
  239. package/api/tests/service/static-arc-handler.spec.ts +101 -0
  240. package/api/tests/service/static-externalized.spec.ts +48 -0
  241. package/api/tests/store/tenant-crosscut.spec.ts +202 -0
  242. package/api/tests/store/tenant-model-spike.spec.ts +177 -0
  243. package/api/tests/store/tenant-model.spec.ts +162 -0
  244. package/api/tests/store/tenant-residual.spec.ts +196 -0
  245. package/api/third.d.ts +4 -0
  246. package/blocklet.yml +1 -1
  247. package/cloudflare/MIGRATION-RUNBOOK.md +3 -8
  248. package/cloudflare/README.md +34 -27
  249. package/cloudflare/STAGING-MIGRATION-GUIDE.md +3 -15
  250. package/cloudflare/build.ts +33 -13
  251. package/cloudflare/cf-adapter.ts +419 -0
  252. package/cloudflare/did-connect-runtime.ts +96 -0
  253. package/cloudflare/did-connect-token-storage.ts +151 -0
  254. package/cloudflare/esbuild-cf-config.cjs +407 -0
  255. package/cloudflare/migrations/0006_tenant_columns.sql +46 -0
  256. package/cloudflare/migrations/0007_tenant_backfill_indexes.sql +65 -0
  257. package/cloudflare/migrations/0008_schema_parity.sql +16 -0
  258. package/cloudflare/migrations/0009_remove_did_space_jobs.sql +5 -0
  259. package/cloudflare/queue-runtime-mode.ts +13 -0
  260. package/cloudflare/run-build.js +33 -403
  261. package/cloudflare/scripts/cf-package-import-probe.mjs +90 -0
  262. package/cloudflare/scripts/didconnect-mock-smoke.mjs +140 -0
  263. package/cloudflare/shims/blocklet-sdk/asset-host-transformer.ts +20 -0
  264. package/cloudflare/shims/blocklet-sdk/config.ts +8 -1
  265. package/cloudflare/shims/blocklet-sdk/login.ts +12 -0
  266. package/cloudflare/shims/blocklet-sdk/service-api.ts +14 -0
  267. package/cloudflare/shims/blocklet-sdk/session.ts +4 -2
  268. package/cloudflare/shims/blocklet-sdk/util-constants.ts +8 -0
  269. package/cloudflare/shims/blocklet-sdk/wallet-authenticator.ts +16 -1
  270. package/cloudflare/shims/blocklet-sdk/wallet-handler.ts +18 -3
  271. package/cloudflare/shims/cron.ts +38 -158
  272. package/cloudflare/shims/events.ts +124 -0
  273. package/cloudflare/shims/fastq.ts +15 -1
  274. package/cloudflare/shims/nedb-storage.ts +16 -8
  275. package/cloudflare/shims/xss.ts +8 -0
  276. package/cloudflare/tenant-middleware.ts +36 -0
  277. package/cloudflare/tests/cf-adapter.spec.ts +244 -0
  278. package/cloudflare/tests/did-connect-token-storage.spec.ts +105 -0
  279. package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
  280. package/cloudflare/tests/worker-handler-gate.spec.ts +69 -0
  281. package/cloudflare/vite.config.ts +53 -45
  282. package/cloudflare/worker.ts +261 -448
  283. package/cloudflare/wrangler.json +0 -6
  284. package/cloudflare/wrangler.jsonc +0 -6
  285. package/cloudflare/wrangler.local-e2e.jsonc +25 -0
  286. package/cloudflare/wrangler.staging.json +0 -6
  287. package/jest.config.js +3 -1
  288. package/package.json +33 -38
  289. package/scripts/bootstrap-inject.ts +166 -0
  290. package/scripts/core-env-whitelist.json +1 -0
  291. package/scripts/e2e-12b-runtime.ts +149 -0
  292. package/scripts/e2e-core-config.ts +125 -0
  293. package/scripts/e2e-d1-tenancy.ts +116 -0
  294. package/scripts/e2e-d2-cron-queue.ts +139 -0
  295. package/scripts/e2e-d3-embedded-multi.ts +171 -0
  296. package/scripts/e2e-hono-s2.ts +125 -0
  297. package/scripts/e2e-hono-s3e.ts +135 -0
  298. package/scripts/e2e-hono-s4.ts +114 -0
  299. package/scripts/e2e-migration-contract.ts +100 -0
  300. package/scripts/e2e-s0.ts +61 -0
  301. package/scripts/e2e-s1.ts +107 -0
  302. package/scripts/e2e-s2.ts +178 -0
  303. package/scripts/e2e-s3.ts +110 -0
  304. package/scripts/e2e-s4.ts +191 -0
  305. package/scripts/e2e-s5.ts +139 -0
  306. package/scripts/e2e-s6.ts +127 -0
  307. package/scripts/e2e-tenant-model.ts +119 -0
  308. package/scripts/e2e-tenant-worker.ts +199 -0
  309. package/scripts/gen-sql-migrations.js +46 -0
  310. package/scripts/phase8-codemod.js +219 -0
  311. package/scripts/phase9a-env-getters-codemod.js +82 -0
  312. package/scripts/scan-core-env.js +109 -0
  313. package/scripts/scan-tenant-queries.js +235 -0
  314. package/scripts/schema-drift-guard.ts +210 -0
  315. package/scripts/tenant-scan-whitelist.json +1 -0
  316. package/src/app.tsx +2 -1
  317. package/src/env.d.ts +13 -1
  318. package/src/libs/service-host.ts +13 -0
  319. package/tsconfig.json +1 -1
  320. package/vite.arc.config.ts +159 -0
  321. package/api/src/libs/did-space.ts +0 -235
  322. package/api/src/libs/middleware.ts +0 -50
  323. package/api/src/libs/security.ts +0 -192
  324. package/api/src/queues/space.ts +0 -662
  325. package/api/src/routes/credit-tokens.ts +0 -38
  326. package/api/src/routes/exchange-rates.ts +0 -87
  327. package/api/src/routes/index.ts +0 -142
  328. package/api/src/routes/integrations/stripe.ts +0 -61
  329. package/api/src/routes/meters.ts +0 -274
  330. package/api/src/routes/passports.ts +0 -68
  331. package/api/src/routes/redirect.ts +0 -20
  332. package/api/src/routes/tool.ts +0 -65
  333. package/api/src/routes/webhook-endpoints.ts +0 -126
  334. package/api/tests/routes/credit-grants.spec.ts +0 -1261
  335. package/cloudflare/did-connect-auth.ts +0 -527
  336. package/cloudflare/shims/did-space-js.ts +0 -17
  337. package/cloudflare/shims/did-space.ts +0 -11
  338. package/cloudflare/shims/express-compat/index.ts +0 -80
  339. package/cloudflare/shims/express-compat/types.ts +0 -41
  340. package/cloudflare/shims/lock.ts +0 -115
  341. package/cloudflare/shims/queue.ts +0 -611
  342. package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +0 -87
  343. package/cloudflare/tests/shims/queue-scheduled.spec.ts +0 -186
@@ -1,235 +0,0 @@
1
- // eslint-disable-next-line import/no-extraneous-dependencies
2
- import { SpaceClient, GetObjectCommand, PutObjectCommand } from '@blocklet/did-space-js';
3
- import { blocklet, wallet } from './auth';
4
- import logger from './logger';
5
- import env from './env';
6
- import { streamToString } from './util';
7
-
8
- // Get user's DID Space endpoint
9
- export const getEndpointAndSpaceDid = async (userDid: string): Promise<{ endpoint: string; spaceDid: string }> => {
10
- const { user } = await blocklet.getUser(userDid);
11
- if (!user) {
12
- throw new Error('User not found');
13
- }
14
- if (!user.didSpace.endpoint) {
15
- throw new Error(`DID Space endpoint is not set for user ${userDid}`);
16
- }
17
- return {
18
- endpoint: user.didSpace.endpoint,
19
- spaceDid: user.didSpace.did,
20
- };
21
- };
22
-
23
- // Initialize DID Space client
24
- export const getSpaceClient = async (userDid: string, endpoint?: string) => {
25
- let spaceEndpoint = endpoint;
26
- if (!spaceEndpoint) {
27
- const result = await getEndpointAndSpaceDid(userDid);
28
- spaceEndpoint = result.endpoint;
29
- }
30
- return new SpaceClient({
31
- endpoint: spaceEndpoint,
32
- wallet,
33
- });
34
- };
35
-
36
- // Universal billing information format
37
- export interface BillingInfo {
38
- tx_hash: string; // Transaction hash
39
- timestamp: number; // Transaction timestamp
40
-
41
- // Basic billing information
42
- invoice_id: string; // Invoice ID
43
- category: string; // Bill category
44
- description?: string; // Bill description: subscription payment, recharge, refund, etc.
45
-
46
- // Payment information
47
- amount: string; // Payment amount
48
- currency: {
49
- // Payment medium information
50
- symbol: string; // Currency symbol
51
- decimal: number; // Decimal places
52
- type: string; // Payment type (arcblock/ethereum)
53
- chain_id: string; // Chain ID
54
- explorer_host: string; // Explorer host
55
- explorer_tx_url: string; // Explorer transaction URL
56
- };
57
-
58
- // Related business information
59
- related?: {
60
- type: string; // Relation type (subscription/invoice)
61
- id: string; // Related entity ID
62
- name: string; // Related entity name
63
- status?: string; // Related entity status
64
- link?: string; // Link to the related entity
65
- period_start?: number; // Period start time
66
- period_end?: number; // Period end time
67
- };
68
-
69
- customer_did: string; // Customer DID
70
- tags?: string[]; // User defined tags
71
- app_pid: string; // App PID
72
- link?: string; // Link to the related entity
73
- }
74
-
75
- // Merge billing information, preserving user customizations
76
- const mergeBillingInfo = (existing: BillingInfo, updated: Partial<BillingInfo>): BillingInfo => {
77
- return {
78
- ...(existing || {}),
79
- ...updated,
80
- // Preserve user customizations if they exist
81
- description: existing?.description || updated?.description,
82
- category: existing?.category || updated?.category || 'other',
83
- tags: Array.from(new Set([...(existing.tags || []), ...(updated.tags || [])])),
84
- };
85
- };
86
-
87
- // Upload or update billing information to DID Space
88
- export const uploadBillingInfo = async (
89
- userDid: string,
90
- billingInfo: BillingInfo,
91
- endpoint?: string
92
- ): Promise<boolean> => {
93
- try {
94
- // Validate required fields
95
- if (!billingInfo.tx_hash || !billingInfo.invoice_id || !billingInfo.amount) {
96
- logger.error('Missing required fields in billingInfo');
97
- return false;
98
- }
99
-
100
- const client = await getSpaceClient(userDid, endpoint);
101
-
102
- const key = `txs/${billingInfo.tx_hash}/metadata.json`;
103
-
104
- let finalBillingInfo = {
105
- ...billingInfo,
106
- app_pid: billingInfo?.app_pid || env.appPid,
107
- };
108
-
109
- // Check existing bill
110
- const existingBill = await client.send(new GetObjectCommand({ key }));
111
- if (existingBill.statusCode === 200 && existingBill.data) {
112
- // Merge with existing data if force update
113
- const existing = JSON.parse(await streamToString(existingBill.data)) as BillingInfo;
114
- finalBillingInfo = mergeBillingInfo(existing, billingInfo);
115
- logger.info('Updating existing billing:', { userDid, txHash: billingInfo.tx_hash });
116
- }
117
-
118
- const payload = new PutObjectCommand({
119
- key,
120
- data: JSON.stringify(finalBillingInfo),
121
- });
122
-
123
- const result = await client.send(payload);
124
-
125
- if (result.statusCode !== 200) {
126
- logger.error('Upload billing info failed:', result);
127
- return false;
128
- }
129
-
130
- logger.info(existingBill.statusCode === 200 ? 'Bill updated:' : 'Bill created:', {
131
- userDid,
132
- txHash: billingInfo.tx_hash,
133
- });
134
- return true;
135
- } catch (error) {
136
- logger.error('Upload billing info error:', error);
137
- return false;
138
- }
139
- };
140
-
141
- // Get specific billing information by transaction hash
142
- export const getBillingInfo = async (
143
- userDid: string,
144
- txHash: string,
145
- endpoint?: string
146
- ): Promise<BillingInfo | null> => {
147
- try {
148
- if (!txHash) {
149
- logger.error('Transaction hash is required');
150
- return null;
151
- }
152
-
153
- const client = await getSpaceClient(userDid, endpoint);
154
- const payload = new GetObjectCommand({
155
- key: `txs/${txHash}/metadata.json`,
156
- });
157
-
158
- const result = await client.send(payload);
159
-
160
- if (result.statusCode !== 200 || !result.data) {
161
- logger.debug('Bill not found:', { userDid, txHash });
162
- return null;
163
- }
164
-
165
- const billingInfo = JSON.parse(await streamToString(result.data)) as BillingInfo;
166
-
167
- // Validate parsed data
168
- if (!billingInfo.tx_hash || !billingInfo.invoice_id) {
169
- logger.error('Invalid billing data structure:', { userDid, txHash });
170
- return null;
171
- }
172
-
173
- return billingInfo;
174
- } catch (error) {
175
- logger.error('Get billing info error:', { userDid, txHash, error });
176
- return null;
177
- }
178
- };
179
-
180
- // Update specific fields of billing information
181
- export const updateBillingInfo = async (
182
- userDid: string,
183
- txHash: string,
184
- updates: Partial<BillingInfo>,
185
- endpoint?: string
186
- ): Promise<boolean> => {
187
- try {
188
- const existing = await getBillingInfo(userDid, txHash, endpoint);
189
- if (!existing) {
190
- logger.error('Billing not found for update:', { userDid, txHash });
191
- return false;
192
- }
193
-
194
- const updated = mergeBillingInfo(existing, updates);
195
- if (!updated.tx_hash || !updated.invoice_id || !updated.amount) {
196
- logger.error('Invalid billing data structure:', { userDid, txHash });
197
- return false;
198
- }
199
- const result = await uploadBillingInfo(userDid, updated, endpoint);
200
- return result;
201
- } catch (error) {
202
- logger.error('Update billing info error:', { userDid, txHash, error });
203
- return false;
204
- }
205
- };
206
-
207
- // Batch upload/update billing information
208
- export const uploadBillingInfoBatch = async (
209
- userDid: string,
210
- billingInfoList: BillingInfo[],
211
- endpoint?: string
212
- ): Promise<boolean[]> => {
213
- try {
214
- if (!Array.isArray(billingInfoList) || billingInfoList.length === 0) {
215
- logger.error('Invalid billingInfoList:', { userDid });
216
- return [];
217
- }
218
-
219
- // Validate all bills before upload
220
- const validBills = billingInfoList.filter((bill) => bill.tx_hash && bill.invoice_id && bill.amount);
221
-
222
- if (validBills.length !== billingInfoList.length) {
223
- logger.warn('Some bills are invalid and will be skipped');
224
- }
225
-
226
- const results = await Promise.all(
227
- validBills.map((billingInfo) => uploadBillingInfo(userDid, billingInfo, endpoint))
228
- );
229
-
230
- return results;
231
- } catch (error) {
232
- logger.error('Batch upload error:', { userDid, error });
233
- return [];
234
- }
235
- };
@@ -1,50 +0,0 @@
1
- /* eslint-disable import/prefer-default-export */
2
- import type { NextFunction, Request, Response } from 'express';
3
- import { verify } from '@blocklet/sdk/lib/util/verify-sign';
4
- import { translate } from '../locales';
5
- import { context } from './context';
6
-
7
- export function ensureI18n() {
8
- return (req: Request, _: Response, next: NextFunction) => {
9
- req.locale = String(req.query.locale || 'en');
10
- req.t = translate;
11
- next();
12
- };
13
- }
14
-
15
- export async function contextMiddleware(req: Request, _res: Response, next: NextFunction) {
16
- const requestId =
17
- (req.headers['x-request-id'] as string) || `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
18
- let requestedBy = 'system';
19
-
20
- // Check component signature
21
- const sig = req.get('x-component-sig');
22
- const componentDid = req.get('x-component-did');
23
- if (sig && componentDid) {
24
- const data = typeof req.body === 'undefined' ? {} : req.body;
25
- const verified = await verify(data, sig);
26
- if (verified) {
27
- requestedBy = componentDid;
28
- }
29
- }
30
-
31
- // Check user DID from headers
32
- if (req.headers['x-user-did']) {
33
- requestedBy = req.headers['x-user-did'] as string;
34
- }
35
-
36
- // Check authenticated user
37
- if (req.user?.did) {
38
- requestedBy = req.user.did;
39
- }
40
-
41
- return context.run(
42
- {
43
- requestId,
44
- requestedBy,
45
- },
46
- async () => {
47
- await next();
48
- }
49
- );
50
- }
@@ -1,192 +0,0 @@
1
- import { auth } from '@blocklet/sdk/lib/middlewares';
2
- import { getVerifyData, verify } from '@blocklet/sdk/lib/util/verify-sign';
3
- import { verifyLoginToken } from '@blocklet/sdk/lib/util/verify-session';
4
- import { getWallet } from '@blocklet/sdk/lib/wallet';
5
- import type { NextFunction, Request, Response } from 'express';
6
- import type { Model } from 'sequelize';
7
-
8
- import { Customer } from '../store/models/customer';
9
-
10
- export const ensureAdmin = auth({ roles: ['owner', 'admin'] });
11
-
12
- const wallet = getWallet();
13
-
14
- type PermissionSpec<T extends Model> = {
15
- component?: boolean; // allow component calls
16
- roles?: string[]; // allow current session user with one of the specified roles
17
- record?: {
18
- // allow record owner
19
- model: T;
20
- field: string;
21
- findById?: (id: string) => Promise<T | null>;
22
- };
23
- mine?: boolean;
24
- embed?: boolean;
25
- ensureLogin?: boolean;
26
- };
27
-
28
- /**
29
- * This middleware is used to authenticate request by session or component call.
30
- * If a request is authenticated, it will set `req.user` to the session user.
31
- * If a request is authenticated by component call, it will set `req.user` to the component user.
32
- * If a request is authenticated by record owner, it will set `req.user` to the session user and set `req.doc` to the record.
33
- */
34
- export function authenticate<T extends Model>({
35
- component,
36
- roles,
37
- record,
38
- mine,
39
- embed,
40
- ensureLogin,
41
- }: PermissionSpec<T>) {
42
- return async (req: Request, res: Response, next: NextFunction) => {
43
- // Dev-only bypass: requires both NODE_ENV=development AND the explicit
44
- // opt-in env ENABLE_DEV_FAKE_AUTH=1, plus the x-dev-fake-did header on
45
- // the request. Lets mobile demo clients that don't go through DID Connect
46
- // (e.g. the local-tunnel backend which bypasses Blocklet Server) still
47
- // exercise real handlers. Production never sets ENABLE_DEV_FAKE_AUTH, so
48
- // this branch can't trigger there; dev defaults off, so we don't
49
- // accidentally regress to fake auth after wiring the real flow.
50
- if (process.env.NODE_ENV === 'development' && process.env.ENABLE_DEV_FAKE_AUTH === '1') {
51
- const devDid = req.get('x-dev-fake-did');
52
- if (devDid) {
53
- req.user = {
54
- did: devDid,
55
- role: 'owner', // satisfies routes that require owner/admin
56
- provider: 'dev',
57
- fullName: 'dev-fake-user',
58
- walletOS: '',
59
- via: 'dev',
60
- };
61
- return next();
62
- }
63
- }
64
-
65
- // Authenticate by Authorization: Bearer <login-token>. The token is a JWT
66
- // signed with this blocklet's session secret (see @blocklet/sdk session
67
- // middleware). When clients hit us through a tunnel that bypasses Blocklet
68
- // Server (so x-user-did is NOT injected), we need to verify the token
69
- // ourselves. verifyLoginToken does local JWT signature verification, no
70
- // HTTP callback. On success we forward into the existing x-user-did branch
71
- // by populating req.headers so the role/mine/record cascade below applies
72
- // unchanged.
73
- const authHeader = req.get('authorization');
74
- if (authHeader && /^Bearer\s+/i.test(authHeader) && !req.headers['x-user-did']) {
75
- const token = authHeader.replace(/^Bearer\s+/i, '').trim();
76
- if (token) {
77
- const session = await verifyLoginToken({ token, strictMode: false }).catch(() => null);
78
- if (session?.did) {
79
- // Some BS versions put a bare base58 address in the JWT, others
80
- // the canonical `did:abt:…` form. Normalize so downstream code
81
- // (entitlement self-check, mine: lookups by req.user.did) sees the
82
- // same shape clients send in `customer_did` query params.
83
- const canonicalDid = session.did.startsWith('did:abt:') ? session.did : `did:abt:${session.did}`;
84
- req.headers['x-user-did'] = canonicalDid;
85
- req.headers['x-user-role'] = `blocklet-${session.role || 'user'}`;
86
- req.headers['x-user-provider'] = session.provider || 'wallet';
87
- req.headers['x-user-fullname'] = encodeURIComponent(session.fullName || '');
88
- req.headers['x-user-wallet-os'] = session.walletOS || '';
89
- }
90
- }
91
- }
92
-
93
- // authenticate by component call
94
- const sig = req.get('x-component-sig');
95
- if (component && sig) {
96
- const { data } = getVerifyData(req as any, 'component');
97
- const verified = await verify(data, sig);
98
- if (!verified) {
99
- return res.status(401).json({ error: 'Invalid signature for component call' });
100
- }
101
-
102
- req.user = {
103
- did: <string>req.get('x-component-did'),
104
- role: 'owner',
105
- provider: 'wallet',
106
- fullName: <string>req.get('x-component-did'),
107
- walletOS: '',
108
- via: 'api',
109
- };
110
-
111
- return next();
112
- }
113
-
114
- // authenticate by authToken for embed
115
- const token = req.query.authToken || '';
116
- const id = req.params.id || req.query.subscription_id || '';
117
- if (embed && token && id) {
118
- try {
119
- const verified = await wallet.verify(id as string, token as string);
120
- if (!verified) {
121
- return res.status(401).json({ error: `Invalid signature for embed: ${id}` });
122
- }
123
-
124
- req.user = {
125
- did: wallet.address,
126
- role: 'owner',
127
- provider: 'wallet',
128
- fullName: 'embed',
129
- walletOS: '',
130
- via: 'embed',
131
- };
132
-
133
- return next();
134
- } catch (err) {
135
- return res.status(401).json({ error: `Invalid signature for embed: ${id}: ${err.message}` });
136
- }
137
- }
138
-
139
- if (req.headers['x-user-did']) {
140
- const role = (<string>req.headers['x-user-role'] || '').replace('blocklet-', '') || 'guest';
141
- req.user = {
142
- did: <string>req.headers['x-user-did'],
143
- role,
144
- provider: <string>req.headers['x-user-provider'],
145
- fullName: decodeURIComponent(<string>req.headers['x-user-fullname']),
146
- walletOS: <string>req.headers['x-user-wallet-os'],
147
- via: 'dashboard',
148
- };
149
-
150
- // authenticate by session user
151
- if (roles) {
152
- if (roles.includes(req.user?.role)) {
153
- return next();
154
- }
155
- }
156
-
157
- if (ensureLogin) {
158
- req.user.via = 'api';
159
- return next();
160
- }
161
-
162
- if (mine) {
163
- const customer = await Customer.findOne({ where: { did: req.user.did } });
164
- if (customer) {
165
- req.customer = customer;
166
- req.query.customer_id = customer.id;
167
- return next();
168
- }
169
- }
170
-
171
- // authenticate by record owner
172
- if (record) {
173
- const { model, field = 'customer_id', findById } = record;
174
- const doc: T | null =
175
- findById && typeof findById === 'function'
176
- ? await findById(req.params.id as string)
177
- : await (model as any).findByPk(req.params.id);
178
- if (doc && doc[field as keyof T]) {
179
- const customer = await Customer.findOne({ where: { did: req.user.did } });
180
- req.doc = doc;
181
- req.customer = customer;
182
- if (customer && customer.id === doc[field as keyof T]) {
183
- req.user.via = 'portal';
184
- return next();
185
- }
186
- }
187
- }
188
- }
189
-
190
- return res.status(403).json({ error: 'Not authorized to perform this action' });
191
- };
192
- }