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
|
@@ -1,18 +1,27 @@
|
|
|
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
|
import isEmail from 'validator/es/lib/isEmail';
|
|
6
9
|
|
|
7
10
|
import { Op, type WhereOptions } from 'sequelize';
|
|
8
11
|
import { BN } from '@ocap/util';
|
|
9
|
-
import { getStakeSummaryByDid, getTokenSummaryByDid, getTokenByAddress } from '
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
import { getStakeSummaryByDid, getTokenSummaryByDid, getTokenByAddress } from '../../integrations/arcblock/stake';
|
|
13
|
+
import {
|
|
14
|
+
createListParamSchema,
|
|
15
|
+
getOrder,
|
|
16
|
+
getWhereFromKvQuery,
|
|
17
|
+
getWhereFromQuery,
|
|
18
|
+
MetadataSchema,
|
|
19
|
+
} from '../../libs/api';
|
|
20
|
+
import { authenticate } from '../../middlewares/hono/security';
|
|
21
|
+
import { formatMetadata } from '../../libs/util';
|
|
22
|
+
import { Customer } from '../../store/models/customer';
|
|
23
|
+
import { blocklet } from '../../libs/auth';
|
|
24
|
+
import logger from '../../libs/logger';
|
|
16
25
|
import {
|
|
17
26
|
Invoice,
|
|
18
27
|
PaymentCurrency,
|
|
@@ -21,14 +30,13 @@ import {
|
|
|
21
30
|
Product,
|
|
22
31
|
Subscription,
|
|
23
32
|
SubscriptionItem,
|
|
24
|
-
} from '
|
|
25
|
-
import { getSubscriptionPaymentAddress, calculateRecommendedRechargeAmount } from '
|
|
26
|
-
import { expandLineItems } from '
|
|
27
|
-
import { handleNotificationPreferenceChange } from '
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const router = Router();
|
|
33
|
+
} from '../../store/models';
|
|
34
|
+
import { getSubscriptionPaymentAddress, calculateRecommendedRechargeAmount } from '../../libs/subscription';
|
|
35
|
+
import { expandLineItems } from '../../libs/session';
|
|
36
|
+
import { handleNotificationPreferenceChange } from '../../queues/notification';
|
|
37
|
+
import { sessionMiddleware } from '../../middlewares/hono/session';
|
|
38
|
+
|
|
39
|
+
const app = new Hono();
|
|
32
40
|
const auth = authenticate<Customer>({ component: true, roles: ['owner', 'admin'] });
|
|
33
41
|
const authPortal = authenticate<Customer>({
|
|
34
42
|
component: true,
|
|
@@ -42,8 +50,8 @@ const authPortal = authenticate<Customer>({
|
|
|
42
50
|
});
|
|
43
51
|
|
|
44
52
|
const schema = createListParamSchema<{ did?: string }>({ did: Joi.string().empty('') });
|
|
45
|
-
|
|
46
|
-
const { page, pageSize, ...query } = await schema.validateAsync(req.query, { stripUnknown: true });
|
|
53
|
+
app.get('/', auth, async (c) => {
|
|
54
|
+
const { page, pageSize, ...query } = await schema.validateAsync(c.req.query(), { stripUnknown: true });
|
|
47
55
|
const where = getWhereFromKvQuery(query.q);
|
|
48
56
|
|
|
49
57
|
if (query.did) {
|
|
@@ -59,21 +67,23 @@ router.get('/', auth, async (req, res) => {
|
|
|
59
67
|
include: [],
|
|
60
68
|
});
|
|
61
69
|
|
|
62
|
-
|
|
70
|
+
return c.json({ count, list, paging: { page, pageSize } });
|
|
63
71
|
} catch (err) {
|
|
64
72
|
logger.error(err);
|
|
65
|
-
|
|
73
|
+
return c.json({ count: 0, list: [], paging: { page, pageSize } });
|
|
66
74
|
}
|
|
67
75
|
});
|
|
68
76
|
|
|
69
77
|
// search customers
|
|
78
|
+
// Static path — registered before /:id so hono matches it first.
|
|
70
79
|
const searchSchema = createListParamSchema<{
|
|
71
80
|
query: string;
|
|
72
81
|
}>({
|
|
73
82
|
query: Joi.string(),
|
|
74
83
|
});
|
|
75
|
-
|
|
76
|
-
const
|
|
84
|
+
app.get('/search', auth, async (c) => {
|
|
85
|
+
const rawQuery = c.req.query();
|
|
86
|
+
const { page, pageSize, query, livemode, q, o } = await searchSchema.validateAsync(rawQuery, {
|
|
77
87
|
stripUnknown: false,
|
|
78
88
|
allowUnknown: true,
|
|
79
89
|
});
|
|
@@ -84,35 +94,35 @@ router.get('/search', auth, async (req, res) => {
|
|
|
84
94
|
}
|
|
85
95
|
const { rows: list, count } = await Customer.findAndCountAll({
|
|
86
96
|
where,
|
|
87
|
-
order: getOrder(
|
|
97
|
+
order: getOrder(rawQuery, [['created_at', o === 'asc' ? 'ASC' : 'DESC']]),
|
|
88
98
|
offset: (page - 1) * pageSize,
|
|
89
99
|
limit: pageSize,
|
|
90
100
|
include: [],
|
|
91
101
|
});
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
return c.json({ count, list, paging: { page, pageSize } });
|
|
94
104
|
});
|
|
95
105
|
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
if (!
|
|
99
|
-
return
|
|
106
|
+
// Static path — registered before /:id so hono matches it first.
|
|
107
|
+
app.get('/me', sessionMiddleware({ accessKey: true }), async (c) => {
|
|
108
|
+
if (!c.get('user')) {
|
|
109
|
+
return c.json({ error: 'Unauthorized' }, 403);
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
try {
|
|
103
|
-
let doc = await Customer.findByPkOrDid(
|
|
104
|
-
const livemode = req.query
|
|
113
|
+
let doc = await Customer.findByPkOrDid(c.get('user').did as string);
|
|
114
|
+
const livemode = c.req.query('livemode') ? !!c.get('livemode') : !!doc?.livemode;
|
|
105
115
|
if (!doc) {
|
|
106
|
-
if (req.query
|
|
107
|
-
const result = await blocklet.getUser(
|
|
108
|
-
return
|
|
116
|
+
if (c.req.query('fallback')) {
|
|
117
|
+
const result = await blocklet.getUser(c.get('user').did);
|
|
118
|
+
return c.json({ ...result.user, address: Customer.formatAddressFromUser(result.user), livemode });
|
|
109
119
|
}
|
|
110
|
-
if (req.query
|
|
120
|
+
if (c.req.query('create')) {
|
|
111
121
|
// create customer
|
|
112
|
-
const { user } = await blocklet.getUser(
|
|
122
|
+
const { user } = await blocklet.getUser(c.get('user').did);
|
|
113
123
|
const customer = await Customer.create({
|
|
114
124
|
livemode: true,
|
|
115
|
-
did:
|
|
125
|
+
did: c.get('user').did,
|
|
116
126
|
name: user.fullName,
|
|
117
127
|
email: user.email,
|
|
118
128
|
phone: user.phone,
|
|
@@ -130,11 +140,11 @@ router.get('/me', sessionMiddleware({ accessKey: true }), async (req, res) => {
|
|
|
130
140
|
});
|
|
131
141
|
doc = customer;
|
|
132
142
|
} else {
|
|
133
|
-
return
|
|
143
|
+
return c.json({ error: 'Customer not found' });
|
|
134
144
|
}
|
|
135
145
|
}
|
|
136
|
-
if (req.query
|
|
137
|
-
return
|
|
146
|
+
if (c.req.query('skipSummary')) {
|
|
147
|
+
return c.json({ ...doc.toJSON(), livemode });
|
|
138
148
|
}
|
|
139
149
|
try {
|
|
140
150
|
const [summary, stake, token] = await Promise.all([
|
|
@@ -142,11 +152,11 @@ router.get('/me', sessionMiddleware({ accessKey: true }), async (req, res) => {
|
|
|
142
152
|
getStakeSummaryByDid(doc.did, livemode),
|
|
143
153
|
getTokenSummaryByDid(doc.did, livemode),
|
|
144
154
|
]);
|
|
145
|
-
|
|
155
|
+
return c.json({ ...doc.toJSON(), summary: { ...summary, stake, token }, livemode });
|
|
146
156
|
} catch (summaryErr) {
|
|
147
157
|
logger.error('get customer summary failed', summaryErr);
|
|
148
|
-
if (req.query
|
|
149
|
-
return
|
|
158
|
+
if (c.req.query('skipError')) {
|
|
159
|
+
return c.json({
|
|
150
160
|
...doc.toJSON(),
|
|
151
161
|
summary: { stake: {}, token: {} },
|
|
152
162
|
livemode,
|
|
@@ -158,147 +168,42 @@ router.get('/me', sessionMiddleware({ accessKey: true }), async (req, res) => {
|
|
|
158
168
|
}
|
|
159
169
|
} catch (err) {
|
|
160
170
|
logger.error('get customer failed', err);
|
|
161
|
-
if (req.query
|
|
162
|
-
return
|
|
171
|
+
if (c.req.query('skipError')) {
|
|
172
|
+
return c.json({
|
|
163
173
|
error: `Failed to get customer: ${err.message}`,
|
|
164
|
-
did:
|
|
165
|
-
name:
|
|
174
|
+
did: c.get('user')?.did,
|
|
175
|
+
name: c.get('user')?.fullName,
|
|
166
176
|
address: {},
|
|
167
|
-
livemode: !!req.query
|
|
177
|
+
livemode: !!c.req.query('livemode'),
|
|
168
178
|
summary: { stake: {}, token: {} },
|
|
169
179
|
});
|
|
170
180
|
}
|
|
171
|
-
return
|
|
181
|
+
return c.json({ error: `Failed to get customer: ${err.message}` }, 500);
|
|
172
182
|
}
|
|
173
183
|
});
|
|
174
184
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
185
|
+
// Static path — registered before /:id so hono matches it first.
|
|
186
|
+
app.get('/recharge', sessionMiddleware({ accessKey: true }), async (c) => {
|
|
187
|
+
if (!c.get('user')) {
|
|
188
|
+
return c.json({ error: 'Unauthorized' }, 403);
|
|
178
189
|
}
|
|
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
|
-
});
|
|
190
|
+
if (!c.req.query('currencyId')) {
|
|
191
|
+
return c.json({ error: 'Currency ID is required' }, 400);
|
|
217
192
|
}
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// get overdue invoices
|
|
221
|
-
router.get('/:id/overdue/invoices', authPortal, async (req, res) => {
|
|
222
193
|
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);
|
|
194
|
+
const customer = await Customer.findByPkOrDid(c.get('user').did as string);
|
|
290
195
|
if (!customer) {
|
|
291
|
-
return
|
|
196
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
292
197
|
}
|
|
293
198
|
|
|
294
|
-
const paymentCurrency = await PaymentCurrency.findByPk(req.query
|
|
199
|
+
const paymentCurrency = await PaymentCurrency.findByPk(c.req.query('currencyId') as string);
|
|
295
200
|
if (!paymentCurrency) {
|
|
296
|
-
return
|
|
201
|
+
return c.json({ error: 'Currency not found' }, 404);
|
|
297
202
|
}
|
|
298
203
|
|
|
299
204
|
const paymentMethod = await PaymentMethod.findByPk(paymentCurrency.payment_method_id);
|
|
300
205
|
if (!paymentMethod) {
|
|
301
|
-
return
|
|
206
|
+
return c.json({ error: 'Payment method not found' }, 404);
|
|
302
207
|
}
|
|
303
208
|
|
|
304
209
|
let subscriptions = await Subscription.findAll({
|
|
@@ -338,7 +243,7 @@ router.get('/recharge', sessionMiddleware({ accessKey: true }), async (req, res)
|
|
|
338
243
|
// Calculate recommended recharge cycle and amount
|
|
339
244
|
const recommendedRecharge = calculateRecommendedRechargeAmount(relatedSubscriptions, paymentCurrency.id);
|
|
340
245
|
|
|
341
|
-
return
|
|
246
|
+
return c.json({
|
|
342
247
|
currency: {
|
|
343
248
|
...paymentCurrency.toJSON(),
|
|
344
249
|
paymentMethod,
|
|
@@ -348,79 +253,142 @@ router.get('/recharge', sessionMiddleware({ accessKey: true }), async (req, res)
|
|
|
348
253
|
});
|
|
349
254
|
} catch (err) {
|
|
350
255
|
logger.error('Error getting balance recharge info', err);
|
|
351
|
-
return
|
|
256
|
+
return c.json({ error: err.message }, 500);
|
|
352
257
|
}
|
|
353
258
|
});
|
|
354
259
|
|
|
355
260
|
// get address token
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
261
|
+
// Static path — registered before /:id so hono matches it first.
|
|
262
|
+
app.get('/payer-token', sessionMiddleware({ accessKey: true }), async (c) => {
|
|
263
|
+
if (!c.get('user')) {
|
|
264
|
+
return c.json({ error: 'Unauthorized' }, 403);
|
|
359
265
|
}
|
|
360
|
-
if (!req.query
|
|
361
|
-
return
|
|
266
|
+
if (!c.req.query('currencyId')) {
|
|
267
|
+
return c.json({ error: 'Currency ID is required' }, 400);
|
|
362
268
|
}
|
|
363
269
|
try {
|
|
364
|
-
let paymentAddress = req.query
|
|
270
|
+
let paymentAddress = c.req.query('payerAddress') as string;
|
|
365
271
|
let customer: Customer | null = null;
|
|
366
272
|
if (!paymentAddress) {
|
|
367
|
-
customer = await Customer.findByPkOrDid(
|
|
273
|
+
customer = await Customer.findByPkOrDid(c.get('user').did as string);
|
|
368
274
|
if (!customer) {
|
|
369
|
-
return
|
|
275
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
370
276
|
}
|
|
371
277
|
paymentAddress = customer.did;
|
|
372
278
|
}
|
|
373
279
|
|
|
374
|
-
const paymentCurrency = await PaymentCurrency.findByPk(req.query
|
|
280
|
+
const paymentCurrency = await PaymentCurrency.findByPk(c.req.query('currencyId') as string);
|
|
375
281
|
if (!paymentCurrency) {
|
|
376
|
-
return
|
|
282
|
+
return c.json({ error: 'Currency not found' }, 404);
|
|
377
283
|
}
|
|
378
284
|
|
|
379
285
|
const paymentMethod = await PaymentMethod.findByPk(paymentCurrency.payment_method_id);
|
|
380
286
|
if (!paymentMethod) {
|
|
381
|
-
return
|
|
287
|
+
return c.json({ error: 'Payment method not found' }, 404);
|
|
382
288
|
}
|
|
383
289
|
|
|
384
290
|
if (!['arcblock', 'ethereum', 'base'].includes(paymentMethod.type)) {
|
|
385
|
-
return
|
|
291
|
+
return c.json({ error: `Payment method not supported: ${paymentMethod.type}` }, 400);
|
|
386
292
|
}
|
|
387
293
|
|
|
388
294
|
if (!paymentAddress) {
|
|
389
|
-
return
|
|
295
|
+
return c.json({ error: `Payment address not found for customer: ${customer?.id}` }, 400);
|
|
390
296
|
}
|
|
391
297
|
|
|
392
298
|
const token = await getTokenByAddress(paymentAddress, paymentMethod, paymentCurrency);
|
|
393
|
-
return
|
|
299
|
+
return c.json({ token, paymentAddress });
|
|
394
300
|
} catch (err) {
|
|
395
301
|
logger.error('Error getting customer payer token', err);
|
|
396
|
-
return
|
|
302
|
+
return c.json({ error: err.message }, 500);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// get overdue invoices — /:id/overdue/invoices registered before plain /:id
|
|
307
|
+
app.get('/:id/overdue/invoices', authPortal, async (c) => {
|
|
308
|
+
try {
|
|
309
|
+
const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
|
|
310
|
+
if (!doc) {
|
|
311
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
312
|
+
}
|
|
313
|
+
const where: WhereOptions<Invoice> = {
|
|
314
|
+
customer_id: doc.id,
|
|
315
|
+
status: ['uncollectible'],
|
|
316
|
+
amount_remaining: { [Op.gt]: '0' },
|
|
317
|
+
};
|
|
318
|
+
if (typeof c.get('livemode') === 'boolean') {
|
|
319
|
+
where.livemode = !!c.get('livemode');
|
|
320
|
+
}
|
|
321
|
+
const { rows: invoices, count } = await Invoice.findAndCountAll({
|
|
322
|
+
where,
|
|
323
|
+
include: [
|
|
324
|
+
{ model: PaymentCurrency, as: 'paymentCurrency' },
|
|
325
|
+
{ model: PaymentMethod, as: 'paymentMethod' },
|
|
326
|
+
],
|
|
327
|
+
});
|
|
328
|
+
if (count === 0) {
|
|
329
|
+
return c.json({
|
|
330
|
+
summary: null,
|
|
331
|
+
invoices: [],
|
|
332
|
+
subscriptionCount: 0,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const summary: Record<string, { amount: string; currency: PaymentCurrency; method: PaymentMethod }> = {};
|
|
336
|
+
invoices.forEach((invoice) => {
|
|
337
|
+
const key = invoice.currency_id;
|
|
338
|
+
if (!summary[key]) {
|
|
339
|
+
summary[key] = {
|
|
340
|
+
amount: '0',
|
|
341
|
+
// @ts-ignore
|
|
342
|
+
currency: invoice.paymentCurrency,
|
|
343
|
+
// @ts-ignore
|
|
344
|
+
method: invoice.paymentMethod,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
if (invoice && summary[key]) {
|
|
348
|
+
// @ts-ignore
|
|
349
|
+
summary[key].amount = new BN(summary[key]?.amount || '0')
|
|
350
|
+
.add(new BN(invoice.amount_remaining || '0'))
|
|
351
|
+
.toString();
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
const subscriptionCount = new Set(invoices.map((x) => x.subscription_id)).size;
|
|
355
|
+
return c.json({
|
|
356
|
+
summary,
|
|
357
|
+
invoices,
|
|
358
|
+
subscriptionCount,
|
|
359
|
+
customer: doc,
|
|
360
|
+
});
|
|
361
|
+
} catch (err) {
|
|
362
|
+
logger.error(err);
|
|
363
|
+
return c.json({ error: `Failed to get overdue invoices: ${err.message}` }, 500);
|
|
397
364
|
}
|
|
398
365
|
});
|
|
399
366
|
|
|
400
|
-
|
|
401
|
-
if (!req.
|
|
402
|
-
return
|
|
367
|
+
app.get('/:id', auth, async (c) => {
|
|
368
|
+
if (!c.req.param('id')) {
|
|
369
|
+
return c.json({ error: 'Customer ID is required' }, 400);
|
|
403
370
|
}
|
|
404
371
|
try {
|
|
405
|
-
const doc = await Customer.findByPkOrDid(req.
|
|
372
|
+
const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
|
|
406
373
|
if (doc) {
|
|
407
|
-
return
|
|
374
|
+
return c.json(doc);
|
|
408
375
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
376
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
377
|
+
if (body.create) {
|
|
378
|
+
if (!c.get('user')) {
|
|
379
|
+
return c.json({ error: 'Unauthorized' }, 403);
|
|
412
380
|
}
|
|
413
|
-
if (req.
|
|
414
|
-
return
|
|
381
|
+
if (c.req.param('id').startsWith('cus_')) {
|
|
382
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
415
383
|
}
|
|
416
|
-
const { user } = await blocklet.getUser(req.
|
|
384
|
+
const { user } = await blocklet.getUser(c.req.param('id'));
|
|
417
385
|
if (!user) {
|
|
418
|
-
return
|
|
386
|
+
return c.json({ error: 'User not found' }, 404);
|
|
419
387
|
}
|
|
420
388
|
const customer = await Customer.create({
|
|
421
389
|
livemode: true,
|
|
422
|
-
did: user?.did || req.
|
|
423
|
-
name: user?.fullName ?? req.
|
|
390
|
+
did: user?.did || c.req.param('id'),
|
|
391
|
+
name: user?.fullName ?? c.req.param('id'),
|
|
424
392
|
email: user?.email ?? '',
|
|
425
393
|
phone: user?.phone ?? '',
|
|
426
394
|
address: Customer.formatAddressFromUser(user),
|
|
@@ -435,21 +403,20 @@ router.get('/:id', auth, async (req, res) => {
|
|
|
435
403
|
customerId: customer.id,
|
|
436
404
|
did: customer.did,
|
|
437
405
|
});
|
|
438
|
-
return
|
|
406
|
+
return c.json(customer);
|
|
439
407
|
}
|
|
440
|
-
return
|
|
408
|
+
return c.json(null, 404);
|
|
441
409
|
} catch (err) {
|
|
442
410
|
logger.error(err);
|
|
443
|
-
return
|
|
411
|
+
return c.json({ error: `Failed to get customer: ${err.message}` }, 500);
|
|
444
412
|
}
|
|
445
413
|
});
|
|
446
414
|
|
|
447
|
-
|
|
415
|
+
app.get('/:id/summary', auth, async (c) => {
|
|
448
416
|
try {
|
|
449
|
-
const doc = await Customer.findByPkOrDid(req.
|
|
417
|
+
const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
|
|
450
418
|
if (!doc) {
|
|
451
|
-
|
|
452
|
-
return;
|
|
419
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
453
420
|
}
|
|
454
421
|
|
|
455
422
|
const [summary, stake, token] = await Promise.all([
|
|
@@ -457,10 +424,10 @@ router.get('/:id/summary', auth, async (req, res) => {
|
|
|
457
424
|
getStakeSummaryByDid(doc.did, doc.livemode),
|
|
458
425
|
getTokenSummaryByDid(doc.did, doc.livemode),
|
|
459
426
|
]);
|
|
460
|
-
|
|
427
|
+
return c.json({ ...summary, stake, token });
|
|
461
428
|
} catch (err) {
|
|
462
429
|
logger.error(err);
|
|
463
|
-
|
|
430
|
+
return c.json(null);
|
|
464
431
|
}
|
|
465
432
|
});
|
|
466
433
|
|
|
@@ -484,20 +451,22 @@ const updatePreferenceSchema = Joi.object({
|
|
|
484
451
|
}).optional(),
|
|
485
452
|
}).unknown(false);
|
|
486
453
|
|
|
487
|
-
|
|
454
|
+
// Static path — registered before PUT /:id so hono matches it first.
|
|
455
|
+
app.put('/preference', sessionMiddleware({ accessKey: true }), async (c) => {
|
|
488
456
|
try {
|
|
489
|
-
if (!
|
|
490
|
-
return
|
|
457
|
+
if (!c.get('user')) {
|
|
458
|
+
return c.json({ error: 'Unauthorized' }, 403);
|
|
491
459
|
}
|
|
492
460
|
|
|
493
|
-
const doc = await Customer.findByPkOrDid(
|
|
461
|
+
const doc = await Customer.findByPkOrDid(c.get('user').did as string);
|
|
494
462
|
if (!doc) {
|
|
495
|
-
return
|
|
463
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
496
464
|
}
|
|
497
465
|
|
|
498
|
-
const
|
|
466
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
467
|
+
const { error, value } = updatePreferenceSchema.validate(body);
|
|
499
468
|
if (error) {
|
|
500
|
-
return
|
|
469
|
+
return c.json({ error: error.message }, 400);
|
|
501
470
|
}
|
|
502
471
|
|
|
503
472
|
// Get old preference before update
|
|
@@ -514,10 +483,10 @@ router.put('/preference', sessionMiddleware({ accessKey: true }), async (req, re
|
|
|
514
483
|
await handleNotificationPreferenceChange(doc.id, value.notification);
|
|
515
484
|
}
|
|
516
485
|
|
|
517
|
-
return
|
|
486
|
+
return c.json(doc);
|
|
518
487
|
} catch (err) {
|
|
519
488
|
logger.error('Failed to update customer preference', err);
|
|
520
|
-
return
|
|
489
|
+
return c.json({ error: `Failed to update preference: ${err.message}` }, 400);
|
|
521
490
|
}
|
|
522
491
|
});
|
|
523
492
|
|
|
@@ -542,29 +511,30 @@ const updateCustomerSchema = Joi.object({
|
|
|
542
511
|
postal_code: Joi.string().max(20).empty(''),
|
|
543
512
|
}).empty(''),
|
|
544
513
|
}).unknown(true);
|
|
545
|
-
|
|
546
|
-
|
|
514
|
+
|
|
515
|
+
app.put('/:id', authPortal, async (c) => {
|
|
547
516
|
try {
|
|
548
|
-
const doc = await Customer.findByPkOrDid(req.
|
|
517
|
+
const doc = await Customer.findByPkOrDid(c.req.param('id') as string);
|
|
549
518
|
if (!doc) {
|
|
550
|
-
return
|
|
519
|
+
return c.json({ error: 'Customer not found' }, 404);
|
|
551
520
|
}
|
|
552
521
|
|
|
553
|
-
const
|
|
522
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
523
|
+
const raw = pick(body, ['metadata', 'name', 'email', 'phone', 'address']);
|
|
554
524
|
const { error } = updateCustomerSchema.validate(raw);
|
|
555
525
|
if (error) {
|
|
556
|
-
return
|
|
526
|
+
return c.json({ error: error.message }, 400);
|
|
557
527
|
}
|
|
558
528
|
if (raw.metadata) {
|
|
559
529
|
raw.metadata = formatMetadata(raw.metadata);
|
|
560
530
|
}
|
|
561
531
|
|
|
562
532
|
await doc.update(raw);
|
|
563
|
-
|
|
533
|
+
return c.json(doc);
|
|
564
534
|
} catch (err) {
|
|
565
535
|
logger.error(err);
|
|
566
|
-
|
|
536
|
+
return c.json(null);
|
|
567
537
|
}
|
|
568
538
|
});
|
|
569
539
|
|
|
570
|
-
export default
|
|
540
|
+
export default app;
|