payment-kit 1.29.1 → 1.29.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/api/dev.ts +41 -2
  2. package/api/hono.d.ts +42 -0
  3. package/api/node-sqlite.d.ts +12 -0
  4. package/api/src/bootstrap.ts +36 -0
  5. package/api/src/crons/base.ts +3 -3
  6. package/api/src/crons/currency.ts +1 -1
  7. package/api/src/crons/index.ts +27 -24
  8. package/api/src/crons/metering-subscription-detection.ts +1 -1
  9. package/api/src/crons/overdue-detection.ts +2 -2
  10. package/api/src/crons/retry-pending-events.ts +6 -0
  11. package/api/src/index.ts +22 -161
  12. package/api/src/integrations/app-store/client.ts +3 -4
  13. package/api/src/integrations/app-store/handlers/subscription.ts +7 -7
  14. package/api/src/integrations/app-store/signed-data-verifier.ts +3 -2
  15. package/api/src/integrations/arcblock/token.ts +21 -7
  16. package/api/src/integrations/google-play/handlers/subscription.ts +6 -6
  17. package/api/src/integrations/google-play/handlers/voided.ts +2 -2
  18. package/api/src/integrations/google-play/verify.ts +3 -2
  19. package/api/src/integrations/iap-reconcile.ts +3 -5
  20. package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
  21. package/api/src/integrations/stripe/handlers/subscription.ts +3 -3
  22. package/api/src/libs/archive/query.ts +19 -0
  23. package/api/src/libs/audit.ts +61 -4
  24. package/api/src/libs/auth.ts +99 -38
  25. package/api/src/libs/context.ts +78 -1
  26. package/api/src/libs/currency.ts +2 -2
  27. package/api/src/libs/dayjs.ts +8 -2
  28. package/api/src/libs/drivers/auth-storage.ts +118 -0
  29. package/api/src/libs/drivers/cron.ts +264 -0
  30. package/api/src/libs/drivers/db.ts +170 -0
  31. package/api/src/libs/drivers/identity.ts +81 -0
  32. package/api/src/libs/drivers/index.ts +40 -0
  33. package/api/src/libs/drivers/locks.ts +226 -0
  34. package/api/src/libs/drivers/migrate-runner.ts +70 -0
  35. package/api/src/libs/drivers/queue.ts +104 -0
  36. package/api/src/libs/drivers/secrets.ts +194 -0
  37. package/api/src/libs/env.ts +170 -54
  38. package/api/src/libs/exchange-rate/service.ts +7 -6
  39. package/api/src/libs/http-fetch-adapter.ts +50 -0
  40. package/api/src/libs/invoice.ts +1 -1
  41. package/api/src/libs/lock.ts +51 -47
  42. package/api/src/libs/logger.ts +48 -8
  43. package/api/src/libs/notification/index.ts +1 -1
  44. package/api/src/libs/notification/template/customer-credit-low-balance.ts +2 -1
  45. package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -1
  46. package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -1
  47. package/api/src/libs/overdraft-protection.ts +1 -1
  48. package/api/src/libs/payout.ts +1 -1
  49. package/api/src/libs/queue/index.ts +259 -52
  50. package/api/src/libs/queue/runtime.ts +175 -0
  51. package/api/src/libs/resource.ts +3 -3
  52. package/api/src/libs/secrets.ts +38 -0
  53. package/api/src/libs/session.ts +3 -2
  54. package/api/src/libs/subscription.ts +5 -5
  55. package/api/src/libs/tenant.ts +92 -0
  56. package/api/src/libs/url.ts +3 -3
  57. package/api/src/libs/util.ts +21 -13
  58. package/api/src/middlewares/hono/cdn.ts +63 -0
  59. package/api/src/middlewares/hono/context.ts +73 -0
  60. package/api/src/middlewares/hono/csrf.ts +72 -0
  61. package/api/src/middlewares/hono/fallback.ts +194 -0
  62. package/api/src/middlewares/hono/pipeline.ts +73 -0
  63. package/api/src/middlewares/hono/resource-mount.ts +42 -0
  64. package/api/src/middlewares/hono/resource.ts +63 -0
  65. package/api/src/middlewares/hono/security.ts +214 -0
  66. package/api/src/middlewares/hono/session.ts +114 -0
  67. package/api/src/middlewares/hono/xss.ts +61 -0
  68. package/api/src/queues/auto-recharge.ts +12 -10
  69. package/api/src/queues/checkout-session.ts +17 -12
  70. package/api/src/queues/credit-consume.ts +40 -36
  71. package/api/src/queues/credit-grant.ts +25 -18
  72. package/api/src/queues/credit-reconciliation.ts +7 -5
  73. package/api/src/queues/discount-status.ts +9 -6
  74. package/api/src/queues/event.ts +12 -4
  75. package/api/src/queues/exchange-rate-health.ts +49 -30
  76. package/api/src/queues/invoice.ts +18 -15
  77. package/api/src/queues/notification.ts +14 -7
  78. package/api/src/queues/payment.ts +41 -28
  79. package/api/src/queues/payout.ts +9 -5
  80. package/api/src/queues/refund.ts +18 -12
  81. package/api/src/queues/subscription.ts +83 -53
  82. package/api/src/queues/token-transfer.ts +15 -10
  83. package/api/src/queues/usage-record.ts +8 -5
  84. package/api/src/queues/vendors/commission.ts +7 -5
  85. package/api/src/queues/vendors/fulfillment-coordinator.ts +17 -13
  86. package/api/src/queues/vendors/fulfillment.ts +4 -2
  87. package/api/src/queues/vendors/return-processor.ts +5 -3
  88. package/api/src/queues/vendors/return-scanner.ts +5 -4
  89. package/api/src/queues/vendors/status-check.ts +10 -7
  90. package/api/src/queues/webhook.ts +60 -32
  91. package/api/src/routes/connect/shared.ts +1 -2
  92. package/api/src/routes/connect/subscribe.ts +3 -3
  93. package/api/src/routes/{archive.ts → hono/archive.ts} +69 -64
  94. package/api/src/routes/{auto-recharge-configs.ts → hono/auto-recharge-configs.ts} +39 -28
  95. package/api/src/routes/{checkout-sessions.ts → hono/checkout-sessions.ts} +790 -923
  96. package/api/src/routes/{coupons.ts → hono/coupons.ts} +93 -76
  97. package/api/src/routes/{credit-grants.ts → hono/credit-grants.ts} +140 -126
  98. package/api/src/routes/hono/credit-tokens.ts +43 -0
  99. package/api/src/routes/{credit-transactions.ts → hono/credit-transactions.ts} +37 -29
  100. package/api/src/routes/{customers.ts → hono/customers.ts} +193 -223
  101. package/api/src/routes/{donations.ts → hono/donations.ts} +41 -32
  102. package/api/src/routes/{entitlements.ts → hono/entitlements.ts} +28 -25
  103. package/api/src/routes/{events.ts → hono/events.ts} +107 -71
  104. package/api/src/routes/{exchange-rate-providers.ts → hono/exchange-rate-providers.ts} +138 -126
  105. package/api/src/routes/hono/exchange-rates.ts +77 -0
  106. package/api/src/routes/hono/index.ts +115 -0
  107. package/api/src/routes/{integrations → hono/integrations}/app-store.ts +68 -48
  108. package/api/src/routes/{integrations → hono/integrations}/google-play.ts +78 -58
  109. package/api/src/routes/hono/integrations/stripe.ts +74 -0
  110. package/api/src/routes/{invoices.ts → hono/invoices.ts} +253 -244
  111. package/api/src/routes/{meter-events.ts → hono/meter-events.ts} +120 -110
  112. package/api/src/routes/hono/meters.ts +288 -0
  113. package/api/src/routes/hono/passports.ts +73 -0
  114. package/api/src/routes/{payment-currencies.ts → hono/payment-currencies.ts} +219 -197
  115. package/api/src/routes/{payment-intents.ts → hono/payment-intents.ts} +136 -132
  116. package/api/src/routes/{payment-links.ts → hono/payment-links.ts} +145 -128
  117. package/api/src/routes/{payment-methods.ts → hono/payment-methods.ts} +125 -93
  118. package/api/src/routes/{payment-stats.ts → hono/payment-stats.ts} +30 -25
  119. package/api/src/routes/{payouts.ts → hono/payouts.ts} +55 -47
  120. package/api/src/routes/{prices.ts → hono/prices.ts} +265 -242
  121. package/api/src/routes/{pricing-table.ts → hono/pricing-table.ts} +94 -87
  122. package/api/src/routes/{products.ts → hono/products.ts} +172 -159
  123. package/api/src/routes/{promotion-codes.ts → hono/promotion-codes.ts} +207 -185
  124. package/api/src/routes/hono/redirect.ts +24 -0
  125. package/api/src/routes/{refunds.ts → hono/refunds.ts} +96 -80
  126. package/api/src/routes/{settings.ts → hono/settings.ts} +64 -55
  127. package/api/src/routes/{subscription-items.ts → hono/subscription-items.ts} +64 -57
  128. package/api/src/routes/{subscriptions.ts → hono/subscriptions.ts} +475 -528
  129. package/api/src/routes/{tax-rates.ts → hono/tax-rates.ts} +71 -70
  130. package/api/src/routes/hono/tool.ts +69 -0
  131. package/api/src/routes/{usage-records.ts → hono/usage-records.ts} +47 -42
  132. package/api/src/routes/{vendor.ts → hono/vendor.ts} +315 -167
  133. package/api/src/routes/{webhook-attempts.ts → hono/webhook-attempts.ts} +17 -13
  134. package/api/src/routes/hono/webhook-endpoints.ts +126 -0
  135. package/api/src/service.ts +667 -0
  136. package/api/src/store/migrations/20230911-seeding.ts +2 -1
  137. package/api/src/store/migrations/20260609-remove-did-space-jobs.ts +23 -0
  138. package/api/src/store/migrations/20260610-tenant-columns.ts +40 -0
  139. package/api/src/store/migrations/20260611-tenant-backfill.ts +33 -0
  140. package/api/src/store/models/auto-recharge-config.ts +22 -10
  141. package/api/src/store/models/checkout-session.ts +15 -14
  142. package/api/src/store/models/coupon.ts +29 -20
  143. package/api/src/store/models/credit-grant.ts +38 -29
  144. package/api/src/store/models/credit-transaction.ts +32 -21
  145. package/api/src/store/models/customer.ts +19 -17
  146. package/api/src/store/models/discount.ts +11 -2
  147. package/api/src/store/models/entitlement-grant.ts +21 -9
  148. package/api/src/store/models/entitlement-product.ts +21 -9
  149. package/api/src/store/models/entitlement.ts +19 -10
  150. package/api/src/store/models/event.ts +18 -9
  151. package/api/src/store/models/exchange-rate-provider.ts +17 -4
  152. package/api/src/store/models/invoice-item.ts +18 -9
  153. package/api/src/store/models/invoice.ts +16 -8
  154. package/api/src/store/models/meter-event.ts +27 -9
  155. package/api/src/store/models/meter.ts +31 -22
  156. package/api/src/store/models/payment-currency.ts +25 -8
  157. package/api/src/store/models/payment-intent.ts +15 -6
  158. package/api/src/store/models/payment-link.ts +15 -6
  159. package/api/src/store/models/payment-method.ts +38 -22
  160. package/api/src/store/models/payment-stat.ts +18 -9
  161. package/api/src/store/models/payout.ts +15 -6
  162. package/api/src/store/models/price-quote.ts +17 -8
  163. package/api/src/store/models/price.ts +24 -12
  164. package/api/src/store/models/pricing-table.ts +29 -20
  165. package/api/src/store/models/product-vendor.ts +20 -10
  166. package/api/src/store/models/product.ts +15 -6
  167. package/api/src/store/models/promotion-code.ts +14 -6
  168. package/api/src/store/models/refund.ts +15 -6
  169. package/api/src/store/models/revenue-snapshot.ts +21 -9
  170. package/api/src/store/models/setting.ts +18 -9
  171. package/api/src/store/models/setup-intent.ts +36 -27
  172. package/api/src/store/models/subscription-item.ts +21 -9
  173. package/api/src/store/models/subscription-schedule.ts +21 -9
  174. package/api/src/store/models/subscription.ts +21 -10
  175. package/api/src/store/models/tax-rate.ts +29 -21
  176. package/api/src/store/models/usage-record.ts +11 -2
  177. package/api/src/store/models/webhook-attempt.ts +18 -9
  178. package/api/src/store/models/webhook-endpoint.ts +18 -9
  179. package/api/src/store/scoped-core.ts +55 -0
  180. package/api/src/store/scoped.ts +247 -0
  181. package/api/src/store/sequelize.ts +66 -22
  182. package/api/src/store/sql-migrations.ts +20 -0
  183. package/api/src/store/tenant-backfill.ts +260 -0
  184. package/api/src/store/tenant-model.ts +124 -0
  185. package/api/src/store/tenant-tables.ts +50 -0
  186. package/api/tests/embedded/embedded-multi-mode-d3.spec.ts +257 -0
  187. package/api/tests/fixtures/bare-query-violation.ts +13 -0
  188. package/api/tests/fixtures/core-env-violation.ts +10 -0
  189. package/api/tests/fixtures/host-read-violation.ts +19 -0
  190. package/api/tests/fixtures/tenants.ts +4 -0
  191. package/api/tests/integrations/iap-tenant.spec.ts +284 -0
  192. package/api/tests/libs/archive-query.spec.ts +26 -0
  193. package/api/tests/libs/audit-tenant.spec.ts +153 -0
  194. package/api/tests/libs/context.spec.ts +204 -0
  195. package/api/tests/libs/core-config.spec.ts +115 -0
  196. package/api/tests/libs/cron-driver-d2.spec.ts +237 -0
  197. package/api/tests/libs/crons-conservation-d2.spec.ts +52 -0
  198. package/api/tests/libs/lock-tenant.spec.ts +66 -0
  199. package/api/tests/libs/scoped.spec.ts +222 -0
  200. package/api/tests/libs/secrets-facade.spec.ts +52 -0
  201. package/api/tests/libs/tenancy-slot-authority.spec.ts +209 -0
  202. package/api/tests/libs/tenant-middleware.spec.ts +42 -0
  203. package/api/tests/libs/tenant-scanner.spec.ts +120 -0
  204. package/api/tests/middlewares/hono/cdn.spec.ts +70 -0
  205. package/api/tests/middlewares/hono/context.spec.ts +113 -0
  206. package/api/tests/middlewares/hono/csrf.spec.ts +136 -0
  207. package/api/tests/middlewares/hono/fallback.spec.ts +67 -0
  208. package/api/tests/middlewares/hono/pipeline.spec.ts +47 -0
  209. package/api/tests/middlewares/hono/security.spec.ts +181 -0
  210. package/api/tests/middlewares/hono/session.spec.ts +42 -0
  211. package/api/tests/middlewares/hono/xss.spec.ts +81 -0
  212. package/api/tests/models/tenant-backfill.spec.ts +287 -0
  213. package/api/tests/models/tenant-columns-model.spec.ts +46 -0
  214. package/api/tests/models/tenant-columns.spec.ts +161 -0
  215. package/api/tests/queues/credit-consume-batch.spec.ts +8 -1
  216. package/api/tests/queues/credit-consume.spec.ts +8 -1
  217. package/api/tests/queues/event-tenant.spec.ts +236 -0
  218. package/api/tests/queues/exchange-rate-health-tenant-d6.spec.ts +62 -0
  219. package/api/tests/queues/queue-parity.spec.ts +249 -0
  220. package/api/tests/queues/queue-runtime-surface.spec.ts +277 -0
  221. package/api/tests/queues/queue-teardown-d2.spec.ts +127 -0
  222. package/api/tests/queues/tenant-matrix-a.spec.ts +245 -0
  223. package/api/tests/queues/tenant-matrix-b.spec.ts +168 -0
  224. package/api/tests/routes/connect/hono-attach.spec.ts +107 -0
  225. package/api/tests/service/collapse.spec.ts +96 -0
  226. package/api/tests/store/tenant-crosscut.spec.ts +202 -0
  227. package/api/tests/store/tenant-model-spike.spec.ts +177 -0
  228. package/api/tests/store/tenant-model.spec.ts +162 -0
  229. package/api/tests/store/tenant-residual.spec.ts +196 -0
  230. package/api/third.d.ts +4 -0
  231. package/blocklet.yml +1 -1
  232. package/cloudflare/README.md +26 -6
  233. package/cloudflare/build.ts +28 -13
  234. package/cloudflare/did-connect-auth.ts +0 -217
  235. package/cloudflare/migrations/0006_tenant_columns.sql +46 -0
  236. package/cloudflare/migrations/0007_tenant_backfill_indexes.sql +65 -0
  237. package/cloudflare/migrations/0008_schema_parity.sql +16 -0
  238. package/cloudflare/migrations/0009_remove_did_space_jobs.sql +5 -0
  239. package/cloudflare/queue-runtime-mode.ts +13 -0
  240. package/cloudflare/run-build.js +10 -56
  241. package/cloudflare/shims/blocklet-sdk/asset-host-transformer.ts +20 -0
  242. package/cloudflare/shims/blocklet-sdk/config.ts +8 -1
  243. package/cloudflare/shims/blocklet-sdk/login.ts +12 -0
  244. package/cloudflare/shims/blocklet-sdk/service-api.ts +14 -0
  245. package/cloudflare/shims/blocklet-sdk/session.ts +4 -2
  246. package/cloudflare/shims/blocklet-sdk/util-constants.ts +8 -0
  247. package/cloudflare/shims/blocklet-sdk/util-csrf.ts +13 -0
  248. package/cloudflare/shims/blocklet-sdk/util-wallet.ts +8 -0
  249. package/cloudflare/shims/cron.ts +38 -158
  250. package/cloudflare/shims/events.ts +124 -0
  251. package/cloudflare/shims/fastq.ts +15 -1
  252. package/cloudflare/shims/nedb-storage.ts +16 -8
  253. package/cloudflare/shims/xss.ts +8 -0
  254. package/cloudflare/tenant-middleware.ts +36 -0
  255. package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
  256. package/cloudflare/tests/worker-handler-gate.spec.ts +44 -0
  257. package/cloudflare/worker.ts +204 -433
  258. package/cloudflare/wrangler.local-e2e.jsonc +26 -0
  259. package/jest.config.js +3 -1
  260. package/package.json +33 -38
  261. package/scripts/core-env-whitelist.json +1 -0
  262. package/scripts/e2e-12b-runtime.ts +149 -0
  263. package/scripts/e2e-core-config.ts +125 -0
  264. package/scripts/e2e-d1-tenancy.ts +116 -0
  265. package/scripts/e2e-d2-cron-queue.ts +139 -0
  266. package/scripts/e2e-d3-embedded-multi.ts +171 -0
  267. package/scripts/e2e-hono-s2.ts +125 -0
  268. package/scripts/e2e-hono-s3e.ts +135 -0
  269. package/scripts/e2e-hono-s4.ts +114 -0
  270. package/scripts/e2e-migration-contract.ts +100 -0
  271. package/scripts/e2e-s0.ts +61 -0
  272. package/scripts/e2e-s1.ts +107 -0
  273. package/scripts/e2e-s2.ts +178 -0
  274. package/scripts/e2e-s3.ts +110 -0
  275. package/scripts/e2e-s4.ts +191 -0
  276. package/scripts/e2e-s5.ts +139 -0
  277. package/scripts/e2e-s6.ts +127 -0
  278. package/scripts/e2e-tenant-model.ts +119 -0
  279. package/scripts/e2e-tenant-worker.ts +199 -0
  280. package/scripts/gen-sql-migrations.js +46 -0
  281. package/scripts/phase8-codemod.js +219 -0
  282. package/scripts/phase9a-env-getters-codemod.js +82 -0
  283. package/scripts/scan-core-env.js +109 -0
  284. package/scripts/scan-tenant-queries.js +235 -0
  285. package/scripts/schema-drift-guard.ts +210 -0
  286. package/scripts/tenant-scan-whitelist.json +1 -0
  287. package/src/env.d.ts +13 -1
  288. package/tsconfig.json +1 -1
  289. package/api/src/libs/did-space.ts +0 -235
  290. package/api/src/libs/middleware.ts +0 -50
  291. package/api/src/libs/security.ts +0 -192
  292. package/api/src/queues/space.ts +0 -662
  293. package/api/src/routes/credit-tokens.ts +0 -38
  294. package/api/src/routes/exchange-rates.ts +0 -87
  295. package/api/src/routes/index.ts +0 -142
  296. package/api/src/routes/integrations/stripe.ts +0 -61
  297. package/api/src/routes/meters.ts +0 -274
  298. package/api/src/routes/passports.ts +0 -68
  299. package/api/src/routes/redirect.ts +0 -20
  300. package/api/src/routes/tool.ts +0 -65
  301. package/api/src/routes/webhook-endpoints.ts +0 -126
  302. package/api/tests/routes/credit-grants.spec.ts +0 -1261
  303. package/cloudflare/shims/did-space-js.ts +0 -17
  304. package/cloudflare/shims/did-space.ts +0 -11
  305. package/cloudflare/shims/express-compat/index.ts +0 -80
  306. package/cloudflare/shims/express-compat/types.ts +0 -41
  307. package/cloudflare/shims/lock.ts +0 -115
  308. package/cloudflare/shims/queue.ts +0 -611
  309. package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +0 -87
  310. package/cloudflare/tests/shims/queue-scheduled.spec.ts +0 -186
