payment-kit 1.27.2 → 1.29.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 (241) 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 +32 -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/crons/retry-pending-events.ts +58 -0
  7. package/api/src/integrations/app-store/apple-root-certs.ts +26 -0
  8. package/api/src/integrations/app-store/client.ts +369 -0
  9. package/api/src/integrations/app-store/handlers/index.ts +46 -0
  10. package/api/src/integrations/app-store/handlers/subscription.ts +635 -0
  11. package/api/src/integrations/app-store/node-apple-receipt-verify.d.ts +17 -0
  12. package/api/src/integrations/app-store/notification-routing.ts +18 -0
  13. package/api/src/integrations/app-store/signed-data-verifier.ts +150 -0
  14. package/api/src/integrations/arcblock/nft.ts +6 -2
  15. package/api/src/integrations/arcblock/stake.ts +3 -2
  16. package/api/src/integrations/arcblock/token.ts +4 -4
  17. package/api/src/integrations/blocklet/notification.ts +1 -1
  18. package/api/src/integrations/ethereum/tx.ts +29 -0
  19. package/api/src/integrations/google-play/client.ts +276 -0
  20. package/api/src/integrations/google-play/handlers/index.ts +69 -0
  21. package/api/src/integrations/google-play/handlers/subscription.ts +565 -0
  22. package/api/src/integrations/google-play/handlers/voided.ts +106 -0
  23. package/api/src/integrations/google-play/setup.ts +43 -0
  24. package/api/src/integrations/google-play/verify.ts +251 -0
  25. package/api/src/integrations/iap-reconcile.ts +415 -0
  26. package/api/src/integrations/stripe/handlers/invoice.ts +70 -53
  27. package/api/src/integrations/stripe/handlers/payment-intent.ts +8 -1
  28. package/api/src/integrations/stripe/resource.ts +8 -0
  29. package/api/src/libs/audit.ts +70 -24
  30. package/api/src/libs/auth.ts +49 -2
  31. package/api/src/libs/chain-error.ts +31 -0
  32. package/api/src/libs/entitlement.ts +399 -0
  33. package/api/src/libs/env.ts +2 -0
  34. package/api/src/libs/error.ts +15 -0
  35. package/api/src/libs/event.ts +42 -1
  36. package/api/src/libs/invoice.ts +69 -34
  37. package/api/src/libs/notification/template/customer-auto-recharge-daily-limit-exceeded.ts +1 -3
  38. package/api/src/libs/notification/template/customer-auto-recharge-failed.ts +1 -3
  39. package/api/src/libs/notification/template/customer-credit-grant-granted.ts +1 -3
  40. package/api/src/libs/notification/template/customer-credit-insufficient.ts +1 -3
  41. package/api/src/libs/notification/template/customer-credit-low-balance.ts +1 -3
  42. package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -3
  43. package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -3
  44. package/api/src/libs/notification/template/one-time-payment-refund-succeeded.ts +1 -3
  45. package/api/src/libs/notification/template/one-time-payment-succeeded.ts +1 -3
  46. package/api/src/libs/notification/template/subscription-renew-failed.ts +1 -3
  47. package/api/src/libs/notification/template/subscription-slippage-exceeded.ts +1 -3
  48. package/api/src/libs/notification/template/subscription-slippage-warning.ts +1 -3
  49. package/api/src/libs/notification/template/subscription-succeeded.ts +1 -1
  50. package/api/src/libs/pagination.ts +14 -9
  51. package/api/src/libs/payment.ts +25 -10
  52. package/api/src/libs/security.ts +51 -0
  53. package/api/src/libs/session.ts +1 -1
  54. package/api/src/libs/subscription.ts +13 -1
  55. package/api/src/libs/timing.ts +35 -0
  56. package/api/src/libs/util.ts +29 -15
  57. package/api/src/libs/wallet-migration.ts +72 -53
  58. package/api/src/queues/auto-recharge.ts +1 -1
  59. package/api/src/queues/credit-consume.ts +94 -12
  60. package/api/src/queues/credit-grant.ts +4 -0
  61. package/api/src/queues/event.ts +39 -21
  62. package/api/src/queues/invoice.ts +1 -0
  63. package/api/src/queues/payment.ts +83 -15
  64. package/api/src/queues/refund.ts +84 -71
  65. package/api/src/queues/subscription.ts +1 -0
  66. package/api/src/queues/webhook.ts +12 -2
  67. package/api/src/routes/checkout-sessions.ts +82 -43
  68. package/api/src/routes/connect/change-payment.ts +2 -0
  69. package/api/src/routes/connect/change-plan.ts +2 -0
  70. package/api/src/routes/connect/pay.ts +12 -3
  71. package/api/src/routes/connect/setup.ts +3 -1
  72. package/api/src/routes/connect/shared.ts +52 -39
  73. package/api/src/routes/connect/subscribe.ts +4 -1
  74. package/api/src/routes/credit-grants.ts +25 -17
  75. package/api/src/routes/donations.ts +2 -2
  76. package/api/src/routes/entitlements.ts +105 -0
  77. package/api/src/routes/events.ts +2 -2
  78. package/api/src/routes/index.ts +12 -2
  79. package/api/src/routes/integrations/app-store.ts +267 -0
  80. package/api/src/routes/integrations/google-play.ts +324 -0
  81. package/api/src/routes/meter-events.ts +16 -6
  82. package/api/src/routes/payment-links.ts +1 -1
  83. package/api/src/routes/payment-methods.ts +131 -1
  84. package/api/src/routes/settings.ts +1 -1
  85. package/api/src/routes/tax-rates.ts +1 -1
  86. package/api/src/store/migrations/20260526-iap-foundation.ts +105 -0
  87. package/api/src/store/models/customer.ts +37 -1
  88. package/api/src/store/models/entitlement-grant.ts +118 -0
  89. package/api/src/store/models/entitlement-product.ts +48 -0
  90. package/api/src/store/models/entitlement.ts +86 -0
  91. package/api/src/store/models/index.ts +9 -0
  92. package/api/src/store/models/invoice.ts +20 -0
  93. package/api/src/store/models/payment-method.ts +66 -1
  94. package/api/src/store/models/price.ts +23 -14
  95. package/api/src/store/models/refund.ts +10 -0
  96. package/api/src/store/models/subscription.ts +14 -0
  97. package/api/src/store/models/types.ts +32 -0
  98. package/api/tests/integrations/app-store/client.spec.ts +335 -0
  99. package/api/tests/integrations/app-store/handlers.spec.ts +480 -0
  100. package/api/tests/integrations/app-store/notifications.spec.ts +381 -0
  101. package/api/tests/integrations/app-store/signed-data-verifier.spec.ts +72 -0
  102. package/api/tests/integrations/app-store/webhook-routing.spec.ts +27 -0
  103. package/api/tests/integrations/google-play/handlers.spec.ts +341 -0
  104. package/api/tests/integrations/google-play/verify.spec.ts +215 -0
  105. package/api/tests/integrations/iap-reconcile.spec.ts +237 -0
  106. package/api/tests/libs/entitlement.spec.ts +347 -0
  107. package/api/tests/libs/wallet-migration.spec.ts +4 -4
  108. package/api/tests/queues/credit-consume-batch.spec.ts +5 -2
  109. package/api/tests/queues/credit-consume.spec.ts +8 -4
  110. package/api/tests/routes/credit-grants.spec.ts +1 -0
  111. package/blocklet.yml +1 -1
  112. package/cloudflare/MIGRATION-CHALLENGES.md +676 -0
  113. package/cloudflare/MIGRATION-RUNBOOK.md +777 -0
  114. package/cloudflare/README.md +499 -0
  115. package/cloudflare/STAGING-MIGRATION-GUIDE.md +602 -0
  116. package/cloudflare/build.ts +151 -0
  117. package/cloudflare/did-connect-auth.ts +527 -0
  118. package/cloudflare/docs/2026-04-22-sdk-1.30.9-upgrade-retro.md +324 -0
  119. package/cloudflare/docs/2026-04-24-queue-ops-followup.md +218 -0
  120. package/cloudflare/docs/cf-queues-ops-alert-analysis.md +663 -0
  121. package/cloudflare/docs/cf-workers-local-dev-and-fixes.md +284 -0
  122. package/cloudflare/docs/cleanup-tasks-2026-05.md +62 -0
  123. package/cloudflare/docs/payment-kit-platform-analysis-2026-04-20.md +354 -0
  124. package/cloudflare/frontend-shims/buffer-polyfill.ts +9 -0
  125. package/cloudflare/frontend-shims/js-sdk.ts +43 -0
  126. package/cloudflare/frontend-shims/mime-types.ts +46 -0
  127. package/cloudflare/frontend-shims/session.ts +24 -0
  128. package/cloudflare/frontend-shims/vite-plugin-noop.ts +6 -0
  129. package/cloudflare/index.html +40 -0
  130. package/cloudflare/migrate-to-d1.js +252 -0
  131. package/cloudflare/migrations/0001_initial_schema.sql +82 -0
  132. package/cloudflare/migrations/0002_indexes.sql +75 -0
  133. package/cloudflare/migrations/0003_locks_and_constraints.sql +18 -0
  134. package/cloudflare/migrations/0004_iap_foundation.sql +72 -0
  135. package/cloudflare/migrations/0005_iap_tenant_backfill.sql +112 -0
  136. package/cloudflare/run-build.js +391 -0
  137. package/cloudflare/scripts/test-decrypt.js +102 -0
  138. package/cloudflare/shims/arcblock-ws.ts +20 -0
  139. package/cloudflare/shims/axios-http-adapter.ts +4 -0
  140. package/cloudflare/shims/axios-lite.ts +117 -0
  141. package/cloudflare/shims/blocklet-sdk/auth-service.ts +33 -0
  142. package/cloudflare/shims/blocklet-sdk/cdn.ts +3 -0
  143. package/cloudflare/shims/blocklet-sdk/component-api.ts +35 -0
  144. package/cloudflare/shims/blocklet-sdk/component.ts +18 -0
  145. package/cloudflare/shims/blocklet-sdk/config.ts +8 -0
  146. package/cloudflare/shims/blocklet-sdk/did.ts +14 -0
  147. package/cloudflare/shims/blocklet-sdk/env.ts +12 -0
  148. package/cloudflare/shims/blocklet-sdk/eventbus.ts +3 -0
  149. package/cloudflare/shims/blocklet-sdk/fallback.ts +3 -0
  150. package/cloudflare/shims/blocklet-sdk/index.ts +11 -0
  151. package/cloudflare/shims/blocklet-sdk/logger.ts +11 -0
  152. package/cloudflare/shims/blocklet-sdk/middlewares.ts +15 -0
  153. package/cloudflare/shims/blocklet-sdk/notification.ts +11 -0
  154. package/cloudflare/shims/blocklet-sdk/security.ts +53 -0
  155. package/cloudflare/shims/blocklet-sdk/session.ts +8 -0
  156. package/cloudflare/shims/blocklet-sdk/verify-session.ts +44 -0
  157. package/cloudflare/shims/blocklet-sdk/verify-sign.ts +38 -0
  158. package/cloudflare/shims/blocklet-sdk/wallet-authenticator.ts +3 -0
  159. package/cloudflare/shims/blocklet-sdk/wallet-handler.ts +6 -0
  160. package/cloudflare/shims/blocklet-sdk/wallet.ts +103 -0
  161. package/cloudflare/shims/cookie-parser.ts +3 -0
  162. package/cloudflare/shims/cors.ts +21 -0
  163. package/cloudflare/shims/cron.ts +189 -0
  164. package/cloudflare/shims/crypto-js-warn.ts +7 -0
  165. package/cloudflare/shims/did-space-js.ts +17 -0
  166. package/cloudflare/shims/did-space.ts +11 -0
  167. package/cloudflare/shims/error.ts +18 -0
  168. package/cloudflare/shims/express-compat/index.ts +80 -0
  169. package/cloudflare/shims/express-compat/types.ts +41 -0
  170. package/cloudflare/shims/fastq.ts +105 -0
  171. package/cloudflare/shims/lock.ts +115 -0
  172. package/cloudflare/shims/mime-types.ts +56 -0
  173. package/cloudflare/shims/nedb-storage.ts +9 -0
  174. package/cloudflare/shims/node-child-process.ts +9 -0
  175. package/cloudflare/shims/node-fs.ts +20 -0
  176. package/cloudflare/shims/node-http.ts +13 -0
  177. package/cloudflare/shims/node-https.ts +4 -0
  178. package/cloudflare/shims/node-misc.ts +15 -0
  179. package/cloudflare/shims/node-net.ts +8 -0
  180. package/cloudflare/shims/node-os.ts +14 -0
  181. package/cloudflare/shims/node-tty.ts +8 -0
  182. package/cloudflare/shims/node-zlib.ts +17 -0
  183. package/cloudflare/shims/noop.ts +26 -0
  184. package/cloudflare/shims/payment-vendor.ts +14 -0
  185. package/cloudflare/shims/querystring.ts +12 -0
  186. package/cloudflare/shims/queue.ts +611 -0
  187. package/cloudflare/shims/rolldown-runtime.ts +43 -0
  188. package/cloudflare/shims/sequelize-d1/datatypes.ts +24 -0
  189. package/cloudflare/shims/sequelize-d1/helpers.ts +46 -0
  190. package/cloudflare/shims/sequelize-d1/index.ts +34 -0
  191. package/cloudflare/shims/sequelize-d1/model.ts +1176 -0
  192. package/cloudflare/shims/sequelize-d1/operators.ts +306 -0
  193. package/cloudflare/shims/sequelize-d1/retry.ts +85 -0
  194. package/cloudflare/shims/sequelize-d1/sequelize-class.ts +119 -0
  195. package/cloudflare/shims/sequelize-d1/timing.ts +81 -0
  196. package/cloudflare/shims/sequelize-d1/types.ts +35 -0
  197. package/cloudflare/shims/stripe-cf.ts +29 -0
  198. package/cloudflare/shims/ws-lite.ts +103 -0
  199. package/cloudflare/shims/xss.ts +3 -0
  200. package/cloudflare/tests/shims/cron.spec.ts +210 -0
  201. package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +87 -0
  202. package/cloudflare/tests/shims/queue-scheduled.spec.ts +186 -0
  203. package/cloudflare/vite.config.ts +162 -0
  204. package/cloudflare/worker.ts +1608 -0
  205. package/cloudflare/wrangler.json +63 -0
  206. package/cloudflare/wrangler.jsonc +75 -0
  207. package/cloudflare/wrangler.staging.json +67 -0
  208. package/cloudflare/wrangler.toml +28 -0
  209. package/jest.config.js +4 -12
  210. package/package.json +30 -22
  211. package/scripts/seed-google-play.ts +79 -0
  212. package/src/app.tsx +62 -4
  213. package/src/components/customer/link.tsx +9 -13
  214. package/src/components/customer/notification-preference.tsx +3 -2
  215. package/src/components/filter-toolbar.tsx +4 -0
  216. package/src/components/invoice/list.tsx +9 -1
  217. package/src/components/invoice-pdf/utils.ts +2 -1
  218. package/src/components/layout/admin.tsx +39 -5
  219. package/src/components/layout/user-cf.tsx +77 -0
  220. package/src/components/payment-intent/actions.tsx +23 -3
  221. package/src/components/payment-method/app-store.tsx +103 -0
  222. package/src/components/payment-method/form.tsx +7 -1
  223. package/src/components/payment-method/google-play.tsx +85 -0
  224. package/src/components/safe-did-address.tsx +75 -0
  225. package/src/components/subscription/list.tsx +20 -0
  226. package/src/libs/patch-user-card.ts +25 -0
  227. package/src/libs/util.ts +5 -7
  228. package/src/locales/en.tsx +63 -0
  229. package/src/locales/zh.tsx +63 -0
  230. package/src/pages/admin/billing/meter-events/index.tsx +4 -0
  231. package/src/pages/admin/billing/subscriptions/detail.tsx +80 -0
  232. package/src/pages/admin/customers/customers/detail.tsx +8 -2
  233. package/src/pages/admin/customers/customers/index.tsx +2 -2
  234. package/src/pages/admin/overview.tsx +3 -1
  235. package/src/pages/admin/settings/payment-methods/create.tsx +12 -0
  236. package/src/pages/admin/settings/payment-methods/index.tsx +1 -1
  237. package/src/pages/customer/subscription/detail.tsx +4 -4
  238. package/tsconfig.api.json +1 -6
  239. package/tsconfig.json +3 -4
  240. package/tsconfig.types.json +2 -1
  241. 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,75 @@
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
+ // Batch crons (subscription scans, queue dispatch, retry backstops) all run in
70
+ // one scheduled() invocation each minute; raise the per-invocation CPU ceiling
71
+ // so they aren't killed mid-run ("Exceeded CPU Limit") on Workers Paid. The
72
+ // structural fix (offload to a Consumer Worker / Paid Queue throughput) is
73
+ // tracked in task-44. No effect on Workers Free (CPU limit is fixed there).
74
+ "limits": { "cpu_ms": 300000 }
75
+ }
@@ -0,0 +1,67 @@
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
+ "limits": { "cpu_ms": 300000 }
67
+ }
@@ -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.29.0",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "prelint": "npm run types",
@@ -47,35 +47,37 @@
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
+ "@apple/app-store-server-library": "^3.1.0",
51
+ "@arcblock/did": "^1.30.9",
52
+ "@arcblock/did-connect-js": "4.0.0",
53
+ "@arcblock/did-connect-react": "^3.5.2",
52
54
  "@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",
