payment-kit 1.27.2 → 1.28.0

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 (184) hide show
  1. package/__blocklet__.js +37 -0
  2. package/api/ocap-1.30-subpath-shims.d.ts +35 -0
  3. package/api/src/crons/index.ts +10 -0
  4. package/api/src/crons/metering-subscription-detection.ts +12 -14
  5. package/api/src/crons/overdue-detection.ts +51 -74
  6. package/api/src/integrations/arcblock/nft.ts +6 -2
  7. package/api/src/integrations/arcblock/stake.ts +3 -2
  8. package/api/src/integrations/arcblock/token.ts +4 -4
  9. package/api/src/integrations/blocklet/notification.ts +1 -1
  10. package/api/src/integrations/ethereum/tx.ts +29 -0
  11. package/api/src/integrations/stripe/handlers/invoice.ts +70 -53
  12. package/api/src/integrations/stripe/handlers/payment-intent.ts +8 -1
  13. package/api/src/integrations/stripe/resource.ts +8 -0
  14. package/api/src/libs/audit.ts +32 -16
  15. package/api/src/libs/auth.ts +49 -2
  16. package/api/src/libs/chain-error.ts +31 -0
  17. package/api/src/libs/error.ts +15 -0
  18. package/api/src/libs/event.ts +42 -1
  19. package/api/src/libs/invoice.ts +69 -34
  20. package/api/src/libs/notification/template/customer-auto-recharge-daily-limit-exceeded.ts +1 -3
  21. package/api/src/libs/notification/template/customer-auto-recharge-failed.ts +1 -3
  22. package/api/src/libs/notification/template/customer-credit-grant-granted.ts +1 -3
  23. package/api/src/libs/notification/template/customer-credit-insufficient.ts +1 -3
  24. package/api/src/libs/notification/template/customer-credit-low-balance.ts +1 -3
  25. package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -3
  26. package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -3
  27. package/api/src/libs/notification/template/one-time-payment-refund-succeeded.ts +1 -3
  28. package/api/src/libs/notification/template/one-time-payment-succeeded.ts +1 -3
  29. package/api/src/libs/notification/template/subscription-renew-failed.ts +1 -3
  30. package/api/src/libs/notification/template/subscription-slippage-exceeded.ts +1 -3
  31. package/api/src/libs/notification/template/subscription-slippage-warning.ts +1 -3
  32. package/api/src/libs/notification/template/subscription-succeeded.ts +1 -1
  33. package/api/src/libs/pagination.ts +14 -9
  34. package/api/src/libs/payment.ts +25 -10
  35. package/api/src/libs/session.ts +1 -1
  36. package/api/src/libs/timing.ts +35 -0
  37. package/api/src/libs/util.ts +16 -15
  38. package/api/src/libs/wallet-migration.ts +72 -53
  39. package/api/src/queues/auto-recharge.ts +1 -1
  40. package/api/src/queues/credit-consume.ts +94 -12
  41. package/api/src/queues/credit-grant.ts +4 -0
  42. package/api/src/queues/event.ts +14 -2
  43. package/api/src/queues/invoice.ts +1 -0
  44. package/api/src/queues/payment.ts +83 -15
  45. package/api/src/queues/refund.ts +84 -71
  46. package/api/src/queues/subscription.ts +1 -0
  47. package/api/src/routes/checkout-sessions.ts +82 -43
  48. package/api/src/routes/connect/change-payment.ts +2 -0
  49. package/api/src/routes/connect/change-plan.ts +2 -0
  50. package/api/src/routes/connect/pay.ts +12 -3
  51. package/api/src/routes/connect/setup.ts +3 -1
  52. package/api/src/routes/connect/shared.ts +52 -39
  53. package/api/src/routes/connect/subscribe.ts +4 -1
  54. package/api/src/routes/credit-grants.ts +25 -17
  55. package/api/src/routes/donations.ts +2 -2
  56. package/api/src/routes/meter-events.ts +16 -6
  57. package/api/src/routes/payment-links.ts +1 -1
  58. package/api/src/routes/payment-methods.ts +1 -1
  59. package/api/src/routes/settings.ts +1 -1
  60. package/api/src/routes/tax-rates.ts +1 -1
  61. package/api/src/store/models/customer.ts +23 -1
  62. package/api/src/store/models/payment-method.ts +4 -0
  63. package/api/src/store/models/price.ts +23 -14
  64. package/api/tests/libs/wallet-migration.spec.ts +4 -4
  65. package/api/tests/queues/credit-consume-batch.spec.ts +5 -2
  66. package/api/tests/queues/credit-consume.spec.ts +8 -4
  67. package/api/tests/routes/credit-grants.spec.ts +1 -0
  68. package/blocklet.yml +1 -1
  69. package/cloudflare/MIGRATION-CHALLENGES.md +676 -0
  70. package/cloudflare/MIGRATION-RUNBOOK.md +777 -0
  71. package/cloudflare/README.md +499 -0
  72. package/cloudflare/STAGING-MIGRATION-GUIDE.md +602 -0
  73. package/cloudflare/build.ts +151 -0
  74. package/cloudflare/did-connect-auth.ts +527 -0
  75. package/cloudflare/docs/2026-04-22-sdk-1.30.9-upgrade-retro.md +324 -0
  76. package/cloudflare/docs/2026-04-24-queue-ops-followup.md +218 -0
  77. package/cloudflare/docs/cf-queues-ops-alert-analysis.md +663 -0
  78. package/cloudflare/docs/cf-workers-local-dev-and-fixes.md +284 -0
  79. package/cloudflare/docs/cleanup-tasks-2026-05.md +62 -0
  80. package/cloudflare/docs/payment-kit-platform-analysis-2026-04-20.md +354 -0
  81. package/cloudflare/frontend-shims/buffer-polyfill.ts +9 -0
  82. package/cloudflare/frontend-shims/js-sdk.ts +43 -0
  83. package/cloudflare/frontend-shims/mime-types.ts +46 -0
  84. package/cloudflare/frontend-shims/session.ts +24 -0
  85. package/cloudflare/frontend-shims/vite-plugin-noop.ts +6 -0
  86. package/cloudflare/index.html +40 -0
  87. package/cloudflare/migrate-to-d1.js +252 -0
  88. package/cloudflare/migrations/0001_initial_schema.sql +82 -0
  89. package/cloudflare/migrations/0002_indexes.sql +75 -0
  90. package/cloudflare/migrations/0003_locks_and_constraints.sql +18 -0
  91. package/cloudflare/run-build.js +390 -0
  92. package/cloudflare/scripts/test-decrypt.js +102 -0
  93. package/cloudflare/shims/arcblock-ws.ts +20 -0
  94. package/cloudflare/shims/axios-http-adapter.ts +4 -0
  95. package/cloudflare/shims/axios-lite.ts +117 -0
  96. package/cloudflare/shims/blocklet-sdk/auth-service.ts +33 -0
  97. package/cloudflare/shims/blocklet-sdk/cdn.ts +3 -0
  98. package/cloudflare/shims/blocklet-sdk/component-api.ts +35 -0
  99. package/cloudflare/shims/blocklet-sdk/component.ts +18 -0
  100. package/cloudflare/shims/blocklet-sdk/config.ts +8 -0
  101. package/cloudflare/shims/blocklet-sdk/did.ts +14 -0
  102. package/cloudflare/shims/blocklet-sdk/env.ts +12 -0
  103. package/cloudflare/shims/blocklet-sdk/eventbus.ts +3 -0
  104. package/cloudflare/shims/blocklet-sdk/fallback.ts +3 -0
  105. package/cloudflare/shims/blocklet-sdk/index.ts +11 -0
  106. package/cloudflare/shims/blocklet-sdk/logger.ts +11 -0
  107. package/cloudflare/shims/blocklet-sdk/middlewares.ts +15 -0
  108. package/cloudflare/shims/blocklet-sdk/notification.ts +11 -0
  109. package/cloudflare/shims/blocklet-sdk/security.ts +53 -0
  110. package/cloudflare/shims/blocklet-sdk/session.ts +8 -0
  111. package/cloudflare/shims/blocklet-sdk/verify-sign.ts +38 -0
  112. package/cloudflare/shims/blocklet-sdk/wallet-authenticator.ts +3 -0
  113. package/cloudflare/shims/blocklet-sdk/wallet-handler.ts +6 -0
  114. package/cloudflare/shims/blocklet-sdk/wallet.ts +103 -0
  115. package/cloudflare/shims/cookie-parser.ts +3 -0
  116. package/cloudflare/shims/cors.ts +21 -0
  117. package/cloudflare/shims/cron.ts +189 -0
  118. package/cloudflare/shims/crypto-js-warn.ts +7 -0
  119. package/cloudflare/shims/did-space-js.ts +17 -0
  120. package/cloudflare/shims/did-space.ts +11 -0
  121. package/cloudflare/shims/error.ts +18 -0
  122. package/cloudflare/shims/express-compat/index.ts +80 -0
  123. package/cloudflare/shims/express-compat/types.ts +41 -0
  124. package/cloudflare/shims/fastq.ts +105 -0
  125. package/cloudflare/shims/lock.ts +115 -0
  126. package/cloudflare/shims/mime-types.ts +56 -0
  127. package/cloudflare/shims/nedb-storage.ts +9 -0
  128. package/cloudflare/shims/node-child-process.ts +9 -0
  129. package/cloudflare/shims/node-fs.ts +20 -0
  130. package/cloudflare/shims/node-http.ts +13 -0
  131. package/cloudflare/shims/node-https.ts +4 -0
  132. package/cloudflare/shims/node-misc.ts +15 -0
  133. package/cloudflare/shims/node-net.ts +8 -0
  134. package/cloudflare/shims/node-os.ts +14 -0
  135. package/cloudflare/shims/node-tty.ts +8 -0
  136. package/cloudflare/shims/node-zlib.ts +17 -0
  137. package/cloudflare/shims/noop.ts +26 -0
  138. package/cloudflare/shims/payment-vendor.ts +14 -0
  139. package/cloudflare/shims/querystring.ts +12 -0
  140. package/cloudflare/shims/queue.ts +585 -0
  141. package/cloudflare/shims/rolldown-runtime.ts +43 -0
  142. package/cloudflare/shims/sequelize-d1/datatypes.ts +24 -0
  143. package/cloudflare/shims/sequelize-d1/helpers.ts +46 -0
  144. package/cloudflare/shims/sequelize-d1/index.ts +34 -0
  145. package/cloudflare/shims/sequelize-d1/model.ts +1157 -0
  146. package/cloudflare/shims/sequelize-d1/operators.ts +293 -0
  147. package/cloudflare/shims/sequelize-d1/retry.ts +85 -0
  148. package/cloudflare/shims/sequelize-d1/sequelize-class.ts +119 -0
  149. package/cloudflare/shims/sequelize-d1/timing.ts +81 -0
  150. package/cloudflare/shims/sequelize-d1/types.ts +35 -0
  151. package/cloudflare/shims/stripe-cf.ts +29 -0
  152. package/cloudflare/shims/ws-lite.ts +103 -0
  153. package/cloudflare/shims/xss.ts +3 -0
  154. package/cloudflare/tests/shims/cron.spec.ts +210 -0
  155. package/cloudflare/tests/shims/queue-scheduled.spec.ts +186 -0
  156. package/cloudflare/vite.config.ts +162 -0
  157. package/cloudflare/worker.ts +1553 -0
  158. package/cloudflare/wrangler.json +63 -0
  159. package/cloudflare/wrangler.jsonc +69 -0
  160. package/cloudflare/wrangler.staging.json +66 -0
  161. package/cloudflare/wrangler.toml +28 -0
  162. package/jest.config.js +4 -12
  163. package/package.json +26 -22
  164. package/src/app.tsx +62 -4
  165. package/src/components/customer/link.tsx +9 -13
  166. package/src/components/customer/notification-preference.tsx +3 -2
  167. package/src/components/filter-toolbar.tsx +4 -0
  168. package/src/components/invoice/list.tsx +9 -1
  169. package/src/components/invoice-pdf/utils.ts +2 -1
  170. package/src/components/layout/admin.tsx +39 -5
  171. package/src/components/layout/user-cf.tsx +77 -0
  172. package/src/components/payment-intent/actions.tsx +23 -3
  173. package/src/components/safe-did-address.tsx +75 -0
  174. package/src/libs/patch-user-card.ts +25 -0
  175. package/src/libs/util.ts +5 -7
  176. package/src/pages/admin/billing/meter-events/index.tsx +4 -0
  177. package/src/pages/admin/customers/customers/detail.tsx +2 -2
  178. package/src/pages/admin/customers/customers/index.tsx +2 -2
  179. package/src/pages/admin/overview.tsx +3 -1
  180. package/src/pages/customer/subscription/detail.tsx +4 -4
  181. package/tsconfig.api.json +1 -6
  182. package/tsconfig.json +3 -4
  183. package/tsconfig.types.json +2 -1
  184. package/vite.config.ts +6 -1
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "payment-kit",
3
+ "main": "dist/worker.js",
4
+ "compatibility_date": "2024-12-01",
5
+ "compatibility_flags": ["nodejs_compat"],
6
+ "assets": {
7
+ "directory": "public",
8
+ "binding": "ASSETS",
9
+ "html_handling": "auto-trailing-slash",
10
+ "not_found_handling": "single-page-application"
11
+ },
12
+ "d1_databases": [
13
+ {
14
+ "binding": "DB",
15
+ "database_name": "payment-kit-prod",
16
+ "database_id": "ea6c75d0-39b8-40cd-a0ae-a2062e77c4b9",
17
+ "migrations_dir": "migrations"
18
+ }
19
+ ],
20
+ "kv_namespaces": [
21
+ {
22
+ "binding": "DID_CONNECT_KV",
23
+ "id": "ca0bd29c73864115b713e1db11d97cd2"
24
+ }
25
+ ],
26
+ "services": [
27
+ {
28
+ "binding": "MEDIA_KIT",
29
+ "service": "media-kit"
30
+ },
31
+ {
32
+ "binding": "AUTH_SERVICE",
33
+ "service": "blocklet-service",
34
+ "entrypoint": "BlockletServiceRPC"
35
+ }
36
+ ],
37
+ "vars": {
38
+ "APP_NAME": "Payment Kit",
39
+ "APP_PID": "zNKuN3XwXN7xq2NsJQjjfwujyqCxx26DhwgV",
40
+ "COMPONENT_DID": "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk",
41
+ "MEDIA_KIT_URL": "https://media-kit.yexiaofang.workers.dev"
42
+ },
43
+ "queues": {
44
+ "producers": [
45
+ {
46
+ "binding": "JOB_QUEUE",
47
+ "queue": "payment-kit-jobs"
48
+ }
49
+ ],
50
+ "consumers": [
51
+ {
52
+ "queue": "payment-kit-jobs",
53
+ "max_batch_size": 10,
54
+ "max_batch_timeout": 5,
55
+ "max_retries": 3,
56
+ "dead_letter_queue": "payment-kit-jobs-dlq"
57
+ }
58
+ ]
59
+ },
60
+ "triggers": {
61
+ "crons": ["*/5 * * * *"]
62
+ }
63
+ }
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "payment-kit",
3
+ "main": "dist/worker.js",
4
+ "compatibility_date": "2024-12-01",
5
+ "compatibility_flags": ["nodejs_compat"],
6
+ "placement": { "mode": "smart" },
7
+ "assets": {
8
+ "directory": "public",
9
+ "binding": "ASSETS",
10
+ "html_handling": "auto-trailing-slash",
11
+ "not_found_handling": "single-page-application"
12
+ },
13
+ "d1_databases": [
14
+ {
15
+ "binding": "DB",
16
+ "database_name": "payment-kit-prod",
17
+ "database_id": "ea6c75d0-39b8-40cd-a0ae-a2062e77c4b9"
18
+ }
19
+ ],
20
+ "kv_namespaces": [
21
+ {
22
+ "binding": "DID_CONNECT_KV",
23
+ "id": "ca0bd29c73864115b713e1db11d97cd2"
24
+ }
25
+ ],
26
+ "hyperdrive": [
27
+ {
28
+ "binding": "HYPERDRIVE",
29
+ "id": "18184fbdb1554c6a9f916d57fbc5d43e"
30
+ }
31
+ ],
32
+ "services": [
33
+ {
34
+ "binding": "MEDIA_KIT",
35
+ "service": "media-kit"
36
+ },
37
+ {
38
+ "binding": "AUTH_SERVICE",
39
+ "service": "blocklet-service",
40
+ "entrypoint": "BlockletServiceRPC"
41
+ }
42
+ ],
43
+ "vars": {
44
+ "APP_NAME": "Payment Kit",
45
+ "APP_PID": "zNKuN3XwXN7xq2NsJQjjfwujyqCxx26DhwgV",
46
+ "COMPONENT_DID": "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk",
47
+ "MEDIA_KIT_URL": "https://media-kit.yexiaofang.workers.dev"
48
+ },
49
+ "queues": {
50
+ "producers": [
51
+ {
52
+ "binding": "JOB_QUEUE",
53
+ "queue": "payment-kit-jobs"
54
+ }
55
+ ],
56
+ "consumers": [
57
+ {
58
+ "queue": "payment-kit-jobs",
59
+ "max_batch_size": 10,
60
+ "max_batch_timeout": 5,
61
+ "max_retries": 3,
62
+ "dead_letter_queue": "payment-kit-jobs-dlq"
63
+ }
64
+ ]
65
+ },
66
+ "triggers": {
67
+ "crons": ["* * * * *"]
68
+ }
69
+ }
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "staging-aigne-hub-payment-kit",
3
+ "main": "dist/worker.js",
4
+ "compatibility_date": "2024-12-01",
5
+ "compatibility_flags": ["nodejs_compat"],
6
+ "placement": { "mode": "smart" },
7
+ "assets": {
8
+ "directory": "public",
9
+ "binding": "ASSETS",
10
+ "html_handling": "auto-trailing-slash",
11
+ "not_found_handling": "single-page-application"
12
+ },
13
+ "d1_databases": [
14
+ {
15
+ "binding": "DB",
16
+ "database_name": "staging-aigne-hub-payment-kit",
17
+ "database_id": "978e726b-ad9d-4dcc-bc66-f0d3fb01a1dd",
18
+ "migrations_dir": "migrations"
19
+ }
20
+ ],
21
+ "kv_namespaces": [
22
+ {
23
+ "binding": "DID_CONNECT_KV",
24
+ "id": "a2c98f81bc974fcabc191766698d170f"
25
+ }
26
+ ],
27
+ "services": [
28
+ {
29
+ "binding": "MEDIA_KIT",
30
+ "service": "staging-aigne-hub-media-kit"
31
+ },
32
+ {
33
+ "binding": "AUTH_SERVICE",
34
+ "service": "blocklet-service-staging",
35
+ "entrypoint": "BlockletServiceRPC"
36
+ }
37
+ ],
38
+ "vars": {
39
+ "APP_NAME": "AIGNE Hub Payment Kit (staging)",
40
+ "APP_PID": "zNKWm5HBgaTLptTZBzjHo6PPFAp8X3n8pabY",
41
+ "APP_URL": "https://staging-aigne-hub-payment-kit.arcblock.workers.dev",
42
+ "COMPONENT_DID": "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk",
43
+ "MEDIA_KIT_URL": "https://staging-aigne-hub-media-kit.arcblock.workers.dev/image-bin",
44
+ "PAYMENT_CHANGE_LOCKED_PRICE": "1"
45
+ },
46
+ "queues": {
47
+ "producers": [
48
+ {
49
+ "binding": "JOB_QUEUE",
50
+ "queue": "staging-aigne-hub-payment-kit-jobs"
51
+ }
52
+ ],
53
+ "consumers": [
54
+ {
55
+ "queue": "staging-aigne-hub-payment-kit-jobs",
56
+ "max_batch_size": 10,
57
+ "max_batch_timeout": 5,
58
+ "max_retries": 3,
59
+ "dead_letter_queue": "staging-aigne-hub-payment-kit-jobs-dlq"
60
+ }
61
+ ]
62
+ },
63
+ "triggers": {
64
+ "crons": ["* * * * *"]
65
+ }
66
+ }
@@ -0,0 +1,28 @@
1
+ name = "payment-kit"
2
+ main = "dist/worker.js"
3
+ compatibility_date = "2024-12-01"
4
+ compatibility_flags = ["nodejs_compat"]
5
+
6
+ [build]
7
+ command = "node run-build.js"
8
+
9
+ [[d1_databases]]
10
+ binding = "DB"
11
+ database_name = "payment-kit-prod"
12
+ database_id = "ea6c75d0-39b8-40cd-a0ae-a2062e77c4b9"
13
+ migrations_dir = "migrations"
14
+
15
+ # Service Binding to DID Connect Auth Worker (blocklet-service)
16
+ [[services]]
17
+ binding = "AUTH_SERVICE"
18
+ service = "blocklet-service"
19
+ entrypoint = "BlockletServiceRPC"
20
+
21
+ [triggers]
22
+ crons = ["* * * * *"]
23
+
24
+ [vars]
25
+ APP_URL = "http://localhost:8787"
26
+ APP_NAME = "Payment Kit"
27
+ APP_PID = "payment-kit-dev"
28
+ COMPONENT_DID = "did:abt:test"
package/jest.config.js CHANGED
@@ -10,19 +10,11 @@ module.exports = {
10
10
  globalTeardown: '../../tools/jest-teardown.js',
11
11
  transform: {
12
12
  '^.+\\.ts$': 'ts-jest',
13
- '^.+\\.js$': [
14
- 'ts-jest',
15
- {
16
- tsconfig: {
17
- allowJs: true,
18
- module: 'commonjs',
19
- target: 'es2019',
20
- },
21
- diagnostics: false,
22
- },
23
- ],
13
+ '^.+\\.js$': ['babel-jest', { presets: [['@babel/preset-env', { targets: { node: 'current' } }]] }],
24
14
  },
25
- transformIgnorePatterns: ['/node_modules/(?!.*(@scure|@noble)/)'],
15
+ transformIgnorePatterns: [
16
+ 'node_modules/(?!.*(@noble|@scure|string-width|strip-ansi|ansi-regex)/)',
17
+ ],
26
18
  testMatch: ['**/tests/**/*.spec.ts'],
27
19
  collectCoverageFrom: ['api/src/**/*.ts'],
28
20
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.27.2",
3
+ "version": "1.28.0",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "prelint": "npm run types",
@@ -47,35 +47,36 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@abtnode/cron": "^1.17.12",
50
- "@arcblock/did": "^1.28.5",
51
- "@arcblock/did-connect-react": "^3.5.1",
50
+ "@arcblock/did": "^1.30.9",
51
+ "@arcblock/did-connect-js": "4.0.0",
52
+ "@arcblock/did-connect-react": "^3.5.2",
52
53
  "@arcblock/did-connect-storage-nedb": "^1.8.0",
53
- "@arcblock/did-util": "^1.28.5",
54
- "@arcblock/jwt": "^1.28.5",
55
- "@arcblock/react-hooks": "^3.5.1",
56
- "@arcblock/ux": "^3.5.1",
57
- "@arcblock/validator": "^1.28.5",
58
- "@arcblock/vc": "^1.28.5",
54
+ "@arcblock/did-util": "^1.30.9",
55
+ "@arcblock/jwt": "^1.30.9",
56
+ "@arcblock/react-hooks": "^3.5.2",
57
+ "@arcblock/ux": "^3.5.2",
58
+ "@arcblock/validator": "^1.30.9",
59
+ "@arcblock/vc": "^1.30.9",
59
60
  "@blocklet/did-space-js": "^1.2.23",
60
61
  "@blocklet/error": "^0.3.5",
61
62
  "@blocklet/js-sdk": "^1.17.12",
62
63
  "@blocklet/logger": "^1.17.12",
63
- "@blocklet/payment-broker-client": "1.27.2",
64
- "@blocklet/payment-react": "1.27.2",
65
- "@blocklet/payment-vendor": "1.27.2",
64
+ "@blocklet/payment-broker-client": "1.28.0",
65
+ "@blocklet/payment-react": "1.28.0",
66
+ "@blocklet/payment-vendor": "1.28.0",
66
67
  "@blocklet/sdk": "^1.17.12",
67
- "@blocklet/ui-react": "^3.5.1",
68
- "@blocklet/uploader": "^0.3.19",
68
+ "@blocklet/ui-react": "^3.5.2",
69
+ "@blocklet/uploader": "^0.3.20",
69
70
  "@blocklet/xss": "^0.3.16",
70
71
  "@mui/icons-material": "^7.1.2",
71
72
  "@mui/lab": "7.0.0-beta.14",
72
73
  "@mui/material": "^7.1.2",
73
74
  "@mui/system": "^7.1.1",
74
- "@ocap/asset": "^1.28.5",
75
- "@ocap/client": "^1.28.5",
76
- "@ocap/mcrypto": "^1.28.5",
77
- "@ocap/util": "^1.28.5",
78
- "@ocap/wallet": "^1.28.5",
75
+ "@ocap/asset": "^1.30.9",
76
+ "@ocap/client": "^1.30.9",
77
+ "@ocap/mcrypto": "^1.30.9",
78
+ "@ocap/util": "^1.30.9",
79
+ "@ocap/wallet": "^1.30.9",
79
80
  "@stripe/react-stripe-js": "^2.9.0",
80
81
  "@stripe/stripe-js": "^2.4.0",
81
82
  "ahooks": "^3.8.5",
@@ -102,13 +103,16 @@
102
103
  "joi": "17.12.2",
103
104
  "json-stable-stringify": "^1.3.0",
104
105
  "jspdf": "^4.2.1",
105
- "lodash": "^4.18.1",
106
+ "lodash": "^4.17.21",
107
+ "lodash-es": "^4.18.1",
106
108
  "morgan": "^1.10.0",
107
109
  "mui-daterange-picker": "^1.0.5",
108
110
  "nanoid": "^3.3.11",
109
111
  "numbro": "^2.5.0",
110
112
  "p-all": "3.0.0",
111
113
  "p-wait-for": "^3.2.0",
114
+ "pg": "^8.20.0",
115
+ "postgres": "^3.4.8",
112
116
  "pretty-ms-i18n": "^1.0.3",
113
117
  "react": "^19.1.0",
114
118
  "react-dom": "^19.1.0",
@@ -133,7 +137,7 @@
133
137
  "devDependencies": {
134
138
  "@abtnode/types": "^1.17.12",
135
139
  "@arcblock/eslint-config-ts": "^0.3.3",
136
- "@blocklet/payment-types": "1.27.2",
140
+ "@blocklet/payment-types": "1.28.0",
137
141
  "@types/cookie-parser": "^1.4.9",
138
142
  "@types/cors": "^2.8.19",
139
143
  "@types/debug": "^4.1.12",
@@ -180,5 +184,5 @@
180
184
  "parser": "typescript"
181
185
  }
182
186
  },
