payment-kit 1.29.0 → 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/docs/2026-06-10-bundle-size-analysis.md +288 -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 +31 -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/node-fetch.ts +35 -0
- 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,70 +1,79 @@
|
|
|
1
|
+
// Phase 3 (express→hono) — hono fork of routes/payment-currencies.ts. Sub-app with
|
|
2
|
+
// routes relative to /api/payment-currencies (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).
|
|
1
5
|
import { fromTokenToUnit } from '@ocap/util';
|
|
2
|
-
import {
|
|
6
|
+
import { Hono } from 'hono';
|
|
3
7
|
import { InferAttributes, Op, WhereOptions } from 'sequelize';
|
|
4
8
|
|
|
5
9
|
import Joi from 'joi';
|
|
6
10
|
import pick from 'lodash/pick';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import { PaymentCurrency, TPaymentCurrency } from '
|
|
12
|
-
import { PaymentMethod } from '
|
|
13
|
-
import { Price, Product } from '
|
|
14
|
-
import { EVM_CHAIN_TYPES } from '
|
|
15
|
-
import { ethWallet, getVaultAddress, wallet } from '
|
|
16
|
-
import { resolveAddressChainTypes } from '
|
|
17
|
-
import { depositVaultQueue } from '
|
|
18
|
-
import { checkDepositVaultAmount } from '
|
|
19
|
-
import { getTokenSummaryByDid } from '
|
|
20
|
-
import { MetadataSchema } from '
|
|
21
|
-
import { getRechargePaymentUrl } from '
|
|
22
|
-
import { checkCurrencySupportRecurring } from '
|
|
23
|
-
|
|
24
|
-
const
|
|
11
|
+
import { fetchErc20Meta } from '../../integrations/ethereum/token';
|
|
12
|
+
import logger from '../../libs/logger';
|
|
13
|
+
import { authenticate } from '../../middlewares/hono/security';
|
|
14
|
+
import { sessionMiddleware } from '../../middlewares/hono/session';
|
|
15
|
+
import { PaymentCurrency, TPaymentCurrency } from '../../store/models/payment-currency';
|
|
16
|
+
import { PaymentMethod } from '../../store/models/payment-method';
|
|
17
|
+
import { Price, Product } from '../../store/models';
|
|
18
|
+
import { EVM_CHAIN_TYPES } from '../../libs/constants';
|
|
19
|
+
import { ethWallet, getVaultAddress, wallet } from '../../libs/auth';
|
|
20
|
+
import { resolveAddressChainTypes } from '../../libs/util';
|
|
21
|
+
import { depositVaultQueue } from '../../queues/payment';
|
|
22
|
+
import { checkDepositVaultAmount } from '../../libs/payment';
|
|
23
|
+
import { getTokenSummaryByDid } from '../../integrations/arcblock/stake';
|
|
24
|
+
import { MetadataSchema } from '../../libs/api';
|
|
25
|
+
import { getRechargePaymentUrl } from '../../libs/currency';
|
|
26
|
+
import { checkCurrencySupportRecurring } from '../../libs/product';
|
|
27
|
+
|
|
28
|
+
const app = new Hono();
|
|
25
29
|
|
|
26
|
-
const user = sessionMiddleware({ accessKey: true });
|
|
27
30
|
const auth = authenticate<PaymentCurrency>({ component: true, roles: ['owner', 'admin'] });
|
|
28
31
|
const authOwner = authenticate<PaymentCurrency>({ component: true, roles: ['owner'] });
|
|
32
|
+
// faithful fork of the express `sessionMiddleware({ accessKey: true })` — populates
|
|
33
|
+
// c.get('user') from a login token OR access key, and does NOT gate (next() either way).
|
|
34
|
+
const user = sessionMiddleware({ accessKey: true });
|
|
35
|
+
|
|
29
36
|
const paymentCurrencyCreateSchema = Joi.object({
|
|
30
37
|
name: Joi.string().max(32).required(),
|
|
31
38
|
description: Joi.string().max(255).required(),
|
|
32
39
|
}).unknown(true);
|
|
33
|
-
router.post('/', auth, async (req, res) => {
|
|
34
|
-
const raw: Partial<TPaymentCurrency> = req.body;
|
|
35
40
|
|
|
36
|
-
|
|
41
|
+
app.post('/', auth, async (c) => {
|
|
42
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
43
|
+
const raw: Partial<TPaymentCurrency> = body;
|
|
44
|
+
|
|
45
|
+
const { error } = paymentCurrencyCreateSchema.validate(pick(body, ['name', 'description']));
|
|
37
46
|
if (error) {
|
|
38
|
-
return
|
|
47
|
+
return c.json({ error: error.message }, 400);
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
if (!raw.payment_method_id) {
|
|
42
|
-
return
|
|
51
|
+
return c.json({ error: 'payment_method_id is required' }, 400);
|
|
43
52
|
}
|
|
44
53
|
if (!raw.name) {
|
|
45
|
-
return
|
|
54
|
+
return c.json({ error: 'payment currency name is required' }, 400);
|
|
46
55
|
}
|
|
47
56
|
if (!raw.description) {
|
|
48
|
-
return
|
|
57
|
+
return c.json({ error: 'payment currency description is required' }, 400);
|
|
49
58
|
}
|
|
50
59
|
const method = await PaymentMethod.findByPk(raw.payment_method_id);
|
|
51
60
|
if (!method) {
|
|
52
|
-
return
|
|
61
|
+
return c.json({ error: 'payment method not found' }, 400);
|
|
53
62
|
}
|
|
54
63
|
raw.logo = raw.logo || method.logo;
|
|
55
64
|
if (!raw.logo) {
|
|
56
|
-
return
|
|
65
|
+
return c.json({ error: 'payment currency logo is required' }, 400);
|
|
57
66
|
}
|
|
58
67
|
if (!raw.contract) {
|
|
59
|
-
return
|
|
68
|
+
return c.json({ error: 'payment currency contract is required' }, 400);
|
|
60
69
|
}
|
|
61
70
|
const exist = await PaymentCurrency.findOne({ where: { contract: raw.contract } });
|
|
62
71
|
if (exist) {
|
|
63
|
-
return
|
|
72
|
+
return c.json({ error: 'payment currency with same contract already exist' }, 400);
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
if (method.type === 'stripe') {
|
|
67
|
-
return
|
|
76
|
+
return c.json({ error: 'Adding method for stripe not supported' }, 400);
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
if (EVM_CHAIN_TYPES.includes(method.type)) {
|
|
@@ -73,7 +82,7 @@ router.post('/', auth, async (req, res) => {
|
|
|
73
82
|
const info = await fetchErc20Meta(client, raw.contract);
|
|
74
83
|
logger.info(`${method.type} erc20 info fetched`, { raw, info });
|
|
75
84
|
if (!info.symbol || !info.decimal) {
|
|
76
|
-
return
|
|
85
|
+
return c.json({ error: `${method.type} token not found` }, 400);
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
const currency = await PaymentCurrency.create({
|
|
@@ -98,10 +107,10 @@ router.post('/', auth, async (req, res) => {
|
|
|
98
107
|
|
|
99
108
|
metadata: {},
|
|
100
109
|
});
|
|
101
|
-
return
|
|
110
|
+
return c.json(currency);
|
|
102
111
|
} catch (err) {
|
|
103
112
|
logger.error(err);
|
|
104
|
-
return
|
|
113
|
+
return c.json({ error: `${method.type} currency contract verify failed` }, 400);
|
|
105
114
|
}
|
|
106
115
|
}
|
|
107
116
|
|
|
@@ -111,7 +120,7 @@ router.post('/', auth, async (req, res) => {
|
|
|
111
120
|
const { state } = await client.getTokenState({ address: raw.contract });
|
|
112
121
|
logger.info('ocap token info fetched', { raw, state });
|
|
113
122
|
if (!state) {
|
|
114
|
-
return
|
|
123
|
+
return c.json({ error: 'ocap token not found' }, 400);
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
const currency = await PaymentCurrency.create({
|
|
@@ -136,18 +145,45 @@ router.post('/', auth, async (req, res) => {
|
|
|
136
145
|
|
|
137
146
|
metadata: {},
|
|
138
147
|
});
|
|
139
|
-
return
|
|
148
|
+
return c.json(currency);
|
|
140
149
|
} catch (err) {
|
|
141
150
|
logger.error(err);
|
|
142
|
-
return
|
|
151
|
+
return c.json({ error: `${method.type} currency contract verify failed` }, 400);
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
|
|
146
|
-
return
|
|
155
|
+
return c.json({ error: `add currency for payment method ${method.type} is not supported` }, 400);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// check if currencies support recurring subscriptions
|
|
159
|
+
// registered before /:id routes so the static segment wins
|
|
160
|
+
app.post('/check-recurring-support', async (c) => {
|
|
161
|
+
try {
|
|
162
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
163
|
+
const { currency_ids: currencyIds } = body as any;
|
|
164
|
+
if (!Array.isArray(currencyIds)) {
|
|
165
|
+
return c.json({ error: 'currency_ids must be an array' }, 400);
|
|
166
|
+
}
|
|
167
|
+
if (currencyIds.length === 0) {
|
|
168
|
+
return c.json({ error: 'currency_ids cannot be empty' }, 400);
|
|
169
|
+
}
|
|
170
|
+
const { notSupportCurrencies, validate } = await checkCurrencySupportRecurring(currencyIds, true);
|
|
171
|
+
return c.json({
|
|
172
|
+
supported: validate,
|
|
173
|
+
unsupported_currencies: notSupportCurrencies.map((cur) => ({
|
|
174
|
+
id: cur.id,
|
|
175
|
+
name: cur.name,
|
|
176
|
+
symbol: cur.symbol,
|
|
177
|
+
})),
|
|
178
|
+
});
|
|
179
|
+
} catch (err) {
|
|
180
|
+
logger.error('check recurring support error', err);
|
|
181
|
+
return c.json({ error: (err as any).message }, 400);
|
|
182
|
+
}
|
|
147
183
|
});
|
|
148
184
|
|
|
149
|
-
|
|
150
|
-
const
|
|
185
|
+
app.get('/', auth, async (c) => {
|
|
186
|
+
const query = c.req.query();
|
|
151
187
|
const where: WhereOptions<InferAttributes<PaymentCurrency>> = {};
|
|
152
188
|
|
|
153
189
|
if (typeof query.active === 'string') {
|
|
@@ -168,13 +204,13 @@ router.get('/', auth, async (req, res) => {
|
|
|
168
204
|
include: [],
|
|
169
205
|
});
|
|
170
206
|
|
|
171
|
-
|
|
207
|
+
return c.json(list);
|
|
172
208
|
});
|
|
173
209
|
|
|
174
|
-
|
|
210
|
+
app.get('/vault-config', auth, async (c) => {
|
|
175
211
|
const vaultAddress = await getVaultAddress();
|
|
176
212
|
if (!vaultAddress) {
|
|
177
|
-
return
|
|
213
|
+
return c.json({
|
|
178
214
|
list: [],
|
|
179
215
|
balances: {},
|
|
180
216
|
});
|
|
@@ -196,16 +232,16 @@ router.get('/vault-config', auth, async (req, res) => {
|
|
|
196
232
|
payment_method_id: {
|
|
197
233
|
[Op.in]: paymentMethodIds,
|
|
198
234
|
},
|
|
199
|
-
livemode: !!
|
|
235
|
+
livemode: !!c.get('livemode'),
|
|
200
236
|
},
|
|
201
237
|
include: [{ model: PaymentMethod, as: 'payment_method' }],
|
|
202
238
|
});
|
|
203
239
|
try {
|
|
204
240
|
const [arcblock, ethereum] = await Promise.all([
|
|
205
|
-
getTokenSummaryByDid(wallet.address, !!
|
|
206
|
-
getTokenSummaryByDid(ethWallet.address, !!
|
|
241
|
+
getTokenSummaryByDid(wallet.address, !!c.get('livemode'), 'arcblock'),
|
|
242
|
+
getTokenSummaryByDid(ethWallet.address, !!c.get('livemode'), EVM_CHAIN_TYPES),
|
|
207
243
|
]);
|
|
208
|
-
return
|
|
244
|
+
return c.json({
|
|
209
245
|
list,
|
|
210
246
|
balances: {
|
|
211
247
|
...arcblock,
|
|
@@ -214,54 +250,87 @@ router.get('/vault-config', auth, async (req, res) => {
|
|
|
214
250
|
});
|
|
215
251
|
} catch (err) {
|
|
216
252
|
logger.error('get token summary failed', err);
|
|
217
|
-
return
|
|
253
|
+
return c.json({ error: (err as any).message, list, balances: {} }, 400);
|
|
218
254
|
}
|
|
219
255
|
} catch (err) {
|
|
220
256
|
logger.error('get payment currency vault config failed', err);
|
|
221
|
-
return
|
|
257
|
+
return c.json({ error: (err as any).message, list: [], balances: {} }, 400);
|
|
222
258
|
}
|
|
223
259
|
});
|
|
224
260
|
|
|
225
|
-
|
|
226
|
-
const
|
|
261
|
+
app.get('/:id/deposit-vault', auth, async (c) => {
|
|
262
|
+
const id = c.req.param('id');
|
|
227
263
|
if (!id) {
|
|
228
|
-
return
|
|
264
|
+
return c.json({ error: 'Missing payment currency id' }, 400);
|
|
229
265
|
}
|
|
230
266
|
try {
|
|
231
267
|
const result = await checkDepositVaultAmount(id);
|
|
232
|
-
return
|
|
268
|
+
return c.json(result);
|
|
233
269
|
} catch (error) {
|
|
234
270
|
logger.error('Error checking deposit vault amount', { error, id });
|
|
235
|
-
return
|
|
271
|
+
return c.json({ error: 'Failed to check deposit vault amount', message: (error as any).message }, 400);
|
|
236
272
|
}
|
|
237
273
|
});
|
|
238
274
|
|
|
239
|
-
|
|
240
|
-
const paymentCurrency = await PaymentCurrency.findByPk(req.
|
|
275
|
+
app.put('/:id/deposit-vault', auth, async (c) => {
|
|
276
|
+
const paymentCurrency = await PaymentCurrency.findByPk(c.req.param('id'));
|
|
241
277
|
if (!paymentCurrency) {
|
|
242
|
-
return
|
|
278
|
+
return c.json({ error: 'Payment currency not found' }, 404);
|
|
243
279
|
}
|
|
244
280
|
const vaultAddress = await getVaultAddress();
|
|
245
281
|
if (!vaultAddress) {
|
|
246
|
-
return
|
|
282
|
+
return c.json({ error: 'Vault address not found' }, 400);
|
|
247
283
|
}
|
|
248
284
|
depositVaultQueue.push({
|
|
249
285
|
id: `deposit-vault-${paymentCurrency.id}`,
|
|
250
286
|
job: { currencyId: paymentCurrency.id },
|
|
251
287
|
});
|
|
252
288
|
logger.info('Deposit vault job pushed', { currencyId: paymentCurrency.id });
|
|
253
|
-
return
|
|
289
|
+
return c.json({ message: 'Deposit vault job pushed' });
|
|
254
290
|
});
|
|
255
291
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
});
|
|
292
|
+
app.get('/:id/recharge-config', user, async (c) => {
|
|
293
|
+
try {
|
|
294
|
+
const id = c.req.param('id');
|
|
260
295
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
296
|
+
const currency = await PaymentCurrency.scope('withRechargeConfig').findByPk(id);
|
|
297
|
+
|
|
298
|
+
if (!currency) {
|
|
299
|
+
return c.json({ error: 'Credit currency not found' }, 404);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (!currency.recharge_config) {
|
|
303
|
+
return c.json({
|
|
304
|
+
currency_id: id,
|
|
305
|
+
recharge_config: null,
|
|
306
|
+
message: 'No recharge config available for this currency',
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const paymentUrl = await getRechargePaymentUrl(currency);
|
|
311
|
+
|
|
312
|
+
let basePrice: (Price & { product: Product }) | null = null;
|
|
313
|
+
if (currency.recharge_config.base_price_id) {
|
|
314
|
+
basePrice = (await Price.findByPk(currency.recharge_config.base_price_id, {
|
|
315
|
+
include: [{ model: Product, as: 'product' }],
|
|
316
|
+
})) as Price & { product: Product };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return c.json({
|
|
320
|
+
currency_id: id,
|
|
321
|
+
currency_info: pick(currency, ['id', 'name', 'symbol', 'decimal', 'type']),
|
|
322
|
+
recharge_config: {
|
|
323
|
+
...currency.recharge_config,
|
|
324
|
+
basePrice,
|
|
325
|
+
payment_url: paymentUrl,
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
} catch (error: any) {
|
|
329
|
+
logger.error('Failed to get currency recharge config', {
|
|
330
|
+
currencyId: c.req.param('id'),
|
|
331
|
+
error: error.message,
|
|
332
|
+
});
|
|
333
|
+
return c.json({ error: 'Internal server error' }, 500);
|
|
265
334
|
}
|
|
266
335
|
});
|
|
267
336
|
|
|
@@ -271,23 +340,25 @@ const UpdateVaultConfigSchema = Joi.object({
|
|
|
271
340
|
withdraw_threshold: Joi.number().min(0).required(),
|
|
272
341
|
buffer_threshold: Joi.number().min(0).required(),
|
|
273
342
|
});
|
|
274
|
-
|
|
343
|
+
|
|
344
|
+
app.put('/:id/vault-config', authOwner, async (c) => {
|
|
275
345
|
try {
|
|
276
|
-
const
|
|
346
|
+
const id = c.req.param('id');
|
|
347
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
277
348
|
|
|
278
|
-
const { error, value: vaultConfig } = UpdateVaultConfigSchema.validate(
|
|
349
|
+
const { error, value: vaultConfig } = UpdateVaultConfigSchema.validate(body);
|
|
279
350
|
if (error) {
|
|
280
|
-
return
|
|
351
|
+
return c.json({ error: error.message }, 400);
|
|
281
352
|
}
|
|
282
353
|
|
|
283
354
|
const paymentCurrency = await PaymentCurrency.findByPk(id);
|
|
284
355
|
if (!paymentCurrency) {
|
|
285
|
-
return
|
|
356
|
+
return c.json({ error: 'payment currency not found' }, 404);
|
|
286
357
|
}
|
|
287
358
|
|
|
288
359
|
const vaultAddress = await getVaultAddress();
|
|
289
360
|
if (!vaultAddress) {
|
|
290
|
-
return
|
|
361
|
+
return c.json({ error: 'Vault address not found' }, 400);
|
|
291
362
|
}
|
|
292
363
|
|
|
293
364
|
const updateData: Partial<TPaymentCurrency> = {
|
|
@@ -301,10 +372,10 @@ router.put('/:id/vault-config', authOwner, async (req, res) => {
|
|
|
301
372
|
|
|
302
373
|
await paymentCurrency.update(updateData);
|
|
303
374
|
|
|
304
|
-
return
|
|
375
|
+
return c.json(paymentCurrency.toJSON());
|
|
305
376
|
} catch (err) {
|
|
306
377
|
logger.error('update payment currency vault config failed', err);
|
|
307
|
-
return
|
|
378
|
+
return c.json({ error: (err as any).message }, 400);
|
|
308
379
|
}
|
|
309
380
|
});
|
|
310
381
|
|
|
@@ -315,26 +386,28 @@ const updateCurrencySchema = Joi.object({
|
|
|
315
386
|
metadata: MetadataSchema,
|
|
316
387
|
symbol: Joi.string().empty('').optional(),
|
|
317
388
|
}).unknown(true);
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
const
|
|
389
|
+
|
|
390
|
+
app.put('/:id', auth, async (c) => {
|
|
391
|
+
const id = c.req.param('id');
|
|
392
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
393
|
+
const raw: Partial<TPaymentCurrency> = body;
|
|
321
394
|
|
|
322
395
|
const { error } = updateCurrencySchema.validate(raw);
|
|
323
396
|
if (error) {
|
|
324
|
-
return
|
|
397
|
+
return c.json({ error: error.message }, 400);
|
|
325
398
|
}
|
|
326
399
|
|
|
327
400
|
const currency = await PaymentCurrency.findByPk(id);
|
|
328
401
|
if (!currency) {
|
|
329
|
-
return
|
|
402
|
+
return c.json({ error: 'Payment currency not found' }, 404);
|
|
330
403
|
}
|
|
331
404
|
if (raw.contract && raw.contract !== currency.contract) {
|
|
332
|
-
return
|
|
405
|
+
return c.json({ error: 'contract cannot be updated' }, 400);
|
|
333
406
|
}
|
|
334
407
|
|
|
335
408
|
const method = await PaymentMethod.findByPk(currency.payment_method_id);
|
|
336
409
|
if (!method) {
|
|
337
|
-
return
|
|
410
|
+
return c.json({ error: 'Payment method not found' }, 400);
|
|
338
411
|
}
|
|
339
412
|
|
|
340
413
|
const updates: Partial<TPaymentCurrency> = {
|
|
@@ -349,33 +422,34 @@ router.put('/:id', auth, async (req, res) => {
|
|
|
349
422
|
}
|
|
350
423
|
|
|
351
424
|
const updatedCurrency = await currency.update(updates);
|
|
352
|
-
return
|
|
425
|
+
return c.json(updatedCurrency);
|
|
353
426
|
});
|
|
354
427
|
|
|
355
428
|
const tokenConfigSchema = Joi.object({
|
|
356
429
|
token_factory_address: Joi.string().required(),
|
|
357
430
|
});
|
|
358
431
|
|
|
359
|
-
|
|
432
|
+
app.put('/:id/token-config', auth, async (c) => {
|
|
360
433
|
try {
|
|
361
|
-
const
|
|
434
|
+
const id = c.req.param('id');
|
|
435
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
362
436
|
|
|
363
|
-
const { error, value } = tokenConfigSchema.validate(
|
|
437
|
+
const { error, value } = tokenConfigSchema.validate(body);
|
|
364
438
|
if (error) {
|
|
365
|
-
return
|
|
439
|
+
return c.json({ error: error.message }, 400);
|
|
366
440
|
}
|
|
367
441
|
|
|
368
442
|
const currency = await PaymentCurrency.findByPk(id);
|
|
369
443
|
if (!currency) {
|
|
370
|
-
return
|
|
444
|
+
return c.json({ error: 'Payment currency not found' }, 404);
|
|
371
445
|
}
|
|
372
446
|
|
|
373
447
|
if (currency.type !== 'credit') {
|
|
374
|
-
return
|
|
448
|
+
return c.json({ error: 'Only credit currencies can have token_config' }, 400);
|
|
375
449
|
}
|
|
376
450
|
|
|
377
451
|
if (currency.token_config) {
|
|
378
|
-
return
|
|
452
|
+
return c.json({ error: 'Token config already exists. Cannot be updated once set.' }, 400);
|
|
379
453
|
}
|
|
380
454
|
|
|
381
455
|
const paymentMethod = await PaymentMethod.findOne({
|
|
@@ -386,7 +460,7 @@ router.put('/:id/token-config', auth, async (req, res) => {
|
|
|
386
460
|
});
|
|
387
461
|
|
|
388
462
|
if (!paymentMethod) {
|
|
389
|
-
return
|
|
463
|
+
return c.json({ error: 'ArcBlock payment method not found' }, 400);
|
|
390
464
|
}
|
|
391
465
|
|
|
392
466
|
const client = paymentMethod.getOcapClient();
|
|
@@ -395,7 +469,7 @@ router.put('/:id/token-config', auth, async (req, res) => {
|
|
|
395
469
|
});
|
|
396
470
|
|
|
397
471
|
if (!tokenFactoryState) {
|
|
398
|
-
return
|
|
472
|
+
return c.json({ error: 'Token factory not found on chain' }, 400);
|
|
399
473
|
}
|
|
400
474
|
|
|
401
475
|
const tokenConfig = {
|
|
@@ -416,28 +490,31 @@ router.put('/:id/token-config', auth, async (req, res) => {
|
|
|
416
490
|
tokenConfig,
|
|
417
491
|
});
|
|
418
492
|
|
|
419
|
-
return
|
|
493
|
+
return c.json(currency.toJSON());
|
|
420
494
|
} catch (err) {
|
|
421
|
-
logger.error('update payment currency token_config failed', {
|
|
422
|
-
|
|
495
|
+
logger.error('update payment currency token_config failed', {
|
|
496
|
+
error: (err as any)?.message,
|
|
497
|
+
id: c.req.param('id'),
|
|
498
|
+
});
|
|
499
|
+
return c.json({ error: (err as any)?.message }, 400);
|
|
423
500
|
}
|
|
424
501
|
});
|
|
425
502
|
|
|
426
|
-
|
|
503
|
+
app.delete('/:id/token-config', auth, async (c) => {
|
|
427
504
|
try {
|
|
428
|
-
const
|
|
505
|
+
const id = c.req.param('id');
|
|
429
506
|
|
|
430
507
|
const currency = await PaymentCurrency.findByPk(id);
|
|
431
508
|
if (!currency) {
|
|
432
|
-
return
|
|
509
|
+
return c.json({ error: 'Payment currency not found' }, 404);
|
|
433
510
|
}
|
|
434
511
|
|
|
435
512
|
if (currency.type !== 'credit') {
|
|
436
|
-
return
|
|
513
|
+
return c.json({ error: 'Only credit currencies can have token_config' }, 400);
|
|
437
514
|
}
|
|
438
515
|
|
|
439
516
|
if (!currency.token_config) {
|
|
440
|
-
return
|
|
517
|
+
return c.json({ error: 'Token config does not exist' }, 400);
|
|
441
518
|
}
|
|
442
519
|
|
|
443
520
|
await currency.update({
|
|
@@ -448,80 +525,25 @@ router.delete('/:id/token-config', auth, async (req, res) => {
|
|
|
448
525
|
currencyId: id,
|
|
449
526
|
});
|
|
450
527
|
|
|
451
|
-
return
|
|
452
|
-
} catch (err) {
|
|
453
|
-
logger.error('delete payment currency token_config failed', { error: err?.message, id: req.params.id });
|
|
454
|
-
return res.status(400).json({ error: err?.message });
|
|
455
|
-
}
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
router.delete('/:id', auth, async (req, res) => {
|
|
459
|
-
const { id } = req.params;
|
|
460
|
-
|
|
461
|
-
const currency = await PaymentCurrency.findByPk(id);
|
|
462
|
-
if (!currency) {
|
|
463
|
-
return res.status(404).json({ error: 'Payment currency not found' });
|
|
464
|
-
}
|
|
465
|
-
const isLocked = await currency.isLocked();
|
|
466
|
-
if (isLocked) {
|
|
467
|
-
return res.status(400).json({ error: 'Can not delete locked payment currency' });
|
|
468
|
-
}
|
|
469
|
-
const isUsed = await currency.isUsed();
|
|
470
|
-
if (isUsed) {
|
|
471
|
-
return res.status(400).json({ error: 'Can not delete payment currency used by other resources' });
|
|
472
|
-
}
|
|
473
|
-
try {
|
|
474
|
-
await currency.destroy();
|
|
475
|
-
return res.status(200).end();
|
|
528
|
+
return c.json(currency.toJSON());
|
|
476
529
|
} catch (err) {
|
|
477
|
-
logger.error('delete payment currency
|
|
478
|
-
|
|
530
|
+
logger.error('delete payment currency token_config failed', {
|
|
531
|
+
error: (err as any)?.message,
|
|
532
|
+
id: c.req.param('id'),
|
|
533
|
+
});
|
|
534
|
+
return c.json({ error: (err as any)?.message }, 400);
|
|
479
535
|
}
|
|
480
536
|
});
|
|
481
537
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
const currency = await PaymentCurrency.scope('withRechargeConfig').findByPk(id);
|
|
487
|
-
|
|
488
|
-
if (!currency) {
|
|
489
|
-
return res.status(404).json({ error: 'Credit currency not found' });
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
if (!currency.recharge_config) {
|
|
493
|
-
return res.json({
|
|
494
|
-
currency_id: id,
|
|
495
|
-
recharge_config: null,
|
|
496
|
-
message: 'No recharge config available for this currency',
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
const paymentUrl = await getRechargePaymentUrl(currency);
|
|
501
|
-
|
|
502
|
-
let basePrice: (Price & { product: Product }) | null = null;
|
|
503
|
-
if (currency.recharge_config.base_price_id) {
|
|
504
|
-
basePrice = (await Price.findByPk(currency.recharge_config.base_price_id, {
|
|
505
|
-
include: [{ model: Product, as: 'product' }],
|
|
506
|
-
})) as Price & { product: Product };
|
|
507
|
-
}
|
|
538
|
+
app.get('/:id', auth, async (c) => {
|
|
539
|
+
const doc = await PaymentCurrency.findOne({
|
|
540
|
+
where: { [Op.or]: [{ id: c.req.param('id') }, { symbol: c.req.param('id') }] },
|
|
541
|
+
});
|
|
508
542
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
currency_info: pick(currency, ['id', 'name', 'symbol', 'decimal', 'type']),
|
|
512
|
-
recharge_config: {
|
|
513
|
-
...currency.recharge_config,
|
|
514
|
-
basePrice,
|
|
515
|
-
payment_url: paymentUrl,
|
|
516
|
-
},
|
|
517
|
-
});
|
|
518
|
-
} catch (error: any) {
|
|
519
|
-
logger.error('Failed to get currency recharge config', {
|
|
520
|
-
currencyId: req.params.id,
|
|
521
|
-
error: error.message,
|
|
522
|
-
});
|
|
523
|
-
return res.status(500).json({ error: 'Internal server error' });
|
|
543
|
+
if (doc) {
|
|
544
|
+
return c.json(doc);
|
|
524
545
|
}
|
|
546
|
+
return c.json(null, 404);
|
|
525
547
|
});
|
|
526
548
|
|
|
527
549
|
const rechargeConfigSchema = Joi.object({
|
|
@@ -534,19 +556,20 @@ const rechargeConfigSchema = Joi.object({
|
|
|
534
556
|
}).optional(),
|
|
535
557
|
}).unknown(true);
|
|
536
558
|
|
|
537
|
-
|
|
538
|
-
const
|
|
559
|
+
app.put('/:id/recharge-config', auth, async (c) => {
|
|
560
|
+
const id = c.req.param('id');
|
|
561
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
539
562
|
const { error, value: rechargeConfig } = rechargeConfigSchema.validate(
|
|
540
|
-
pick(
|
|
563
|
+
pick(body, ['base_price_id', 'payment_link_id', 'checkout_url', 'settings'])
|
|
541
564
|
);
|
|
542
565
|
if (error) {
|
|
543
|
-
return
|
|
566
|
+
return c.json({ error: error.message }, 400);
|
|
544
567
|
}
|
|
545
568
|
|
|
546
569
|
const currency = await PaymentCurrency.findByPk(id);
|
|
547
570
|
|
|
548
571
|
if (!currency) {
|
|
549
|
-
return
|
|
572
|
+
return c.json({ error: 'Credit currency not found' }, 404);
|
|
550
573
|
}
|
|
551
574
|
|
|
552
575
|
if (rechargeConfig.base_price_id) {
|
|
@@ -559,7 +582,7 @@ router.put('/:id/recharge-config', auth, async (req, res) => {
|
|
|
559
582
|
})) as Price & { product: Product };
|
|
560
583
|
|
|
561
584
|
if (!basePrice) {
|
|
562
|
-
return
|
|
585
|
+
return c.json({ error: 'Base price not found or inactive' }, 404);
|
|
563
586
|
}
|
|
564
587
|
}
|
|
565
588
|
|
|
@@ -571,36 +594,35 @@ router.put('/:id/recharge-config', auth, async (req, res) => {
|
|
|
571
594
|
tiersCount: rechargeConfig.price_tiers?.length || 0,
|
|
572
595
|
});
|
|
573
596
|
|
|
574
|
-
return
|
|
597
|
+
return c.json({
|
|
575
598
|
currency_id: id,
|
|
576
599
|
recharge_config: rechargeConfig,
|
|
577
600
|
message: 'Recharge config updated successfully',
|
|
578
601
|
});
|
|
579
602
|
});
|
|
580
603
|
|
|
581
|
-
|
|
582
|
-
|
|
604
|
+
app.delete('/:id', auth, async (c) => {
|
|
605
|
+
const id = c.req.param('id');
|
|
606
|
+
|
|
607
|
+
const currency = await PaymentCurrency.findByPk(id);
|
|
608
|
+
if (!currency) {
|
|
609
|
+
return c.json({ error: 'Payment currency not found' }, 404);
|
|
610
|
+
}
|
|
611
|
+
const isLocked = await currency.isLocked();
|
|
612
|
+
if (isLocked) {
|
|
613
|
+
return c.json({ error: 'Can not delete locked payment currency' }, 400);
|
|
614
|
+
}
|
|
615
|
+
const isUsed = await currency.isUsed();
|
|
616
|
+
if (isUsed) {
|
|
617
|
+
return c.json({ error: 'Can not delete payment currency used by other resources' }, 400);
|
|
618
|
+
}
|
|
583
619
|
try {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
return res.status(400).json({ error: 'currency_ids must be an array' });
|
|
587
|
-
}
|
|
588
|
-
if (currencyIds.length === 0) {
|
|
589
|
-
return res.status(400).json({ error: 'currency_ids cannot be empty' });
|
|
590
|
-
}
|
|
591
|
-
const { notSupportCurrencies, validate } = await checkCurrencySupportRecurring(currencyIds, true);
|
|
592
|
-
return res.json({
|
|
593
|
-
supported: validate,
|
|
594
|
-
unsupported_currencies: notSupportCurrencies.map((c) => ({
|
|
595
|
-
id: c.id,
|
|
596
|
-
name: c.name,
|
|
597
|
-
symbol: c.symbol,
|
|
598
|
-
})),
|
|
599
|
-
});
|
|
620
|
+
await currency.destroy();
|
|
621
|
+
return c.body(null, 200);
|
|
600
622
|
} catch (err) {
|
|
601
|
-
logger.error('
|
|
602
|
-
return
|
|
623
|
+
logger.error('delete payment currency error', err);
|
|
624
|
+
return c.json({ error: 'Delete payment currency failed' }, 400);
|
|
603
625
|
}
|
|
604
626
|
});
|
|
605
627
|
|
|
606
|
-
export default
|
|
628
|
+
export default app;
|