55
+ "@arcblock/did-util": "^1.30.9",
56
+ "@arcblock/jwt": "^1.30.9",
57
+ "@arcblock/react-hooks": "^3.5.2",
58
+ "@arcblock/ux": "^3.5.2",
59
+ "@arcblock/validator": "^1.30.9",
60
+ "@arcblock/vc": "^1.30.9",
59
61
  "@blocklet/did-space-js": "^1.2.23",
60
62
  "@blocklet/error": "^0.3.5",
61
63
  "@blocklet/js-sdk": "^1.17.12",
62
64
  "@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",
65
+ "@blocklet/payment-broker-client": "1.29.0",
66
+ "@blocklet/payment-react": "1.29.0",
67
+ "@blocklet/payment-vendor": "1.29.0",
66
68
  "@blocklet/sdk": "^1.17.12",
67
- "@blocklet/ui-react": "^3.5.1",
68
- "@blocklet/uploader": "^0.3.19",
69
+ "@blocklet/ui-react": "^3.5.2",
70
+ "@blocklet/uploader": "^0.3.20",
69
71
  "@blocklet/xss": "^0.3.16",
70
72
  "@mui/icons-material": "^7.1.2",
71
73
  "@mui/lab": "7.0.0-beta.14",
72
74
  "@mui/material": "^7.1.2",