183
- "gitHead": "ba98e644bcbf88924039ba8990bba673198e6a61"
187
+ "gitHead": "1486b54f913b83fb42323a89cce7503814d0685a"
184
188
  }
package/src/app.tsx CHANGED
@@ -1,22 +1,26 @@
1
1
  import './global.css';
2
+ import './libs/patch-user-card';
2
3
 
3
4
  import Center from '@arcblock/ux/lib/Center';
4
5
  import withTracker from '@arcblock/ux/lib/withTracker';
5
6
  import { ConfigProvider } from '@arcblock/ux/lib/Config';
6
7
  import { ToastProvider } from '@arcblock/ux/lib/Toast';
7
8
  import { CircularProgress } from '@mui/material';
8
- import React, { Suspense } from 'react';
9
+ import React, { Suspense, useEffect } from 'react';
9
10
  import { ErrorBoundary } from 'react-error-boundary';
10
11
  import { PaymentThemeProvider, usePreventWheel } from '@blocklet/payment-react';
11
- import { Navigate, Route, BrowserRouter as Router, Routes } from 'react-router-dom';
12
+ import { Navigate, Route, BrowserRouter as Router, Routes, useNavigate } from 'react-router-dom';
12
13
  import { joinURL } from 'ufo';
13
14
 
14
15
  import ErrorFallback from './components/error-fallback';
