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,21 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
// Phase 3 (express→hono) — hono fork of routes/settings.ts. Sub-app with
|
|
2
|
+
// routes relative to /api/settings (mounted via mountResourceGroup). The
|
|
3
|
+
// business logic is unchanged; only the express plumbing becomes hono:
|
|
4
|
+
// req.body → c.get('sanitizedBody'); res.status(n).json(x) → c.json(x, n).
|
|
5
|
+
import { Hono } from 'hono';
|
|
2
6
|
import pick from 'lodash/pick';
|
|
3
7
|
import { Op, type WhereOptions } from 'sequelize';
|
|
4
8
|
|
|
5
9
|
import Joi from 'joi';
|
|
6
10
|
import merge from 'lodash/merge';
|
|
7
|
-
import { PaymentCurrency } from '
|
|
8
|
-
import { PaymentMethod } from '
|
|
9
|
-
import { authenticate } from '
|
|
10
|
-
import { Setting } from '
|
|
11
|
-
import logger from '
|
|
12
|
-
import { createListParamSchema, getOrder, getWhereFromKvQuery } from '
|
|
11
|
+
import { PaymentCurrency } from '../../store/models/payment-currency';
|
|
12
|
+
import { PaymentMethod } from '../../store/models/payment-method';
|
|
13
|
+
import { authenticate } from '../../middlewares/hono/security';
|
|
14
|
+
import { Setting } from '../../store/models';
|
|
15
|
+
import logger from '../../libs/logger';
|
|
16
|
+
import { createListParamSchema, getOrder, getWhereFromKvQuery } from '../../libs/api';
|
|
13
17
|
|
|
14
|
-
const
|
|
18
|
+
const app = new Hono();
|
|
15
19
|
const authAdmin = authenticate<Setting>({ component: true, roles: ['owner', 'admin'] });
|
|
16
|
-
|
|
20
|
+
|
|
21
|
+
app.get('/', async (c) => {
|
|
17
22
|
const attributes = ['id', 'name', 'symbol', 'decimal', 'logo', 'payment_method_id', 'maximum_precision', 'type'];
|
|
18
|
-
const where: WhereOptions<PaymentMethod> = { livemode:
|
|
23
|
+
const where: WhereOptions<PaymentMethod> = { livemode: c.get('livemode'), active: true };
|
|
19
24
|
|
|
20
25
|
const methods = await PaymentMethod.findAll({
|
|
21
26
|
where,
|
|
@@ -30,13 +35,13 @@ router.get('/', async (req, res) => {
|
|
|
30
35
|
method.payment_currencies = method.payment_currencies?.map((x) => pick(x, attributes));
|
|
31
36
|
});
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
return c.json({
|
|
34
39
|
paymentMethods: methods.map((x) => ({
|
|
35
40
|
...pick(x, ['id', 'name', 'type', 'logo', 'payment_currencies', 'default_currency_id', 'maximum_precision']),
|
|
36
41
|
api_host: x.settings?.arcblock?.api_host,
|
|
37
42
|
})),
|
|
38
43
|
baseCurrency: await PaymentCurrency.findOne({
|
|
39
|
-
where: { is_base_currency: true, livemode:
|
|
44
|
+
where: { is_base_currency: true, livemode: c.get('livemode') },
|
|
40
45
|
attributes,
|
|
41
46
|
}),
|
|
42
47
|
});
|
|
@@ -54,10 +59,10 @@ const donateListSchema = createListParamSchema<{
|
|
|
54
59
|
componentDid: Joi.string().empty(''),
|
|
55
60
|
});
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
app.get('/donate', async (c) => {
|
|
58
63
|
try {
|
|
59
64
|
const { page, pageSize, mountLocation, description, componentDid, ...query } = await donateListSchema.validateAsync(
|
|
60
|
-
req.query,
|
|
65
|
+
c.req.query(),
|
|
61
66
|
{ stripUnknown: false, allowUnknown: true }
|
|
62
67
|
);
|
|
63
68
|
|
|
@@ -81,34 +86,34 @@ router.get('/donate', async (req, res) => {
|
|
|
81
86
|
|
|
82
87
|
const { rows: list, count } = await Setting.findAndCountAll({
|
|
83
88
|
where,
|
|
84
|
-
order: getOrder(req.query, [['created_at', query.o === 'asc' ? 'ASC' : 'DESC']]),
|
|
89
|
+
order: getOrder(c.req.query(), [['created_at', query.o === 'asc' ? 'ASC' : 'DESC']]),
|
|
85
90
|
offset: (page - 1) * pageSize,
|
|
86
91
|
limit: pageSize,
|
|
87
92
|
distinct: true,
|
|
88
93
|
});
|
|
89
94
|
|
|
90
|
-
return
|
|
95
|
+
return c.json({ count, list, paging: { page, pageSize } });
|
|
91
96
|
} catch (err) {
|
|
92
97
|
logger.error(err);
|
|
93
|
-
return
|
|
98
|
+
return c.json({ error: err.message }, 400);
|
|
94
99
|
}
|
|
95
100
|
});
|
|
96
101
|
|
|
97
|
-
|
|
102
|
+
app.get('/donate/:mountLocationOrId', async (c) => {
|
|
98
103
|
try {
|
|
99
|
-
if (!req.
|
|
100
|
-
return
|
|
104
|
+
if (!c.req.param('mountLocationOrId')) {
|
|
105
|
+
return c.json({ error: 'mountLocation is required' }, 400);
|
|
101
106
|
}
|
|
102
107
|
const settings = await Setting.findOne({
|
|
103
108
|
where: {
|
|
104
109
|
type: 'donate',
|
|
105
|
-
[Op.or]: [{ mount_location: req.
|
|
110
|
+
[Op.or]: [{ mount_location: c.req.param('mountLocationOrId') }, { id: c.req.param('mountLocationOrId') }],
|
|
106
111
|
},
|
|
107
112
|
});
|
|
108
|
-
return
|
|
113
|
+
return c.json(settings);
|
|
109
114
|
} catch (err) {
|
|
110
115
|
logger.error(err);
|
|
111
|
-
return
|
|
116
|
+
return c.json({ error: err.message }, 400);
|
|
112
117
|
}
|
|
113
118
|
});
|
|
114
119
|
|
|
@@ -121,13 +126,15 @@ const SettingSchema = Joi.object({
|
|
|
121
126
|
livemode: Joi.boolean().optional(),
|
|
122
127
|
componentDid: Joi.string().optional(),
|
|
123
128
|
}).unknown(true);
|
|
124
|
-
|
|
129
|
+
|
|
130
|
+
app.post('/', async (c) => {
|
|
125
131
|
try {
|
|
126
|
-
const
|
|
132
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
133
|
+
const { error } = SettingSchema.validate(body);
|
|
127
134
|
if (error) {
|
|
128
|
-
return
|
|
135
|
+
return c.json({ error: error.message }, 400);
|
|
129
136
|
}
|
|
130
|
-
const { type, mountLocation, settings = {}, active = true, description, livemode = true, componentDid } =
|
|
137
|
+
const { type, mountLocation, settings = {}, active = true, description, livemode = true, componentDid } = body;
|
|
131
138
|
const raw = {
|
|
132
139
|
type,
|
|
133
140
|
mount_location: mountLocation,
|
|
@@ -163,7 +170,7 @@ router.post('/', async (req, res) => {
|
|
|
163
170
|
|
|
164
171
|
const { error: amountError } = amountSchema.validate(settings.amount);
|
|
165
172
|
if (amountError) {
|
|
166
|
-
return
|
|
173
|
+
return c.json({ error: amountError.message }, 400);
|
|
167
174
|
}
|
|
168
175
|
}
|
|
169
176
|
|
|
@@ -180,7 +187,7 @@ router.post('/', async (req, res) => {
|
|
|
180
187
|
});
|
|
181
188
|
const { error: notificationError, value: notificationSettings } = notificationSchema.validate(settings);
|
|
182
189
|
if (notificationError) {
|
|
183
|
-
return
|
|
190
|
+
return c.json({ error: notificationError.message }, 400);
|
|
184
191
|
}
|
|
185
192
|
raw.settings = notificationSettings;
|
|
186
193
|
}
|
|
@@ -196,13 +203,13 @@ router.post('/', async (req, res) => {
|
|
|
196
203
|
component_did: componentDid,
|
|
197
204
|
});
|
|
198
205
|
}
|
|
199
|
-
return
|
|
206
|
+
return c.json(exist);
|
|
200
207
|
}
|
|
201
208
|
const doc = await Setting.create(raw);
|
|
202
|
-
return
|
|
209
|
+
return c.json(doc);
|
|
203
210
|
} catch (err) {
|
|
204
211
|
logger.error(err);
|
|
205
|
-
return
|
|
212
|
+
return c.json({ error: err.message }, 400);
|
|
206
213
|
}
|
|
207
214
|
});
|
|
208
215
|
|
|
@@ -211,13 +218,15 @@ const UpdateSettingSchema = Joi.object({
|
|
|
211
218
|
active: Joi.boolean().optional(),
|
|
212
219
|
description: Joi.string().optional(),
|
|
213
220
|
}).unknown(true);
|
|
214
|
-
|
|
221
|
+
|
|
222
|
+
app.put('/:mountLocationOrId', authAdmin, async (c) => {
|
|
215
223
|
try {
|
|
216
|
-
const
|
|
224
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
225
|
+
const { error } = UpdateSettingSchema.validate(body);
|
|
217
226
|
if (error) {
|
|
218
|
-
return
|
|
227
|
+
return c.json({ error: error.message }, 400);
|
|
219
228
|
}
|
|
220
|
-
const { settings = {}, active = true, description } =
|
|
229
|
+
const { settings = {}, active = true, description } = body;
|
|
221
230
|
const raw: {
|
|
222
231
|
active: boolean;
|
|
223
232
|
settings: Record<string, any>;
|
|
@@ -233,16 +242,16 @@ router.put('/:mountLocationOrId', authAdmin, async (req, res) => {
|
|
|
233
242
|
where: {
|
|
234
243
|
[Op.or]: [
|
|
235
244
|
{
|
|
236
|
-
id: req.
|
|
245
|
+
id: c.req.param('mountLocationOrId'),
|
|
237
246
|
},
|
|
238
247
|
{
|
|
239
|
-
mount_location: req.
|
|
248
|
+
mount_location: c.req.param('mountLocationOrId'),
|
|
240
249
|
},
|
|
241
250
|
],
|
|
242
251
|
},
|
|
243
252
|
});
|
|
244
253
|
if (!setting) {
|
|
245
|
-
return
|
|
254
|
+
return c.json({ error: 'Setting not found' }, 404);
|
|
246
255
|
}
|
|
247
256
|
raw.settings = merge({}, setting.settings, settings);
|
|
248
257
|
if (setting.type === 'donate') {
|
|
@@ -259,7 +268,7 @@ router.put('/:mountLocationOrId', authAdmin, async (req, res) => {
|
|
|
259
268
|
|
|
260
269
|
const { error: amountError } = amountSchema.validate(settings.amount);
|
|
261
270
|
if (amountError) {
|
|
262
|
-
return
|
|
271
|
+
return c.json({ error: amountError.message }, 400);
|
|
263
272
|
}
|
|
264
273
|
raw.settings.amount = settings.amount;
|
|
265
274
|
}
|
|
@@ -272,62 +281,62 @@ router.put('/:mountLocationOrId', authAdmin, async (req, res) => {
|
|
|
272
281
|
});
|
|
273
282
|
const { error: notificationError, value: notificationSettings } = notificationSchema.validate(settings);
|
|
274
283
|
if (notificationError) {
|
|
275
|
-
return
|
|
284
|
+
return c.json({ error: notificationError.message }, 400);
|
|
276
285
|
}
|
|
277
286
|
raw.settings = notificationSettings;
|
|
278
287
|
}
|
|
279
288
|
const doc = await setting.update(raw);
|
|
280
|
-
return
|
|
289
|
+
return c.json(doc);
|
|
281
290
|
} catch (err) {
|
|
282
291
|
logger.error(err);
|
|
283
|
-
return
|
|
292
|
+
return c.json({ error: err.message }, 400);
|
|
284
293
|
}
|
|
285
294
|
});
|
|
286
295
|
|
|
287
|
-
|
|
296
|
+
app.delete('/:mountLocationOrId', authAdmin, async (c) => {
|
|
288
297
|
try {
|
|
289
298
|
const setting = await Setting.findOne({
|
|
290
299
|
where: {
|
|
291
300
|
[Op.or]: [
|
|
292
301
|
{
|
|
293
|
-
id: req.
|
|
302
|
+
id: c.req.param('mountLocationOrId'),
|
|
294
303
|
},
|
|
295
304
|
{
|
|
296
|
-
mount_location: req.
|
|
305
|
+
mount_location: c.req.param('mountLocationOrId'),
|
|
297
306
|
},
|
|
298
307
|
],
|
|
299
308
|
},
|
|
300
309
|
});
|
|
301
310
|
if (!setting) {
|
|
302
|
-
return
|
|
311
|
+
return c.json({ error: `Setting ${c.req.param('mountLocationOrId')} not found` }, 404);
|
|
303
312
|
}
|
|
304
313
|
await setting.destroy();
|
|
305
|
-
return
|
|
314
|
+
return c.json({ message: `Setting ${c.req.param('mountLocationOrId')} deleted` });
|
|
306
315
|
} catch (err) {
|
|
307
316
|
logger.error(err);
|
|
308
|
-
return
|
|
317
|
+
return c.json({ error: err.message }, 400);
|
|
309
318
|
}
|
|
310
319
|
});
|
|
311
320
|
|
|
312
|
-
|
|
321
|
+
app.get('/:mountLocationOrId', authAdmin, async (c) => {
|
|
313
322
|
try {
|
|
314
323
|
const setting = await Setting.findOne({
|
|
315
324
|
where: {
|
|
316
325
|
[Op.or]: [
|
|
317
326
|
{
|
|
318
|
-
id: req.
|
|
327
|
+
id: c.req.param('mountLocationOrId'),
|
|
319
328
|
},
|
|
320
329
|
{
|
|
321
|
-
mount_location: req.
|
|
330
|
+
mount_location: c.req.param('mountLocationOrId'),
|
|
322
331
|
},
|
|
323
332
|
],
|
|
324
333
|
},
|
|
325
334
|
});
|
|
326
|
-
return
|
|
335
|
+
return c.json(setting);
|
|
327
336
|
} catch (err) {
|
|
328
337
|
logger.error(err);
|
|
329
|
-
return
|
|
338
|
+
return c.json({ error: err.message }, 400);
|
|
330
339
|
}
|
|
331
340
|
});
|
|
332
341
|
|
|
333
|
-
export default
|
|
342
|
+
export default app;
|
|
@@ -1,25 +1,30 @@
|
|
|
1
|
+
// Phase 3 (express→hono) — hono fork of routes/subscription-items.ts. Sub-app with
|
|
2
|
+
// routes relative to /api/subscription-items (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 dayjs from 'dayjs';
|
|
2
|
-
import {
|
|
6
|
+
import { Hono } from 'hono';
|
|
3
7
|
import Joi from 'joi';
|
|
4
8
|
import pick from 'lodash/pick';
|
|
5
9
|
import type { WhereOptions } from 'sequelize';
|
|
6
10
|
|
|
7
|
-
import { createListParamSchema, getOrder, MetadataSchema } from '
|
|
8
|
-
import { authenticate } from '
|
|
9
|
-
import { expandLineItems } from '
|
|
10
|
-
import { formatMetadata } from '
|
|
11
|
-
import { Price, Product, Subscription, SubscriptionItem, UsageRecord } from '
|
|
12
|
-
import { forwardUsageRecordToStripe } from '
|
|
13
|
-
import { usageRecordQueue } from '
|
|
14
|
-
import logger from '
|
|
11
|
+
import { createListParamSchema, getOrder, MetadataSchema } from '../../libs/api';
|
|
12
|
+
import { authenticate } from '../../middlewares/hono/security';
|
|
13
|
+
import { expandLineItems } from '../../libs/session';
|
|
14
|
+
import { formatMetadata } from '../../libs/util';
|
|
15
|
+
import { Price, Product, Subscription, SubscriptionItem, UsageRecord } from '../../store/models';
|
|
16
|
+
import { forwardUsageRecordToStripe } from '../../integrations/stripe/resource';
|
|
17
|
+
import { usageRecordQueue } from '../../queues/usage-record';
|
|
18
|
+
import logger from '../../libs/logger';
|
|
15
19
|
|
|
16
|
-
const
|
|
20
|
+
const app = new Hono();
|
|
17
21
|
const auth = authenticate<SubscriptionItem>({ component: true, roles: ['owner', 'admin'] });
|
|
18
22
|
|
|
19
23
|
// FIXME: handle payment_behavior, proration_behavior
|
|
20
24
|
// @link https://stripe.com/docs/api/subscription_items/create
|
|
21
|
-
|
|
22
|
-
const
|
|
25
|
+
app.post('/', auth, async (c) => {
|
|
26
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
27
|
+
const raw: Partial<SubscriptionItem> = pick(body, [
|
|
23
28
|
'subscription_id',
|
|
24
29
|
'price_id',
|
|
25
30
|
'quantity',
|
|
@@ -30,14 +35,14 @@ router.post('/', auth, async (req, res) => {
|
|
|
30
35
|
where: { price_id: raw.price_id, subscription_id: raw.subscription_id },
|
|
31
36
|
});
|
|
32
37
|
if (exist) {
|
|
33
|
-
return
|
|
38
|
+
return c.json({ error: `SubscriptionItem already exist: ${exist.id}` }, 400);
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
raw.livemode =
|
|
41
|
+
raw.livemode = c.get('livemode');
|
|
37
42
|
if (raw.metadata) {
|
|
38
43
|
const { error: metadataError } = MetadataSchema.validate(raw.metadata);
|
|
39
44
|
if (metadataError) {
|
|
40
|
-
return
|
|
45
|
+
return c.json({ error: `metadata invalid: ${metadataError.message}` }, 400);
|
|
41
46
|
}
|
|
42
47
|
raw.metadata = formatMetadata(raw.metadata);
|
|
43
48
|
}
|
|
@@ -48,9 +53,9 @@ router.post('/', auth, async (req, res) => {
|
|
|
48
53
|
subscriptionId: doc.subscription_id,
|
|
49
54
|
priceId: doc.price_id,
|
|
50
55
|
quantity: doc.quantity,
|
|
51
|
-
requestedBy:
|
|
56
|
+
requestedBy: c.get('user')?.did,
|
|
52
57
|
});
|
|
53
|
-
return
|
|
58
|
+
return c.json(doc);
|
|
54
59
|
});
|
|
55
60
|
|
|
56
61
|
// @link https://stripe.com/docs/api/subscription_items/list
|
|
@@ -61,8 +66,8 @@ const schema = createListParamSchema<{
|
|
|
61
66
|
subscription_id: Joi.string(),
|
|
62
67
|
price_id: Joi.string(),
|
|
63
68
|
});
|
|
64
|
-
|
|
65
|
-
const { page, pageSize, ...query } = await schema.validateAsync(req.query, { stripUnknown: true });
|
|
69
|
+
app.get('/', auth, async (c) => {
|
|
70
|
+
const { page, pageSize, ...query } = await schema.validateAsync(c.req.query(), { stripUnknown: true });
|
|
66
71
|
const where: WhereOptions<SubscriptionItem> = {};
|
|
67
72
|
|
|
68
73
|
if (query.subscription_id) {
|
|
@@ -80,7 +85,7 @@ router.get('/', auth, async (req, res) => {
|
|
|
80
85
|
try {
|
|
81
86
|
const { rows, count } = await SubscriptionItem.findAndCountAll({
|
|
82
87
|
where,
|
|
83
|
-
order: getOrder(req.query, [['created_at', 'DESC']]),
|
|
88
|
+
order: getOrder(c.req.query(), [['created_at', 'DESC']]),
|
|
84
89
|
offset: (page - 1) * pageSize,
|
|
85
90
|
limit: pageSize,
|
|
86
91
|
include: [],
|
|
@@ -101,45 +106,45 @@ router.get('/', auth, async (req, res) => {
|
|
|
101
106
|
// @ts-ignore
|
|
102
107
|
expandLineItems(list, Array.from(productMap.values()), pricesJson);
|
|
103
108
|
}
|
|
104
|
-
|
|
109
|
+
return c.json({ count, list, paging: { page, pageSize } });
|
|
105
110
|
} catch (err) {
|
|
106
111
|
logger.error(err);
|
|
107
|
-
|
|
112
|
+
return c.json({ count: 0, list: [], paging: { page, pageSize } });
|
|
108
113
|
}
|
|
109
114
|
});
|
|
110
115
|
|
|
111
116
|
// @link https://stripe.com/docs/api/subscription_items/retrieve
|
|
112
|
-
|
|
117
|
+
app.get('/:id', auth, async (c) => {
|
|
113
118
|
try {
|
|
114
119
|
const doc = await SubscriptionItem.findOne({
|
|
115
|
-
where: { id: req.
|
|
120
|
+
where: { id: c.req.param('id') },
|
|
116
121
|
include: [{ model: Price, as: 'price' }],
|
|
117
122
|
});
|
|
118
123
|
if (doc) {
|
|
119
|
-
|
|
120
|
-
} else {
|
|
121
|
-
res.status(404).json(null);
|
|
124
|
+
return c.json(doc);
|
|
122
125
|
}
|
|
126
|
+
return c.json(null, 404);
|
|
123
127
|
} catch (err) {
|
|
124
128
|
logger.error(err);
|
|
125
|
-
|
|
129
|
+
return c.json({ error: `Failed to get subscription item: ${(err as any).message}` }, 500);
|
|
126
130
|
}
|
|
127
131
|
});
|
|
128
132
|
|
|
129
|
-
|
|
130
|
-
const
|
|
133
|
+
app.put('/:id', auth, async (c) => {
|
|
134
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
135
|
+
const doc = await SubscriptionItem.findByPk(c.req.param('id'));
|
|
131
136
|
|
|
132
137
|
if (!doc) {
|
|
133
|
-
return
|
|
138
|
+
return c.json({ error: `SubscriptionItem not found: ${c.req.param('id')}` }, 404);
|
|
134
139
|
}
|
|
135
140
|
|
|
136
|
-
const updates: Partial<SubscriptionItem> = pick(
|
|
141
|
+
const updates: Partial<SubscriptionItem> = pick(body, ['price_id', 'quantity', 'billing_thresholds', 'metadata']);
|
|
137
142
|
if (updates.price_id) {
|
|
138
143
|
const exist = await SubscriptionItem.findOne({
|
|
139
144
|
where: { price_id: updates.price_id, subscription_id: doc.subscription_id },
|
|
140
145
|
});
|
|
141
146
|
if (exist) {
|
|
142
|
-
return
|
|
147
|
+
return c.json({ error: `SubscriptionItem already exist: ${exist.id}` }, 400);
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
if (!updates.quantity) {
|
|
@@ -150,7 +155,7 @@ router.put('/:id', auth, async (req, res) => {
|
|
|
150
155
|
if (updates.metadata) {
|
|
151
156
|
const { error: metadataError } = MetadataSchema.validate(updates.metadata);
|
|
152
157
|
if (metadataError) {
|
|
153
|
-
return
|
|
158
|
+
return c.json({ error: `metadata invalid: ${metadataError.message}` }, 400);
|
|
154
159
|
}
|
|
155
160
|
updates.metadata = formatMetadata(updates.metadata);
|
|
156
161
|
}
|
|
@@ -160,21 +165,22 @@ router.put('/:id', auth, async (req, res) => {
|
|
|
160
165
|
id: doc.id,
|
|
161
166
|
subscriptionId: doc.subscription_id,
|
|
162
167
|
updatedFields: Object.keys(updates),
|
|
163
|
-
requestedBy:
|
|
168
|
+
requestedBy: c.get('user')?.did,
|
|
164
169
|
});
|
|
165
|
-
return
|
|
170
|
+
return c.json(doc);
|
|
166
171
|
});
|
|
167
172
|
|
|
168
173
|
// TODO: handle proration_behavior
|
|
169
174
|
// @link https://stripe.com/docs/api/subscription_items/delete
|
|
170
|
-
|
|
171
|
-
const
|
|
175
|
+
app.delete('/:id', auth, async (c) => {
|
|
176
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
177
|
+
const doc = await SubscriptionItem.findByPk(c.req.param('id'));
|
|
172
178
|
|
|
173
179
|
if (!doc) {
|
|
174
|
-
return
|
|
180
|
+
return c.json({ error: 'subscription item not found' }, 404);
|
|
175
181
|
}
|
|
176
182
|
|
|
177
|
-
if (
|
|
183
|
+
if (body.clear_usage) {
|
|
178
184
|
const price = await Price.findByPk(doc.price_id);
|
|
179
185
|
if (price?.recurring?.usage_type === 'metered') {
|
|
180
186
|
await UsageRecord.destroy({ where: { subscription_item_id: doc.id } });
|
|
@@ -185,41 +191,42 @@ router.delete('/:id', auth, async (req, res) => {
|
|
|
185
191
|
logger.info('SubscriptionItem deleted', {
|
|
186
192
|
id: doc.id,
|
|
187
193
|
subscriptionId: doc.subscription_id,
|
|
188
|
-
clearUsage:
|
|
189
|
-
requestedBy:
|
|
194
|
+
clearUsage: body.clear_usage,
|
|
195
|
+
requestedBy: c.get('user')?.did,
|
|
190
196
|
});
|
|
191
|
-
return
|
|
197
|
+
return c.json(doc);
|
|
192
198
|
});
|
|
193
199
|
|
|
194
|
-
|
|
195
|
-
const
|
|
200
|
+
app.post('/:id/add-usage-quantity', auth, async (c) => {
|
|
201
|
+
const livemode = c.get('livemode');
|
|
196
202
|
if (livemode) {
|
|
197
|
-
return
|
|
203
|
+
return c.json({ error: 'add usage quantity not allowed in livemode' }, 403);
|
|
198
204
|
}
|
|
199
205
|
|
|
200
|
-
const
|
|
206
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
207
|
+
const quantity = Number(body.quantity);
|
|
201
208
|
if (quantity <= 0) {
|
|
202
|
-
return
|
|
209
|
+
return c.json({ error: 'quantity must be positive' }, 400);
|
|
203
210
|
}
|
|
204
211
|
|
|
205
212
|
try {
|
|
206
|
-
const subscriptionItem = await SubscriptionItem.findByPk(req.
|
|
213
|
+
const subscriptionItem = await SubscriptionItem.findByPk(c.req.param('id'));
|
|
207
214
|
if (!subscriptionItem) {
|
|
208
|
-
return
|
|
215
|
+
return c.json({ error: `SubscriptionItem(${c.req.param('id')}) item not found` }, 404);
|
|
209
216
|
}
|
|
210
217
|
const subscription = await Subscription.findByPk(subscriptionItem.subscription_id);
|
|
211
218
|
if (!subscription) {
|
|
212
|
-
return
|
|
219
|
+
return c.json({ error: `Subscription not found: ${subscriptionItem.subscription_id}` }, 400);
|
|
213
220
|
}
|
|
214
221
|
|
|
215
222
|
if (subscription.isImmutable()) {
|
|
216
|
-
return
|
|
223
|
+
return c.json({ error: `subscription(${subscription.id}) is immutable` }, 400);
|
|
217
224
|
}
|
|
218
225
|
|
|
219
226
|
const now = dayjs().unix();
|
|
220
227
|
|
|
221
228
|
if (now < subscription.current_period_start || now > subscription.current_period_end) {
|
|
222
|
-
return
|
|
229
|
+
return c.json({ error: 'timestamp must be within the current period' }, 400);
|
|
223
230
|
}
|
|
224
231
|
|
|
225
232
|
const usageRecord = await UsageRecord.create({
|
|
@@ -234,7 +241,7 @@ router.post('/:id/add-usage-quantity', auth, async (req, res) => {
|
|
|
234
241
|
subscriptionId: subscription.id,
|
|
235
242
|
quantity,
|
|
236
243
|
timestamp: now,
|
|
237
|
-
requestedBy:
|
|
244
|
+
requestedBy: c.get('user')?.did,
|
|
238
245
|
});
|
|
239
246
|
|
|
240
247
|
if (subscription.billing_thresholds?.amount_gte) {
|
|
@@ -260,11 +267,11 @@ router.post('/:id/add-usage-quantity', auth, async (req, res) => {
|
|
|
260
267
|
action: 'increment',
|
|
261
268
|
});
|
|
262
269
|
|
|
263
|
-
return
|
|
270
|
+
return c.json(usageRecord);
|
|
264
271
|
} catch (err) {
|
|
265
272
|
logger.error(err);
|
|
266
|
-
return
|
|
273
|
+
return c.json({ error: `Failed to add usage quantity: ${(err as any).message}` }, 400);
|
|
267
274
|
}
|
|
268
275
|
});
|
|
269
276
|
|
|
270
|
-
export default
|
|
277
|
+
export default app;
|