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.
- package/api/dev.ts +41 -2
- package/api/hono.d.ts +42 -0
- package/api/node-sqlite.d.ts +12 -0
- package/api/src/bootstrap.ts +36 -0
- package/api/src/crons/base.ts +3 -3
- package/api/src/crons/currency.ts +1 -1
- package/api/src/crons/index.ts +27 -24
- package/api/src/crons/metering-subscription-detection.ts +1 -1
- package/api/src/crons/overdue-detection.ts +2 -2
- package/api/src/crons/retry-pending-events.ts +6 -0
- package/api/src/index.ts +22 -161
- package/api/src/integrations/app-store/client.ts +3 -4
- package/api/src/integrations/app-store/handlers/subscription.ts +7 -7
- package/api/src/integrations/app-store/signed-data-verifier.ts +3 -2
- package/api/src/integrations/arcblock/token.ts +21 -7
- package/api/src/integrations/google-play/handlers/subscription.ts +6 -6
- package/api/src/integrations/google-play/handlers/voided.ts +2 -2
- package/api/src/integrations/google-play/verify.ts +3 -2
- package/api/src/integrations/iap-reconcile.ts +3 -5
- package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
- package/api/src/integrations/stripe/handlers/subscription.ts +3 -3
- package/api/src/libs/archive/query.ts +19 -0
- package/api/src/libs/audit.ts +61 -4
- package/api/src/libs/auth.ts +99 -38
- package/api/src/libs/context.ts +78 -1
- package/api/src/libs/currency.ts +2 -2
- package/api/src/libs/dayjs.ts +8 -2
- package/api/src/libs/drivers/auth-storage.ts +118 -0
- package/api/src/libs/drivers/cron.ts +264 -0
- package/api/src/libs/drivers/db.ts +170 -0
- package/api/src/libs/drivers/identity.ts +81 -0
- package/api/src/libs/drivers/index.ts +40 -0
- package/api/src/libs/drivers/locks.ts +226 -0
- package/api/src/libs/drivers/migrate-runner.ts +70 -0
- package/api/src/libs/drivers/queue.ts +104 -0
- package/api/src/libs/drivers/secrets.ts +194 -0
- package/api/src/libs/env.ts +170 -54
- package/api/src/libs/exchange-rate/service.ts +7 -6
- package/api/src/libs/http-fetch-adapter.ts +50 -0
- package/api/src/libs/invoice.ts +1 -1
- package/api/src/libs/lock.ts +51 -47
- package/api/src/libs/logger.ts +48 -8
- package/api/src/libs/notification/index.ts +1 -1
- package/api/src/libs/notification/template/customer-credit-low-balance.ts +2 -1
- package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -1
- package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -1
- package/api/src/libs/overdraft-protection.ts +1 -1
- package/api/src/libs/payout.ts +1 -1
- package/api/src/libs/queue/index.ts +259 -52
- package/api/src/libs/queue/runtime.ts +175 -0
- package/api/src/libs/resource.ts +3 -3
- package/api/src/libs/secrets.ts +38 -0
- package/api/src/libs/session.ts +3 -2
- package/api/src/libs/subscription.ts +5 -5
- package/api/src/libs/tenant.ts +92 -0
- package/api/src/libs/url.ts +3 -3
- package/api/src/libs/util.ts +21 -13
- package/api/src/middlewares/hono/cdn.ts +63 -0
- package/api/src/middlewares/hono/context.ts +73 -0
- package/api/src/middlewares/hono/csrf.ts +72 -0
- package/api/src/middlewares/hono/fallback.ts +194 -0
- package/api/src/middlewares/hono/pipeline.ts +73 -0
- package/api/src/middlewares/hono/resource-mount.ts +42 -0
- package/api/src/middlewares/hono/resource.ts +63 -0
- package/api/src/middlewares/hono/security.ts +214 -0
- package/api/src/middlewares/hono/session.ts +114 -0
- package/api/src/middlewares/hono/xss.ts +61 -0
- package/api/src/queues/auto-recharge.ts +12 -10
- package/api/src/queues/checkout-session.ts +17 -12
- package/api/src/queues/credit-consume.ts +40 -36
- package/api/src/queues/credit-grant.ts +25 -18
- package/api/src/queues/credit-reconciliation.ts +7 -5
- package/api/src/queues/discount-status.ts +9 -6
- package/api/src/queues/event.ts +12 -4
- package/api/src/queues/exchange-rate-health.ts +49 -30
- package/api/src/queues/invoice.ts +18 -15
- package/api/src/queues/notification.ts +14 -7
- package/api/src/queues/payment.ts +41 -28
- package/api/src/queues/payout.ts +9 -5
- package/api/src/queues/refund.ts +18 -12
- package/api/src/queues/subscription.ts +83 -53
- package/api/src/queues/token-transfer.ts +15 -10
- package/api/src/queues/usage-record.ts +8 -5
- package/api/src/queues/vendors/commission.ts +7 -5
- package/api/src/queues/vendors/fulfillment-coordinator.ts +17 -13
- package/api/src/queues/vendors/fulfillment.ts +4 -2
- package/api/src/queues/vendors/return-processor.ts +5 -3
- package/api/src/queues/vendors/return-scanner.ts +5 -4
- package/api/src/queues/vendors/status-check.ts +10 -7
- package/api/src/queues/webhook.ts +60 -32
- package/api/src/routes/connect/shared.ts +1 -2
- package/api/src/routes/connect/subscribe.ts +3 -3
- package/api/src/routes/{archive.ts → hono/archive.ts} +69 -64
- package/api/src/routes/{auto-recharge-configs.ts → hono/auto-recharge-configs.ts} +39 -28
- package/api/src/routes/{checkout-sessions.ts → hono/checkout-sessions.ts} +790 -923
- package/api/src/routes/{coupons.ts → hono/coupons.ts} +93 -76
- package/api/src/routes/{credit-grants.ts → hono/credit-grants.ts} +140 -126
- package/api/src/routes/hono/credit-tokens.ts +43 -0
- package/api/src/routes/{credit-transactions.ts → hono/credit-transactions.ts} +37 -29
- package/api/src/routes/{customers.ts → hono/customers.ts} +193 -223
- package/api/src/routes/{donations.ts → hono/donations.ts} +41 -32
- package/api/src/routes/{entitlements.ts → hono/entitlements.ts} +28 -25
- package/api/src/routes/{events.ts → hono/events.ts} +107 -71
- package/api/src/routes/{exchange-rate-providers.ts → hono/exchange-rate-providers.ts} +138 -126
- package/api/src/routes/hono/exchange-rates.ts +77 -0
- package/api/src/routes/hono/index.ts +115 -0
- package/api/src/routes/{integrations → hono/integrations}/app-store.ts +68 -48
- package/api/src/routes/{integrations → hono/integrations}/google-play.ts +78 -58
- package/api/src/routes/hono/integrations/stripe.ts +74 -0
- package/api/src/routes/{invoices.ts → hono/invoices.ts} +253 -244
- package/api/src/routes/{meter-events.ts → hono/meter-events.ts} +120 -110
- package/api/src/routes/hono/meters.ts +288 -0
- package/api/src/routes/hono/passports.ts +73 -0
- package/api/src/routes/{payment-currencies.ts → hono/payment-currencies.ts} +219 -197
- package/api/src/routes/{payment-intents.ts → hono/payment-intents.ts} +136 -132
- package/api/src/routes/{payment-links.ts → hono/payment-links.ts} +145 -128
- package/api/src/routes/{payment-methods.ts → hono/payment-methods.ts} +125 -93
- package/api/src/routes/{payment-stats.ts → hono/payment-stats.ts} +30 -25
- package/api/src/routes/{payouts.ts → hono/payouts.ts} +55 -47
- package/api/src/routes/{prices.ts → hono/prices.ts} +265 -242
- package/api/src/routes/{pricing-table.ts → hono/pricing-table.ts} +94 -87
- package/api/src/routes/{products.ts → hono/products.ts} +172 -159
- package/api/src/routes/{promotion-codes.ts → hono/promotion-codes.ts} +207 -185
- package/api/src/routes/hono/redirect.ts +24 -0
- package/api/src/routes/{refunds.ts → hono/refunds.ts} +96 -80
- package/api/src/routes/{settings.ts → hono/settings.ts} +64 -55
- package/api/src/routes/{subscription-items.ts → hono/subscription-items.ts} +64 -57
- package/api/src/routes/{subscriptions.ts → hono/subscriptions.ts} +475 -528
- package/api/src/routes/{tax-rates.ts → hono/tax-rates.ts} +71 -70
- package/api/src/routes/hono/tool.ts +69 -0
- package/api/src/routes/{usage-records.ts → hono/usage-records.ts} +47 -42
- package/api/src/routes/{vendor.ts → hono/vendor.ts} +315 -167
- package/api/src/routes/{webhook-attempts.ts → hono/webhook-attempts.ts} +17 -13
- package/api/src/routes/hono/webhook-endpoints.ts +126 -0
- package/api/src/service.ts +667 -0
- package/api/src/store/migrations/20230911-seeding.ts +2 -1
- package/api/src/store/migrations/20260609-remove-did-space-jobs.ts +23 -0
- package/api/src/store/migrations/20260610-tenant-columns.ts +40 -0
- package/api/src/store/migrations/20260611-tenant-backfill.ts +33 -0
- package/api/src/store/models/auto-recharge-config.ts +22 -10
- package/api/src/store/models/checkout-session.ts +15 -14
- package/api/src/store/models/coupon.ts +29 -20
- package/api/src/store/models/credit-grant.ts +38 -29
- package/api/src/store/models/credit-transaction.ts +32 -21
- package/api/src/store/models/customer.ts +19 -17
- package/api/src/store/models/discount.ts +11 -2
- package/api/src/store/models/entitlement-grant.ts +21 -9
- package/api/src/store/models/entitlement-product.ts +21 -9
- package/api/src/store/models/entitlement.ts +19 -10
- package/api/src/store/models/event.ts +18 -9
- package/api/src/store/models/exchange-rate-provider.ts +17 -4
- package/api/src/store/models/invoice-item.ts +18 -9
- package/api/src/store/models/invoice.ts +16 -8
- package/api/src/store/models/meter-event.ts +27 -9
- package/api/src/store/models/meter.ts +31 -22
- package/api/src/store/models/payment-currency.ts +25 -8
- package/api/src/store/models/payment-intent.ts +15 -6
- package/api/src/store/models/payment-link.ts +15 -6
- package/api/src/store/models/payment-method.ts +38 -22
- package/api/src/store/models/payment-stat.ts +18 -9
- package/api/src/store/models/payout.ts +15 -6
- package/api/src/store/models/price-quote.ts +17 -8
- package/api/src/store/models/price.ts +24 -12
- package/api/src/store/models/pricing-table.ts +29 -20
- package/api/src/store/models/product-vendor.ts +20 -10
- package/api/src/store/models/product.ts +15 -6
- package/api/src/store/models/promotion-code.ts +14 -6
- package/api/src/store/models/refund.ts +15 -6
- package/api/src/store/models/revenue-snapshot.ts +21 -9
- package/api/src/store/models/setting.ts +18 -9
- package/api/src/store/models/setup-intent.ts +36 -27
- package/api/src/store/models/subscription-item.ts +21 -9
- package/api/src/store/models/subscription-schedule.ts +21 -9
- package/api/src/store/models/subscription.ts +21 -10
- package/api/src/store/models/tax-rate.ts +29 -21
- package/api/src/store/models/usage-record.ts +11 -2
- package/api/src/store/models/webhook-attempt.ts +18 -9
- package/api/src/store/models/webhook-endpoint.ts +18 -9
- package/api/src/store/scoped-core.ts +55 -0
- package/api/src/store/scoped.ts +247 -0
- package/api/src/store/sequelize.ts +66 -22
- package/api/src/store/sql-migrations.ts +20 -0
- package/api/src/store/tenant-backfill.ts +260 -0
- package/api/src/store/tenant-model.ts +124 -0
- package/api/src/store/tenant-tables.ts +50 -0
- package/api/tests/embedded/embedded-multi-mode-d3.spec.ts +257 -0
- package/api/tests/fixtures/bare-query-violation.ts +13 -0
- package/api/tests/fixtures/core-env-violation.ts +10 -0
- package/api/tests/fixtures/host-read-violation.ts +19 -0
- package/api/tests/fixtures/tenants.ts +4 -0
- package/api/tests/integrations/iap-tenant.spec.ts +284 -0
- package/api/tests/libs/archive-query.spec.ts +26 -0
- package/api/tests/libs/audit-tenant.spec.ts +153 -0
- package/api/tests/libs/context.spec.ts +204 -0
- package/api/tests/libs/core-config.spec.ts +115 -0
- package/api/tests/libs/cron-driver-d2.spec.ts +237 -0
- package/api/tests/libs/crons-conservation-d2.spec.ts +52 -0
- package/api/tests/libs/lock-tenant.spec.ts +66 -0
- package/api/tests/libs/scoped.spec.ts +222 -0
- package/api/tests/libs/secrets-facade.spec.ts +52 -0
- package/api/tests/libs/tenancy-slot-authority.spec.ts +209 -0
- package/api/tests/libs/tenant-middleware.spec.ts +42 -0
- package/api/tests/libs/tenant-scanner.spec.ts +120 -0
- package/api/tests/middlewares/hono/cdn.spec.ts +70 -0
- package/api/tests/middlewares/hono/context.spec.ts +113 -0
- package/api/tests/middlewares/hono/csrf.spec.ts +136 -0
- package/api/tests/middlewares/hono/fallback.spec.ts +67 -0
- package/api/tests/middlewares/hono/pipeline.spec.ts +47 -0
- package/api/tests/middlewares/hono/security.spec.ts +181 -0
- package/api/tests/middlewares/hono/session.spec.ts +42 -0
- package/api/tests/middlewares/hono/xss.spec.ts +81 -0
- package/api/tests/models/tenant-backfill.spec.ts +287 -0
- package/api/tests/models/tenant-columns-model.spec.ts +46 -0
- package/api/tests/models/tenant-columns.spec.ts +161 -0
- package/api/tests/queues/credit-consume-batch.spec.ts +8 -1
- package/api/tests/queues/credit-consume.spec.ts +8 -1
- package/api/tests/queues/event-tenant.spec.ts +236 -0
- package/api/tests/queues/exchange-rate-health-tenant-d6.spec.ts +62 -0
- package/api/tests/queues/queue-parity.spec.ts +249 -0
- package/api/tests/queues/queue-runtime-surface.spec.ts +277 -0
- package/api/tests/queues/queue-teardown-d2.spec.ts +127 -0
- package/api/tests/queues/tenant-matrix-a.spec.ts +245 -0
- package/api/tests/queues/tenant-matrix-b.spec.ts +168 -0
- package/api/tests/routes/connect/hono-attach.spec.ts +107 -0
- package/api/tests/service/collapse.spec.ts +96 -0
- package/api/tests/store/tenant-crosscut.spec.ts +202 -0
- package/api/tests/store/tenant-model-spike.spec.ts +177 -0
- package/api/tests/store/tenant-model.spec.ts +162 -0
- package/api/tests/store/tenant-residual.spec.ts +196 -0
- package/api/third.d.ts +4 -0
- package/blocklet.yml +1 -1
- package/cloudflare/README.md +26 -6
- package/cloudflare/build.ts +28 -13
- package/cloudflare/did-connect-auth.ts +0 -217
- package/cloudflare/migrations/0006_tenant_columns.sql +46 -0
- package/cloudflare/migrations/0007_tenant_backfill_indexes.sql +65 -0
- package/cloudflare/migrations/0008_schema_parity.sql +16 -0
- package/cloudflare/migrations/0009_remove_did_space_jobs.sql +5 -0
- package/cloudflare/queue-runtime-mode.ts +13 -0
- package/cloudflare/run-build.js +10 -56
- package/cloudflare/shims/blocklet-sdk/asset-host-transformer.ts +20 -0
- package/cloudflare/shims/blocklet-sdk/config.ts +8 -1
- package/cloudflare/shims/blocklet-sdk/login.ts +12 -0
- package/cloudflare/shims/blocklet-sdk/service-api.ts +14 -0
- package/cloudflare/shims/blocklet-sdk/session.ts +4 -2
- package/cloudflare/shims/blocklet-sdk/util-constants.ts +8 -0
- package/cloudflare/shims/blocklet-sdk/util-csrf.ts +13 -0
- package/cloudflare/shims/blocklet-sdk/util-wallet.ts +8 -0
- package/cloudflare/shims/cron.ts +38 -158
- package/cloudflare/shims/events.ts +124 -0
- package/cloudflare/shims/fastq.ts +15 -1
- package/cloudflare/shims/nedb-storage.ts +16 -8
- package/cloudflare/shims/xss.ts +8 -0
- package/cloudflare/tenant-middleware.ts +36 -0
- package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
- package/cloudflare/tests/worker-handler-gate.spec.ts +44 -0
- package/cloudflare/worker.ts +204 -433
- package/cloudflare/wrangler.local-e2e.jsonc +26 -0
- package/jest.config.js +3 -1
- package/package.json +33 -38
- package/scripts/core-env-whitelist.json +1 -0
- package/scripts/e2e-12b-runtime.ts +149 -0
- package/scripts/e2e-core-config.ts +125 -0
- package/scripts/e2e-d1-tenancy.ts +116 -0
- package/scripts/e2e-d2-cron-queue.ts +139 -0
- package/scripts/e2e-d3-embedded-multi.ts +171 -0
- package/scripts/e2e-hono-s2.ts +125 -0
- package/scripts/e2e-hono-s3e.ts +135 -0
- package/scripts/e2e-hono-s4.ts +114 -0
- package/scripts/e2e-migration-contract.ts +100 -0
- package/scripts/e2e-s0.ts +61 -0
- package/scripts/e2e-s1.ts +107 -0
- package/scripts/e2e-s2.ts +178 -0
- package/scripts/e2e-s3.ts +110 -0
- package/scripts/e2e-s4.ts +191 -0
- package/scripts/e2e-s5.ts +139 -0
- package/scripts/e2e-s6.ts +127 -0
- package/scripts/e2e-tenant-model.ts +119 -0
- package/scripts/e2e-tenant-worker.ts +199 -0
- package/scripts/gen-sql-migrations.js +46 -0
- package/scripts/phase8-codemod.js +219 -0
- package/scripts/phase9a-env-getters-codemod.js +82 -0
- package/scripts/scan-core-env.js +109 -0
- package/scripts/scan-tenant-queries.js +235 -0
- package/scripts/schema-drift-guard.ts +210 -0
- package/scripts/tenant-scan-whitelist.json +1 -0
- package/src/env.d.ts +13 -1
- package/tsconfig.json +1 -1
- package/api/src/libs/did-space.ts +0 -235
- package/api/src/libs/middleware.ts +0 -50
- package/api/src/libs/security.ts +0 -192
- package/api/src/queues/space.ts +0 -662
- package/api/src/routes/credit-tokens.ts +0 -38
- package/api/src/routes/exchange-rates.ts +0 -87
- package/api/src/routes/index.ts +0 -142
- package/api/src/routes/integrations/stripe.ts +0 -61
- package/api/src/routes/meters.ts +0 -274
- package/api/src/routes/passports.ts +0 -68
- package/api/src/routes/redirect.ts +0 -20
- package/api/src/routes/tool.ts +0 -65
- package/api/src/routes/webhook-endpoints.ts +0 -126
- package/api/tests/routes/credit-grants.spec.ts +0 -1261
- package/cloudflare/shims/did-space-js.ts +0 -17
- package/cloudflare/shims/did-space.ts +0 -11
- package/cloudflare/shims/express-compat/index.ts +0 -80
- package/cloudflare/shims/express-compat/types.ts +0 -41
- package/cloudflare/shims/lock.ts +0 -115
- package/cloudflare/shims/queue.ts +0 -611
- package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +0 -87
- 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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 ||
|
|
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 ||
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 (
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
package/api/src/libs/audit.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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,
|
package/api/src/libs/auth.ts
CHANGED
|
@@ -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).
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
|
|
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 =
|
|
49
|
-
export const ethWallet: WalletObject =
|
|
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.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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 =
|
|
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
|
-
|
|
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: {
|