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,12 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
// Phase 3 (express→hono) — hono fork of routes/credit-grants.ts. Sub-app with
|
|
2
|
+
// routes relative to /api/credit-grants (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
|
+
// req.(c.get('customer_id') ?? query.customer_id) → (c.get('customer_id') ?? c.req.query('customer_id')) [SECURITY]
|
|
6
|
+
import { Hono } from 'hono';
|
|
2
7
|
import Joi from 'joi';
|
|
3
8
|
import { BN, fromTokenToUnit } from '@ocap/util';
|
|
4
9
|
|
|
5
10
|
import { literal, OrderItem, fn, col, Op } from 'sequelize';
|
|
6
11
|
import pick from 'lodash/pick';
|
|
7
|
-
import { createListParamSchema, getOrder, getWhereFromKvQuery, MetadataSchema } from '
|
|
8
|
-
import logger from '
|
|
9
|
-
import { authenticate } from '
|
|
12
|
+
import { createListParamSchema, getOrder, getWhereFromKvQuery, MetadataSchema } from '../../libs/api';
|
|
13
|
+
import logger from '../../libs/logger';
|
|
14
|
+
import { authenticate } from '../../middlewares/hono/security';
|
|
10
15
|
import {
|
|
11
16
|
AutoRechargeConfig,
|
|
12
17
|
ChainType,
|
|
@@ -19,22 +24,22 @@ import {
|
|
|
19
24
|
Price,
|
|
20
25
|
Product,
|
|
21
26
|
Subscription,
|
|
22
|
-
} from '
|
|
23
|
-
import { createCreditGrant, getCreditGrantStats } from '
|
|
24
|
-
import { expireGrant } from '
|
|
25
|
-
import { getMeterPriceIdsFromSubscription } from '
|
|
26
|
-
import { blocklet } from '
|
|
27
|
-
import { formatMetadata } from '
|
|
28
|
-
import { getPriceUintAmountByCurrency } from '
|
|
29
|
-
import { checkTokenBalance } from '
|
|
30
|
-
import { measurePhase } from '
|
|
31
|
-
import { getExchangeRateService } from '
|
|
32
|
-
import { getExchangeRateSymbol, hasTokenAddress } from '
|
|
33
|
-
import { isRateBelowMinAcceptableRate } from '
|
|
34
|
-
import { trimDecimals } from '
|
|
35
|
-
import { systemMaxPendingAmount } from '
|
|
36
|
-
|
|
37
|
-
const
|
|
27
|
+
} from '../../store/models';
|
|
28
|
+
import { createCreditGrant, getCreditGrantStats } from '../../libs/credit-grant';
|
|
29
|
+
import { expireGrant } from '../../queues/credit-grant';
|
|
30
|
+
import { getMeterPriceIdsFromSubscription } from '../../libs/subscription';
|
|
31
|
+
import { blocklet } from '../../libs/auth';
|
|
32
|
+
import { formatMetadata } from '../../libs/util';
|
|
33
|
+
import { getPriceUintAmountByCurrency } from '../../libs/price';
|
|
34
|
+
import { checkTokenBalance } from '../../libs/payment';
|
|
35
|
+
import { measurePhase } from '../../libs/timing';
|
|
36
|
+
import { getExchangeRateService } from '../../libs/exchange-rate/service';
|
|
37
|
+
import { getExchangeRateSymbol, hasTokenAddress } from '../../libs/exchange-rate/token-address-mapping';
|
|
38
|
+
import { isRateBelowMinAcceptableRate } from '../../libs/slippage';
|
|
39
|
+
import { trimDecimals } from '../../libs/math-utils';
|
|
40
|
+
import { systemMaxPendingAmount } from '../../libs/env';
|
|
41
|
+
|
|
42
|
+
const app = new Hono();
|
|
38
43
|
const auth = authenticate<CreditGrant>({ component: true, roles: ['owner', 'admin'] });
|
|
39
44
|
const authMine = authenticate<CreditGrant>({ component: true, roles: ['owner', 'admin'], mine: true });
|
|
40
45
|
const authPortal = authenticate<CreditGrant>({
|
|
@@ -92,14 +97,14 @@ async function expandScopePrices(creditGrant: CreditGrant) {
|
|
|
92
97
|
return [];
|
|
93
98
|
}
|
|
94
99
|
|
|
95
|
-
|
|
100
|
+
app.get('/', authMine, async (c) => {
|
|
96
101
|
try {
|
|
97
|
-
const { page, pageSize, ...query } = await listSchema.validateAsync(req.query, { stripUnknown: true });
|
|
102
|
+
const { page, pageSize, ...query } = await listSchema.validateAsync(c.req.query(), { stripUnknown: true });
|
|
98
103
|
const where = getWhereFromKvQuery(query.q);
|
|
99
|
-
if (query.customer_id) {
|
|
100
|
-
const customer = await Customer.findByPkOrDid(query.customer_id);
|
|
104
|
+
if (c.get('customer_id') ?? query.customer_id) {
|
|
105
|
+
const customer = await Customer.findByPkOrDid(c.get('customer_id') ?? query.customer_id);
|
|
101
106
|
if (!customer) {
|
|
102
|
-
throw new Error(`Customer ${query.customer_id} not found`);
|
|
107
|
+
throw new Error(`Customer ${c.get('customer_id') ?? query.customer_id} not found`);
|
|
103
108
|
}
|
|
104
109
|
where.customer_id = customer.id;
|
|
105
110
|
}
|
|
@@ -128,7 +133,7 @@ router.get('/', authMine, async (req, res) => {
|
|
|
128
133
|
where.livemode = query.livemode;
|
|
129
134
|
}
|
|
130
135
|
|
|
131
|
-
const order: OrderItem[] = getOrder(req.query);
|
|
136
|
+
const order: OrderItem[] = getOrder(c.req.query());
|
|
132
137
|
// 默认granted 、pending、depleted 排序
|
|
133
138
|
order.unshift([literal("CASE status WHEN 'granted' THEN 1 WHEN 'pending' THEN 2 ELSE 3 END"), 'ASC']);
|
|
134
139
|
|
|
@@ -143,35 +148,36 @@ router.get('/', authMine, async (req, res) => {
|
|
|
143
148
|
],
|
|
144
149
|
});
|
|
145
150
|
|
|
146
|
-
|
|
151
|
+
return c.json({ count, list, paging: { page, pageSize } });
|
|
147
152
|
} catch (err) {
|
|
148
153
|
logger.error('Error listing credit grants', err);
|
|
149
|
-
|
|
154
|
+
return c.json({ error: err.message }, 400);
|
|
150
155
|
}
|
|
151
156
|
});
|
|
152
157
|
|
|
153
|
-
|
|
158
|
+
// Static path — registered before /:id so hono matches it first.
|
|
159
|
+
app.get('/summary', authMine, async (c) => {
|
|
154
160
|
try {
|
|
155
|
-
const customerId = req.query
|
|
161
|
+
const customerId = (c.get('customer_id') ?? c.req.query('customer_id')) as string | undefined;
|
|
156
162
|
if (!customerId) {
|
|
157
|
-
return
|
|
163
|
+
return c.json({ error: 'customer_id is required' }, 400);
|
|
158
164
|
}
|
|
159
165
|
|
|
160
166
|
const customer = await Customer.findByPkOrDid(customerId as string);
|
|
161
167
|
if (!customer) {
|
|
162
|
-
return
|
|
168
|
+
return c.json({ error: `Customer ${customerId} not found` }, 404);
|
|
163
169
|
}
|
|
164
170
|
|
|
165
|
-
const
|
|
171
|
+
const subscriptionId = c.req.query('subscription_id');
|
|
166
172
|
if (subscriptionId && typeof subscriptionId !== 'string') {
|
|
167
|
-
return
|
|
173
|
+
return c.json({ error: 'subscription_id must be a string' }, 400);
|
|
168
174
|
}
|
|
169
175
|
|
|
170
176
|
let priceIds: string[] = [];
|
|
171
177
|
if (subscriptionId) {
|
|
172
178
|
const subscription = await Subscription.findByPk(subscriptionId);
|
|
173
179
|
if (!subscription) {
|
|
174
|
-
return
|
|
180
|
+
return c.json({ error: 'Subscription not found' }, 404);
|
|
175
181
|
}
|
|
176
182
|
priceIds = await getMeterPriceIdsFromSubscription(subscription);
|
|
177
183
|
}
|
|
@@ -181,10 +187,10 @@ router.get('/summary', authMine, async (req, res) => {
|
|
|
181
187
|
priceIds,
|
|
182
188
|
});
|
|
183
189
|
|
|
184
|
-
return
|
|
190
|
+
return c.json(result);
|
|
185
191
|
} catch (err: any) {
|
|
186
|
-
logger.error('get credit balance failed', { error: err.message, customerId: req.
|
|
187
|
-
return
|
|
192
|
+
logger.error('get credit balance failed', { error: err.message, customerId: c.req.param('customer_id') });
|
|
193
|
+
return c.json({ error: err.message }, 400);
|
|
188
194
|
}
|
|
189
195
|
});
|
|
190
196
|
|
|
@@ -196,22 +202,23 @@ const holdersSchema = Joi.object({
|
|
|
196
202
|
});
|
|
197
203
|
|
|
198
204
|
// Get all holders (customers with balance) for a specific credit currency
|
|
199
|
-
|
|
205
|
+
// Static path — registered before /:id so hono matches it first.
|
|
206
|
+
app.get('/holders', auth, async (c) => {
|
|
200
207
|
try {
|
|
201
|
-
const { error, value } = holdersSchema.validate(req.query, { stripUnknown: true });
|
|
208
|
+
const { error, value } = holdersSchema.validate(c.req.query(), { stripUnknown: true });
|
|
202
209
|
if (error) {
|
|
203
|
-
return
|
|
210
|
+
return c.json({ error: error.message }, 400);
|
|
204
211
|
}
|
|
205
212
|
|
|
206
213
|
const { currency_id: currencyId, page, pageSize, livemode } = value;
|
|
207
214
|
|
|
208
215
|
const currency = await PaymentCurrency.findByPk(currencyId);
|
|
209
216
|
if (!currency) {
|
|
210
|
-
return
|
|
217
|
+
return c.json({ error: `PaymentCurrency ${currencyId} not found` }, 404);
|
|
211
218
|
}
|
|
212
219
|
|
|
213
220
|
if (currency.type !== 'credit') {
|
|
214
|
-
return
|
|
221
|
+
return c.json({ error: 'Currency must be of type credit' }, 400);
|
|
215
222
|
}
|
|
216
223
|
|
|
217
224
|
// Build where clause for credit grants
|
|
@@ -250,7 +257,7 @@ router.get('/holders', auth, async (req, res) => {
|
|
|
250
257
|
grantCount: parseInt(item.grantCount || '0', 10),
|
|
251
258
|
}));
|
|
252
259
|
|
|
253
|
-
return
|
|
260
|
+
return c.json({
|
|
254
261
|
holders,
|
|
255
262
|
currency: {
|
|
256
263
|
id: currency.id,
|
|
@@ -267,7 +274,7 @@ router.get('/holders', auth, async (req, res) => {
|
|
|
267
274
|
});
|
|
268
275
|
} catch (err: any) {
|
|
269
276
|
logger.error('Error getting credit holders', { error: err.message });
|
|
270
|
-
return
|
|
277
|
+
return c.json({ error: err.message }, 400);
|
|
271
278
|
}
|
|
272
279
|
});
|
|
273
280
|
|
|
@@ -281,11 +288,12 @@ const checkAutoRechargeSchema = Joi.object({
|
|
|
281
288
|
.optional(),
|
|
282
289
|
});
|
|
283
290
|
|
|
284
|
-
|
|
291
|
+
// Static path — registered before /:id so hono matches it first.
|
|
292
|
+
app.get('/verify-availability', authMine, async (c) => {
|
|
285
293
|
try {
|
|
286
|
-
const { error, value } = checkAutoRechargeSchema.validate(req.query, { stripUnknown: true });
|
|
294
|
+
const { error, value } = checkAutoRechargeSchema.validate(c.req.query(), { stripUnknown: true });
|
|
287
295
|
if (error) {
|
|
288
|
-
return
|
|
296
|
+
return c.json({ error: error.message }, 400);
|
|
289
297
|
}
|
|
290
298
|
|
|
291
299
|
const { customer_id: customerId, currency_id: currencyId } = value;
|
|
@@ -297,10 +305,10 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
297
305
|
PaymentCurrency.findByPk(currencyId),
|
|
298
306
|
]);
|
|
299
307
|
if (!customer) {
|
|
300
|
-
return
|
|
308
|
+
return c.json({ error: `Customer ${customerId} not found` }, 404);
|
|
301
309
|
}
|
|
302
310
|
if (!currency) {
|
|
303
|
-
return
|
|
311
|
+
return c.json({ error: `PaymentCurrency ${currencyId} not found` }, 404);
|
|
304
312
|
}
|
|
305
313
|
|
|
306
314
|
// AutoRechargeConfig needs customer.id which we now have
|
|
@@ -324,7 +332,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
324
332
|
| null;
|
|
325
333
|
|
|
326
334
|
if (!config) {
|
|
327
|
-
return
|
|
335
|
+
return c.json({
|
|
328
336
|
can_continue: false,
|
|
329
337
|
has_auto_recharge: false,
|
|
330
338
|
reason: 'auto_recharge_config_not_found',
|
|
@@ -333,21 +341,21 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
333
341
|
|
|
334
342
|
// 1. Check config completeness
|
|
335
343
|
if (!config.rechargeCurrency) {
|
|
336
|
-
return
|
|
344
|
+
return c.json({
|
|
337
345
|
can_continue: false,
|
|
338
346
|
reason: 'recharge_currency_not_found',
|
|
339
347
|
});
|
|
340
348
|
}
|
|
341
349
|
|
|
342
350
|
if (!config.price) {
|
|
343
|
-
return
|
|
351
|
+
return c.json({
|
|
344
352
|
can_continue: false,
|
|
345
353
|
reason: 'price_not_found',
|
|
346
354
|
});
|
|
347
355
|
}
|
|
348
356
|
|
|
349
357
|
if (!config.paymentMethod) {
|
|
350
|
-
return
|
|
358
|
+
return c.json({
|
|
351
359
|
can_continue: false,
|
|
352
360
|
reason: 'payment_method_not_found',
|
|
353
361
|
});
|
|
@@ -355,7 +363,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
355
363
|
|
|
356
364
|
// 2. Check if stripe (balance check not supported)
|
|
357
365
|
if (config.paymentMethod.type === 'stripe') {
|
|
358
|
-
return
|
|
366
|
+
return c.json({
|
|
359
367
|
can_continue: false,
|
|
360
368
|
reason: 'balance_check_not_supported',
|
|
361
369
|
});
|
|
@@ -364,7 +372,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
364
372
|
// 3. Check price amount
|
|
365
373
|
const priceAmount = await getPriceUintAmountByCurrency(config.price, config.rechargeCurrency.id);
|
|
366
374
|
if (!priceAmount) {
|
|
367
|
-
return
|
|
375
|
+
return c.json({
|
|
368
376
|
can_continue: false,
|
|
369
377
|
reason: 'invalid_price_amount',
|
|
370
378
|
});
|
|
@@ -378,7 +386,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
378
386
|
const [pendingSummary] = await MeterEvent.getPendingAmounts({
|
|
379
387
|
customerId: customer.id,
|
|
380
388
|
currencyId,
|
|
381
|
-
livemode:
|
|
389
|
+
livemode: c.get('livemode'),
|
|
382
390
|
});
|
|
383
391
|
pendingAmount = pendingSummary?.[currencyId] || '0';
|
|
384
392
|
}
|
|
@@ -386,14 +394,14 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
386
394
|
const pendingAmountBN = new BN(pendingAmount);
|
|
387
395
|
|
|
388
396
|
// 5. Check system-level maximum pending amount limit (highest priority, cannot be bypassed)
|
|
389
|
-
// systemMaxPendingAmount is configured via PAYMENT_KIT_MAX_PENDING_AMOUNT (in token format)
|
|
390
|
-
if (systemMaxPendingAmount > 0 && pendingAmountBN.gt(new BN(0))) {
|
|
397
|
+
// systemMaxPendingAmount() is configured via PAYMENT_KIT_MAX_PENDING_AMOUNT (in token format)
|
|
398
|
+
if (systemMaxPendingAmount() > 0 && pendingAmountBN.gt(new BN(0))) {
|
|
391
399
|
const systemMaxPendingAmountBN = fromTokenToUnit(
|
|
392
|
-
trimDecimals(String(systemMaxPendingAmount), currency.decimal),
|
|
400
|
+
trimDecimals(String(systemMaxPendingAmount()), currency.decimal),
|
|
393
401
|
currency.decimal
|
|
394
402
|
);
|
|
395
403
|
if (pendingAmountBN.gt(systemMaxPendingAmountBN)) {
|
|
396
|
-
return
|
|
404
|
+
return c.json({
|
|
397
405
|
can_continue: false,
|
|
398
406
|
reason: 'system_pending_limit_exceeded',
|
|
399
407
|
pending_amount: pendingAmount,
|
|
@@ -411,7 +419,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
411
419
|
currency.decimal
|
|
412
420
|
);
|
|
413
421
|
if (pendingAmountBN.gt(maxPendingAmountBN)) {
|
|
414
|
-
return
|
|
422
|
+
return c.json({
|
|
415
423
|
can_continue: false,
|
|
416
424
|
reason: 'pending_amount_exceeds_limit',
|
|
417
425
|
pending_amount: pendingAmount,
|
|
@@ -428,7 +436,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
428
436
|
]?.payer || customer.did;
|
|
429
437
|
|
|
430
438
|
if (!payer) {
|
|
431
|
-
return
|
|
439
|
+
return c.json({
|
|
432
440
|
can_continue: false,
|
|
433
441
|
reason: 'payer_not_found',
|
|
434
442
|
});
|
|
@@ -440,7 +448,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
440
448
|
// Get credit amount per recharge from price metadata
|
|
441
449
|
const creditConfig = config.price.metadata?.credit_config;
|
|
442
450
|
if (!creditConfig || !creditConfig.credit_amount) {
|
|
443
|
-
return
|
|
451
|
+
return c.json({
|
|
444
452
|
can_continue: false,
|
|
445
453
|
reason: 'credit_config_not_found',
|
|
446
454
|
});
|
|
@@ -461,7 +469,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
461
469
|
// Check if required recharge times exceeds limit
|
|
462
470
|
const maxRechargeTimes = value.max_recharge_times;
|
|
463
471
|
if (requiredRechargeTimesBN.gt(new BN(maxRechargeTimes))) {
|
|
464
|
-
return
|
|
472
|
+
return c.json({
|
|
465
473
|
can_continue: false,
|
|
466
474
|
reason: 'too_many_recharges_required',
|
|
467
475
|
pending_amount: pendingAmount,
|
|
@@ -489,7 +497,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
489
497
|
const requiredRechargeTimes = requiredRechargeTimesBN.toNumber();
|
|
490
498
|
const remainingAttempts = maxAttempts - Number(attemptCount);
|
|
491
499
|
if (requiredRechargeTimes > remainingAttempts) {
|
|
492
|
-
return
|
|
500
|
+
return c.json({
|
|
493
501
|
can_continue: false,
|
|
494
502
|
reason: 'daily_limit_reached',
|
|
495
503
|
detail: 'attempt_limit_exceeded',
|
|
@@ -502,7 +510,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
502
510
|
if (maxAmount.gt(new BN(0))) {
|
|
503
511
|
const remainingAmount = maxAmount.sub(new BN(totalAmountStats || '0'));
|
|
504
512
|
if (requiredPaymentAmount.gt(remainingAmount)) {
|
|
505
|
-
return
|
|
513
|
+
return c.json({
|
|
506
514
|
can_continue: false,
|
|
507
515
|
reason: 'daily_limit_reached',
|
|
508
516
|
detail: 'amount_limit_exceeded',
|
|
@@ -526,7 +534,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
526
534
|
);
|
|
527
535
|
|
|
528
536
|
if (!balanceResult.sufficient) {
|
|
529
|
-
return
|
|
537
|
+
return c.json({
|
|
530
538
|
can_continue: false,
|
|
531
539
|
reason: 'insufficient_balance',
|
|
532
540
|
payment_account_balance: balanceResult.token?.balance || '0',
|
|
@@ -548,7 +556,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
548
556
|
);
|
|
549
557
|
|
|
550
558
|
if (!balanceResult.sufficient) {
|
|
551
|
-
return
|
|
559
|
+
return c.json({
|
|
552
560
|
can_continue: false,
|
|
553
561
|
reason: 'insufficient_balance',
|
|
554
562
|
payment_account_balance: balanceResult.token?.balance || '0',
|
|
@@ -568,7 +576,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
568
576
|
// 7a. Check if exchange rate is available
|
|
569
577
|
const methodType = config.paymentMethod.type as ChainType;
|
|
570
578
|
if (!hasTokenAddress(config.rechargeCurrency.symbol, methodType)) {
|
|
571
|
-
return
|
|
579
|
+
return c.json({
|
|
572
580
|
can_continue: false,
|
|
573
581
|
reason: 'exchange_rate_not_supported',
|
|
574
582
|
is_dynamic_pricing: true,
|
|
@@ -590,7 +598,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
590
598
|
error: err.message,
|
|
591
599
|
symbol: config.rechargeCurrency.symbol,
|
|
592
600
|
});
|
|
593
|
-
return
|
|
601
|
+
return c.json({
|
|
594
602
|
can_continue: false,
|
|
595
603
|
reason: 'exchange_rate_fetch_failed',
|
|
596
604
|
is_dynamic_pricing: true,
|
|
@@ -610,7 +618,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
610
618
|
};
|
|
611
619
|
|
|
612
620
|
if (rateBelowMin) {
|
|
613
|
-
return
|
|
621
|
+
return c.json({
|
|
614
622
|
can_continue: false,
|
|
615
623
|
reason: 'slippage_exceeded',
|
|
616
624
|
is_dynamic_pricing: true,
|
|
@@ -636,7 +644,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
636
644
|
canCoverPending = creditAmount.gte(pendingAmountBN);
|
|
637
645
|
|
|
638
646
|
if (!canCoverPending) {
|
|
639
|
-
return
|
|
647
|
+
return c.json({
|
|
640
648
|
can_continue: false,
|
|
641
649
|
reason: 'cannot_cover_pending_single_recharge',
|
|
642
650
|
is_dynamic_pricing: true,
|
|
@@ -649,7 +657,7 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
649
657
|
}
|
|
650
658
|
}
|
|
651
659
|
|
|
652
|
-
return
|
|
660
|
+
return c.json({
|
|
653
661
|
can_continue: true,
|
|
654
662
|
payment_account_sufficient: true,
|
|
655
663
|
payment_account_balance: balanceResult?.token?.balance || '0',
|
|
@@ -662,10 +670,10 @@ router.get('/verify-availability', authMine, async (req, res) => {
|
|
|
662
670
|
} catch (err: any) {
|
|
663
671
|
logger.error('check auto recharge failed', {
|
|
664
672
|
error: err.message,
|
|
665
|
-
customerId: req.query
|
|
666
|
-
currencyId: req.query
|
|
673
|
+
customerId: c.get('customer_id') ?? c.req.query('customer_id'),
|
|
674
|
+
currencyId: c.req.query('currency_id'),
|
|
667
675
|
});
|
|
668
|
-
return
|
|
676
|
+
return c.json({ error: err.message }, 400);
|
|
669
677
|
}
|
|
670
678
|
});
|
|
671
679
|
|
|
@@ -686,11 +694,12 @@ const statsSchema = Joi.object({
|
|
|
686
694
|
});
|
|
687
695
|
|
|
688
696
|
// Get credit grant statistics with flexible filtering
|
|
689
|
-
|
|
697
|
+
// Static path — registered before /:id so hono matches it first.
|
|
698
|
+
app.get('/stats', auth, async (c) => {
|
|
690
699
|
try {
|
|
691
|
-
const { error, value } = statsSchema.validate(req.query, { stripUnknown: true });
|
|
700
|
+
const { error, value } = statsSchema.validate(c.req.query(), { stripUnknown: true });
|
|
692
701
|
if (error) {
|
|
693
|
-
return
|
|
702
|
+
return c.json({ error: error.message }, 400);
|
|
694
703
|
}
|
|
695
704
|
|
|
696
705
|
const {
|
|
@@ -703,7 +712,7 @@ router.get('/stats', auth, async (req, res) => {
|
|
|
703
712
|
} = value;
|
|
704
713
|
|
|
705
714
|
if (startDate > endDate) {
|
|
706
|
-
return
|
|
715
|
+
return c.json({ error: 'start_date must be less than or equal to end_date' }, 400);
|
|
707
716
|
}
|
|
708
717
|
|
|
709
718
|
const result = await getCreditGrantStats({
|
|
@@ -715,22 +724,22 @@ router.get('/stats', auth, async (req, res) => {
|
|
|
715
724
|
timezoneOffset,
|
|
716
725
|
});
|
|
717
726
|
|
|
718
|
-
return
|
|
727
|
+
return c.json(result);
|
|
719
728
|
} catch (err: any) {
|
|
720
|
-
logger.error('Error getting credit grant stats', { error: err.message, query: req.query });
|
|
721
|
-
return
|
|
729
|
+
logger.error('Error getting credit grant stats', { error: err.message, query: c.req.query() });
|
|
730
|
+
return c.json({ error: err.message }, 400);
|
|
722
731
|
}
|
|
723
732
|
});
|
|
724
733
|
|
|
725
|
-
|
|
726
|
-
const creditGrant = (await CreditGrant.findByPk(req.
|
|
734
|
+
app.get('/:id', authPortal, async (c) => {
|
|
735
|
+
const creditGrant = (await CreditGrant.findByPk(c.req.param('id'), {
|
|
727
736
|
include: [
|
|
728
737
|
{ model: Customer, as: 'customer' },
|
|
729
738
|
{ model: PaymentCurrency, as: 'paymentCurrency' },
|
|
730
739
|
],
|
|
731
740
|
})) as CreditGrant & { paymentCurrency?: PaymentCurrency };
|
|
732
741
|
if (!creditGrant) {
|
|
733
|
-
return
|
|
742
|
+
return c.json({ error: `Credit grant ${c.req.param('id')} not found` }, 404);
|
|
734
743
|
}
|
|
735
744
|
|
|
736
745
|
let paymentMethod = null;
|
|
@@ -738,40 +747,41 @@ router.get('/:id', authPortal, async (req, res) => {
|
|
|
738
747
|
paymentMethod = await PaymentMethod.findByPk(creditGrant.paymentCurrency.payment_method_id);
|
|
739
748
|
}
|
|
740
749
|
const expandedPrices = await expandScopePrices(creditGrant);
|
|
741
|
-
return
|
|
750
|
+
return c.json({
|
|
742
751
|
...creditGrant.toJSON(),
|
|
743
752
|
items: expandedPrices,
|
|
744
753
|
paymentMethod,
|
|
745
754
|
});
|
|
746
755
|
});
|
|
747
756
|
|
|
748
|
-
|
|
757
|
+
app.post('/', auth, async (c) => {
|
|
749
758
|
try {
|
|
750
|
-
const
|
|
759
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
760
|
+
const { error } = creditGrantSchema.validate(body);
|
|
751
761
|
if (error) {
|
|
752
|
-
return
|
|
762
|
+
return c.json({ error: `Credit grant create request invalid: ${error.message}` }, 400);
|
|
753
763
|
}
|
|
754
764
|
|
|
755
765
|
// 获取币种信息用于金额转换
|
|
756
|
-
const currencyId =
|
|
766
|
+
const currencyId = body.currency_id;
|
|
757
767
|
if (!currencyId) {
|
|
758
|
-
return
|
|
768
|
+
return c.json({ error: 'currency_id is required' }, 400);
|
|
759
769
|
}
|
|
760
770
|
|
|
761
771
|
const paymentCurrency = await PaymentCurrency.findByPk(currencyId);
|
|
762
772
|
if (!paymentCurrency) {
|
|
763
|
-
return
|
|
773
|
+
return c.json({ error: `PaymentCurrency ${currencyId} not found` }, 404);
|
|
764
774
|
}
|
|
765
775
|
|
|
766
|
-
let customer = await Customer.findByPkOrDid(
|
|
776
|
+
let customer = await Customer.findByPkOrDid(body.customer_id);
|
|
767
777
|
if (!customer) {
|
|
768
|
-
if (
|
|
769
|
-
return
|
|
778
|
+
if (body.customer_id.startsWith('cus_')) {
|
|
779
|
+
return c.json({ error: `Customer ${body.customer_id} not found` }, 404);
|
|
770
780
|
}
|
|
771
|
-
const did =
|
|
781
|
+
const did = body.customer_id;
|
|
772
782
|
const { user: userInfo } = await blocklet.getUser(did);
|
|
773
783
|
if (!userInfo) {
|
|
774
|
-
return
|
|
784
|
+
return c.json({ error: `User ${did} not found` }, 404);
|
|
775
785
|
}
|
|
776
786
|
customer = await Customer.create({
|
|
777
787
|
livemode: true,
|
|
@@ -793,9 +803,9 @@ router.post('/', auth, async (req, res) => {
|
|
|
793
803
|
});
|
|
794
804
|
}
|
|
795
805
|
|
|
796
|
-
const unitAmount = fromTokenToUnit(
|
|
797
|
-
let applicabilityConfig =
|
|
798
|
-
if (!
|
|
806
|
+
const unitAmount = fromTokenToUnit(body.amount, paymentCurrency.decimal).toString();
|
|
807
|
+
let applicabilityConfig = body.applicability_config;
|
|
808
|
+
if (!body.applicability_config || !body.applicability_config.scope?.prices) {
|
|
799
809
|
applicabilityConfig = {
|
|
800
810
|
scope: {
|
|
801
811
|
price_type: 'metered',
|
|
@@ -807,26 +817,26 @@ router.post('/', auth, async (req, res) => {
|
|
|
807
817
|
amount: unitAmount,
|
|
808
818
|
currency_id: currencyId,
|
|
809
819
|
customer_id: customer.id,
|
|
810
|
-
name:
|
|
811
|
-
category:
|
|
812
|
-
priority:
|
|
813
|
-
effective_at:
|
|
814
|
-
expires_at:
|
|
820
|
+
name: body.name,
|
|
821
|
+
category: body.category,
|
|
822
|
+
priority: body.priority,
|
|
823
|
+
effective_at: body.effective_at,
|
|
824
|
+
expires_at: body.expires_at,
|
|
815
825
|
applicability_config: applicabilityConfig,
|
|
816
|
-
metadata:
|
|
817
|
-
livemode:
|
|
818
|
-
created_via:
|
|
819
|
-
created_by:
|
|
826
|
+
metadata: body.metadata,
|
|
827
|
+
livemode: c.get('livemode'),
|
|
828
|
+
created_via: c.get('user')?.via || 'api',
|
|
829
|
+
created_by: c.get('user')?.did,
|
|
820
830
|
});
|
|
821
831
|
|
|
822
|
-
return
|
|
832
|
+
return c.json({
|
|
823
833
|
...creditGrant.toJSON(),
|
|
824
834
|
customer,
|
|
825
835
|
paymentCurrency,
|
|
826
836
|
});
|
|
827
837
|
} catch (err: any) {
|
|
828
|
-
logger.error('create credit grant failed', { error: err, request:
|
|
829
|
-
return
|
|
838
|
+
logger.error('create credit grant failed', { error: err, request: c.get('sanitizedBody') ?? {} });
|
|
839
|
+
return c.json({ error: err.message }, 400);
|
|
830
840
|
}
|
|
831
841
|
});
|
|
832
842
|
|
|
@@ -835,15 +845,16 @@ const updateSchema = Joi.object({
|
|
|
835
845
|
expired: Joi.boolean().optional(),
|
|
836
846
|
});
|
|
837
847
|
|
|
838
|
-
|
|
839
|
-
const
|
|
848
|
+
app.put('/:id', auth, async (c) => {
|
|
849
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
850
|
+
const creditGrant = await CreditGrant.findByPk(c.req.param('id'));
|
|
840
851
|
if (!creditGrant) {
|
|
841
|
-
return
|
|
852
|
+
return c.json({ error: `Credit grant ${c.req.param('id')} not found` }, 404);
|
|
842
853
|
}
|
|
843
854
|
|
|
844
|
-
const { error, value } = updateSchema.validate(pick(
|
|
855
|
+
const { error, value } = updateSchema.validate(pick(body, ['metadata', 'expired']), { stripUnknown: true });
|
|
845
856
|
if (error) {
|
|
846
|
-
return
|
|
857
|
+
return c.json({ error: `Credit grant update request invalid: ${error.message}` }, 400);
|
|
847
858
|
}
|
|
848
859
|
|
|
849
860
|
// Handle metadata update
|
|
@@ -855,21 +866,24 @@ router.put('/:id', auth, async (req, res) => {
|
|
|
855
866
|
if (value.expired === true) {
|
|
856
867
|
// Only pending or granted grants can be expired
|
|
857
868
|
if (!['pending', 'granted'].includes(creditGrant.status)) {
|
|
858
|
-
return
|
|
859
|
-
|
|
860
|
-
|
|
869
|
+
return c.json(
|
|
870
|
+
{
|
|
871
|
+
error: `Cannot expire credit grant with status '${creditGrant.status}'. Only 'pending' or 'granted' grants can be expired.`,
|
|
872
|
+
},
|
|
873
|
+
400
|
|
874
|
+
);
|
|
861
875
|
}
|
|
862
876
|
|
|
863
877
|
await expireGrant(creditGrant);
|
|
864
878
|
|
|
865
879
|
logger.info('Credit grant manually expired', {
|
|
866
|
-
creditGrantId: req.
|
|
880
|
+
creditGrantId: c.req.param('id'),
|
|
867
881
|
previousStatus: creditGrant.status,
|
|
868
|
-
requestedBy:
|
|
882
|
+
requestedBy: c.get('user')?.did,
|
|
869
883
|
});
|
|
870
884
|
}
|
|
871
885
|
|
|
872
|
-
return
|
|
886
|
+
return c.json({ success: true });
|
|
873
887
|
});
|
|
874
888
|
|
|
875
|
-
export default
|
|
889
|
+
export default app;
|