15
- import UserLayout from './components/layout/user';
16
+ import UserLayoutDefault from './components/layout/user';
17
+ import UserLayoutCF from './components/layout/user-cf';
16
18
  import { TransitionProvider } from './components/progress-bar';
17
19
  import { SessionProvider } from './contexts/session';
18
20
  import { translations } from './locales';
19
21
 
22
+ const UserLayout = (window as any).blocklet?.cloudflareWorker ? UserLayoutCF : UserLayoutDefault;
23
+
20
24
  const HomePage = React.lazy(() => import('./pages/home'));
21
25
  const CheckoutPage = React.lazy(() => import('./pages/checkout'));
22
26
  const AdminPage = React.lazy(() => import('./pages/admin'));
@@ -43,10 +47,63 @@ const CustomerBalanceRecharge = React.lazy(() => import('./pages/customer/rechar
43
47
  // },
44
48
  // });
45
49
 
50
+ /**
51
+ * Intercept anchor clicks on internal URLs and do client-side navigation.
52
+ *
53
+ * Some third-party components (e.g. @blocklet/ui-react Dashboard sidebar) render
54
+ * navigation items as `<a href="/admin">` with `external: true`, which triggers
55
+ * a full page reload on click. That reload re-runs session bootstrap and races
56
+ * with page-level auth checks, sometimes redirecting the user back to "/".
57
+ *
58
+ * This handler catches all left-clicks on `<a>` elements with same-origin URLs
59
+ * and converts them into React Router navigation. Modified clicks (cmd/ctrl/shift),
60
+ * target="_blank", and external URLs are left untouched.
61
+ */
62
+ function useInternalLinkInterceptor() {
63
+ const navigate = useNavigate();
64
+ useEffect(() => {
65
+ const handler = (e: MouseEvent) => {
66
+ // Only primary button, no modifier keys (so cmd+click still opens in new tab)
67
+ if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
68
+ const anchor = (e.target as HTMLElement | null)?.closest('a');
69
+ if (!anchor) return;
70
+ // Skip if not a real anchor with href
71
+ const href = anchor.getAttribute('href');
72
+ if (!href) return;
73
+ // Skip target="_blank" / download / rel=external
74
+ if (anchor.target && anchor.target !== '' && anchor.target !== '_self') return;
75
+ if (anchor.hasAttribute('download')) return;
76
+ // Skip non-http(s) schemes: mailto:, tel:, javascript:, #hash, etc.
77
+ if (/^[a-z]+:/i.test(href) && !/^https?:/i.test(href)) return;
78
+ if (href.startsWith('#')) return;
79
+ // Resolve to absolute URL and check same-origin
80
+ let url: URL;
81
+ try {
82
+ url = new URL(href, window.location.href);
83
+ } catch {
84
+ return;
85
+ }
86
+ if (url.origin !== window.location.origin) return;
87
+ // Convert to client-side navigation — strip basename prefix so React Router
88
+ // doesn't double it (e.g. /payment/integrations → /integrations when basename="/payment/")
89
+ e.preventDefault();
90
+ const prefix = window?.blocklet?.prefix || '/';
91
+ let navPath = url.pathname + url.search + url.hash;
92
+ if (prefix !== '/' && navPath.startsWith(prefix.replace(/\/$/, ''))) {
93
+ navPath = navPath.slice(prefix.replace(/\/$/, '').length) || '/';
94
+ }
95
+ navigate(navPath);
96
+ };
97
+ document.addEventListener('click', handler);
98
+ return () => document.removeEventListener('click', handler);
99
+ }, [navigate]);
100
+ }
101
+
46
102
  function App() {
103
+ useInternalLinkInterceptor();
47
104
  return (
48
105
  <TransitionProvider>
49
- <ErrorBoundary FallbackComponent={ErrorFallback} onReset={window.location.reload}>
106
+ <ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => window.location.reload()}>
50
107
  <Suspense
51
108
  fallback={
52
109
  <Center>
@@ -194,6 +251,7 @@ export default function WrappedApp() {
194
251
  <PaymentThemeProvider>
195
252
  <SessionProvider
196
253
  serviceHost={prefix}
254
+ useSocket={!(window as any).blocklet?.cloudflareWorker}
197
255
  protectedRoutes={['/admin/*', '/customer/*', '/integrations/*'].map((item) => joinURL(prefix, item))}>
198
256
  <Router basename={prefix}>
199
257
  <AppWithTracker />
@@ -51,19 +51,15 @@ export default function CustomerLink({
51
51
  popupInfoType={InfoType.Minimal}
52
52
  showDid={size !== 'small'}
53
53
  popupShowDid
54
- {...(customer?.metadata?.anonymous === true
55
- ? {
56
- user: {
57
- fullName: customer.name || customer.email,
58
- did: customer.did,
59
- email: customer.email,
60
- avatar: getCustomerAvatar(
61
- customer?.did,
62
- customer?.updated_at ? new Date(customer.updated_at).toISOString() : ''
63
- ),
64
- },
65
- }
66
- : {})}
54
+ user={{
55
+ fullName: customer.name || customer.email,
56
+ did: customer.did,
57
+ email: customer.email,
58
+ avatar: getCustomerAvatar(
59
+ customer?.did,
60
+ customer?.updated_at ? new Date(customer.updated_at).toISOString() : ''
61
+ ),
62
+ }}
67
63
  {...cardProps}
68
64
  />
69
65
  );
@@ -18,7 +18,7 @@ import {
18
18
  ClickAwayListener,
19
19
  } from '@mui/material';
20
20
  import { useRequest } from 'ahooks';
21
- import { FormProvider, useForm, Controller, Control } from 'react-hook-form';
21
+ import { FormProvider, useForm, Controller } from 'react-hook-form';
22
22
  import { useMobile } from '@blocklet/payment-react';
23
23
  import api from '../../libs/api';
24
24
 
@@ -61,7 +61,8 @@ function TimeSelector({
61
61
  timeFormatErrorMessage,
62
62
  }: {
63
63
  name: string;
64
- control: Control<any>;
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ control: any;
65
66
  required: boolean;
66
67
  timeFormatErrorMessage: string;
67
68
  }) {
@@ -309,6 +309,10 @@ function SearchCustomers({ setSearch, search = defaultProps.search }: Pick<Props
309
309
  e.stopPropagation();
310
310
  }}
311
311
  onClick={(e) => e.stopPropagation()}
312
+ // Stop MUI <Menu>'s built-in letter-key navigation from stealing keystrokes
313
+ // (it jumps to a MenuItem starting with the typed letter, which makes the
314
+ // search field appear to "swallow" inputs like 'a'/'w'). See #1357.
315
+ onKeyDown={(e) => e.stopPropagation()}
312
316
  />
313
317
  </Box>
314
318
  {customers.map((x: any) => (
@@ -431,7 +431,15 @@ export default function InvoiceList({
431
431
  sort: true,
432
432
  customBodyRenderLite: (_: string, index: number) => {
433
433
  const item = data.list[index] as TInvoiceExpanded;
434
- return <InvoiceLink invoice={item}>{formatTime(item.updated_at)}</InvoiceLink>;
434
+ const hasPeriod = item.period_start > 0 && item.period_end > 0;
435
+ const content = <InvoiceLink invoice={item}>{formatTime(item.updated_at)}</InvoiceLink>;
436
+ if (!hasPeriod) return content;
437
+ return (
438
+ <Tooltip
439
+ title={`${t('common.billingPeriod')}: ${formatTime(item.period_start * 1000)} ~ ${formatTime(item.period_end * 1000)}`}>
440
+ {content}
441
+ </Tooltip>
442
+ );
435
443
  },
436
444
  },
437
445
  },
@@ -17,7 +17,8 @@ export async function loadFont(): Promise<boolean> {
17
17
  }
18
18
  }
19
19
 
20
- export function loadImage(url: string): Promise<HTMLImageElement> {
20
+ export function loadImage(url: string): Promise<HTMLImageElement | null> {
21
+ if (!url) return Promise.resolve(null);
21
22
  return new Promise((resolve, reject) => {
22
23
  const img = new Image();
23
24
  img.crossOrigin = 'anonymous';
@@ -1,13 +1,36 @@
1
1
  /* eslint-disable react-hooks/exhaustive-deps */
2
- import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
2
  import { PaymentProvider } from '@blocklet/payment-react';
4
3
  import Dashboard from '@blocklet/ui-react/lib/Dashboard';
5
4
  import { styled } from '@mui/system';
6
5
  import { useEffect } from 'react';
7
6
 
8
- import { Typography } from '@mui/material';
7
+ import { Box, CircularProgress } from '@mui/material';
9
8
  import { useSessionContext } from '../../contexts/session';
10
9
 
10
+ function LayoutLoading() {
11
+ return (
12
+ <Box
13
+ sx={{
14
+ minHeight: '100vh',
15
+ display: 'flex',
16
+ alignItems: 'center',
17
+ justifyContent: 'center',
18
+ }}>
19
+ <CircularProgress />
20
+ </Box>
21
+ );
22
+ }
23
+
24
+ // window.blocklet is bootstrapped synchronously in public/index.html (CF) but
25
+ // the full payload is overlaid from /__blocklet__.js. Don't render children
26
+ // (which may fire API requests on mount) until the critical fields are in
27
+ // place — otherwise requests go out against a half-initialized config and
28
+ // can cascade into auth failures / unintended redirects.
29
+ function isBlockletReady() {
30
+ const b = (window as any).blocklet;
31
+ return !!(b && b.appPid && b.prefix);
32
+ }
33
+
11
34
  const Root = styled(Dashboard)<{ padding: string }>`
12
35
  width: 100%;
13
36
  background-color: ${({ theme }) => theme.palette.background.default};
@@ -62,12 +85,11 @@ const Root = styled(Dashboard)<{ padding: string }>`
62
85
  `;
63
86
 
64
87
  export default function Layout(props: any) {
65
- const { t } = useLocaleContext();
66
88
  const { session, connectApi, events } = useSessionContext();
67
89
 
68
90
  useEffect(() => {
69
91
  events.once('logout', () => {
70
- window.location.href = '/';
92
+ window.location.href = window?.blocklet?.prefix || '/';
71
93
  });
72
94
  }, []);
73
95
 
@@ -78,6 +100,14 @@ export default function Layout(props: any) {
78
100
  }
79
101
  }, [session.initialized]);
80
102
 
103
+ // Show a loading state — not a "Redirecting..." text and not a blank page —
104
+ // while either window.blocklet or the session is still resolving. Admin
105
+ // pages fire authenticated API requests on mount, so children must not
106
+ // render until both are fully in place.
107
+ if (!isBlockletReady() || !session.initialized) {
108
+ return <LayoutLoading />;
109
+ }
110
+
81
111
  if (session.user) {
82
112
  return (
83
113
  <PaymentProvider session={session} connect={connectApi}>
@@ -86,5 +116,9 @@ export default function Layout(props: any) {
86
116
  );
87
117
  }
88
118
 
89
- return <Typography>{t('common.redirecting')}</Typography>;
119
+ // Session initialized but not logged in: the effect above triggers a
120
+ // redirect-based login. Keep showing the loading state (instead of a
121
+ // "Redirecting..." text) so the user isn't confused when the actual
122
+ // navigation hasn't happened yet.
123
+ return <LayoutLoading />;
90
124
  }