73
75
  "@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",
76
+ "@ocap/asset": "^1.30.9",
77
+ "@ocap/client": "^1.30.9",
78
+ "@ocap/mcrypto": "^1.30.9",
79
+ "@ocap/util": "^1.30.9",
80
+ "@ocap/wallet": "^1.30.9",
79
81
  "@stripe/react-stripe-js": "^2.9.0",
80
82
  "@stripe/stripe-js": "^2.4.0",
81
83
  "ahooks": "^3.8.5",
@@ -97,18 +99,23 @@
97
99
  "fastq": "^1.19.1",
98
100
  "flat": "^5.0.2",
99
101
  "google-libphonenumber": "^3.2.42",
102
+ "google-play-billing-validator": "^2.1.3",
100
103
  "html2canvas": "^1.4.1",
101
104
  "iframe-resizer-react": "^1.1.1",
102
105
  "joi": "17.12.2",
103
106
  "json-stable-stringify": "^1.3.0",
104
107
  "jspdf": "^4.2.1",
105
- "lodash": "^4.18.1",
108
+ "lodash": "^4.17.21",
109
+ "lodash-es": "^4.18.1",
106
110
  "morgan": "^1.10.0",
107
111
  "mui-daterange-picker": "^1.0.5",
108
112
  "nanoid": "^3.3.11",
