payment-kit 1.29.1 → 1.29.3
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 +47 -0
- package/api/src/crons/base.ts +3 -3
- package/api/src/crons/currency.ts +1 -1
- package/api/src/crons/index.ts +41 -37
- 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/crons/tenant-fanout.ts +82 -0
- package/api/src/host-node/did-connect-runtime-node.ts +33 -0
- package/api/src/host-node/serve-static-arc.ts +68 -0
- package/api/src/host-node/serve-static.ts +41 -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 +247 -47
- package/api/src/libs/context.ts +89 -1
- package/api/src/libs/currency.ts +2 -2
- package/api/src/libs/dayjs.ts +8 -2
- package/api/src/libs/did-connect/runtime-did-connect-js.ts +88 -0
- package/api/src/libs/did-connect/tenant-identity.ts +221 -0
- 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 +142 -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 +60 -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 +271 -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 +80 -0
- package/api/src/middlewares/hono/csrf.ts +83 -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 +209 -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 +38 -21
- 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 +41 -11
- 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 +64 -37
- package/api/src/queues/payout.ts +37 -21
- package/api/src/queues/refund.ts +36 -18
- 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} +199 -224
- 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} +98 -83
- 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 +814 -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 +82 -23
- 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/bootstrap/bootstrap.spec.ts +162 -0
- package/api/tests/crons/tenant-fanout.spec.ts +158 -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/did-connect-runtime-js.spec.ts +98 -0
- package/api/tests/libs/did-connect-tenant-identity.spec.ts +159 -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/service-host.spec.ts +37 -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 +292 -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/service/didconnect-storage-slot.spec.ts +60 -0
- package/api/tests/service/fail-closed-http.spec.ts +79 -0
- package/api/tests/service/static-arc-handler.spec.ts +101 -0
- package/api/tests/service/static-externalized.spec.ts +48 -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/MIGRATION-RUNBOOK.md +3 -8
- package/cloudflare/README.md +34 -27
- package/cloudflare/STAGING-MIGRATION-GUIDE.md +3 -15
- package/cloudflare/build.ts +33 -13
- package/cloudflare/cf-adapter.ts +419 -0
- package/cloudflare/did-connect-runtime.ts +96 -0
- package/cloudflare/did-connect-token-storage.ts +151 -0
- package/cloudflare/esbuild-cf-config.cjs +407 -0
- 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 +33 -403
- package/cloudflare/scripts/cf-package-import-probe.mjs +90 -0
- package/cloudflare/scripts/didconnect-mock-smoke.mjs +140 -0
- 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/wallet-authenticator.ts +16 -1
- package/cloudflare/shims/blocklet-sdk/wallet-handler.ts +18 -3
- 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/cf-adapter.spec.ts +244 -0
- package/cloudflare/tests/did-connect-token-storage.spec.ts +105 -0
- package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
- package/cloudflare/tests/worker-handler-gate.spec.ts +69 -0
- package/cloudflare/vite.config.ts +53 -45
- package/cloudflare/worker.ts +261 -448
- package/cloudflare/wrangler.json +0 -6
- package/cloudflare/wrangler.jsonc +0 -6
- package/cloudflare/wrangler.local-e2e.jsonc +25 -0
- package/cloudflare/wrangler.staging.json +0 -6
- package/jest.config.js +3 -1
- package/package.json +33 -38
- package/scripts/bootstrap-inject.ts +166 -0
- 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/app.tsx +2 -1
- package/src/env.d.ts +13 -1
- package/src/libs/service-host.ts +13 -0
- package/tsconfig.json +1 -1
- package/vite.arc.config.ts +159 -0
- 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/did-connect-auth.ts +0 -527
- 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
|
@@ -1,18 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Phase 3 (express→hono) — hono fork of routes/customers.ts. Sub-app with
|
|
2
|
+
// routes relative to /api/customers (mounted via mountResourceGroup). The
|
|
3
|
+
// business logic is unchanged; only the express plumbing becomes hono:
|
|
4
|
+
// req.body → c.get('sanitizedBody') ?? {}; res.status(n).json(x) → c.json(x, n).
|
|
5
|
+
import { Hono } from 'hono';
|
|
3
6
|
import Joi from 'joi';
|
|
4
7
|
import pick from 'lodash/pick';
|
|
5
|
-
|
|
8
|
+
// Use the CommonJS build (`lib`), not the ESM `es` build: `es/lib/isEmail.js`
|
|
9
|
+
// uses extensionless relative imports (`./util/assertString`) that Node ESM
|
|
10
|
+
// strict resolution rejects with ERR_MODULE_NOT_FOUND when payment-core is
|
|
11
|
+
// embedded in a Node host like arc. The `lib` build is plain CJS and resolves
|
|
12
|
+
// everywhere. Same default export.
|
|
13
|
+
import isEmail from 'validator/lib/isEmail';
|
|
6
14
|
|
|
7
15
|
import { Op, type WhereOptions } from 'sequelize';
|
|
8
16
|
import { BN } from '@ocap/util';
|
|
9
|
-
import { getStakeSummaryByDid, getTokenSummaryByDid, getTokenByAddress } from '
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
import { getStakeSummaryByDid, getTokenSummaryByDid, getTokenByAddress } from '../../integrations/arcblock/stake';
|
|
18
|
+
import {
|
|
19
|
+
createListParamSchema,
|
|
20
|
+
getOrder,
|
|
21
|
+
getWhereFromKvQuery,
|
|
22
|
+
getWhereFromQuery,
|
|
23
|
+
MetadataSchema,
|
|
24
|
+
} from '../../libs/api';
|
|
25
|
+
import { authenticate } from '../../middlewares/hono/security';
|
|
26
|
+
import { formatMetadata } from '../../libs/util';
|
|
27
|
+
import { Customer } from '../../store/models/customer';
|
|
28
|
+
import { blocklet } from '../../libs/auth';
|
|
29
|
+
import logger from '../../libs/logger';
|
|
16
30
|
import {
|
|
17
31
|
Invoice,
|
|
18
32
|
PaymentCurrency,
|
|
@@ -21,14 +35,13 @@ import {
|
|
|
21
35
|
Product,
|
|
22
36
|
Subscription,
|
|
23
37
|
SubscriptionItem,
|
|
24
|
-
} from '
|
|
25
|
-
import { getSubscriptionPaymentAddress, calculateRecommendedRechargeAmount } from '
|
|
26
|
-
import { expandLineItems } from '
|
|
27
|
-
import { handleNotificationPreferenceChange } from '
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const router = Router();
|
|
38
|
+
} from '../../store/models';
|
|
39
|
+
import { getSubscriptionPaymentAddress, calculateRecommendedRechargeAmount } from '../../libs/subscription';
|
|
40
|
+
import { expandLineItems } from '../../libs/session';
|
|
41
|
+
import { handleNotificationPreferenceChange } from '../../queues/notification';
|
|
42
|
+
import { sessionMiddleware } from '../../middlewares/hono/session';
|
|
43
|
+
|
|
44
|
+
const app = new Hono();
|
|
32
45
|
const auth = authenticate<Customer>({ component: true, roles: ['owner', 'admin'] });
|
|
33
46
|
const authPortal = authenticate<Customer>({
|
|
34
47
|
component: true,
|
|
@@ -42,8 +55,8 @@ const authPortal = authenticate<Customer>({
|
|
|
42
55
|
});
|
|
43
56
|
|
|
44
57
|
const schema = createListParamSchema<{ did?: string }>({ did: Joi.string().empty('') });
|
|
45
|
-
|
|
46
|
-
const { page, pageSize, ...query } = await schema.validateAsync(req.query, { stripUnknown: true });
|
|
58
|
+
app.get('/', auth, async (c) => {
|
|
59
|
+
const { page, pageSize, ...query } = await schema.validateAsync(c.req.query(), { stripUnknown: true });
|
|
47
60
|
const where = getWhereFromKvQuery(query.q);
|
|
48
61
|
|
|
49
62
|
if (query.did) {
|
|
@@ -59,21 +72,23 @@ router.get('/', auth, async (req, res) => {
|
|
|
59
72
|
include: [],
|
|
60
73
|
});
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
return c.json({ count, list, paging: { page, pageSize } });
|
|
63
76
|
} catch (err) {
|
|
64
77
|
logger.error(err);
|
|
65
|
-
|
|
78
|
+
return c.json({ count: 0, list: [], paging: { page, pageSize } });
|
|
66
79
|
}
|
|
67
80
|
});
|
|
68
81
|
|
|
69
82
|
// search customers
|
|
83
|
+
// Static path — registered before /:id so hono matches it first.
|
|
70
84
|
const searchSchema = createListParamSchema<{
|
|
71
85
|
query: string;
|
|
72
86
|
}>({
|
|
73
87
|
query: Joi.string(),
|
|
74
88
|
});
|
|
75
|
-
|
|
76
|
-
const
|
|
89
|
+
app.get('/search', auth, async (c) => {
|
|
90
|
+
const rawQuery = c.req.query();
|
|
91
|
+
const { page, pageSize, query, livemode, q, o } = await searchSchema.validateAsync(rawQuery, {
|
|
77
92
|
stripUnknown: false,
|
|
78
93
|
allowUnknown: true,
|
|
79
94
|
});
|
|
@@ -84,35 +99,35 @@ router.get('/search', auth, async (req, res) => {
|
|
|
84
99
|
}
|
|
85
100
|
const { rows: list, count } = await Customer.findAndCountAll({
|
|
86
101
|
where,
|
|
87
|
-
order: getOrder(
|
|
102
|
+
order: getOrder(rawQuery, [['created_at', o === 'asc' ? 'ASC' : 'DESC']]),
|
|
88
103
|
offset: (page - 1) * pageSize,
|
|
89
104
|
limit: pageSize,
|
|
90
105
|
include: [],
|
|
91
106
|
});
|
|
92
107
|
|
|
93
|
-
|
|
108
|
+
return c.json({ count, list, paging: { page, pageSize } });
|
|
94
109
|
});
|
|
95
110
|
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
if (!
|
|
99
|
-
return
|
|
111
|
+
// Static path — registered before /:id so hono matches it first.
|
|
112
|
+
app.get('/me', sessionMiddleware({ accessKey: true }), async (c) => {
|
|
113
|
+
if (!c.get('user')) {
|
|
114
|
+
return c.json({ error: 'Unauthorized' }, 403);
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
try {
|
|
103
|
-
let doc = await Customer.findByPkOrDid(
|
|
104
|
-
const livemode = req.query
|
|
118
|
+
let doc = await Customer.findByPkOrDid(c.get('user').did as string);
|
|
119
|
+
const livemode = c.req.query('livemode') ? !!c.get('livemode') : !!doc?.livemode;
|
|
105
120
|
if (!doc) {
|
|
106
|
-
if (req.query
|
|
107
|
-
const result = await blocklet.getUser(
|
|
108
|
-
return
|
|
121
|
+
if (c.req.query('fallback')) {
|
|
122
|
+
const result = await blocklet.getUser(c.get('user').did);
|
|
123
|
+
return c.json({ ...result.user, address: Customer.formatAddressFromUser(result.user), livemode });
|
|
109
124
|
}
|
|
110
|
-
if (req.query
|
|
125
|
+
if (c.req.query('create')) {
|
|
111
126
|
// create customer
|
|
112
|
-
const { user } = await blocklet.getUser(
|
|
127
|
+
const { user } = await blocklet.getUser(c.get('user').did);
|
|
113
128
|
const customer = await Customer.create({
|
|
114
129
|
livemode: true,
|
|
115
|
-
did:
|
|
130
|
+
did: c.get('user').did,
|
|
116
131
|
name: user.fullName,
|
|
117
132
|
email: user.email,
|
|
118
133
|
phone: user.phone,
|
|
@@ -130,11 +145,11 @@ router.get('/me', sessionMiddleware({ accessKey: true }), async (req, res) => {
|
|
|
130
145
|
});
|
|
131
146
|
doc = customer;
|
|
132
147
|
} else {
|
|
133
|
-
return
|
|
148
|
+
return c.json({ error: 'Customer not found' });
|
|
134
149
|
}
|
|
135
150
|
}
|
|
136
|
-
if (req.query
|
|
137
|
-
return
|
|
151
|
+
if (c.req.query('skipSummary')) {
|
|
152
|
+
return c.json({ ...doc.toJSON(), livemode });
|
|
138
153
|
}
|
|
139
154
|
try {
|
|
140
155
|
const [summary, stake, token] = await Promise.all([
|
|
@@ -142,11 +157,11 @@ router.get('/me', sessionMiddleware({ accessKey: true }), async (req, res) => {
|
|
|
142
157
|
getStakeSummaryByDid(doc.did, livemode),
|
|
143
158
|
getTokenSummaryByDid(doc.did, livemode),
|
|
144
159
|
]);
|
|
145
|
-
|
|
160
|
+
return c.json({ ...doc.toJSON(), summary: { ...summary, stake, token }, livemode });
|
|
146
161
|
} catch (summaryErr) {
|
|
147
162
|
logger.error('get customer summary failed', summaryErr);
|
|
148
|
-
if (req.query
|
|
149
|
-
return
|
|
163
|
+
if (c.req.query('skipError')) {
|
|
164
|
+
return c.json({
|
|
150
165
|
...doc.toJSON(),
|
|
151
166
|
summary: { stake: {}, token: {} },
|
|
152
167
|
livemode,
|
|
@@ -158,147 +173,42 @@ router.get('/me', sessionMiddleware({ accessKey: true }), async (req, res) => {
|
|
|
158
173
|
}
|
|
159
174
|
} catch (err) {
|
|
160
175
|
logger.error('get customer failed', err);
|
|
161
|
-
if (req.query
|
|
162
|
-
return
|
|
176
|
+
if (c.req.query('skipError')) {
|
|
177
|
+
return c.json({
|
|
163
178
|
error: `Failed to get customer: ${err.message}`,
|
|
164
|
-
did:
|
|
165
|
-
name:
|
|
179
|
+
did: c.get('user')?.did,
|
|
180
|
+
name: c.get('user')?.fullName,
|
|
166
181
|
address: {},
|
|
167
|
-
livemode: !!req.query
|
|
182
|
+
livemode: !!c.req.query('livemode'),
|
|
168
183
|
summary: { stake: {}, token: {} },
|
|
169
184
|
});
|
|
170
185
|
}
|
|
171
|
-
return
|
|
186
|
+
return c.json({ error: `Failed to get customer: ${err.message}` }, 500);
|
|
172
187
|
}
|
|
173
188
|
});
|
|
174
189
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
190
|
+
// Static path — registered before /:id so hono matches it first.
|
|
191
|
+
app.get('/recharge', sessionMiddleware({ accessKey: true }), async (c) => {
|
|
192
|
+
if (!c.get('user')) {
|
|
193
|
+
return c.json({ error: 'Unauthorized' }, 403);
|
|
178
194
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const jobId = `space-${userDid}`;
|
|
182
|
-
const { endpoint } = await getEndpointAndSpaceDid(userDid);
|
|
183
|
-
if (endpoint) {
|
|
184
|
-
const mainTask = await spaceQueue.get(jobId);
|
|
185
|
-
if (mainTask) {
|
|
186
|
-
return res.json({
|
|
187
|
-
success: true,
|
|
188
|
-
message: 'Billing data sync already in progress',
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
spaceQueue.push({
|
|
192
|
-
id: jobId,
|
|
193
|
-
job: {
|
|
194
|
-
type: 'customer',
|
|
195
|
-
data: {
|
|
196
|
-
id: userDid,
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
delay: 60, // delay 1min
|
|
200
|
-
});
|
|
201
|
-
logger.info('Queued billing sync to DID Space for user:', { did: req.user.did });
|
|
202
|
-
|
|
203
|
-
return res.json({
|
|
204
|
-
success: true,
|
|
205
|
-
message: 'Billing data sync will start soon',
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
return res.json({
|
|
209
|
-
success: false,
|
|
210
|
-
message: 'No endpoint found for the user',
|
|
211
|
-
});
|
|
212
|
-
} catch (error) {
|
|
213
|
-
return res.json({
|
|
214
|
-
success: false,
|
|
215
|
-
message: error.message,
|
|
216
|
-
});
|
|
195
|
+
if (!c.req.query('currencyId')) {
|
|
196
|
+
return c.json({ error: 'Currency ID is required' }, 400);
|
|
217
197
|
}
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// get overdue invoices
|
|
221
|
-
router.get('/:id/overdue/invoices', authPortal, async (req, res) => {
|
|
222
198
|
try {
|
|
223
|
-
const
|
|
224
|
-
if (!doc) {
|
|
225
|
-
return res.status(404).json({ error: 'Customer not found' });
|
|
226
|
-
}
|
|
227
|
-
const where: WhereOptions<Invoice> = {
|
|
228
|
-
customer_id: doc.id,
|
|
229
|
-
status: ['uncollectible'],
|
|
230
|
-
amount_remaining: { [Op.gt]: '0' },
|
|
231
|
-
};
|
|
232
|
-
if (typeof req.livemode === 'boolean') {
|
|
233
|
-
where.livemode = !!req.livemode;
|
|
234
|
-
}
|
|
235
|
-
const { rows: invoices, count } = await Invoice.findAndCountAll({
|
|
236
|
-
where,
|
|
237
|
-
include: [
|
|
238
|
-
{ model: PaymentCurrency, as: 'paymentCurrency' },
|
|
239
|
-
{ model: PaymentMethod, as: 'paymentMethod' },
|
|
240
|
-
],
|
|
241
|
-
});
|
|
242
|
-
if (count === 0) {
|
|
243
|
-
return res.json({
|
|
244
|
-
summary: null,
|
|
245
|
-
invoices: [],
|
|
246
|
-
subscriptionCount: 0,
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
const summary: Record<string, { amount: string; currency: PaymentCurrency; method: PaymentMethod }> = {};
|
|
250
|
-
invoices.forEach((invoice) => {
|
|
251
|
-
const key = invoice.currency_id;
|
|
252
|
-
if (!summary[key]) {
|
|
253
|
-
summary[key] = {
|
|
254
|
-
amount: '0',
|
|
255
|
-
// @ts-ignore
|
|
256
|
-
currency: invoice.paymentCurrency,
|
|
257
|
-
// @ts-ignore
|
|
258
|
-
method: invoice.paymentMethod,
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
if (invoice && summary[key]) {
|
|
262
|
-
// @ts-ignore
|
|
263
|
-
summary[key].amount = new BN(summary[key]?.amount || '0')
|
|
264
|
-
.add(new BN(invoice.amount_remaining || '0'))
|
|
265
|
-
.toString();
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
const subscriptionCount = new Set(invoices.map((x) => x.subscription_id)).size;
|
|
269
|
-
return res.json({
|
|
270
|
-
summary,
|
|
271
|
-
invoices,
|
|
272
|
-
subscriptionCount,
|
|
273
|
-
customer: doc,
|
|
274
|
-
});
|
|
275
|
-
} catch (err) {
|
|
276
|
-
logger.error(err);
|
|
277
|
-
return res.status(500).json({ error: `Failed to get overdue invoices: ${err.message}` });
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
router.get('/recharge', sessionMiddleware({ accessKey: true }), async (req, res) => {
|
|
282
|
-
if (!req.user) {
|
|
283
|
-
return res.status(403).json({ error: 'Unauthorized' });
|
|
284
|
-
}
|
|
285
|
-
if (!req.query.currencyId) {
|
|
286
|
-
return res.status(400).json({ error: 'Currency ID is required' });
|
|
287
|
-
}
|
|
288
|
-
try {
|
|
289
|
-
const customer = await Customer.findByPkOrDid(req.user.did as string);
|
|
199
|
+
const customer = await Customer.findByPkOrDid(c.get('user').did as string);
|
|
290
200
|
if (!customer) {
|
|
291
|
-
return
|
|
201
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
292
202
|
}
|
|
293
203
|
|
|
294
|
-
const paymentCurrency = await PaymentCurrency.findByPk(req.query
|
|
204
|
+
const paymentCurrency = await PaymentCurrency.findByPk(c.req.query('currencyId') as string);
|
|
295
205
|
if (!paymentCurrency) {
|
|
296
|
-
return
|
|
206
|
+
return c.json({ error: 'Currency not found' }, 404);
|
|
297
207
|
}
|
|
298
208
|
|
|
299
209
|
const paymentMethod = await PaymentMethod.findByPk(paymentCurrency.payment_method_id);
|
|
300
210
|
if (!paymentMethod) {
|
|
301
|
-
return
|
|
211
|
+
return c.json({ error: 'Payment method not found' }, 404);
|
|
302
212
|
}
|
|
303
213
|
|
|
304
214
|
let subscriptions = await Subscription.findAll({
|
|
@@ -338,7 +248,7 @@ router.get('/recharge', sessionMiddleware({ accessKey: true }), async (req, res)
|
|
|
338
248
|
// Calculate recommended recharge cycle and amount
|
|
339
249
|
const recommendedRecharge = calculateRecommendedRechargeAmount(relatedSubscriptions, paymentCurrency.id);
|
|
340
250
|
|
|
341
|
-
return
|
|
251
|
+
return c.json({
|
|
342
252
|
currency: {
|
|
343
253
|
...paymentCurrency.toJSON(),
|
|
344
254
|
paymentMethod,
|
|
@@ -348,79 +258,142 @@ router.get('/recharge', sessionMiddleware({ accessKey: true }), async (req, res)
|
|
|
348
258
|
});
|
|
349
259
|
} catch (err) {
|
|
350
260
|
logger.error('Error getting balance recharge info', err);
|
|
351
|
-
return
|
|
261
|
+
return c.json({ error: err.message }, 500);
|
|
352
262
|
}
|
|
353
263
|
});
|
|
354
264
|
|
|
355
265
|
// get address token
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
266
|
+
// Static path — registered before /:id so hono matches it first.
|
|
267
|
+
app.get('/payer-token', sessionMiddleware({ accessKey: true }), async (c) => {
|
|
268
|
+
if (!c.get('user')) {
|
|
269
|
+
return c.json({ error: 'Unauthorized' }, 403);
|
|
359
270
|
}
|
|
360
|
-
if (!req.query
|
|
361
|
-
return
|
|
271
|
+
if (!c.req.query('currencyId')) {
|
|
272
|
+
return c.json({ error: 'Currency ID is required' }, 400);
|
|
362
273
|
}
|
|
363
274
|
try {
|
|
364
|
-
let paymentAddress = req.query
|
|
275
|
+
let paymentAddress = c.req.query('payerAddress') as string;
|
|
365
276
|
let customer: Customer | null = null;
|
|
366
277
|
if (!paymentAddress) {
|
|
367
|
-
customer = await Customer.findByPkOrDid(
|
|
278
|
+
customer = await Customer.findByPkOrDid(c.get('user').did as string);
|
|
368
279
|
if (!customer) {
|
|
369
|
-
return
|
|
280
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
370
281
|
}
|
|
371
282
|
paymentAddress = customer.did;
|
|
372
283
|
}
|
|
373
284
|
|
|
374
|
-
const paymentCurrency = await PaymentCurrency.findByPk(req.query
|
|
285
|
+
const paymentCurrency = await PaymentCurrency.findByPk(c.req.query('currencyId') as string);
|
|
375
286
|
if (!paymentCurrency) {
|
|
376
|
-
return
|
|
287
|
+
return c.json({ error: 'Currency not found' }, 404);
|
|
377
288
|
}
|
|
378
289
|
|
|
379
290
|
const paymentMethod = await PaymentMethod.findByPk(paymentCurrency.payment_method_id);
|
|
380
291
|
if (!paymentMethod) {
|
|
381
|
-
return
|
|
292
|
+
return c.json({ error: 'Payment method not found' }, 404);
|
|
382
293
|
}
|
|
383
294
|
|
|
384
295
|
if (!['arcblock', 'ethereum', 'base'].includes(paymentMethod.type)) {
|
|
385
|
-
return
|
|
296
|
+
return c.json({ error: `Payment method not supported: ${paymentMethod.type}` }, 400);
|
|
386
297
|
}
|
|
387
298
|
|
|
388
299
|
if (!paymentAddress) {
|
|
389
|
-
return
|
|
300
|
+
return c.json({ error: `Payment address not found for customer: ${customer?.id}` }, 400);
|
|
390
301
|
}
|
|
391
302
|
|
|
392
303
|
const token = await getTokenByAddress(paymentAddress, paymentMethod, paymentCurrency);
|
|
393
|
-
return
|
|
304
|
+
return c.json({ token, paymentAddress });
|
|
394
305
|
} catch (err) {
|
|
395
306
|
logger.error('Error getting customer payer token', err);
|
|
396
|
-
return
|
|
307
|
+
return c.json({ error: err.message }, 500);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// get overdue invoices — /:id/overdue/invoices registered before plain /:id
|
|
312
|
+
app.get('/:id/overdue/invoices', authPortal, async (c) => {
|
|
313
|
+
try {
|
|
314
|
+
const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
|
|
315
|
+
if (!doc) {
|
|
316
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
317
|
+
}
|
|
318
|
+
const where: WhereOptions<Invoice> = {
|
|
319
|
+
customer_id: doc.id,
|
|
320
|
+
status: ['uncollectible'],
|
|
321
|
+
amount_remaining: { [Op.gt]: '0' },
|
|
322
|
+
};
|
|
323
|
+
if (typeof c.get('livemode') === 'boolean') {
|
|
324
|
+
where.livemode = !!c.get('livemode');
|
|
325
|
+
}
|
|
326
|
+
const { rows: invoices, count } = await Invoice.findAndCountAll({
|
|
327
|
+
where,
|
|
328
|
+
include: [
|
|
329
|
+
{ model: PaymentCurrency, as: 'paymentCurrency' },
|
|
330
|
+
{ model: PaymentMethod, as: 'paymentMethod' },
|
|
331
|
+
],
|
|
332
|
+
});
|
|
333
|
+
if (count === 0) {
|
|
334
|
+
return c.json({
|
|
335
|
+
summary: null,
|
|
336
|
+
invoices: [],
|
|
337
|
+
subscriptionCount: 0,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const summary: Record<string, { amount: string; currency: PaymentCurrency; method: PaymentMethod }> = {};
|
|
341
|
+
invoices.forEach((invoice) => {
|
|
342
|
+
const key = invoice.currency_id;
|
|
343
|
+
if (!summary[key]) {
|
|
344
|
+
summary[key] = {
|
|
345
|
+
amount: '0',
|
|
346
|
+
// @ts-ignore
|
|
347
|
+
currency: invoice.paymentCurrency,
|
|
348
|
+
// @ts-ignore
|
|
349
|
+
method: invoice.paymentMethod,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
if (invoice && summary[key]) {
|
|
353
|
+
// @ts-ignore
|
|
354
|
+
summary[key].amount = new BN(summary[key]?.amount || '0')
|
|
355
|
+
.add(new BN(invoice.amount_remaining || '0'))
|
|
356
|
+
.toString();
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
const subscriptionCount = new Set(invoices.map((x) => x.subscription_id)).size;
|
|
360
|
+
return c.json({
|
|
361
|
+
summary,
|
|
362
|
+
invoices,
|
|
363
|
+
subscriptionCount,
|
|
364
|
+
customer: doc,
|
|
365
|
+
});
|
|
366
|
+
} catch (err) {
|
|
367
|
+
logger.error(err);
|
|
368
|
+
return c.json({ error: `Failed to get overdue invoices: ${err.message}` }, 500);
|
|
397
369
|
}
|
|
398
370
|
});
|
|
399
371
|
|
|
400
|
-
|
|
401
|
-
if (!req.
|
|
402
|
-
return
|
|
372
|
+
app.get('/:id', auth, async (c) => {
|
|
373
|
+
if (!c.req.param('id')) {
|
|
374
|
+
return c.json({ error: 'Customer ID is required' }, 400);
|
|
403
375
|
}
|
|
404
376
|
try {
|
|
405
|
-
const doc = await Customer.findByPkOrDid(req.
|
|
377
|
+
const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
|
|
406
378
|
if (doc) {
|
|
407
|
-
return
|
|
379
|
+
return c.json(doc);
|
|
408
380
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
381
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
382
|
+
if (body.create) {
|
|
383
|
+
if (!c.get('user')) {
|
|
384
|
+
return c.json({ error: 'Unauthorized' }, 403);
|
|
412
385
|
}
|
|
413
|
-
if (req.
|
|
414
|
-
return
|
|
386
|
+
if (c.req.param('id').startsWith('cus_')) {
|
|
387
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
415
388
|
}
|
|
416
|
-
const { user } = await blocklet.getUser(req.
|
|
389
|
+
const { user } = await blocklet.getUser(c.req.param('id'));
|
|
417
390
|
if (!user) {
|
|
418
|
-
return
|
|
391
|
+
return c.json({ error: 'User not found' }, 404);
|
|
419
392
|
}
|
|
420
393
|
const customer = await Customer.create({
|
|
421
394
|
livemode: true,
|
|
422
|
-
did: user?.did || req.
|
|
423
|
-
name: user?.fullName ?? req.
|
|
395
|
+
did: user?.did || c.req.param('id'),
|
|
396
|
+
name: user?.fullName ?? c.req.param('id'),
|
|
424
397
|
email: user?.email ?? '',
|
|
425
398
|
phone: user?.phone ?? '',
|
|
426
399
|
address: Customer.formatAddressFromUser(user),
|
|
@@ -435,21 +408,20 @@ router.get('/:id', auth, async (req, res) => {
|
|
|
435
408
|
customerId: customer.id,
|
|
436
409
|
did: customer.did,
|
|
437
410
|
});
|
|
438
|
-
return
|
|
411
|
+
return c.json(customer);
|
|
439
412
|
}
|
|
440
|
-
return
|
|
413
|
+
return c.json(null, 404);
|
|
441
414
|
} catch (err) {
|
|
442
415
|
logger.error(err);
|
|
443
|
-
return
|
|
416
|
+
return c.json({ error: `Failed to get customer: ${err.message}` }, 500);
|
|
444
417
|
}
|
|
445
418
|
});
|
|
446
419
|
|
|
447
|
-
|
|
420
|
+
app.get('/:id/summary', auth, async (c) => {
|
|
448
421
|
try {
|
|
449
|
-
const doc = await Customer.findByPkOrDid(req.
|
|
422
|
+
const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
|
|
450
423
|
if (!doc) {
|
|
451
|
-
|
|
452
|
-
return;
|
|
424
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
453
425
|
}
|
|
454
426
|
|
|
455
427
|
const [summary, stake, token] = await Promise.all([
|
|
@@ -457,10 +429,10 @@ router.get('/:id/summary', auth, async (req, res) => {
|
|
|
457
429
|
getStakeSummaryByDid(doc.did, doc.livemode),
|
|
458
430
|
getTokenSummaryByDid(doc.did, doc.livemode),
|
|
459
431
|
]);
|
|
460
|
-
|
|
432
|
+
return c.json({ ...summary, stake, token });
|
|
461
433
|
} catch (err) {
|
|
462
434
|
logger.error(err);
|
|
463
|
-
|
|
435
|
+
return c.json(null);
|
|
464
436
|
}
|
|
465
437
|
});
|
|
466
438
|
|
|
@@ -484,20 +456,22 @@ const updatePreferenceSchema = Joi.object({
|
|
|
484
456
|
}).optional(),
|
|
485
457
|
}).unknown(false);
|
|
486
458
|
|
|
487
|
-
|
|
459
|
+
// Static path — registered before PUT /:id so hono matches it first.
|
|
460
|
+
app.put('/preference', sessionMiddleware({ accessKey: true }), async (c) => {
|
|
488
461
|
try {
|
|
489
|
-
if (!
|
|
490
|
-
return
|
|
462
|
+
if (!c.get('user')) {
|
|
463
|
+
return c.json({ error: 'Unauthorized' }, 403);
|
|
491
464
|
}
|
|
492
465
|
|
|
493
|
-
const doc = await Customer.findByPkOrDid(
|
|
466
|
+
const doc = await Customer.findByPkOrDid(c.get('user').did as string);
|
|
494
467
|
if (!doc) {
|
|
495
|
-
return
|
|
468
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
496
469
|
}
|
|
497
470
|
|
|
498
|
-
const
|
|
471
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
472
|
+
const { error, value } = updatePreferenceSchema.validate(body);
|
|
499
473
|
if (error) {
|
|
500
|
-
return
|
|
474
|
+
return c.json({ error: error.message }, 400);
|
|
501
475
|
}
|
|
502
476
|
|
|
503
477
|
// Get old preference before update
|
|
@@ -514,10 +488,10 @@ router.put('/preference', sessionMiddleware({ accessKey: true }), async (req, re
|
|
|
514
488
|
await handleNotificationPreferenceChange(doc.id, value.notification);
|
|
515
489
|
}
|
|
516
490
|
|
|
517
|
-
return
|
|
491
|
+
return c.json(doc);
|
|
518
492
|
} catch (err) {
|
|
519
493
|
logger.error('Failed to update customer preference', err);
|
|
520
|
-
return
|
|
494
|
+
return c.json({ error: `Failed to update preference: ${err.message}` }, 400);
|
|
521
495
|
}
|
|
522
496
|
});
|
|
523
497
|
|
|
@@ -542,29 +516,30 @@ const updateCustomerSchema = Joi.object({
|
|
|
542
516
|
postal_code: Joi.string().max(20).empty(''),
|
|
543
517
|
}).empty(''),
|
|
544
518
|
}).unknown(true);
|
|
545
|
-
|
|
546
|
-
|
|
519
|
+
|
|
520
|
+
app.put('/:id', authPortal, async (c) => {
|
|
547
521
|
try {
|
|
548
|
-
const doc = await Customer.findByPkOrDid(req.
|
|
522
|
+
const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
|
|
549
523
|
if (!doc) {
|
|
550
|
-
return
|
|
524
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
551
525
|
}
|
|
552
526
|
|
|
553
|
-
const
|
|
527
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
528
|
+
const raw = pick(body, ['metadata', 'name', 'email', 'phone', 'address']);
|
|
554
529
|
const { error } = updateCustomerSchema.validate(raw);
|
|
555
530
|
if (error) {
|
|
556
|
-
return
|
|
531
|
+
return c.json({ error: error.message }, 400);
|
|
557
532
|
}
|
|
558
533
|
if (raw.metadata) {
|
|
559
534
|
raw.metadata = formatMetadata(raw.metadata);
|
|
560
535
|
}
|
|
561
536
|
|
|
562
537
|
await doc.update(raw);
|
|
563
|
-
|
|
538
|
+
return c.json(doc);
|
|
564
539
|
} catch (err) {
|
|
565
540
|
logger.error(err);
|
|
566
|
-
|
|
541
|
+
return c.json(null);
|
|
567
542
|
}
|
|
568
543
|
});
|
|
569
544
|
|
|
570
|
-
export default
|
|
545
|
+
export default app;
|