@@ -1,87 +0,0 @@
1
- import { Router } from 'express';
2
-
3
- import { getExchangeRateService } from '../libs/exchange-rate/service';
4
- import { getExchangeRateSymbol, hasTokenAddress } from '../libs/exchange-rate/token-address-mapping';
5
- import { authenticate } from '../libs/security';
6
- import { ChainType, PaymentCurrency, PaymentMethod } from '../store/models';
7
-
8
- const router = Router();
9
- const auth = authenticate({ component: true, roles: ['owner', 'admin'], ensureLogin: true });
10
- const exchangeRateService = getExchangeRateService();
11
-
12
- /**
13
- * Validate if exchange rate can be fetched for a currency
14
- * This is used during price creation/editing to validate the base currency
15
- */
16
- router.post('/validate', auth, async (req, res) => {
17
- try {
18
- const { currency: currencyId } = req.body;
19
-
20
- if (!currencyId) {
21
- return res.status(400).json({ error: 'currency is required' });
22
- }
23
-
24
- const paymentCurrency = (await PaymentCurrency.findByPk(currencyId, {
25
- include: [
26
- {
27
- model: PaymentMethod,
28
- as: 'payment_method',
29
- },
30
- ],
31
- })) as PaymentCurrency & { payment_method: PaymentMethod };
32
-
33
- if (!paymentCurrency) {
34
- return res.status(400).json({ error: 'Currency not found' });
35
- }
36
-
37
- if (paymentCurrency.payment_method?.type === 'stripe') {
38
- return res.status(400).json({
39
- error: `Currency ${paymentCurrency.symbol} is not supported.`,
40
- supported: false,
41
- });
42
- }
43
-
44
- // Check if token has address mapping
45
- if (!hasTokenAddress(paymentCurrency.symbol, paymentCurrency.payment_method?.type as ChainType)) {
46
- return res.status(400).json({
47
- error: `Currency ${paymentCurrency.symbol} is not supported.`,
48
- supported: false,
49
- });
50
- }
51
-
52
- // Try to fetch exchange rate
53
- try {
54
- const rateSymbol = getExchangeRateSymbol(
55
- paymentCurrency.symbol,
56
- paymentCurrency.payment_method?.type as ChainType
57
- );
58
- const rateResult = await exchangeRateService.getRate(rateSymbol);
59
-
60
- // Return full rate info consistent with checkout-sessions exchange-rate endpoint
61
- return res.json({
62
- supported: true,
63
- currency: paymentCurrency.symbol,
64
- base_currency: 'USD',
65
- rate: rateResult.rate,
66
- timestamp_ms: rateResult.timestamp_ms,
67
- fetched_at: rateResult.fetched_at,
68
- provider_id: rateResult.provider_id,
69
- provider_name: rateResult.provider_name,
70
- provider_display: rateResult.provider_display,
71
- providers: rateResult.providers,
72
- consensus_method: rateResult.consensus_method,
73
- degraded: rateResult.degraded,
74
- degraded_reason: rateResult.degraded_reason,
75
- });
76
- } catch (error: any) {
77
- return res.status(400).json({
78
- error: `Failed to fetch exchange rate for ${paymentCurrency.symbol}: ${error.message}`,
79
- supported: false,
80
- });
81
- }
82
- } catch (error: any) {
83
- return res.status(400).json({ error: error.message });
84
- }
85
- });
86
-
87
- export default router;
@@ -1,142 +0,0 @@
1
- import { Router } from 'express';
2
-
3
- import { PaymentCurrency } from '../store/models/payment-currency';
4
- import autoRechargeConfigs from './auto-recharge-configs';
5
- import checkoutSessions from './checkout-sessions';
6
- import coupons from './coupons';
7
- import creditGrants from './credit-grants';
8
- import creditTokens from './credit-tokens';
9
- import creditTransactions from './credit-transactions';
10
- import customers from './customers';
11
- import donations from './donations';
12
- import events from './events';
13
- import entitlements from './entitlements';
14
- import appStore from './integrations/app-store';
15
- import googlePlay from './integrations/google-play';
16
- import stripe from './integrations/stripe';
17
- import invoices from './invoices';
18
- import meterEvents from './meter-events';
19
- import taxRates from './tax-rates';
20
- import meters from './meters';
21
- import passports from './passports';
22
- import paymentCurrencies from './payment-currencies';
23
- import paymentIntents from './payment-intents';
24
- import paymentLinks from './payment-links';
25
- import paymentMethods from './payment-methods';
26
- import paymentStats from './payment-stats';
27
- import payouts from './payouts';
28
- import prices from './prices';
29
- import pricingTables from './pricing-table';
30
- import products from './products';
31
- import promotionCodes from './promotion-codes';
32
- import redirect from './redirect';
33
- import refunds from './refunds';
34
- import exchangeRateProviders from './exchange-rate-providers';
35
- import exchangeRates from './exchange-rates';
36
- import settings from './settings';
37
- import subscriptionItems from './subscription-items';
38
- import subscriptions from './subscriptions';
39
- import usageRecords from './usage-records';
40
- import webhookAttempts from './webhook-attempts';
41
- import webhookEndpoints from './webhook-endpoints';
42
- import vendor from './vendor';
43
- import tool from './tool';
44
- import archive from './archive';
45
-
46
- const router = Router();
47
-
48
- router.use((req, _, next) => {
49
- if (req.query.livemode) {
50
- try {
51
- req.livemode = !!JSON.parse(String(req.query.livemode));
52
- } catch {
53
- req.livemode = true;
54
- }
55
- } else if (typeof req.livemode !== 'boolean') {
56
- // CF Workers' createExpressReq pre-populates req.livemode from the worker's
57
- // PAYMENT_LIVEMODE env var; honor that when set. Express dev has no
58
- // upstream, so we fall back to the env var ourselves — defaulting to
59
- // livemode=true unless explicitly disabled via PAYMENT_LIVEMODE=false.
60
- req.livemode = process.env.PAYMENT_LIVEMODE !== 'false';
61
- }
62
-
63
- next();
64
- });
65
-
66
- // Lazy-load base currency with TTL cache to avoid DB query on every request
67
- const baseCurrencyCache = new Map<string, { data: any; expires: number }>();
68
- const BASE_CURRENCY_TTL = 5 * 60_000; // 5 minutes (invalidated on model update)
69
-
70
- // Lazily register hooks after models are initialized (avoid top-level addHook before Sequelize init)
71
- let baseCurrencyHooksRegistered = false;
72
- function ensureBaseCurrencyHooks() {
73
- if (baseCurrencyHooksRegistered) return;
74
- baseCurrencyHooksRegistered = true;
75
- PaymentCurrency.addHook('afterUpdate', 'invalidateBaseCurrencyCache', () => {
76
- baseCurrencyCache.clear();
77
- });
78
- PaymentCurrency.addHook('afterDestroy', 'invalidateBaseCurrencyCacheOnDelete', () => {
79
- baseCurrencyCache.clear();
80
- });
81
- }
82
-
83
- // Base currency middleware — only needed by routes that use req.currency
84
- // (checkout-sessions, donations, payment-links, prices, products)
85
- const loadBaseCurrency = async (req: any, _: any, next: any) => {
86
- ensureBaseCurrencyHooks();
87
- const key = `base_${req.livemode}`;
88
- const cached = baseCurrencyCache.get(key);
89
- if (cached && cached.expires > Date.now()) {
90
- req.baseCurrency = cached.data;
91
- } else {
92
- req.baseCurrency = await PaymentCurrency.findOne({ where: { is_base_currency: true, livemode: req.livemode } });
93
- if (req.baseCurrency) {
94
- baseCurrencyCache.set(key, { data: req.baseCurrency, expires: Date.now() + BASE_CURRENCY_TTL });
95
- }
96
- }
97
- next();
98
- };
99
-
100
- router.use('/auto-recharge-configs', autoRechargeConfigs);
101
- router.use('/checkout-sessions', loadBaseCurrency, checkoutSessions);
102
- router.use('/coupons', coupons);
103
- router.use('/credit-grants', creditGrants);
104
- router.use('/credit-tokens', creditTokens);
105
- router.use('/credit-transactions', creditTransactions);
106
- router.use('/customers', customers);
107
- router.use('/donations', loadBaseCurrency, donations);
108
- router.use('/events', events);
109
- router.use('/invoices', invoices);
110
- router.use('/integrations/stripe', stripe);
111
- router.use('/integrations/google-play', googlePlay);
112
- router.use('/integrations/app-store', appStore);
113
- router.use('/entitlements', entitlements);
114
- router.use('/meter-events', meterEvents);
115
- router.use('/meters', meters);
116
- router.use('/passports', passports);
117
- router.use('/payment-intents', paymentIntents);
118
- router.use('/payment-links', loadBaseCurrency, paymentLinks);
119
- router.use('/payment-methods', paymentMethods);
120
- router.use('/payment-currencies', paymentCurrencies);
121
- router.use('/payment-stats', paymentStats);
122
- router.use('/prices', loadBaseCurrency, prices);
123
- router.use('/pricing-tables', pricingTables);
124
- router.use('/tax-rates', taxRates);
125
- router.use('/products', loadBaseCurrency, products);
126
- router.use('/promotion-codes', promotionCodes);
127
- router.use('/exchange-rate-providers', exchangeRateProviders);
128
- router.use('/exchange-rates', exchangeRates);
129
- router.use('/payouts', payouts);
130
- router.use('/redirect', redirect);
131
- router.use('/refunds', refunds);
132
- router.use('/settings', settings);
133
- router.use('/subscription-items', subscriptionItems);
134
- router.use('/subscriptions', subscriptions);
135
- router.use('/usage-records', usageRecords);
136
- router.use('/webhook-attempts', webhookAttempts);
137
- router.use('/webhook-endpoints', webhookEndpoints);
138
- router.use('/vendors', vendor);
139
- router.use('/tool', tool);
140
- router.use('/archive', archive);
141
-
142
- export default router;
@@ -1,61 +0,0 @@
1
- import { env } from '@blocklet/sdk/lib/env';
2
- import express, { NextFunction, Request, Response, Router } from 'express';
3
- import get from 'lodash/get';
4
-
5
- import handleStripeEvent from '../../integrations/stripe/handlers';
6
- import logger from '../../libs/logger';
7
- import { STRIPE_EVENTS } from '../../libs/util';
8
- import { PaymentMethod } from '../../store/models';
9
-
10
- const router = Router();
11
-
12
- const verifyWebhookSig = async (req: Request, res: Response, next: NextFunction) => {
13
- try {
14
- const signature = req.get('stripe-signature');
15
- if (!signature) {
16
- return res.status(400).json({ error: 'No stripe webhook signature found' });
17
- }
18
-
19
- const json = JSON.parse(req.body.toString('utf8'));
20
- const method = await PaymentMethod.findOne({ where: { type: 'stripe', livemode: json.livemode } });
21
- if (!method) {
22
- return res.status(400).json({ error: 'No stripe payment method found' });
23
- }
24
-
25
- const stripe = method.getStripeClient();
26
- const settings = PaymentMethod.decryptSettings(method.settings);
27
- const secret = process.env.STRIPE_WEBHOOK_SECRET || settings.stripe?.webhook_signing_secret;
28
- req.stripeEvent = stripe.webhooks.constructEvent(req.body, signature, secret as string);
29
- req.stripeClient = stripe;
30
-
31
- return next();
32
- } catch (err) {
33
- logger.error('verify signature error', { error: err });
34
- return res.status(400).json({ error: err.message });
35
- }
36
- };
37
-
38
- const handleEvent = async (req: Request, res: Response) => {
39
- const { stripeEvent, stripeClient } = req;
40
-
41
- if (STRIPE_EVENTS.includes(stripeEvent.type) === false) {
42
- logger.debug('webhook event not interested', { id: stripeEvent.id, type: stripeEvent.type });
43
- return res.json({ skipped: true });
44
- }
45
-
46
- // only events from this app should be processed
47
- const appPid = get(stripeEvent, 'data.object.metadata.appPid');
48
- if (appPid && appPid !== env.appPid) {
49
- logger.debug('webhook event for other app', { id: stripeEvent.id, type: stripeEvent.type });
50
- return res.json({ skipped: true });
51
- }
52
-
53
- logger.debug('webhook received event', { id: stripeEvent.id, type: stripeEvent.type });
54
- await handleStripeEvent(stripeEvent, stripeClient);
55
-
56
- return res.json({ received: true });
57
- };
58
-
59
- router.post('/webhook', express.raw({ type: 'application/json' }), verifyWebhookSig, handleEvent);
60
-
61
- export default router;
@@ -1,274 +0,0 @@
1
- import { Router } from 'express';
2
- import Joi from 'joi';
3
- import pick from 'lodash/pick';
4
-
5
- import { Op } from 'sequelize';
6
- import { createListParamSchema, getOrder, getWhereFromKvQuery, MetadataSchema } from '../libs/api';
7
- import logger from '../libs/logger';
8
- import { authenticate } from '../libs/security';
9
- import { formatMetadata } from '../libs/util';
10
- import { Meter, PaymentCurrency, PaymentMethod } from '../store/models';
11
-
12
- const router = Router();
13
- const auth = authenticate<Meter>({ component: true, roles: ['owner', 'admin'] });
14
-
15
- const meterSchema = Joi.object({
16
- name: Joi.string().max(64).required(),
17
- event_name: Joi.string().max(64).required(),
18
- aggregation_method: Joi.string().valid('sum', 'count', 'last').default('sum'),
19
- unit: Joi.string().max(32).required(),
20
- currency_id: Joi.string().max(40).optional(),
21
- decimal: Joi.number().integer().min(2).max(18).default(10),
22
- description: Joi.string().max(255).allow('').optional(),
23
- metadata: MetadataSchema,
24
- component_did: Joi.string().max(40).optional(),
25
- token: Joi.object({
26
- tokenFactoryAddress: Joi.string().required(),
27
- }).optional(),
28
- }).unknown(true);
29
-
30
- const updateMeterSchema = Joi.object({
31
- name: Joi.string().max(64).optional(),
32
- description: Joi.string().max(255).allow('').optional(),
33
- status: Joi.string().valid('active', 'inactive').optional(),
34
- });
35
-
36
- const listSchema = createListParamSchema<{ event_name?: string }>({
37
- event_name: Joi.string().empty(''),
38
- });
39
-
40
- router.get('/', auth, async (req, res) => {
41
- try {
42
- const { page, pageSize, ...query } = await listSchema.validateAsync(req.query, { stripUnknown: true });
43
- const where = getWhereFromKvQuery(query.q);
44
-
45
- if (typeof query.livemode === 'boolean') {
46
- where.livemode = query.livemode;
47
- }
48
- if (query.event_name) {
49
- where.event_name = query.event_name;
50
- }
51
-
52
- const { rows: list, count } = await Meter.findAndCountAll({
53
- where,
54
- order: getOrder(query, [['created_at', query.o === 'asc' ? 'ASC' : 'DESC']]),
55
- offset: (page - 1) * pageSize,
56
- limit: pageSize,
57
- include: [{ model: PaymentCurrency, as: 'paymentCurrency' }],
58
- });
59
-
60
- res.json({ count, list, paging: { page, pageSize } });
61
- } catch (err) {
62
- logger.error('Error listing meters', err);
63
- res.status(400).json({ error: err?.message });
64
- }
65
- });
66
-
67
- router.post('/', auth, async (req, res) => {
68
- try {
69
- const { error } = meterSchema.validate(req.body);
70
- if (error) {
71
- return res.status(400).json({ error: `Meter create request invalid: ${error.message}` });
72
- }
73
-
74
- const existing = await Meter.findOne({
75
- where: { event_name: req.body.event_name },
76
- });
77
- if (existing) {
78
- return res.status(409).json({ error: `Meter with event_name "${req.body.event_name}" already exists` });
79
- }
80
-
81
- if (['count', 'last'].includes(req.body.aggregation_method)) {
82
- return res.status(400).json({ error: 'Aggregation method is not supported' });
83
- }
84
-
85
- const needArcblockMethod = req.body.token?.tokenFactoryAddress || !req.body.currency_id;
86
- const arcblockMethod = needArcblockMethod
87
- ? await PaymentMethod.findOne({ where: { livemode: !!req.livemode, type: 'arcblock' } })
88
- : null;
89
- if (needArcblockMethod && !arcblockMethod) {
90
- throw new Error('ArcBlock payment method not found');
91
- }
92
-
93
- let tokenConfig: Record<string, any> | undefined;
94
- if (req.body.token?.tokenFactoryAddress) {
95
- const client = arcblockMethod!.getOcapClient();
96
- const { state: tokenFactoryState } = await client.getTokenFactoryState({
97
- address: req.body.token.tokenFactoryAddress,
98
- });
99
- if (!tokenFactoryState) {
100
- return res.status(400).json({ error: 'Token factory not found on chain' });
101
- }
102
- tokenConfig = {
103
- address: tokenFactoryState.token.address,
104
- symbol: tokenFactoryState.token.symbol,
105
- name: tokenFactoryState.token.name,
106
- decimal: tokenFactoryState.token.decimal,
107
- token_factory_address: tokenFactoryState.address,
108
- };
109
- }
110
-
111
- const meterData = {
112
- ...pick(req.body, ['name', 'event_name', 'aggregation_method', 'unit', 'currency_id', 'description', 'metadata']),
113
- livemode: !!req.livemode,
114
- created_via: req.user?.via || 'api',
115
- status: req.body.status || 'active',
116
- metadata: formatMetadata(req.body.metadata),
117
- };
118
-
119
- if (!meterData.currency_id) {
120
- const paymentCurrency = await PaymentCurrency.createForMeter(meterData, arcblockMethod!.id, tokenConfig, {
121
- decimal: req.body.decimal,
122
- });
123
- meterData.currency_id = paymentCurrency.id;
124
- }
125
-
126
- const meter = await Meter.create(meterData);
127
-
128
- const result = await Meter.findByPk(meter.id, {
129
- include: [{ model: PaymentCurrency, as: 'paymentCurrency' }],
130
- });
131
-
132
- logger.info('Meter created', { meterId: meter.id, eventName: meter.event_name });
133
- return res.json(result);
134
- } catch (err) {
135
- logger.error('create meter failed', { error: err?.message, request: req.body });
136
- return res.status(400).json({ error: err?.message });
137
- }
138
- });
139
-
140
- // Public endpoint: only returns safe fields, no auth required
141
- const PUBLIC_METER_FIELDS = ['id', 'name', 'event_name', 'status', 'unit', 'description', 'currency_id'] as const;
142
-
143
- router.get('/public/:id', async (req, res) => {
144
- try {
145
- const meter = await Meter.findOne({
146
- where: {
147
- [Op.or]: [{ id: req.params.id }, { event_name: req.params.id }],
148
- },
149
- include: [{ model: PaymentCurrency, as: 'paymentCurrency' }],
150
- });
151
-
152
- if (!meter) {
153
- return res.status(404).json({ error: 'Meter not found' });
154
- }
155
-
156
- return res.json({
157
- ...pick(meter.toJSON(), PUBLIC_METER_FIELDS),
158
- paymentCurrency: (meter as any).paymentCurrency
159
- ? pick((meter as any).paymentCurrency.toJSON(), ['id', 'name', 'symbol', 'decimal', 'logo', 'type'])
160
- : null,
161
- });
162
- } catch (err) {
163
- logger.error('get public meter failed', { error: err?.message, meterId: req.params.id });
164
- return res.status(400).json({ error: err?.message });
165
- }
166
- });
167
-
168
- router.get('/:id', auth, async (req, res) => {
169
- try {
170
- const meter = await Meter.findOne({
171
- where: {
172
- [Op.or]: [{ id: req.params.id }, { event_name: req.params.id }],
173
- },
174
- include: [{ model: PaymentCurrency.scope('withRechargeConfig'), as: 'paymentCurrency' }],
175
- });
176
-
177
- if (!meter) {
178
- return res.status(404).json({ error: 'Meter not found' });
179
- }
180
-
181
- return res.json(meter);
182
- } catch (err) {
183
- logger.error('get meter failed', { error: err?.message, meterId: req.params.id });
184
- return res.status(400).json({ error: err?.message });
185
- }
186
- });
187
-
188
- router.put('/:id', auth, async (req, res) => {
189
- try {
190
- const { error } = updateMeterSchema.validate(pick(req.body, ['name', 'description', 'status']));
191
- if (error) {
192
- return res.status(400).json({ error: `Meter update request invalid: ${error.message}` });
193
- }
194
-
195
- const meter = await Meter.findByPk(req.params.id, {
196
- include: [{ model: PaymentCurrency, as: 'paymentCurrency' }],
197
- });
198
- if (!meter) {
199
- return res.status(404).json({ error: 'Meter not found' });
200
- }
201
-
202
- const updateData: any = {
203
- ...pick(req.body, ['name', 'description', 'status']),
204
- unit: req.body.unit || meter.unit,
205
- updated_by: req.user?.did,
206
- };
207
-
208
- if (req.body.metadata) {
209
- const { error: metadataError } = MetadataSchema.validate(req.body.metadata);
210
- if (metadataError) {
211
- return res.status(400).json({ error: `metadata invalid: ${metadataError.message}` });
212
- }
213
- updateData.metadata = formatMetadata(req.body.metadata);
214
- }
215
-
216
- await meter.update(updateData);
217
-
218
- return res.json(meter);
219
- } catch (err) {
220
- logger.error('update meter failed', { error: err?.message, meterId: req.params.id });
221
- return res.status(400).json({ error: err?.message });
222
- }
223
- });
224
-
225
- router.put('/:id/activate', auth, async (req, res) => {
226
- try {
227
- const meter = await Meter.findByPk(req.params.id, {
228
- include: [{ model: PaymentCurrency, as: 'paymentCurrency' }],
229
- });
230
- if (!meter) {
231
- return res.status(404).json({ error: 'Meter not found' });
232
- }
233
-
234
- if (meter.status === 'active') {
235
- return res.status(400).json({ error: 'Meter is already active' });
236
- }
237
-
238
- await meter.update({
239
- status: 'active',
240
- updated_by: req.user?.did,
241
- });
242
- return res.json(meter);
243
- } catch (err) {
244
- logger.error('activate meter failed', { error: err?.message, meterId: req.params.id });
245
- return res.status(400).json({ error: err?.message });
246
- }
247
- });
248
-
249
- router.put('/:id/deactivate', auth, async (req, res) => {
250
- try {
251
- const meter = await Meter.findByPk(req.params.id, {
252
- include: [{ model: PaymentCurrency, as: 'paymentCurrency' }],
253
- });
254
- if (!meter) {
255
- return res.status(404).json({ error: 'Meter not found' });
256
- }
257
-
258
- if (meter.status === 'inactive') {
259
- return res.status(400).json({ error: 'Meter is already inactive' });
260
- }
261
-
262
- await meter.update({
263
- status: 'inactive',
264
- updated_by: req.user?.did,
265
- });
266
-
267
- return res.json(meter);
268
- } catch (err) {
269
- logger.error('deactivate meter failed', { error: err?.message, meterId: req.params.id });
270
- return res.status(400).json({ error: err?.message });
271
- }
272
- });
273
-
274
- export default router;
@@ -1,68 +0,0 @@
1
- import { Router } from 'express';
2
-
3
- import { updatePassportExtra } from '../integrations/blocklet/passport';
4
- import { blocklet } from '../libs/auth';
5
- import { authenticate } from '../libs/security';
6
- import { PaymentLink, PricingTable, Product } from '../store/models';
7
-
8
- const router = Router();
9
- const auth = authenticate<any>({ component: false, roles: ['owner', 'admin'] });
10
-
11
- router.get('/', auth, async (_, res) => {
12
- const result = await blocklet.getRoles();
13
- res.json(result.roles);
14
- });
15
-
16
- router.put('/assign', auth, async (req, res) => {
17
- const { name, id } = req.body;
18
-
19
- if (!id) {
20
- return res.status(400).json({ message: 'payment entry or product id is required' });
21
- }
22
- if (!name) {
23
- return res.status(400).json({ message: 'passport name is required' });
24
- }
25
-
26
- if (id.startsWith('plink_')) {
27
- const doc = await PaymentLink.findByPk(id);
28
- if (!doc?.active) {
29
- return res.status(400).json({ message: 'payment link is not active' });
30
- }
31
-
32
- const result = await updatePassportExtra(name, { acquire: { pay: id } });
33
- return res.json(result);
34
- }
35
-
36
- if (id.startsWith('prctbl_')) {
37
- const doc = await PricingTable.findByPk(id);
38
- if (!doc?.active) {
39
- return res.status(400).json({ message: 'pricing table is not active' });
40
- }
41
-
42
- const result = await updatePassportExtra(name, { acquire: { pay: id } });
43
- return res.json(result);
44
- }
45
-
46
- if (id.startsWith('prod_')) {
47
- const doc = await Product.findByPk(id);
48
- if (!doc?.active) {
49
- return res.status(400).json({ message: 'product is not active' });
50
- }
51
-
52
- await doc.update({ metadata: { ...doc.metadata, passport: name } });
53
- const result = await updatePassportExtra(name, { payment: { product: id } });
54
- return res.json(result);
55
- }
56
-
57
- return res.status(400).json({ message: 'pay link is not support' });
58
- });
59
-
60
- router.delete('/assign/:name', auth, async (req, res) => {
61
- const result = await updatePassportExtra(req.params.name as string, {
62
- payment: { product: '' },
63
- acquire: { pay: '' },
64
- });
65
- return res.json(result);
66
- });
67
-
68
- export default router;
@@ -1,20 +0,0 @@
1
- import qs from 'querystring';
2
-
3
- import { getUrl } from '@blocklet/sdk/lib/component';
4
- import { Router } from 'express';
5
-
6
- const router = Router();
7
-
8
- router.get('/checkout/:entryId', (req, res) => {
9
- const { entryId } = req.params;
10
- if (entryId.startsWith('plink_')) {
11
- return res.redirect(getUrl(`/checkout/pay/${entryId}?${qs.stringify(req.query as any)}`));
12
- }
13
- if (entryId.startsWith('prctbl_')) {
14
- return res.redirect(getUrl(`/checkout/pricing-table/${entryId}?${qs.stringify(req.query as any)}`));
15
- }
16
-
17
- return res.redirect(getUrl('/'));
18
- });
19
-
20
- export default router;