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,23 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
// Phase 3 (express→hono) — hono fork of routes/vendor.ts. Sub-app with
|
|
2
|
+
// routes relative to /api/vendor (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
|
+
// res.redirect(u) → return c.redirect(u).
|
|
6
|
+
import { VendorAuth } from '@blocklet/payment-vendor';
|
|
7
|
+
import { toBase58 } from '@ocap/util';
|
|
2
8
|
import { getUrl } from '@blocklet/sdk/lib/component';
|
|
3
|
-
import {
|
|
9
|
+
import { Hono } from 'hono';
|
|
10
|
+
import type { MiddlewareHandler } from 'hono';
|
|
4
11
|
import Joi from 'joi';
|
|
5
12
|
|
|
6
13
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
7
14
|
import { gte } from 'semver';
|
|
8
15
|
import { Op } from 'sequelize';
|
|
9
|
-
import { MetadataSchema } from '
|
|
10
|
-
import { wallet } from '
|
|
11
|
-
import dayjs from '
|
|
12
|
-
import env from '
|
|
13
|
-
import logger from '
|
|
14
|
-
import { authenticate } from '
|
|
15
|
-
import { formatToShortUrl } from '
|
|
16
|
-
import { getBlockletJson } from '
|
|
17
|
-
import { VendorFulfillmentService } from '
|
|
18
|
-
import { buildRefundInfoFromPayout } from '
|
|
19
|
-
import { CheckoutSession, Invoice, Payout, PaymentCurrency, Subscription } from '
|
|
20
|
-
import { ProductVendor } from '
|
|
16
|
+
import { MetadataSchema } from '../../libs/api';
|
|
17
|
+
import { wallet } from '../../libs/auth';
|
|
18
|
+
import dayjs from '../../libs/dayjs';
|
|
19
|
+
import env, { isDevelopmentEnv } from '../../libs/env';
|
|
20
|
+
import logger from '../../libs/logger';
|
|
21
|
+
import { authenticate } from '../../middlewares/hono/security';
|
|
22
|
+
import { formatToShortUrl } from '../../libs/url';
|
|
23
|
+
import { getBlockletJson } from '../../libs/util';
|
|
24
|
+
import { VendorFulfillmentService } from '../../libs/vendor-util/fulfillment';
|
|
25
|
+
import { buildRefundInfoFromPayout } from '../../libs/vendor-util/tool';
|
|
26
|
+
import { CheckoutSession, Invoice, Payout, PaymentCurrency, Subscription } from '../../store/models';
|
|
27
|
+
import { ProductVendor } from '../../store/models/product-vendor';
|
|
21
28
|
|
|
22
29
|
const VENDOR_DID = {
|
|
23
30
|
launcher: 'z8iZkFBbrVQxZHvcWWB3Sa2TrfGmSeFz9MSU7',
|
|
@@ -67,45 +74,60 @@ const vendorRedirectQuerySchema = Joi.object({
|
|
|
67
74
|
target: Joi.string().valid('home', 'dashboard').default('home').allow(''),
|
|
68
75
|
});
|
|
69
76
|
|
|
70
|
-
function validateParams(schema: Joi.ObjectSchema) {
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
function validateParams(schema: Joi.ObjectSchema): MiddlewareHandler {
|
|
78
|
+
// eslint-disable-next-line require-await -- MiddlewareHandler contract is async; body is sync
|
|
79
|
+
return async (c, next) => {
|
|
80
|
+
const params: Record<string, string> = {};
|
|
81
|
+
// Collect all named params from the matched route
|
|
82
|
+
try {
|
|
83
|
+
const raw = c.req.param();
|
|
84
|
+
Object.assign(params, raw);
|
|
85
|
+
} catch (_) {
|
|
86
|
+
// no params
|
|
87
|
+
}
|
|
88
|
+
const { error } = schema.validate(params);
|
|
73
89
|
if (error) {
|
|
74
90
|
logger.error('Invalid parameters', {
|
|
75
91
|
error,
|
|
76
|
-
params
|
|
77
|
-
});
|
|
78
|
-
return res.status(400).json({
|
|
79
|
-
error: 'Invalid parameters',
|
|
80
|
-
message: error.message,
|
|
92
|
+
params,
|
|
81
93
|
});
|
|
94
|
+
return c.json(
|
|
95
|
+
{
|
|
96
|
+
error: 'Invalid parameters',
|
|
97
|
+
message: error.message,
|
|
98
|
+
},
|
|
99
|
+
400
|
|
100
|
+
);
|
|
82
101
|
}
|
|
83
102
|
return next();
|
|
84
103
|
};
|
|
85
104
|
}
|
|
86
105
|
|
|
87
|
-
function validateQuery(schema: Joi.ObjectSchema) {
|
|
88
|
-
|
|
89
|
-
|
|
106
|
+
function validateQuery(schema: Joi.ObjectSchema): MiddlewareHandler {
|
|
107
|
+
// eslint-disable-next-line require-await -- MiddlewareHandler contract is async; body is sync
|
|
108
|
+
return async (c, next) => {
|
|
109
|
+
const query = c.req.query();
|
|
110
|
+
const { error, value } = schema.validate(query);
|
|
90
111
|
if (error) {
|
|
91
112
|
logger.error('Invalid query parameters', {
|
|
92
113
|
error,
|
|
93
|
-
query
|
|
114
|
+
query,
|
|
94
115
|
});
|
|
95
|
-
return
|
|
116
|
+
return c.redirect('/404');
|
|
96
117
|
}
|
|
97
|
-
|
|
118
|
+
// Store validated/defaulted query in context for downstream use
|
|
119
|
+
c.set('validatedQuery' as any, value);
|
|
98
120
|
return next();
|
|
99
121
|
};
|
|
100
122
|
}
|
|
101
123
|
|
|
102
|
-
async function getAllVendors(
|
|
124
|
+
async function getAllVendors(c: any) {
|
|
103
125
|
try {
|
|
104
126
|
const vendors = await ProductVendor.findAll({
|
|
105
127
|
order: [['created_at', 'DESC']],
|
|
106
128
|
});
|
|
107
129
|
|
|
108
|
-
return
|
|
130
|
+
return c.json({
|
|
109
131
|
data: vendors,
|
|
110
132
|
pk: wallet.publicKey,
|
|
111
133
|
total: vendors.length,
|
|
@@ -115,24 +137,24 @@ async function getAllVendors(_req: any, res: any) {
|
|
|
115
137
|
error,
|
|
116
138
|
stack: error.stack,
|
|
117
139
|
});
|
|
118
|
-
return
|
|
140
|
+
return c.json({ error: 'Internal server error. Failed to get vendors' }, 500);
|
|
119
141
|
}
|
|
120
142
|
}
|
|
121
143
|
|
|
122
|
-
async function getVendorInfo(
|
|
144
|
+
async function getVendorInfo(c: any) {
|
|
123
145
|
try {
|
|
124
|
-
const vendor = await ProductVendor.findByPk(req.
|
|
146
|
+
const vendor = await ProductVendor.findByPk(c.req.param('id'));
|
|
125
147
|
if (!vendor) {
|
|
126
|
-
return
|
|
148
|
+
return c.json({ error: 'Vendor not found' }, 404);
|
|
127
149
|
}
|
|
128
150
|
|
|
129
|
-
return
|
|
151
|
+
return c.json({ data: vendor });
|
|
130
152
|
} catch (error: any) {
|
|
131
153
|
logger.error('Failed to get vendor', {
|
|
132
154
|
error,
|
|
133
|
-
id: req.
|
|
155
|
+
id: c.req.param('id'),
|
|
134
156
|
});
|
|
135
|
-
return
|
|
157
|
+
return c.json({ error: 'Internal server error. Failed to get vendor' }, 500);
|
|
136
158
|
}
|
|
137
159
|
}
|
|
138
160
|
|
|
@@ -181,14 +203,18 @@ async function prepareVendorData(appUrlInput: string, vendorType: 'launcher' | '
|
|
|
181
203
|
};
|
|
182
204
|
}
|
|
183
205
|
|
|
184
|
-
async function createVendor(
|
|
206
|
+
async function createVendor(c: any) {
|
|
185
207
|
try {
|
|
186
|
-
const
|
|
208
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
209
|
+
const { error, value } = createVendorSchema.validate(body);
|
|
187
210
|
if (error) {
|
|
188
|
-
return
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
211
|
+
return c.json(
|
|
212
|
+
{
|
|
213
|
+
error: 'Validation failed',
|
|
214
|
+
message: error.message,
|
|
215
|
+
},
|
|
216
|
+
400
|
|
217
|
+
);
|
|
192
218
|
}
|
|
193
219
|
|
|
194
220
|
const { vendor_key: vendorKey, vendor_type: type, name, description, metadata, status } = value;
|
|
@@ -199,12 +225,12 @@ async function createVendor(req: any, res: any) {
|
|
|
199
225
|
where: { vendor_key: vendorKey },
|
|
200
226
|
});
|
|
201
227
|
if (existingVendor) {
|
|
202
|
-
return
|
|
228
|
+
return c.json({ error: 'Vendor key already exists' }, 400);
|
|
203
229
|
}
|
|
204
230
|
|
|
205
231
|
const preparedData = await prepareVendorData(value.app_url, vendorType, metadata);
|
|
206
232
|
if ('error' in preparedData) {
|
|
207
|
-
return
|
|
233
|
+
return c.json({ error: preparedData.error }, 400);
|
|
208
234
|
}
|
|
209
235
|
|
|
210
236
|
const vendor = await ProductVendor.create({
|
|
@@ -214,32 +240,36 @@ async function createVendor(req: any, res: any) {
|
|
|
214
240
|
name,
|
|
215
241
|
description,
|
|
216
242
|
status: status || 'active',
|
|
217
|
-
created_by:
|
|
243
|
+
created_by: c.get('user')?.did || 'admin',
|
|
218
244
|
});
|
|
219
245
|
|
|
220
246
|
logger.info('Vendor created', { vendorId: vendor.id, vendorKey: vendor.vendor_key });
|
|
221
|
-
return
|
|
247
|
+
return c.json({ data: vendor }, 201);
|
|
222
248
|
} catch (error: any) {
|
|
223
249
|
logger.error('Failed to create vendor', {
|
|
224
250
|
error,
|
|
225
251
|
});
|
|
226
|
-
return
|
|
252
|
+
return c.json({ error: 'Internal server error' }, 500);
|
|
227
253
|
}
|
|
228
254
|
}
|
|
229
255
|
|
|
230
|
-
async function updateVendor(
|
|
256
|
+
async function updateVendor(c: any) {
|
|
231
257
|
try {
|
|
232
|
-
const vendor = await ProductVendor.findByPk(req.
|
|
258
|
+
const vendor = await ProductVendor.findByPk(c.req.param('id'));
|
|
233
259
|
if (!vendor) {
|
|
234
|
-
return
|
|
260
|
+
return c.json({ error: 'Vendor not found' }, 404);
|
|
235
261
|
}
|
|
236
262
|
|
|
237
|
-
const
|
|
263
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
264
|
+
const { error, value } = updateVendorSchema.validate(body);
|
|
238
265
|
if (error) {
|
|
239
|
-
return
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
266
|
+
return c.json(
|
|
267
|
+
{
|
|
268
|
+
error: 'Validation failed',
|
|
269
|
+
message: error.message,
|
|
270
|
+
},
|
|
271
|
+
400
|
|
272
|
+
);
|
|
243
273
|
}
|
|
244
274
|
|
|
245
275
|
const { vendor_type: type, vendor_key: vendorKey, name, description, status, metadata } = value;
|
|
@@ -249,14 +279,14 @@ async function updateVendor(req: any, res: any) {
|
|
|
249
279
|
where: { vendor_key: vendorKey },
|
|
250
280
|
});
|
|
251
281
|
if (existingVendor) {
|
|
252
|
-
return
|
|
282
|
+
return c.json({ error: 'Vendor key already exists' }, 400);
|
|
253
283
|
}
|
|
254
284
|
}
|
|
255
285
|
|
|
256
286
|
const vendorType = (type || 'launcher') as 'launcher' | 'didnames';
|
|
257
287
|
const preparedData = await prepareVendorData(value.app_url, vendorType, metadata);
|
|
258
288
|
if ('error' in preparedData) {
|
|
259
|
-
return
|
|
289
|
+
return c.json({ error: preparedData.error }, 400);
|
|
260
290
|
}
|
|
261
291
|
|
|
262
292
|
const updates = {
|
|
@@ -271,38 +301,41 @@ async function updateVendor(req: any, res: any) {
|
|
|
271
301
|
await vendor.update(Object.fromEntries(Object.entries(updates).filter(([, v]) => v !== undefined)));
|
|
272
302
|
|
|
273
303
|
logger.info('Vendor updated', { vendorId: vendor.id });
|
|
274
|
-
return
|
|
304
|
+
return c.json({ data: vendor });
|
|
275
305
|
} catch (error: any) {
|
|
276
|
-
logger.error('Failed to update vendor', { error, id: req.
|
|
277
|
-
return
|
|
306
|
+
logger.error('Failed to update vendor', { error, id: c.req.param('id') });
|
|
307
|
+
return c.json({ error: 'Internal server error' }, 500);
|
|
278
308
|
}
|
|
279
309
|
}
|
|
280
310
|
|
|
281
|
-
async function deleteVendor(
|
|
311
|
+
async function deleteVendor(c: any) {
|
|
282
312
|
try {
|
|
283
|
-
const vendor = await ProductVendor.findByPk(req.
|
|
313
|
+
const vendor = await ProductVendor.findByPk(c.req.param('id'));
|
|
284
314
|
if (!vendor) {
|
|
285
|
-
return
|
|
315
|
+
return c.json({ error: 'Vendor not found' }, 404);
|
|
286
316
|
}
|
|
287
317
|
|
|
288
318
|
await vendor.destroy();
|
|
289
319
|
|
|
290
320
|
logger.info('Vendor deleted', { vendorId: vendor.id });
|
|
291
|
-
return
|
|
321
|
+
return c.json({ success: true });
|
|
292
322
|
} catch (error: any) {
|
|
293
|
-
logger.error('Failed to delete vendor', { error, id: req.
|
|
294
|
-
return
|
|
323
|
+
logger.error('Failed to delete vendor', { error, id: c.req.param('id') });
|
|
324
|
+
return c.json({ error: 'Internal server error' }, 500);
|
|
295
325
|
}
|
|
296
326
|
}
|
|
297
327
|
|
|
298
|
-
async function testVendorConnection(
|
|
328
|
+
async function testVendorConnection(c: any) {
|
|
299
329
|
try {
|
|
300
|
-
const vendor = await ProductVendor.findByPk(req.
|
|
330
|
+
const vendor = await ProductVendor.findByPk(c.req.param('id'));
|
|
301
331
|
if (!vendor) {
|
|
302
|
-
return
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
332
|
+
return c.json(
|
|
333
|
+
{
|
|
334
|
+
code: 'VENDOR_NOT_FOUND',
|
|
335
|
+
error: 'Vendor not found',
|
|
336
|
+
},
|
|
337
|
+
404
|
|
338
|
+
);
|
|
306
339
|
}
|
|
307
340
|
|
|
308
341
|
const vendorAdapter = await VendorFulfillmentService.getVendorAdapter(vendor.vendor_key);
|
|
@@ -310,32 +343,41 @@ async function testVendorConnection(req: any, res: any) {
|
|
|
310
343
|
|
|
311
344
|
if (result.connected) {
|
|
312
345
|
if (env.appId !== result.did) {
|
|
313
|
-
return
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
346
|
+
return c.json(
|
|
347
|
+
{
|
|
348
|
+
code: 'VENDOR_CONNECT_TEST_FAILED',
|
|
349
|
+
error: 'Vendor connection test failed! Please check the DID in the vendor preferences config',
|
|
350
|
+
},
|
|
351
|
+
400
|
|
352
|
+
);
|
|
317
353
|
}
|
|
318
|
-
return
|
|
354
|
+
return c.json({ ...result }, 200);
|
|
319
355
|
}
|
|
320
356
|
|
|
321
357
|
if (result.error) {
|
|
322
|
-
return
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
358
|
+
return c.json(
|
|
359
|
+
{
|
|
360
|
+
code: 'VENDOR_CONNECT_TEST_FAILED',
|
|
361
|
+
error: result.error,
|
|
362
|
+
},
|
|
363
|
+
400
|
|
364
|
+
);
|
|
326
365
|
}
|
|
327
366
|
|
|
328
367
|
if (!result.sdkVersion) {
|
|
329
|
-
return
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
368
|
+
return c.json(
|
|
369
|
+
{
|
|
370
|
+
code: 'VENDOR_CONNECT_TEST_FAILED',
|
|
371
|
+
error: 'Vendor SDK version is too low. Please upgrade to the latest version (>=1.21.4)',
|
|
372
|
+
},
|
|
373
|
+
400
|
|
374
|
+
);
|
|
333
375
|
}
|
|
334
376
|
|
|
335
|
-
return
|
|
377
|
+
return c.json({ ...result }, 400);
|
|
336
378
|
} catch (error: any) {
|
|
337
|
-
logger.error('Failed to test vendor connection', { error, id: req.
|
|
338
|
-
return
|
|
379
|
+
logger.error('Failed to test vendor connection', { error, id: c.req.param('id') });
|
|
380
|
+
return c.json({ error: 'Internal server error' }, 500);
|
|
339
381
|
}
|
|
340
382
|
}
|
|
341
383
|
|
|
@@ -503,45 +545,50 @@ async function getVendorStatus(sessionId: string) {
|
|
|
503
545
|
return result;
|
|
504
546
|
}
|
|
505
547
|
|
|
506
|
-
async function getVendorFulfillmentStatus(
|
|
507
|
-
const { sessionId } = req.
|
|
548
|
+
async function getVendorFulfillmentStatus(c: any) {
|
|
549
|
+
const { sessionId } = c.req.param();
|
|
508
550
|
|
|
509
551
|
try {
|
|
510
552
|
const status = await getVendorStatus(sessionId);
|
|
511
553
|
|
|
512
554
|
if (status.code) {
|
|
513
|
-
return
|
|
555
|
+
return c.json({ error: status.error }, status.code);
|
|
514
556
|
}
|
|
515
|
-
return
|
|
557
|
+
return c.json(status);
|
|
516
558
|
} catch (error: any) {
|
|
517
|
-
return
|
|
559
|
+
return c.json({ error: error.message }, 500);
|
|
518
560
|
}
|
|
519
561
|
}
|
|
520
562
|
|
|
521
|
-
async function getVendorFulfillmentDetail(
|
|
522
|
-
const { sessionId } = req.
|
|
523
|
-
const
|
|
563
|
+
async function getVendorFulfillmentDetail(c: any) {
|
|
564
|
+
const { sessionId } = c.req.param();
|
|
565
|
+
const shortUrl = c.req.query('shortUrl');
|
|
566
|
+
const ownerDid = c.req.query('ownerDid');
|
|
567
|
+
const appDid = c.req.query('appDid');
|
|
524
568
|
|
|
525
569
|
try {
|
|
526
570
|
const detail = await processVendorOrders(sessionId, 'getOrder', shortUrl === 'true', ownerDid, appDid);
|
|
527
571
|
if (detail.code) {
|
|
528
|
-
return
|
|
572
|
+
return c.json({ error: detail.error }, detail.code);
|
|
529
573
|
}
|
|
530
|
-
return
|
|
574
|
+
return c.json(detail);
|
|
531
575
|
} catch (error: any) {
|
|
532
|
-
return
|
|
576
|
+
return c.json({ error: error.message }, 500);
|
|
533
577
|
}
|
|
534
578
|
}
|
|
535
579
|
|
|
536
|
-
async function redirectToVendor(
|
|
537
|
-
const { subscriptionId } = req.
|
|
538
|
-
|
|
580
|
+
async function redirectToVendor(c: any) {
|
|
581
|
+
const { subscriptionId } = c.req.param();
|
|
582
|
+
// validated query values are stored by validateQuery middleware
|
|
583
|
+
const validatedQuery = c.get('validatedQuery' as any) ?? {};
|
|
584
|
+
const vendorId = validatedQuery.vendorId ?? c.req.query('vendorId');
|
|
585
|
+
const target = validatedQuery.target ?? c.req.query('target');
|
|
539
586
|
|
|
540
587
|
try {
|
|
541
588
|
const checkoutSession = await CheckoutSession.findBySubscriptionId(subscriptionId);
|
|
542
589
|
if (!checkoutSession) {
|
|
543
590
|
logger.warn('CheckoutSession not found for subscription[redirect to vendor]', { subscriptionId });
|
|
544
|
-
return
|
|
591
|
+
return c.redirect('/404');
|
|
545
592
|
}
|
|
546
593
|
|
|
547
594
|
const order = checkoutSession?.vendor_info?.find((item) => item.vendor_id === vendorId);
|
|
@@ -551,16 +598,16 @@ async function redirectToVendor(req: any, res: any) {
|
|
|
551
598
|
vendorId,
|
|
552
599
|
availableVendors: checkoutSession.vendor_info?.map((v) => v.vendor_key) || [],
|
|
553
600
|
});
|
|
554
|
-
return
|
|
601
|
+
return c.redirect('/404');
|
|
555
602
|
}
|
|
556
603
|
|
|
557
|
-
const isOwner =
|
|
604
|
+
const isOwner = c.get('user').did === checkoutSession.customer_did;
|
|
558
605
|
|
|
559
606
|
if (!isOwner) {
|
|
560
607
|
if (order.app_url) {
|
|
561
|
-
return
|
|
608
|
+
return c.redirect(order.app_url);
|
|
562
609
|
}
|
|
563
|
-
return
|
|
610
|
+
return c.redirect('/404');
|
|
564
611
|
}
|
|
565
612
|
|
|
566
613
|
const result = await executeVendorOperation({
|
|
@@ -578,11 +625,11 @@ async function redirectToVendor(req: any, res: any) {
|
|
|
578
625
|
error: result.error,
|
|
579
626
|
message: result.message,
|
|
580
627
|
});
|
|
581
|
-
return
|
|
628
|
+
return c.redirect('/404');
|
|
582
629
|
}
|
|
583
630
|
|
|
584
631
|
const redirectUrl = target === 'dashboard' ? result.data.dashboardUrl : result.data.homeUrl;
|
|
585
|
-
return
|
|
632
|
+
return c.redirect(redirectUrl);
|
|
586
633
|
} catch (error: any) {
|
|
587
634
|
logger.error('Failed to redirect to vendor service', {
|
|
588
635
|
error,
|
|
@@ -590,17 +637,18 @@ async function redirectToVendor(req: any, res: any) {
|
|
|
590
637
|
vendorId,
|
|
591
638
|
target,
|
|
592
639
|
});
|
|
593
|
-
return
|
|
640
|
+
return c.redirect('/404');
|
|
594
641
|
}
|
|
595
642
|
}
|
|
596
643
|
|
|
597
|
-
async function getCancelledSessions(
|
|
598
|
-
const
|
|
644
|
+
async function getCancelledSessions(c: any) {
|
|
645
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
646
|
+
const { error } = sessionIdsParamSchema.validate(body);
|
|
599
647
|
if (error) {
|
|
600
|
-
return
|
|
648
|
+
return c.json({ error: error.message }, 400);
|
|
601
649
|
}
|
|
602
650
|
|
|
603
|
-
const { sessionIds = [] } =
|
|
651
|
+
const { sessionIds = [] } = body;
|
|
604
652
|
|
|
605
653
|
const allCheckoutSessions = await CheckoutSession.findAll({
|
|
606
654
|
where: { id: { [Op.in]: sessionIds } },
|
|
@@ -621,22 +669,22 @@ async function getCancelledSessions(req: any, res: any) {
|
|
|
621
669
|
const cancelledSessions = allCheckoutSessions.filter(
|
|
622
670
|
(item) => item.subscription_id && cancelledSubIds.includes(item.subscription_id)
|
|
623
671
|
);
|
|
624
|
-
return
|
|
672
|
+
return c.json({ cancelledSessions });
|
|
625
673
|
}
|
|
626
674
|
|
|
627
|
-
async function getVendorSubscription(
|
|
628
|
-
const { sessionId } = req.
|
|
675
|
+
async function getVendorSubscription(c: any) {
|
|
676
|
+
const { sessionId } = c.req.param();
|
|
629
677
|
|
|
630
678
|
const checkoutSession = await CheckoutSession.findByPk(sessionId);
|
|
631
679
|
|
|
632
680
|
if (!checkoutSession) {
|
|
633
|
-
return
|
|
681
|
+
return c.json({ error: 'Checkout session not found' }, 404);
|
|
634
682
|
}
|
|
635
683
|
|
|
636
684
|
const subscription = await Subscription.findByPk(checkoutSession.subscription_id);
|
|
637
685
|
|
|
638
686
|
if (!subscription) {
|
|
639
|
-
return
|
|
687
|
+
return c.json({ error: 'Subscription not found' }, 404);
|
|
640
688
|
}
|
|
641
689
|
|
|
642
690
|
const invoices = await Invoice.findAll({
|
|
@@ -667,56 +715,57 @@ async function getVendorSubscription(req: any, res: any) {
|
|
|
667
715
|
limit: 20,
|
|
668
716
|
});
|
|
669
717
|
|
|
670
|
-
return
|
|
718
|
+
return c.json({
|
|
671
719
|
subscription: subscription.toJSON(),
|
|
672
720
|
billing_history: invoices,
|
|
673
721
|
});
|
|
674
722
|
}
|
|
675
723
|
|
|
676
|
-
async function handleSubscriptionRedirect(
|
|
677
|
-
const { sessionId } = req.
|
|
724
|
+
async function handleSubscriptionRedirect(c: any) {
|
|
725
|
+
const { sessionId } = c.req.param();
|
|
678
726
|
|
|
679
727
|
const checkoutSession = await CheckoutSession.findByPk(sessionId);
|
|
680
728
|
if (!checkoutSession) {
|
|
681
|
-
return
|
|
729
|
+
return c.json({ error: 'Checkout session not found' }, 404);
|
|
682
730
|
}
|
|
683
731
|
|
|
684
|
-
return
|
|
732
|
+
return c.redirect(getUrl(`/customer/subscription/${checkoutSession.subscription_id}`));
|
|
685
733
|
}
|
|
686
734
|
|
|
687
|
-
async function vendorRefund(
|
|
688
|
-
const
|
|
689
|
-
const
|
|
735
|
+
async function vendorRefund(c: any) {
|
|
736
|
+
const id = c.req.param('id');
|
|
737
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
738
|
+
const { reason } = body;
|
|
690
739
|
try {
|
|
691
740
|
const payout = await Payout.findByPk(id);
|
|
692
741
|
|
|
693
742
|
if (!payout) {
|
|
694
|
-
return
|
|
743
|
+
return c.json({ error: 'Payout not found' }, 404);
|
|
695
744
|
}
|
|
696
745
|
|
|
697
746
|
if (payout.status !== 'paid') {
|
|
698
|
-
return
|
|
747
|
+
return c.json({ error: 'Only paid payouts can be refunded' }, 400);
|
|
699
748
|
}
|
|
700
749
|
|
|
701
750
|
if (!payout.vendor_info?.vendor_id || !payout.vendor_info?.order_id) {
|
|
702
|
-
return
|
|
751
|
+
return c.json({ error: 'Payout has no vendor information' }, 400);
|
|
703
752
|
}
|
|
704
753
|
|
|
705
754
|
const vendor = await ProductVendor.findByPk(payout.vendor_info.vendor_id);
|
|
706
755
|
if (!vendor) {
|
|
707
|
-
return
|
|
756
|
+
return c.json({ error: 'Vendor not found' }, 404);
|
|
708
757
|
}
|
|
709
758
|
|
|
710
759
|
let refundInfo;
|
|
711
760
|
try {
|
|
712
761
|
refundInfo = await buildRefundInfoFromPayout(payout, reason);
|
|
713
762
|
} catch (err: any) {
|
|
714
|
-
return
|
|
763
|
+
return c.json({ error: err.message }, 400);
|
|
715
764
|
}
|
|
716
765
|
|
|
717
766
|
const vendorAdapter = await VendorFulfillmentService.getVendorAdapter(vendor.vendor_key);
|
|
718
767
|
if (!vendorAdapter) {
|
|
719
|
-
return
|
|
768
|
+
return c.json({ error: `No adapter found for vendor: ${vendor.vendor_key}` }, 404);
|
|
720
769
|
}
|
|
721
770
|
|
|
722
771
|
logger.info('Requesting refund from vendor', {
|
|
@@ -724,7 +773,7 @@ async function vendorRefund(req: any, res: any) {
|
|
|
724
773
|
vendorId: vendor.id,
|
|
725
774
|
orderId: payout.vendor_info.order_id,
|
|
726
775
|
refundInfo,
|
|
727
|
-
requestedBy:
|
|
776
|
+
requestedBy: c.get('user')?.did,
|
|
728
777
|
});
|
|
729
778
|
|
|
730
779
|
const result = await vendorAdapter.requestRefund({
|
|
@@ -739,7 +788,7 @@ async function vendorRefund(req: any, res: any) {
|
|
|
739
788
|
refundResult: result.data?.refundResult,
|
|
740
789
|
});
|
|
741
790
|
await payout.update({ status: 'reverted', description: reason || 'Refund requested by admin' });
|
|
742
|
-
return
|
|
791
|
+
return c.json({ data: result.data?.refundResult });
|
|
743
792
|
}
|
|
744
793
|
logger.error('Vendor payout refund failed', {
|
|
745
794
|
payoutId: payout.id,
|
|
@@ -747,49 +796,148 @@ async function vendorRefund(req: any, res: any) {
|
|
|
747
796
|
refundInfo,
|
|
748
797
|
refundResult: result.data?.refundResult,
|
|
749
798
|
});
|
|
750
|
-
return
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
799
|
+
return c.json(
|
|
800
|
+
{
|
|
801
|
+
error: result.message || 'Vendor payout refund failed',
|
|
802
|
+
data: result.data?.refundResult,
|
|
803
|
+
requestedBy: c.get('user')?.did,
|
|
804
|
+
},
|
|
805
|
+
400
|
|
806
|
+
);
|
|
755
807
|
} catch (err: any) {
|
|
756
808
|
logger.error('Request refund failed', {
|
|
757
809
|
error: err,
|
|
758
|
-
payoutId: req.
|
|
759
|
-
requestedBy:
|
|
810
|
+
payoutId: c.req.param('id'),
|
|
811
|
+
requestedBy: c.get('user')?.did,
|
|
760
812
|
});
|
|
761
|
-
return
|
|
813
|
+
return c.json({ error: err.message || 'Failed to request refund' }, 500);
|
|
762
814
|
}
|
|
763
815
|
}
|
|
764
816
|
|
|
765
|
-
function getVendorConnectTest(
|
|
766
|
-
const sdkVersion = req.
|
|
817
|
+
function getVendorConnectTest(c: any) {
|
|
818
|
+
const sdkVersion = c.req.header('x-broker-vendor-version');
|
|
767
819
|
if (sdkVersion && gte(sdkVersion, '1.21.4')) {
|
|
768
|
-
return
|
|
820
|
+
return c.json({ connected: true, sdkVersion });
|
|
769
821
|
}
|
|
770
|
-
return
|
|
822
|
+
return c.json({
|
|
771
823
|
connected: false,
|
|
772
824
|
sdkVersion,
|
|
773
825
|
error: 'Vendor SDK version is too low, please upgrade to the latest version (>=1.21.4)',
|
|
774
826
|
});
|
|
775
827
|
}
|
|
776
828
|
|
|
777
|
-
const
|
|
829
|
+
const app = new Hono();
|
|
778
830
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
)
|
|
831
|
+
// Hono-native version of middleware.ensureVendorAuth — inlined because the
|
|
832
|
+
// upstream middleware is express-specific (req.vendor, req.vendorRequest).
|
|
833
|
+
const ensureVendorAuth: MiddlewareHandler = async (c, next) => {
|
|
834
|
+
try {
|
|
835
|
+
let vendorPk = c.req.header('x-broker-vendor-did') as string;
|
|
836
|
+
const sig = c.req.header('x-broker-vendor-sig') as string;
|
|
837
|
+
|
|
838
|
+
if (!vendorPk) {
|
|
839
|
+
logger.warn('Authentication failed: Missing vendor ID header', { url: c.req.url });
|
|
840
|
+
return c.json(
|
|
841
|
+
{
|
|
842
|
+
error: 'Vendor public key is required',
|
|
843
|
+
code: 'MISSING_VENDOR_PUBLIC_KEY',
|
|
844
|
+
},
|
|
845
|
+
400
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
if (!sig) {
|
|
850
|
+
logger.warn('Authentication failed: Missing signature header', { url: c.req.url });
|
|
851
|
+
return c.json(
|
|
852
|
+
{
|
|
853
|
+
error: 'Vendor signature is required',
|
|
854
|
+
code: 'MISSING_SIGNATURE',
|
|
855
|
+
},
|
|
856
|
+
400
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
vendorPk = vendorPk.startsWith('0x') ? toBase58(vendorPk) : vendorPk;
|
|
861
|
+
const isGetRequest = c.req.method === 'GET';
|
|
862
|
+
const requestBody = isGetRequest ? (c.req.query() as any) : ((c.get('sanitizedBody') ?? {}) as any);
|
|
863
|
+
|
|
864
|
+
if (!requestBody || typeof requestBody !== 'object') {
|
|
865
|
+
return c.json(
|
|
866
|
+
{
|
|
867
|
+
error: 'Valid request body is required',
|
|
868
|
+
code: 'INVALID_REQUEST_BODY',
|
|
869
|
+
},
|
|
870
|
+
400
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const vendor = await ProductVendor.findOne({ where: { 'extends.appPk': vendorPk } }).then((v) => v as any);
|
|
875
|
+
|
|
876
|
+
if (!vendor) {
|
|
877
|
+
logger.warn('Authorization failed: Vendor not found or inactive', {
|
|
878
|
+
url: c.req.url,
|
|
879
|
+
vendorId: `${vendorPk.substring(0, 10)}...`,
|
|
880
|
+
});
|
|
881
|
+
return c.json(
|
|
882
|
+
{
|
|
883
|
+
error: 'Vendor not found or inactive',
|
|
884
|
+
code: 'VENDOR_NOT_ALLOWED',
|
|
885
|
+
},
|
|
886
|
+
403
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const signedRequest = { ...requestBody, signature: sig };
|
|
891
|
+
const verified = await VendorAuth.verifyRequest(signedRequest, vendor.extends.appPk);
|
|
892
|
+
|
|
893
|
+
if (!verified) {
|
|
894
|
+
logger.warn('Authentication failed: Invalid request signature', {
|
|
895
|
+
url: c.req.url,
|
|
896
|
+
vendorId: vendor.id,
|
|
897
|
+
appId: vendor.extends.appId,
|
|
898
|
+
});
|
|
899
|
+
return c.json(
|
|
900
|
+
{
|
|
901
|
+
error: 'Invalid request signature',
|
|
902
|
+
code: 'INVALID_SIGNATURE',
|
|
903
|
+
},
|
|
904
|
+
401
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
c.set('vendor' as any, vendor);
|
|
909
|
+
c.set('vendorRequest' as any, signedRequest);
|
|
910
|
+
|
|
911
|
+
// eslint-disable-next-line @typescript-eslint/return-await -- do NOT await: downstream errors must not be caught by this auth catch
|
|
912
|
+
return next();
|
|
913
|
+
} catch (error: any) {
|
|
914
|
+
logger.error('Vendor authentication middleware error', {
|
|
915
|
+
url: c.req.url,
|
|
916
|
+
error: error.message,
|
|
917
|
+
stack: error.stack,
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
return c.json(
|
|
921
|
+
{
|
|
922
|
+
error: 'Authentication failed',
|
|
923
|
+
code: 'AUTHENTICATION_ERROR',
|
|
924
|
+
details: isDevelopmentEnv() ? error.message : undefined,
|
|
925
|
+
},
|
|
926
|
+
500
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
};
|
|
782
930
|
|
|
783
|
-
|
|
784
|
-
|
|
931
|
+
app.get('/order/:sessionId/status', loginAuth, validateParams(sessionIdParamSchema), getVendorFulfillmentStatus);
|
|
932
|
+
app.get('/order/:sessionId/detail', loginAuth, validateParams(sessionIdParamSchema), getVendorFulfillmentDetail);
|
|
785
933
|
|
|
786
934
|
// Those for Vendor Call
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
935
|
+
app.get('/connectTest', ensureVendorAuth, getVendorConnectTest);
|
|
936
|
+
app.post('/subscription/cancelled', ensureVendorAuth, getCancelledSessions);
|
|
937
|
+
app.get('/subscription/:sessionId/redirect', handleSubscriptionRedirect);
|
|
938
|
+
app.get('/subscription/:sessionId', ensureVendorAuth, getVendorSubscription);
|
|
791
939
|
|
|
792
|
-
|
|
940
|
+
app.get(
|
|
793
941
|
'/open/:subscriptionId',
|
|
794
942
|
loginAuth,
|
|
795
943
|
validateParams(subscriptionIdParamSchema),
|
|
@@ -797,14 +945,14 @@ router.get(
|
|
|
797
945
|
redirectToVendor
|
|
798
946
|
);
|
|
799
947
|
|
|
800
|
-
|
|
948
|
+
app.post('/refund/:id', authAdmin, vendorRefund);
|
|
801
949
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
950
|
+
app.get('/', getAllVendors);
|
|
951
|
+
app.get('/:id', authAdmin, validateParams(vendorIdParamSchema), getVendorInfo);
|
|
952
|
+
app.post('/', authAdmin, createVendor);
|
|
953
|
+
app.put('/:id', authAdmin, validateParams(vendorIdParamSchema), updateVendor);
|
|
954
|
+
app.delete('/:id', authAdmin, validateParams(vendorIdParamSchema), deleteVendor);
|
|
807
955
|
|
|
808
|
-
|
|
956
|
+
app.post('/:id/test-connection', authAdmin, validateParams(vendorIdParamSchema), testVendorConnection);
|
|
809
957
|
|
|
810
|
-
export default
|
|
958
|
+
export default app;
|