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,662 +0,0 @@
1
- import { getUrl } from '@blocklet/sdk';
2
- import { literal, Op } from 'sequelize';
3
- import { events } from '../libs/event';
4
- import logger from '../libs/logger';
5
- import createQueue from '../libs/queue';
6
- import { uploadBillingInfo, BillingInfo, getEndpointAndSpaceDid } from '../libs/did-space';
7
- import {
8
- Customer,
9
- EVMChainType,
10
- Invoice,
11
- Job,
12
- PaymentCurrency,
13
- PaymentIntent,
14
- PaymentMethod,
15
- PaymentMethodSettings,
16
- Refund,
17
- Subscription,
18
- TInvoiceExpanded,
19
- TRefundExpanded,
20
- } from '../store/models';
21
- import { CHARGE_SUPPORTED_CHAIN_TYPES } from '../libs/constants';
22
- import env from '../libs/env';
23
- import dayjs from '../libs/dayjs';
24
- import { getExplorerTxUrl } from '../libs/util';
25
-
26
- // Types
27
- export type SpaceUploadType = 'invoice' | 'refund' | 'customer' | 'customerInvoiceChunk' | 'customerRefundChunk';
28
- export type SpaceUploadData = {
29
- invoice: { id: string };
30
- refund: { id: string };
31
- customer: { id: string };
32
- customerInvoiceChunk: {
33
- customerId: string;
34
- chunkIndex: number;
35
- totalChunks: number;
36
- ids: string[];
37
- processTime: number;
38
- };
39
- customerRefundChunk: {
40
- customerId: string;
41
- chunkIndex: number;
42
- totalChunks: number;
43
- ids: string[];
44
- processTime: number;
45
- };
46
- };
47
-
48
- export type SpaceUploadJob = {
49
- type: SpaceUploadType;
50
- data: SpaceUploadData[SpaceUploadType];
51
- };
52
-
53
- // Utility functions
54
- const categoryMap = {
55
- stake: 'stake',
56
- slash_stake: 'slashStake',
57
- overdraft_protection: 'fee',
58
- stake_overdraft_protection: 'stake',
59
- recharge: 'recharge',
60
- return_stake: 'returnStake',
61
- auto_recharge: 'recharge',
62
- } as const;
63
-
64
- const getBillingCategory = (billingReason: string): string => {
65
- if (
66
- billingReason.includes('stake') ||
67
- billingReason.includes('recharge') ||
68
- billingReason === 'overdraft_protection'
69
- ) {
70
- return categoryMap[billingReason as keyof typeof categoryMap] || 'payment';
71
- }
72
- return 'payment';
73
- };
74
-
75
- const createSubscriptionInfo = (subscription: Subscription) => ({
76
- type: 'subscription' as const,
77
- id: subscription.id,
78
- name: subscription.description || subscription.id,
79
- status: subscription.status,
80
- period_start: subscription.current_period_start,
81
- period_end: subscription.current_period_end,
82
- link: getUrl(`customer/subscription/${subscription.id}`),
83
- });
84
-
85
- const handleInvoicePaid = async (invoiceId: string) => {
86
- const invoice = (await Invoice.findByPk(invoiceId, {
87
- include: [
88
- {
89
- model: Customer,
90
- as: 'customer',
91
- attributes: ['did'],
92
- },
93
- {
94
- model: PaymentCurrency,
95
- as: 'paymentCurrency',
96
- attributes: ['symbol', 'decimal'],
97
- },
98
- {
99
- model: PaymentMethod,
100
- as: 'paymentMethod',
101
- attributes: ['type', 'settings'],
102
- },
103
- {
104
- model: Subscription,
105
- as: 'subscription',
106
- attributes: ['id', 'description', 'status', 'current_period_start', 'current_period_end'],
107
- },
108
- {
109
- model: PaymentIntent,
110
- as: 'paymentIntent',
111
- },
112
- ],
113
- })) as TInvoiceExpanded | null;
114
-
115
- // Validation
116
- if (!invoice) {
117
- logger.info('Upload invoice skipped because invoice not found:', { invoiceId });
118
- return;
119
- }
120
-
121
- if (invoice.metadata?.did_space_uploaded) {
122
- logger.info('Upload invoice skipped because invoice already uploaded:', { invoiceId });
123
- return;
124
- }
125
-
126
- const paymentDetails = invoice.paymentIntent?.payment_details || invoice.metadata?.payment_details;
127
- if (!paymentDetails) {
128
- logger.info('Upload invoice skipped because payment details not found:', { invoiceId });
129
- return;
130
- }
131
-
132
- if (!CHARGE_SUPPORTED_CHAIN_TYPES.includes(invoice?.paymentMethod?.type)) {
133
- logger.info('Upload invoice skipped because payment method is not supported:', {
134
- invoiceId,
135
- paymentMethod: invoice?.paymentMethod?.type,
136
- });
137
- return;
138
- }
139
-
140
- const txHash = paymentDetails[invoice.paymentMethod?.type].tx_hash;
141
- if (!txHash) {
142
- logger.info('Upload invoice skipped because tx hash not found:', { invoiceId });
143
- return;
144
- }
145
-
146
- let spaceDid = null;
147
- let endpoint = null;
148
- try {
149
- const result = await getEndpointAndSpaceDid(invoice.customer?.did);
150
- spaceDid = result.spaceDid;
151
- endpoint = result.endpoint;
152
- } catch (error) {
153
- logger.info('Customer space endpoint not available:', {
154
- invoiceId,
155
- did: invoice.customer?.did,
156
- error: (error as Error).message,
157
- });
158
- return;
159
- }
160
-
161
- const methodInfo = (invoice.paymentMethod?.settings as PaymentMethodSettings)?.[
162
- invoice.paymentMethod?.type as 'arcblock' | EVMChainType
163
- ];
164
- // Create billing info
165
- const billInfo: BillingInfo = {
166
- tx_hash: txHash,
167
- timestamp: dayjs(invoice.updated_at).unix(),
168
- invoice_id: invoice.id,
169
- category: getBillingCategory(invoice.billing_reason),
170
- description: invoice.description || 'Payment received',
171
- amount: invoice.amount_paid,
172
- currency: {
173
- symbol: invoice.paymentCurrency?.symbol,
174
- decimal: invoice.paymentCurrency?.decimal || 18,
175
- type: invoice.paymentMethod?.type || '',
176
- chain_id: methodInfo?.chain_id || '',
177
- explorer_host: methodInfo?.explorer_host || '',
178
- explorer_tx_url: methodInfo?.explorer_host
179
- ? getExplorerTxUrl({
180
- explorerHost: methodInfo?.explorer_host,
181
- txHash,
182
- type: invoice.paymentMethod?.type || '',
183
- })
184
- : '',
185
- },
186
- customer_did: invoice.customer?.did,
187
- link: getUrl(`customer/invoice/${invoice.id}`),
188
- app_pid: env.appPid,
189
- };
190
-
191
- if (invoice.subscription_id && invoice.subscription) {
192
- billInfo.related = createSubscriptionInfo(invoice.subscription as Subscription);
193
- }
194
-
195
- // Upload billing info
196
- const result = await uploadBillingInfo(invoice.customer?.did, billInfo, endpoint);
197
- if (result) {
198
- // @ts-ignore
199
- await invoice.update({
200
- metadata: {
201
- ...invoice.metadata,
202
- did_space_uploaded: true,
203
- did_space: spaceDid,
204
- },
205
- });
206
- logger.info('Successfully uploaded paid invoice:', {
207
- invoiceId: invoice.id,
208
- txHash,
209
- customerDid: invoice.customer?.did,
210
- });
211
- } else {
212
- logger.error('Failed to upload paid invoice:', {
213
- invoiceId: invoice.id,
214
- txHash,
215
- customerDid: invoice.customer?.did,
216
- });
217
- }
218
- };
219
-
220
- const handleRefundPaid = async (refundId: string) => {
221
- const refund = (await Refund.findByPk(refundId, {
222
- include: [
223
- { model: Customer, as: 'customer', attributes: ['did'] },
224
- {
225
- model: PaymentMethod,
226
- as: 'paymentMethod',
227
- attributes: ['type', 'settings'],
228
- },
229
- {
230
- model: PaymentIntent,
231
- as: 'paymentIntent',
232
- },
233
- {
234
- model: PaymentCurrency,
235
- as: 'paymentCurrency',
236
- attributes: ['symbol', 'decimal'],
237
- },
238
- ],
239
- })) as TRefundExpanded | null;
240
- if (!refund) {
241
- logger.info('Upload refund skipped because refund not found:', { refundId });
242
- return;
243
- }
244
-
245
- if (refund.metadata?.did_space_uploaded) {
246
- logger.info('Upload refund skipped because refund already uploaded:', { refundId });
247
- return;
248
- }
249
-
250
- if (!CHARGE_SUPPORTED_CHAIN_TYPES.includes(refund.paymentMethod?.type)) {
251
- logger.info('Upload refund skipped because payment method is not supported:', {
252
- refundId,
253
- paymentMethod: refund.paymentMethod?.type,
254
- });
255
- return;
256
- }
257
- // @ts-ignore
258
- const txHash = refund?.payment_details?.[refund.paymentMethod?.type]?.tx_hash;
259
- if (!txHash) {
260
- logger.info('Upload refund skipped because tx hash not found:', { refundId });
261
- return;
262
- }
263
-
264
- let spaceDid = null;
265
- let endpoint = null;
266
- try {
267
- const result = await getEndpointAndSpaceDid(refund.customer?.did);
268
- spaceDid = result.spaceDid;
269
- endpoint = result.endpoint;
270
- } catch (error) {
271
- logger.info('Customer space endpoint not available:', {
272
- refundId,
273
- did: refund.customer?.did,
274
- error: (error as Error).message,
275
- });
276
- return;
277
- }
278
-
279
- let invoice = null;
280
- if (refund.invoice_id) {
281
- invoice = await Invoice.findByPk(refund.invoice_id, {
282
- include: [{ model: Customer, as: 'customer', attributes: ['did'] }],
283
- });
284
- }
285
- const methodInfo = (refund.paymentMethod?.settings as PaymentMethodSettings)?.[
286
- refund.paymentMethod?.type as 'arcblock' | EVMChainType
287
- ];
288
- const billInfo: BillingInfo = {
289
- tx_hash: txHash,
290
- timestamp: dayjs(refund.updated_at).unix(),
291
- invoice_id: refund.id,
292
- category: 'refund',
293
- description: refund.description || 'Refund',
294
- amount: refund.amount,
295
- currency: {
296
- symbol: refund.paymentCurrency?.symbol,
297
- decimal: refund.paymentCurrency?.decimal || 18,
298
- type: refund.paymentMethod?.type || '',
299
- chain_id: methodInfo?.chain_id || '',
300
- explorer_host: methodInfo?.explorer_host || '',
301
- explorer_tx_url: methodInfo?.explorer_host
302
- ? getExplorerTxUrl({
303
- explorerHost: methodInfo?.explorer_host,
304
- txHash,
305
- type: refund.paymentMethod?.type || '',
306
- })
307
- : '',
308
- },
309
- customer_did: refund.customer?.did,
310
- link: getUrl(`customer/invoice/${refund.invoice_id}`),
311
- app_pid: env.appPid,
312
- };
313
- if (invoice) {
314
- billInfo.related = {
315
- type: 'invoice',
316
- id: invoice.id,
317
- name: invoice.description || invoice.id,
318
- link: getUrl(`customer/invoice/${invoice.id}`),
319
- };
320
- }
321
-
322
- const result = await uploadBillingInfo(refund.customer?.did, billInfo, endpoint);
323
- if (result) {
324
- // @ts-ignore
325
- await refund.update({
326
- metadata: {
327
- ...refund.metadata,
328
- did_space_uploaded: true,
329
- did_space: spaceDid,
330
- },
331
- });
332
- logger.info('Successfully uploaded paid refund:', {
333
- refundId: refund.id,
334
- txHash,
335
- customerDid: refund.customer?.did,
336
- });
337
- } else {
338
- logger.error('Failed to upload paid refund:', { refundId: refund.id, txHash, customerDid: refund.customer?.did });
339
- }
340
- };
341
-
342
- /**
343
- * create batch tasks for invoice or refund
344
- * @param type 'invoice' | 'refund'
345
- * @param customerId customer id
346
- * @param records records list
347
- * @param now current timestamp
348
- * @returns number of new tasks created
349
- */
350
- const createBatchTasks = (
351
- type: 'invoice' | 'refund',
352
- customerId: string,
353
- records: { id: string; created_at?: Date }[],
354
- now: number
355
- ): { tasksCreated: number; totalRecords: number } => {
356
- if (records.length === 0) {
357
- return { tasksCreated: 0, totalRecords: 0 };
358
- }
359
-
360
- const recordIds = records.map((record) => record.id);
361
- const recordLength = recordIds.length;
362
- const batchSize = 30;
363
- const chunks = Math.ceil(recordLength / batchSize);
364
- let tasksCreated = 0;
365
-
366
- for (let i = 0; i < chunks; i++) {
367
- const start = i * batchSize;
368
- const end = Math.min(start + batchSize, recordLength);
369
- const chunkRecordIds = recordIds.slice(start, end);
370
- const processTime = new Date(records?.[start]?.created_at || now).getTime();
371
- const chunkId = `space-${customerId}-${type}-chunk-${i}-${processTime}`;
372
-
373
- spaceQueue.push({
374
- id: chunkId,
375
- job: {
376
- type: type === 'invoice' ? 'customerInvoiceChunk' : 'customerRefundChunk',
377
- data: {
378
- customerId,
379
- chunkIndex: i,
380
- totalChunks: chunks,
381
- ids: chunkRecordIds,
382
- processTime,
383
- },
384
- },
385
- delay: 60 * (i + 1), // add 1 minute delay for each chunk
386
- });
387
-
388
- tasksCreated++;
389
- }
390
-
391
- logger.info(`Created ${type} chunk tasks`, {
392
- customerId,
393
- chunks,
394
- totalRecords: recordLength,
395
- });
396
-
397
- return {
398
- tasksCreated,
399
- totalRecords: recordLength,
400
- };
401
- };
402
-
403
- const syncCustomerBillingToSpace = async (customerId: string) => {
404
- try {
405
- const now = Date.now();
406
- const customer = await Customer.findByPkOrDid(customerId);
407
- if (!customer) {
408
- logger.info('Customer not found:', { customerId });
409
- return;
410
- }
411
-
412
- try {
413
- await getEndpointAndSpaceDid(customer.did);
414
- } catch (error) {
415
- logger.info('Customer space endpoint not available:', {
416
- customerId,
417
- did: customer.did,
418
- error: (error as Error).message,
419
- });
420
- return;
421
- }
422
-
423
- const validMethods = await PaymentMethod.findAll({
424
- where: {
425
- type: { [Op.in]: CHARGE_SUPPORTED_CHAIN_TYPES },
426
- },
427
- attributes: ['id'],
428
- });
429
-
430
- if (!validMethods.length) {
431
- logger.info('No valid payment methods found', { customerId });
432
- return;
433
- }
434
-
435
- const validMethodIds = validMethods.map((method) => method.id);
436
-
437
- let invoiceProcessTime = 0;
438
- let refundProcessTime = 0;
439
- try {
440
- // get the latest process time from the queue
441
- const [invoiceProcessQueue, refundProcessQueue] = await Promise.all([
442
- Job.findOne({
443
- where: {
444
- queue: 'did-space',
445
- id: { [Op.like]: `space-${customerId}-invoice-chunk-%` },
446
- cancelled: false,
447
- },
448
- order: [['created_at', 'DESC']],
449
- }),
450
- Job.findOne({
451
- where: {
452
- queue: 'did-space',
453
- id: { [Op.like]: `space-${customerId}-refund-chunk-%` },
454
- cancelled: false,
455
- },
456
- order: [['created_at', 'DESC']],
457
- }),
458
- ]);
459
-
460
- invoiceProcessTime = invoiceProcessQueue?.job?.data?.processTime ?? 0;
461
- refundProcessTime = refundProcessQueue?.job?.data?.processTime ?? 0;
462
- logger.info('Invoice and refund process time', {
463
- customerId,
464
- invoiceProcessTime,
465
- refundProcessTime,
466
- });
467
- } catch (error) {
468
- logger.error('Failed to get invoice and refund process time', { customerId, error });
469
- }
470
-
471
- let [invoices, refunds] = await Promise.all([
472
- Invoice.findAll({
473
- where: {
474
- status: 'paid',
475
- 'metadata.did_space_uploaded': { [Op.not]: true },
476
- customer_id: customer.id,
477
- default_payment_method_id: { [Op.in]: validMethodIds },
478
- // only process invoices after the latest process time
479
- created_at: { [Op.gte]: invoiceProcessTime },
480
- },
481
- attributes: ['id', 'metadata', 'payment_intent_id', 'created_at'],
482
- order: [['created_at', 'DESC']],
483
- }),
484
- Refund.findAll({
485
- where: {
486
- status: 'succeeded',
487
- 'metadata.did_space_uploaded': { [Op.not]: true },
488
- customer_id: customer.id,
489
- payment_method_id: { [Op.in]: validMethodIds },
490
- [Op.and]: [literal('payment_details IS NOT NULL')],
491
- // only process refunds after the latest process time
492
- created_at: { [Op.gte]: refundProcessTime },
493
- },
494
- attributes: ['id', 'metadata', 'payment_details', 'created_at'],
495
- order: [['created_at', 'DESC']],
496
- }),
497
- ]);
498
-
499
- invoices = invoices.filter((x) => x.metadata?.payment_details || x.payment_intent_id);
500
- refunds = refunds.filter((x) => x.payment_details);
501
-
502
- const invoicesLength = invoices.length;
503
- const refundsLength = refunds.length;
504
- logger.info('Found records to upload:', {
505
- customerId,
506
- did: customer.did,
507
- invoiceCount: invoicesLength,
508
- refundCount: refundsLength,
509
- });
510
-
511
- if (invoicesLength === 0 && refundsLength === 0) {
512
- logger.info('No records to process', { customerId });
513
- return;
514
- }
515
-
516
- const invoiceChunks = createBatchTasks('invoice', customerId, invoices, now);
517
-
518
- const refundChunks = createBatchTasks('refund', customerId, refunds, now);
519
-
520
- logger.info('Completed creating all chunk tasks', {
521
- customerId,
522
- invoiceChunks: invoiceChunks.tasksCreated,
523
- refundChunks: refundChunks.tasksCreated,
524
- totalRecords: invoicesLength + refundsLength,
525
- });
526
- } catch (error) {
527
- logger.error('Failed to create chunk tasks:', {
528
- customerId,
529
- error: error instanceof Error ? error.message : String(error),
530
- });
531
- }
532
- };
533
-
534
- const handleCustomerInvoiceChunk = async (data: SpaceUploadData['customerInvoiceChunk']) => {
535
- const { customerId, chunkIndex, totalChunks, ids: invoiceIds } = data;
536
-
537
- logger.info('Processing invoice chunk', {
538
- customerId,
539
- chunkIndex: chunkIndex + 1,
540
- totalChunks,
541
- invoiceCount: invoiceIds.length,
542
- });
543
-
544
- try {
545
- await Promise.all(
546
- invoiceIds.map((invoiceId) =>
547
- handleInvoicePaid(invoiceId).catch((error) => {
548
- logger.error('Failed to process invoice:', {
549
- customerId,
550
- invoiceId,
551
- error: error.message,
552
- });
553
- })
554
- )
555
- );
556
-
557
- logger.info('Completed invoice chunk processing', {
558
- customerId,
559
- chunkIndex: chunkIndex + 1,
560
- totalChunks,
561
- processedInvoices: invoiceIds.length,
562
- });
563
- } catch (error) {
564
- logger.error('Invoice chunk processing failed:', {
565
- customerId,
566
- chunkIndex,
567
- error: error instanceof Error ? error.message : String(error),
568
- });
569
- }
570
- };
571
-
572
- const handleCustomerRefundChunk = async (data: SpaceUploadData['customerRefundChunk']) => {
573
- const { customerId, chunkIndex, totalChunks, ids: refundIds } = data;
574
-
575
- logger.info('Processing refund chunk', {
576
- customerId,
577
- chunkIndex: chunkIndex + 1,
578
- totalChunks,
579
- refundCount: refundIds.length,
580
- });
581
-
582
- try {
583
- await Promise.all(
584
- refundIds.map((refundId) =>
585
- handleRefundPaid(refundId).catch((error) => {
586
- logger.error('Failed to process refund:', {
587
- customerId,
588
- refundId,
589
- error: error.message,
590
- });
591
- })
592
- )
593
- );
594
-
595
- logger.info('Completed refund chunk processing', {
596
- customerId,
597
- chunkIndex: chunkIndex + 1,
598
- totalChunks,
599
- processedRefunds: refundIds.length,
600
- });
601
- } catch (error) {
602
- logger.error('Refund chunk processing failed:', {
603
- customerId,
604
- chunkIndex,
605
- error: error instanceof Error ? error.message : String(error),
606
- });
607
- }
608
- };
609
-
610
- const handlers = {
611
- invoice: (data: SpaceUploadData['invoice']) => handleInvoicePaid(data.id),
612
- refund: (data: SpaceUploadData['refund']) => handleRefundPaid(data.id),
613
- customer: (data: SpaceUploadData['customer']) => syncCustomerBillingToSpace(data.id),
614
- customerInvoiceChunk: (data: SpaceUploadData['customerInvoiceChunk']) => handleCustomerInvoiceChunk(data),
615
- customerRefundChunk: (data: SpaceUploadData['customerRefundChunk']) => handleCustomerRefundChunk(data),
616
- };
617
-
618
- export const handleSpaceUpload = async (job: SpaceUploadJob) => {
619
- logger.info('Starting to handle space upload', job);
620
- const handler = handlers[job.type];
621
- if (!handler) {
622
- logger.error('No handler found for job type', { job });
623
- return;
624
- }
625
- // @ts-ignore
626
- await handler(job.data);
627
- };
628
-
629
- export const spaceQueue = createQueue<SpaceUploadJob>({
630
- name: 'did-space',
631
- onJob: handleSpaceUpload,
632
- options: {
633
- concurrency: 5,
634
- maxRetries: 3,
635
- enableScheduledJob: true,
636
- },
637
- });
638
-
639
- spaceQueue.on('failed', ({ id, job, error }) => {
640
- logger.error('Space upload job failed', { id, job, error });
641
- });
642
-
643
- export const startUploadBillingInfoListener = () => {
644
- events.on('invoice.paid', (invoice) => {
645
- spaceQueue.push({
646
- id: `space-${invoice.id}`,
647
- job: {
648
- type: 'invoice',
649
- data: { id: invoice.id },
650
- },
651
- });
652
- });
653
-
654
- events.on('refund.succeeded', (refund) => {
655
- spaceQueue.push({
656
- id: `space-${refund.id}`,
657
- job: { type: 'refund', data: { id: refund.id } },
658
- });
659
- });
660
-
661
- logger.info('Space upload listeners started');
662
- };
@@ -1,38 +0,0 @@
1
- import { Router } from 'express';
2
- import Joi from 'joi';
3
-
4
- import logger from '../libs/logger';
5
- import { authenticate } from '../libs/security';
6
- import { createToken } from '../integrations/arcblock/token';
7
-
8
- const router = Router();
9
- const auth = authenticate({ component: true, roles: ['owner', 'admin'] });
10
-
11
- const createTokenSchema = Joi.object({
12
- name: Joi.string().max(64).required(),
13
- symbol: Joi.string().max(16).required(),
14
- decimal: Joi.number().integer().min(2).max(18).default(10),
15
- }).unknown(true);
16
-
17
- router.post('/', auth, async (req, res) => {
18
- try {
19
- const { error } = createTokenSchema.validate(req.body);
20
- if (error) {
21
- return res.status(400).json({ error: `Token create request invalid: ${error.message}` });
22
- }
23
-
24
- const tokenFactoryState = await createToken({
25
- name: req.body.name,
26
- symbol: req.body.symbol,
27
- decimal: req.body.decimal,
28
- livemode: !!req.livemode,
29
- });
30
-
31
- return res.json(tokenFactoryState);
32
- } catch (err) {
33
- logger.error('create credit token failed', { error: err?.message, request: req.body });
34
- return res.status(400).json({ error: err?.message });
35
- }
36
- });
37
-
38
- export default router;