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
@@ -26,6 +26,7 @@ import type {
26
26
  } from '@apple/app-store-server-library';
27
27
 
28
28
  import logger from '../../libs/logger';
29
+ import { appStoreSkipSignatureVerify, isProduction } from '../../libs/env';
29
30
  import { APPLE_ROOT_CERTS } from './apple-root-certs';
30
31
 
31
32
  const verifierCache = new Map<string, SignedDataVerifier>();
@@ -46,13 +47,13 @@ async function getVerifier(bundleId: string, environment: 'production' | 'sandbo
46
47
  }
47
48
 
48
49
  export function isSignatureVerificationSkipped(): boolean {
49
- if (process.env.APP_STORE_SKIP_SIGNATURE_VERIFY !== 'true') return false;
50
+ if (!appStoreSkipSignatureVerify()) return false;
50
51
  // Production fail-closed: even when the bypass flag is set we refuse to
51
52
  // honor it in production, and log loudly so the misconfiguration is
52
53
  // visible. The flag exists for unit tests / local sandbox debugging where
53
54
  // the synthetic JWS isn't signed by Apple; in production it would silently
54
55
  // downgrade a critical trust boundary to decode-only (CWE-347).
55
- if (process.env.BLOCKLET_MODE === 'production') {
56
+ if (isProduction()) {
56
57
  logger.error(
57
58
  'app_store: APP_STORE_SKIP_SIGNATURE_VERIFY=true ignored in production — JWS signature verification stays enabled'
58
59
  );
@@ -5,16 +5,30 @@ import { BN, fromTokenToUnit, fromUnitToToken, toBN, toBase58, toBase64 } from '
5
5
  import { types } from '@ocap/mcrypto';
6
6
  import { verify as verifyVC } from '@arcblock/vc';
7
7
  import { BlockletService } from '@blocklet/sdk/service/blocklet';
8
+ import env, { blockletAppHost } from '../../libs/env';
8
9
 
9
10
  import { PaymentMethod } from '../../store/models';
10
11
  import type { TPaymentCurrency } from '../../store/models';
11
12
  import { wallet } from '../../libs/auth';
12
13
  import logger from '../../libs/logger';
13
- import env from '../../libs/env';
14
14
  import { sleep } from '../../libs/util';
15
15
  import { getGasPayerExtra } from '../../libs/payment';
16
16
 
17
- const blockletService = new BlockletService();
17
+ // Lazy singleton: `new BlockletService()` runs `checkBlockletEnvironment()`,
18
+ // which throws unless BLOCKLET_APP_ID/DID/EK/ABT_NODE_* are present. Those env
19
+ // vars only exist inside a blocklet runtime — when payment-core is embedded in
20
+ // a host like arc, constructing this at module-init crashes the whole graph on
21
+ // import. Defer to first use so the import is environment-agnostic; the routing
22
+ // -rule feature (the only consumer) simply requires the host to supply that env
23
+ // when it is actually exercised.
24
+ // eslint-disable-next-line @typescript-eslint/naming-convention -- intentional _-prefixed module singleton
25
+ let _blockletService: InstanceType<typeof BlockletService> | undefined;
26
+ function getBlockletService(): InstanceType<typeof BlockletService> {
27
+ if (!_blockletService) {
28
+ _blockletService = new BlockletService();
29
+ }
30
+ return _blockletService;
31
+ }
18
32
 
19
33
  export function isOnchainCredit(paymentCurrency: TPaymentCurrency) {
20
34
  return paymentCurrency.type === 'credit' && !!paymentCurrency.token_config;
@@ -154,7 +168,7 @@ async function createTokenVC(data: { tokenAddress: string; symbol: string; websi
154
168
  * Step 2: Publish VC (Verifiable Credential) for token via routing rule
155
169
  */
156
170
  async function publishTokenVC(vc: any) {
157
- const { blocklet: blockletInfo } = await blockletService.getBlocklet();
171
+ const { blocklet: blockletInfo } = await getBlockletService().getBlocklet();
158
172
  const site = blockletInfo?.site;
159
173
 
160
174
  if (!site?.id) {
@@ -226,7 +240,7 @@ async function publishTokenVC(vc: any) {
226
240
  try {
227
241
  let result;
228
242
  if (isUpdate) {
229
- result = await blockletService.updateRoutingRule({
243
+ result = await getBlockletService().updateRoutingRule({
230
244
  id: site.id,
231
245
  rule: {
232
246
  id: existingRule.id,
@@ -234,7 +248,7 @@ async function publishTokenVC(vc: any) {
234
248
  },
235
249
  });
236
250
  } else {
237
- result = await blockletService.addRoutingRule({
251
+ result = await getBlockletService().addRoutingRule({
238
252
  id: site.id,
239
253
  rule,
240
254
  });
@@ -279,7 +293,7 @@ export async function createToken(data: { name: string; symbol: string; decimal?
279
293
  name: data.name,
280
294
  symbol: data.symbol,
281
295
  description: `Token created by ${env.appName || 'Payment Kit'}`,
282
- website: env.appUrl || process.env.BLOCKLET_APP_HOST,
296
+ website: env.appUrl || blockletAppHost(),
283
297
  icon: '',
284
298
  maxTotalSupply: null,
285
299
  decimal: data.decimal ?? 10,
@@ -307,7 +321,7 @@ export async function createToken(data: { name: string; symbol: string; decimal?
307
321
  const vc = await createTokenVC({
308
322
  tokenAddress: factoryItx.token.address,
309
323
  symbol: data.symbol,
310
- website: env.appUrl || process.env.BLOCKLET_APP_HOST!,
324
+ website: env.appUrl || blockletAppHost()!,
311
325
  chainId,
312
326
  });
313
327
 
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { Op } from 'sequelize';
9
9
 
10
- import { createEvent } from '../../../libs/audit';
10
+ import { createEvent, reportAuditFailure } from '../../../libs/audit';
11
11
  import logger from '../../../libs/logger';
12
12
  import { Customer, PaymentMethod, Price, Subscription, SubscriptionItem } from '../../../store/models';
13
13
  import { GooglePlayClient, GooglePlaySubscriptionPurchase } from '../client';
@@ -268,12 +268,12 @@ async function handleRenewedOrDeferred({
268
268
  },
269
269
  },
270
270
  });
271
- createEvent('Subscription', 'customer.subscription.started', subscription).catch(console.error);
271
+ createEvent('Subscription', 'customer.subscription.started', subscription).catch(reportAuditFailure);
272
272
  }
273
273
 
274
274
  async function handleResumed(subscription: Subscription): Promise<void> {
275
275
  await subscription.update({ status: 'active' });
276
- createEvent('Subscription', 'customer.subscription.started', subscription).catch(console.error);
276
+ createEvent('Subscription', 'customer.subscription.started', subscription).catch(reportAuditFailure);
277
277
  }
278
278
 
279
279
  async function markPastDue(subscription: Subscription): Promise<void> {
@@ -293,7 +293,7 @@ async function scheduleCancelAtPeriodEnd(subscription: Subscription): Promise<vo
293
293
  async function markExpired(subscription: Subscription): Promise<void> {
294
294
  if (['canceled', 'incomplete_expired'].includes(subscription.status as string)) return;
295
295
  await subscription.update({ status: 'canceled', canceled_at: Math.floor(Date.now() / 1000) });
296
- createEvent('Subscription', 'customer.subscription.deleted', subscription).catch(console.error);
296
+ createEvent('Subscription', 'customer.subscription.deleted', subscription).catch(reportAuditFailure);
297
297
  }
298
298
 
299
299
  /**
@@ -526,7 +526,7 @@ export async function ingestVerifiedGooglePlayPurchase({
526
526
  return { subscription: winner, isFirstSubscribe: false, purchase };
527
527
  }
528
528
 
529
- createEvent('Subscription', 'customer.subscription.started', subscription).catch(console.error);
529
+ createEvent('Subscription', 'customer.subscription.started', subscription).catch(reportAuditFailure);
530
530
  logger.info('google_play verify: subscription created', {
531
531
  subscriptionId: subscription.id,
532
532
  customerId: customer.id,
@@ -561,5 +561,5 @@ async function handleRevoked(subscription: Subscription): Promise<void> {
561
561
  subscriptionId: subscription.id,
562
562
  });
563
563
 
564
- createEvent('Subscription', 'customer.subscription.deleted', subscription).catch(console.error);
564
+ createEvent('Subscription', 'customer.subscription.deleted', subscription).catch(reportAuditFailure);
565
565
  }
@@ -11,7 +11,7 @@
11
11
  // refundType: 1 = FULL, 2 = QUANTITY_BASED
12
12
  // }
13
13
 
14
- import { createEvent } from '../../../libs/audit';
14
+ import { createEvent, reportAuditFailure } from '../../../libs/audit';
15
15
  import logger from '../../../libs/logger';
16
16
  import { Subscription } from '../../../store/models';
17
17
 
@@ -91,7 +91,7 @@ export async function handleGooglePlayVoidedPurchase({
91
91
  google_play_voided_refund_type: refundType,
92
92
  },
93
93
  });
94
- createEvent('Subscription', 'customer.subscription.deleted', subscription).catch(console.error);
94
+ createEvent('Subscription', 'customer.subscription.deleted', subscription).catch(reportAuditFailure);
95
95
  }
96
96
 
97
97
  // TODO: create a Refund row for audit. Blocked on payment_intent_id schema —
@@ -9,6 +9,7 @@
9
9
  // Cloudflare Workers(payment-kit 是统一代码)。
10
10
 
11
11
  import logger from '../../libs/logger';
12
+ import { googlePubsubSkipSignatureVerify, isProduction } from '../../libs/env';
12
13
 
13
14
  export type PubSubJwtClaims = {
14
15
  iss: string;
@@ -80,7 +81,7 @@ export type VerifyOptions = {
80
81
  };
81
82
 
82
83
  function defaultSkipSignature(): boolean {
83
- return process.env.GOOGLE_PUBSUB_SKIP_SIGNATURE_VERIFY === 'true';
84
+ return googlePubsubSkipSignatureVerify();
84
85
  }
85
86
 
86
87
  /**
@@ -93,7 +94,7 @@ function defaultSkipSignature(): boolean {
93
94
  */
94
95
  function effectiveSkipSignature(requested: boolean): boolean {
95
96
  if (!requested) return false;
96
- if (process.env.BLOCKLET_MODE === 'production') {
97
+ if (isProduction()) {
97
98
  logger.error(
98
99
  'google_play: signature verification skip refused in production — Pub/Sub JWT signature verification stays enabled'
99
100
  );
@@ -20,6 +20,7 @@
20
20
  // every 5 minutes by default; tuneable via `IAP_RECONCILE_CRON_TIME`.
21
21
 
22
22
  import { Op } from 'sequelize';
23
+ import { iapReconcileBatchSize } from '../libs/env';
23
24
 
24
25
  import { createEvent } from '../libs/audit';
25
26
  import logger from '../libs/logger';
@@ -30,9 +31,6 @@ import { GooglePlayClient, GooglePlaySubscriptionPurchase } from './google-play/
30
31
  /** Don't re-check subs that were updated by a webhook within the last 5 minutes. */
31
32
  const RECENT_UPDATE_GUARD_MS = 5 * 60 * 1000;
32
33
 
33
- /** Per-channel batch cap so a single cron tick can't stall on Apple/Google rate limits. */
34
- const DEFAULT_BATCH_SIZE = Number(process.env.IAP_RECONCILE_BATCH_SIZE ?? '100');
35
-
36
34
  type ReconcileStats = { checked: number; updated: number; errors: number };
37
35
 
38
36
  const emptyStats = (): ReconcileStats => ({ checked: 0, updated: 0, errors: 0 });
@@ -149,7 +147,7 @@ async function backfillMissingSubscriptionItems(): Promise<void> {
149
147
  // App Store
150
148
  // ---------------------------------------------------------------------------
151
149
 
152
- export async function reconcileAppStore(batchSize = DEFAULT_BATCH_SIZE): Promise<ReconcileStats> {
150
+ export async function reconcileAppStore(batchSize = iapReconcileBatchSize()): Promise<ReconcileStats> {
153
151
  const stats = emptyStats();
154
152
  const methods = await PaymentMethod.findAll({ where: { type: 'app_store' } });
155
153
  if (methods.length === 0) return stats;
@@ -293,7 +291,7 @@ export async function applyAppStoreTransactionDrift(
293
291
  // Google Play
294
292
  // ---------------------------------------------------------------------------
295
293
 
296
- export async function reconcileGooglePlay(batchSize = DEFAULT_BATCH_SIZE): Promise<ReconcileStats> {
294
+ export async function reconcileGooglePlay(batchSize = iapReconcileBatchSize()): Promise<ReconcileStats> {
297
295
  const stats = emptyStats();
298
296
  const methods = await PaymentMethod.findAll({ where: { type: 'google_play' } });
299
297
  if (methods.length === 0) return stats;
@@ -6,7 +6,7 @@ import type Stripe from 'stripe';
6
6
 
7
7
  import type { WhereOptions } from 'sequelize';
8
8
  import { checkUsageReportEmpty } from '../../../libs/subscription';
9
- import { createEvent } from '../../../libs/audit';
9
+ import { createEvent, reportAuditFailure } from '../../../libs/audit';
10
10
  import { getLock } from '../../../libs/lock';
11
11
  import logger from '../../../libs/logger';
12
12
  import {
@@ -361,7 +361,7 @@ export async function handleStripeInvoiceCreated(event: TEventExpanded, client:
361
361
  createEvent('Subscription', 'usage.report.empty', subscription, {
362
362
  usageReportStart,
363
363
  usageReportEnd,
364
- }).catch(console.error);
364
+ }).catch(reportAuditFailure);
365
365
  logger.info('create usage report empty event', {
366
366
  subscriptionId: subscription.id,
367
367
  usageReportStart,
@@ -12,7 +12,7 @@ import {
12
12
  } from '../../../libs/subscription';
13
13
  import { CheckoutSession, PaymentMethod, Subscription, TEventExpanded } from '../../../store/models';
14
14
  import { getCheckoutSessionSubscriptionIds } from '../../../libs/session';
15
- import { createEvent } from '../../../libs/audit';
15
+ import { createEvent, reportAuditFailure } from '../../../libs/audit';
16
16
 
17
17
  export async function handleStripeSubscriptionSucceed(subscription: Subscription, status: string) {
18
18
  if (!subscription.payment_details?.stripe?.subscription_id) {
@@ -51,9 +51,9 @@ export async function handleStripeSubscriptionSucceed(subscription: Subscription
51
51
 
52
52
  await subscription.update({ status });
53
53
  if (subscription.trial_end && subscription.trial_end > Date.now() / 1000 && subscription.status === 'trialing') {
54
- createEvent('Subscription', 'customer.subscription.trial_start', subscription).catch(console.error);
54
+ createEvent('Subscription', 'customer.subscription.trial_start', subscription).catch(reportAuditFailure);
55
55
  } else if (subscription.status === 'active') {
56
- createEvent('Subscription', 'customer.subscription.started', subscription).catch(console.error);
56
+ createEvent('Subscription', 'customer.subscription.started', subscription).catch(reportAuditFailure);
57
57
  }
58
58
  logger.info('subscription become active on stripe event', { id: subscription.id, status: subscription.status });
59
59
 
@@ -5,9 +5,14 @@ import path from 'path';
5
5
  import { QueryTypes, Op } from 'sequelize';
6
6
 
7
7
  import logger from '../logger';
8
+ import { getInstanceDid } from '../context';
9
+ import { getTenantMode } from '../tenant';
10
+ import { TENANT_TABLES } from '../../store/tenant-tables';
8
11
  import { ArchiveMetadata } from '../../store/models/archive-metadata';
9
12
  import { listArchiveFiles, openArchiveSequelize } from './store';
10
13
 
14
+ const TENANT_TABLE_SET: ReadonlySet<string> = new Set(TENANT_TABLES);
15
+
11
16
  type ArchiveQueryParams = {
12
17
  table: string;
13
18
  id?: string;
@@ -52,6 +57,20 @@ function buildWhereClause(params: ArchiveQueryParams) {
52
57
  replacements.customerId = params.customer_id;
53
58
  }
54
59
 
60
+ // 洞 G (Phase 4): archived rows are snapshots of tenant tables — scope the
61
+ // read by instance_did so an admin can't query another tenant's archive.
62
+ // multi mode is strict (fail-closed); single mode also accepts legacy
63
+ // pre-backfill snapshots (instance_did NULL), which all belong to the one
64
+ // deployment tenant (mirrors the resolveRowTenant single-mode default).
65
+ if (TENANT_TABLE_SET.has(params.table)) {
66
+ replacements.instance_did = getInstanceDid();
67
+ if (getTenantMode() === 'single') {
68
+ conditions.push('(instance_did = :instance_did OR instance_did IS NULL)');
69
+ } else {
70
+ conditions.push('instance_did = :instance_did');
71
+ }
72
+ }
73
+
55
74
  return { clause: conditions.join(' AND '), replacements };
56
75
  }
57
76
 
@@ -5,9 +5,58 @@ import type { EventType } from '../store/models';
5
5
  import { Event } from '../store/models/event';
6
6
  import { events } from './event';
7
7
  import { context } from './context';
8
+ import logger from './logger';
9
+ import { TENANT_CONTEXT_MISSING, TENANT_MISMATCH, TenantError } from './tenant';
8
10
 
9
11
  const API_VERSION = '2023-09-05';
10
12
 
13
+ /**
14
+ * Phase 4 (W1-3): tenant of an event, fail-closed.
15
+ * Order: model.instance_did, then TenantContext. Both missing or mutually
16
+ * contradictory -> reject. The rejection only kills the EVENT (the business
17
+ * transaction that triggered the hook is never blocked — see
18
+ * reportAuditFailure and the fire-and-forget catch sites in store/models).
19
+ */
20
+ function resolveEventTenant(model?: { instance_did?: string | null } | null): string {
21
+ const fromModel = model?.instance_did || undefined;
22
+ // the EXPLICIT context tenant (withTenant), not the single-mode default
23
+ // fill — otherwise every model row of a non-default tenant would falsely
24
+ // conflict with the deployment app DID in single mode
25
+ const explicitContext = context.getContext().instanceDid || undefined;
26
+ if (fromModel && explicitContext && fromModel !== explicitContext) {
27
+ throw new TenantError(
28
+ TENANT_MISMATCH,
29
+ `event tenant conflict: model carries ${fromModel} but context is ${explicitContext}`
30
+ );
31
+ }
32
+ const tenant = fromModel || explicitContext;
33
+ if (tenant) return tenant;
34
+ // neither source explicit: single mode falls back to the app DID,
35
+ // multi mode fails closed
36
+ return context.getInstanceDid();
37
+ }
38
+
39
+ /**
40
+ * Create an event row under its resolved tenant. The event belongs to the
41
+ * model's tenant, which may differ from the ambient context (e.g. a system /
42
+ * cross-tenant path acting on another tenant's row). Now that Event extends
43
+ * TenantModel, the create must run under that tenant so stampTenant stamps the
44
+ * matching instance_did instead of rejecting the explicit one as a conflict.
45
+ */
46
+ function createEventUnderTenant(instanceDid: string, values: any): Promise<any> {
47
+ return context.withTenant(instanceDid, () => Event.create(values));
48
+ }
49
+
50
+ /**
51
+ * Structured fire-and-forget alert for failed event creation. Tenant
52
+ * rejections keep their dedicated codes so monitoring/tests can assert them;
53
+ * everything else is EVENT_CREATE_FAILED. Mode-independent and never throws.
54
+ */
55
+ export function reportAuditFailure(err: any): void {
56
+ const code = err?.code === TENANT_MISMATCH || err?.code === TENANT_CONTEXT_MISSING ? err.code : 'EVENT_CREATE_FAILED';
57
+ logger.error('[audit] event creation failed', { code, message: err?.message || String(err) });
58
+ }
59
+
11
60
  /**
12
61
  * Invoke every registered listener for `eventName` and await any Promise
13
62
  * results. EventEmitter.emit() returns sync — listener async work would
@@ -58,6 +107,7 @@ export function createEvent(
58
107
  }
59
108
 
60
109
  async function doCreateEvent(scope: string, type: LiteralUnion<EventType, string>, model: any, options: any = {}) {
110
+ const instanceDid = resolveEventTenant(model);
61
111
  const data: any = {
62
112
  object: model.dataValues,
63
113
  };
@@ -65,8 +115,9 @@ async function doCreateEvent(scope: string, type: LiteralUnion<EventType, string
65
115
  data.previous_attributes = pick(model._previousDataValues, options.fields);
66
116
  }
67
117
 
68
- const event = await Event.create({
118
+ const event = await createEventUnderTenant(instanceDid, {
69
119
  type,
120
+ instance_did: instanceDid,
70
121
  api_version: API_VERSION,
71
122
  livemode: !!model.livemode,
72
123
  object_id: model.id,
@@ -113,8 +164,10 @@ export async function createStatusEvent(
113
164
  }
114
165
 
115
166
  const suffix = config[data.object.status];
116
- const event = await Event.create({
167
+ const instanceDid = resolveEventTenant(model);
168
+ const event = await createEventUnderTenant(instanceDid, {
117
169
  type: [prefix, suffix].join('.'),
170
+ instance_did: instanceDid,
118
171
  api_version: API_VERSION,
119
172
  livemode: !!model.livemode,
120
173
  object_id: model.id,
@@ -150,8 +203,10 @@ export async function createCustomEvent(
150
203
  return;
151
204
  }
152
205
 
153
- const event = await Event.create({
206
+ const instanceDid = resolveEventTenant(model);
207
+ const event = await createEventUnderTenant(instanceDid, {
154
208
  type: [prefix, suffix].join('.'),
209
+ instance_did: instanceDid,
155
210
  api_version: API_VERSION,
156
211
  livemode: !!model.livemode,
157
212
  object_id: model.id,
@@ -185,9 +240,11 @@ export async function createFlexibleEvent(
185
240
  } = {}
186
241
  ) {
187
242
  const { livemode = false, requestedBy, metadata = {} } = options;
243
+ const instanceDid = resolveEventTenant(null);
188
244
 
189
- const event = await Event.create({
245
+ const event = await createEventUnderTenant(instanceDid, {
190
246
  type,
247
+ instance_did: instanceDid,
191
248
  api_version: API_VERSION,
192
249
  livemode,
193
250
  object_id: objectId,
@@ -1,18 +1,51 @@
1
1
  import path from 'path';
2
2
 
3
- import AuthStorage from '@arcblock/did-connect-storage-nedb';
4
- // @ts-ignore
5
- import { BlockletService } from '@blocklet/sdk/service/auth';
6
- import { getWallet, getAccessWallet } from '@blocklet/sdk/lib/wallet';
7
- import { WalletAuthenticator } from '@blocklet/sdk/lib/wallet-authenticator';
8
- import { WalletHandlers } from '@blocklet/sdk/lib/wallet-handler';
9
- import type { Request } from 'express';
10
3
  import type { LiteralUnion } from 'type-fest';
11
4
  import type { WalletObject } from '@ocap/wallet';
5
+ import env, { blockletAppId } from './env';
12
6
 
13
- import env from './env';
14
7
  import logger from './logger';
15
8
 
9
+ // Phase 13b: every blocklet-runtime binding below is constructed LAZILY, on first
10
+ // property access, instead of at module import. Importing this module — and the
11
+ // modules that transitively pull it in (libs/util, libs/payment, the models) —
12
+ // therefore runs NO blocklet side effects, so createEmbeddedPaymentService +
13
+ // rpc.entitlements.check work in a bare host with only the explicit config/db/
14
+ // tenancy slots: no BLOCKLET_APP_SK/PK, no BLOCKLET_DATA_DIR, no ABT_NODE_*, no
15
+ // notification patch. The node/full-handler and background-lifecycle paths still
16
+ // materialize the real bindings on first use — transparently, through the proxies —
17
+ // so every existing consumer (`wallet`, `ethWallet`, `blocklet`, `handlers`,
18
+ // `authenticator`) keeps working unchanged.
19
+
20
+ /** Transparent lazy proxy: defer factory() until the first property access. */
21
+ function lazyProxy<T extends object>(factory: () => T): T {
22
+ let instance: T | undefined;
23
+ const resolve = (): T => {
24
+ instance ??= factory();
25
+ return instance;
26
+ };
27
+ return new Proxy({} as T, {
28
+ get(_t, prop) {
29
+ const obj = resolve();
30
+ const value: any = (obj as any)[prop];
31
+ if (typeof value !== 'function') return value;
32
+ // Don't re-bind jest mock functions: binding returns a plain wrapper that
33
+ // strips jest's mock helpers (.mockImplementation / .mockResolvedValue),
34
+ // which would make `jest.spyOn(blocklet, 'method')` unusable. Mocks ignore
35
+ // `this`, so leaving them unbound is safe. No-op in production (no mocks).
36
+ if (value._isMockFunction) return value;
37
+ return value.bind(obj);
38
+ },
39
+ set(_t, prop, value) {
40
+ (resolve() as any)[prop] = value;
41
+ return true;
42
+ },
43
+ has(_t, prop) {
44
+ return prop in (resolve() as any);
45
+ },
46
+ });
47
+ }
48
+
16
49
  // Workaround #2: @blocklet/sdk's notification.getSender() uses
17
50
  // `getWallet().address` (BLOCKLET_APP_SK derived) as the sender appDid for
18
51
  // relay/EventBus broadcasts. On migrated blocklets that address (e.g. zNKti3…)
@@ -28,25 +61,38 @@ import logger from './logger';
28
61
  // Path must be `@blocklet/sdk/service/notification` (no `lib/`) — in Node it
29
62
  // is a thin re-export that resolves via the module cache to the same object
30
63
  // as `@blocklet/sdk/lib/service/notification`; in CF Workers the esbuild
31
- // config aliases it to a no-op shim (patching a no-op is harmless). Using
32
- // the `lib/` path breaks the CF build because it does not have an alias
33
- // and falls through to `@blocklet/sdk` `shims/blocklet-sdk/index.ts/lib/...`.
34
- // Same root cause as the WalletAuthenticator override below remove once the
35
- // upstream sdk is patched.
36
- // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require, import/no-extraneous-dependencies
37
- const notificationExports = require('@blocklet/sdk/service/notification');
64
+ // config aliases it to a no-op shim (patching a no-op is harmless).
65
+ //
66
+ // Phase 13b: applied LAZILY (once) the first time a blocklet binding is
67
+ // materialized, instead of at module import so a bare host that never touches a
68
+ // binding never requires the wallet/notification modules. The override closure
69
+ // still reads getWallet()/getAccessWallet() at send time, exactly as before.
70
+ let notificationPatched = false;
71
+ function ensureNotificationPatch(): void {
72
+ if (notificationPatched) return;
73
+ notificationPatched = true;
74
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
75
+ const { getWallet, getAccessWallet } = require('@blocklet/sdk/lib/wallet');
76
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
77
+ const notificationExports = require('@blocklet/sdk/service/notification');
78
+ notificationExports.getSender = () => ({
79
+ appDid: blockletAppId() || getWallet(undefined, '', 'sk').address,
80
+ wallet: getAccessWallet(),
81
+ });
82
+ // Note: do NOT invoke getSender() here — that would force a wallet read at patch
83
+ // time. Log only the expected appId (config-derived, env-free).
84
+ logger.info('[sdk-patch] notification.getSender overridden', { expectedAppId: blockletAppId() });
85
+ }
38
86
 
39
- notificationExports.getSender = () => ({
40
- appDid: process.env.BLOCKLET_APP_ID || getWallet(undefined, '', 'sk').address,
41
- wallet: getAccessWallet(),
42
- });
43
- logger.info('[sdk-patch] notification.getSender overridden', {
44
- appDid: notificationExports.getSender().appDid,
45
- expectedAppId: process.env.BLOCKLET_APP_ID,
46
- });
87
+ function makeWallet(...args: any[]): WalletObject {
88
+ ensureNotificationPatch();
89
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
90
+ const { getWallet } = require('@blocklet/sdk/lib/wallet');
91
+ return getWallet(...args);
92
+ }
47
93
 
48
- export const wallet: WalletObject = getWallet();
49
- export const ethWallet: WalletObject = getWallet('ethereum');
94
+ export const wallet: WalletObject = lazyProxy(() => makeWallet());
95
+ export const ethWallet: WalletObject = lazyProxy(() => makeWallet('ethereum'));
50
96
 
51
97
  // Workaround for migrated blocklets where `BLOCKLET_APP_SK` is a rotating session
52
98
  // key whose derived address ≠ appId. Upstream @blocklet/sdk's WalletAuthenticator
@@ -58,21 +104,34 @@ export const ethWallet: WalletObject = getWallet('ethereum');
58
104
  //
59
105
  // Force the authenticator to derive its signing wallet from BLOCKLET_APP_PK
60
106
  // (permanent app pk → address = appId), aligning with the fix in
61
- // @blocklet/sdk PR #12810 that already corrected getDelegatee. Remove once the
62
- // upstream wallet-authenticator.ts is patched accordingly.
63
- export const authenticator = new WalletAuthenticator({
64
- wallet: getWallet(undefined, '', 'sk'),
107
+ // @blocklet/sdk PR #12810 that already corrected getDelegatee.
108
+ export const authenticator: any = lazyProxy(() => {
109
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
110
+ const { WalletAuthenticator } = require('@blocklet/sdk/lib/wallet-authenticator');
111
+ return new WalletAuthenticator({ wallet: makeWallet(undefined, '', 'sk') });
65
112
  });
66
- export const handlers = new WalletHandlers({
67
- authenticator,
68
- tokenStorage: new AuthStorage({
69
- dbPath: path.join(env.dataDir, 'auth.db'),
70
- // @ts-ignore
71
- onload: console.warn,
72
- }),
113
+
114
+ export const handlers: any = lazyProxy(() => {
115
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
116
+ const AuthStorage = require('@arcblock/did-connect-storage-nedb');
117
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
118
+ const { WalletHandlers } = require('@blocklet/sdk/lib/wallet-handler');
119
+ return new WalletHandlers({
120
+ authenticator,
121
+ tokenStorage: new AuthStorage({
122
+ dbPath: path.join(env.dataDir, 'auth.db'),
123
+ // @ts-ignore
124
+ onload: console.warn,
125
+ }),
126
+ });
73
127
  });
74
128
 
75
- export const blocklet = new BlockletService();
129
+ export const blocklet: any = lazyProxy(() => {
130
+ ensureNotificationPatch();
131
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
132
+ const { BlockletService } = require('@blocklet/sdk/service/auth');
133
+ return new BlockletService();
134
+ });
76
135
 
77
136
  export async function getVaultAddress() {
78
137
  try {
@@ -88,7 +147,9 @@ export async function getVaultAddress() {
88
147
  }
89
148
 
90
149
  export type CallbackArgs = {
91
- request: Request & { context: Record<string, any> };
150
+ // did-connect-js passes its framework-adapted request to handler callbacks
151
+ // (createHonoRequest under attachHono); only `.context` is read here.
152
+ request: { context: Record<string, any>; [key: string]: any };
92
153
  userDid: string;
93
154
  userPk: string;
94
155
  didwallet: {