113
+ "node-apple-receipt-verify": "^1.15.0",
109
114
  "numbro": "^2.5.0",
110
115
  "p-all": "3.0.0",
111
116
  "p-wait-for": "^3.2.0",
117
+ "pg": "^8.20.0",
118
+ "postgres": "^3.4.8",
112
119
  "pretty-ms-i18n": "^1.0.3",
113
120
  "react": "^19.1.0",
114
121
  "react-dom": "^19.1.0",
@@ -133,13 +140,14 @@
133
140
  "devDependencies": {
134
141
  "@abtnode/types": "^1.17.12",
135
142
  "@arcblock/eslint-config-ts": "^0.3.3",
136
- "@blocklet/payment-types": "1.27.2",
143
+ "@blocklet/payment-types": "1.29.0",
137
144
  "@types/cookie-parser": "^1.4.9",
138
145
  "@types/cors": "^2.8.19",
139
146
  "@types/debug": "^4.1.12",
140
147
  "@types/dotenv-flow": "^3.3.3",
141
148
  "@types/express": "^4.17.23",
142
149
  "@types/node": "^18.19.112",
150
+ "@types/node-apple-receipt-verify": "^1.7.5",
143
151
  "@types/react": "^18.3.23",
144
152
  "@types/react-dom": "^18.3.7",
145
153
  "@vitejs/plugin-react": "^4.6.0",
@@ -180,5 +188,5 @@
180
188
  "parser": "typescript"
181
189
  }
182
190
  },
183
- "gitHead": "ba98e644bcbf88924039ba8990bba673198e6a61"
191
+ "gitHead": "02334964fbf505ea2fd27081039542d1f9868d57"
184
192
  }
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env tsx
2
+ /* eslint-disable no-console */
3
+ //
4
+ // Seed a `google_play` PaymentMethod from a service account JSON file.
5
+ //
6
+ // Usage:
7
+ // GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH=/path/to/sa.json \
8
+ // GOOGLE_PLAY_PACKAGE_NAME=io.arcblock.aistro \
9
+ // tsx scripts/seed-google-play.ts
10
+ //
11
+ // Optional:
12
+ // PAYMENT_METHOD_ID existing id to update; otherwise a new one is created
13
+ // PAYMENT_METHOD_LIVEMODE "true" (default) | "false"
14
+ //
15
+ // The JSON contents are stored encrypted via PaymentMethod.encryptSettings.
16
+
17
+ import 'dotenv-flow/config';
18
+ import { readFileSync } from 'fs';
19
+
20
+ import { PaymentMethod } from '../api/src/store/models';
21
+ import { sequelize } from '../api/src/store/sequelize';
22
+ import migrate from '../api/src/store/migrate';
23
+ import { initialize } from '../api/src/store/models';
24
+
25
+ async function main(): Promise<void> {
26
+ const jsonPath = process.env.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH;
27
+ const packageName = process.env.GOOGLE_PLAY_PACKAGE_NAME;
28
+ if (!jsonPath || !packageName) {
29
+ console.error('Missing GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_PATH or GOOGLE_PLAY_PACKAGE_NAME');
30
+ process.exit(1);
31
+ }
32
+ const livemode = process.env.PAYMENT_METHOD_LIVEMODE !== 'false';
33
+ const existingId = process.env.PAYMENT_METHOD_ID;
34
+
35
+ const raw = readFileSync(jsonPath, 'utf8');
36
+ // Validate JSON shape early
37
+ const parsed = JSON.parse(raw);
38
+ if (!parsed.client_email || !parsed.private_key) {
39
+ throw new Error('service account JSON missing client_email or private_key');
40
+ }
41
+
42
+ initialize(sequelize);
43
+ await migrate();
44
+
45
+ const settings = PaymentMethod.encryptSettings({
46
+ google_play: {
47
+ package_name: packageName,
48
+ service_account_json: raw,
49
+ pubsub_topic_name: '',
50
+ },
51
+ });
52
+
53
+ if (existingId) {
54
+ const method = await PaymentMethod.findByPk(existingId);
55
+ if (!method) throw new Error(`PaymentMethod ${existingId} not found`);
56
+ await method.update({ settings, active: true, livemode });
57
+ console.log('updated PaymentMethod', method.id);
58
+ } else {
59
+ const method = await PaymentMethod.create({
60
+ type: 'google_play',
61
+ name: `Google Play (${packageName})`,
62
+ description: 'In-App Billing via Google Play Console',
63
+ logo: '',
64
+ active: true,
65
+ livemode,
66
+ confirmation: { type: 'callback' },
67
+ settings,
68
+ features: { recurring: true, refund: true, dispute: false },
69
+ } as any);
70
+ console.log('created PaymentMethod', method.id, `client_email=${parsed.client_email}`);
71
+ }
72
+
73
+ await sequelize.close();
74
+ }
75
+
76
+ main().catch((err) => {
77
+ console.error(err);
78
+ process.exit(1);
79
+ });
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';