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,14 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
// Phase 3 (express→hono) — hono fork of routes/tax-rates.ts. Sub-app with
|
|
2
|
+
// routes relative to /api/tax-rates (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 Joi from 'joi';
|
|
3
7
|
|
|
4
8
|
import { CustomError } from '@blocklet/error';
|
|
5
9
|
import { literal } from 'sequelize';
|
|
6
|
-
import { authenticate } from '
|
|
7
|
-
import { createListParamSchema, getOrder, getWhereFromKvQuery, MetadataSchema } from '
|
|
8
|
-
import { formatMetadata } from '
|
|
9
|
-
import { InvoiceItem, TaxRate } from '
|
|
10
|
+
import { authenticate } from '../../middlewares/hono/security';
|
|
11
|
+
import { createListParamSchema, getOrder, getWhereFromKvQuery, MetadataSchema } from '../../libs/api';
|
|
12
|
+
import { formatMetadata } from '../../libs/util';
|
|
13
|
+
import { InvoiceItem, TaxRate } from '../../store/models';
|
|
10
14
|
|
|
11
|
-
const
|
|
15
|
+
const app = new Hono();
|
|
12
16
|
const auth = authenticate({ component: true, roles: ['owner', 'admin'] });
|
|
13
17
|
|
|
14
18
|
const createTaxRateSchema = Joi.object({
|
|
@@ -42,19 +46,18 @@ const listSchema = createListParamSchema<{
|
|
|
42
46
|
});
|
|
43
47
|
|
|
44
48
|
// List tax rates
|
|
45
|
-
|
|
49
|
+
app.get('/', auth, async (c) => {
|
|
46
50
|
try {
|
|
47
|
-
const { error, value: validated } = listSchema.validate(req.query, { stripUnknown: true });
|
|
51
|
+
const { error, value: validated } = listSchema.validate(c.req.query(), { stripUnknown: true });
|
|
48
52
|
if (error) {
|
|
49
|
-
|
|
50
|
-
return;
|
|
53
|
+
return c.json({ error: `Validation error: ${error.message}` }, 400);
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
const { page, pageSize, ...query } = validated;
|
|
54
57
|
const where = getWhereFromKvQuery(query.q);
|
|
55
58
|
|
|
56
|
-
if (typeof
|
|
57
|
-
where.livemode =
|
|
59
|
+
if (typeof c.get('livemode') === 'boolean') {
|
|
60
|
+
where.livemode = c.get('livemode');
|
|
58
61
|
}
|
|
59
62
|
if (query.country) {
|
|
60
63
|
where.country = query.country;
|
|
@@ -68,7 +71,7 @@ router.get('/', auth, async (req, res) => {
|
|
|
68
71
|
|
|
69
72
|
const { rows: list, count } = await TaxRate.findAndCountAll({
|
|
70
73
|
where,
|
|
71
|
-
order: getOrder(req.query, [['created_at', 'DESC']]),
|
|
74
|
+
order: getOrder(c.req.query(), [['created_at', 'DESC']]),
|
|
72
75
|
offset: (page - 1) * pageSize,
|
|
73
76
|
limit: pageSize,
|
|
74
77
|
attributes: {
|
|
@@ -85,39 +88,48 @@ router.get('/', auth, async (req, res) => {
|
|
|
85
88
|
},
|
|
86
89
|
});
|
|
87
90
|
|
|
88
|
-
|
|
91
|
+
return c.json({ count, list });
|
|
89
92
|
} catch (error) {
|
|
90
|
-
|
|
93
|
+
return c.json({ error: (error as any)?.message }, 400);
|
|
91
94
|
}
|
|
92
95
|
});
|
|
93
96
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
// Find matching tax rate for a given location and tax code
|
|
98
|
+
// static path registered before /:id so it wins over the param route
|
|
99
|
+
app.post('/match', auth, async (c) => {
|
|
100
|
+
const schema = Joi.object({
|
|
101
|
+
country: Joi.string().length(2).uppercase().required(),
|
|
102
|
+
state: Joi.string().max(50).empty('').optional(),
|
|
103
|
+
postal_code: Joi.string().max(20).empty('').optional(),
|
|
104
|
+
tax_code: Joi.string().max(20).empty('').optional(),
|
|
101
105
|
});
|
|
102
106
|
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
const { error, value: data } = schema.validate(c.get('sanitizedBody') ?? {}, { stripUnknown: true });
|
|
108
|
+
if (error) {
|
|
109
|
+
return c.json({ error: error.message }, 400);
|
|
105
110
|
}
|
|
106
111
|
|
|
107
|
-
|
|
112
|
+
const matchedRate = await TaxRate.findMatchingRate({
|
|
113
|
+
country: data.country,
|
|
114
|
+
state: data.state,
|
|
115
|
+
postalCode: data.postal_code,
|
|
116
|
+
taxCode: data.tax_code,
|
|
117
|
+
livemode: c.get('livemode'),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return c.json(matchedRate);
|
|
108
121
|
});
|
|
109
122
|
|
|
110
123
|
// Create a new tax rate
|
|
111
|
-
|
|
112
|
-
const { error, value: data } = createTaxRateSchema.validate(
|
|
124
|
+
app.post('/', auth, async (c) => {
|
|
125
|
+
const { error, value: data } = createTaxRateSchema.validate(c.get('sanitizedBody') ?? {}, { stripUnknown: true });
|
|
113
126
|
if (error) {
|
|
114
|
-
|
|
115
|
-
return;
|
|
127
|
+
return c.json({ error: `Validation failed: ${error.message}` }, 400);
|
|
116
128
|
}
|
|
117
129
|
|
|
118
130
|
const existingRate = await TaxRate.findOne({
|
|
119
131
|
where: {
|
|
120
|
-
livemode:
|
|
132
|
+
livemode: c.get('livemode'),
|
|
121
133
|
country: data.country,
|
|
122
134
|
state: data.state || null,
|
|
123
135
|
postal_code: data.postal_code || null,
|
|
@@ -134,25 +146,40 @@ router.post('/', auth, async (req, res) => {
|
|
|
134
146
|
|
|
135
147
|
const taxRate = await TaxRate.create({
|
|
136
148
|
...data,
|
|
137
|
-
livemode:
|
|
149
|
+
livemode: c.get('livemode'),
|
|
138
150
|
metadata: formatMetadata(data.metadata),
|
|
139
151
|
});
|
|
140
152
|
|
|
141
|
-
|
|
153
|
+
return c.json(taxRate);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Get a specific tax rate
|
|
157
|
+
app.get('/:id', auth, async (c) => {
|
|
158
|
+
const taxRate = await TaxRate.findOne({
|
|
159
|
+
where: {
|
|
160
|
+
id: c.req.param('id'),
|
|
161
|
+
livemode: c.get('livemode'),
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (!taxRate) {
|
|
166
|
+
throw new CustomError(404, 'Tax rate not found');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return c.json(taxRate);
|
|
142
170
|
});
|
|
143
171
|
|
|
144
172
|
// Update a tax rate
|
|
145
|
-
|
|
146
|
-
const { error, value: data } = updateTaxRateSchema.validate(
|
|
173
|
+
app.put('/:id', auth, async (c) => {
|
|
174
|
+
const { error, value: data } = updateTaxRateSchema.validate(c.get('sanitizedBody') ?? {}, { stripUnknown: true });
|
|
147
175
|
if (error) {
|
|
148
|
-
|
|
149
|
-
return;
|
|
176
|
+
return c.json({ error: `Validation failed: ${error.message}` }, 400);
|
|
150
177
|
}
|
|
151
178
|
|
|
152
179
|
const taxRate = await TaxRate.findOne({
|
|
153
180
|
where: {
|
|
154
|
-
id: req.
|
|
155
|
-
livemode:
|
|
181
|
+
id: c.req.param('id'),
|
|
182
|
+
livemode: c.get('livemode'),
|
|
156
183
|
},
|
|
157
184
|
});
|
|
158
185
|
|
|
@@ -165,15 +192,15 @@ router.put('/:id', auth, async (req, res) => {
|
|
|
165
192
|
metadata: data.metadata ? formatMetadata(data.metadata) : taxRate.metadata,
|
|
166
193
|
});
|
|
167
194
|
|
|
168
|
-
|
|
195
|
+
return c.json(taxRate);
|
|
169
196
|
});
|
|
170
197
|
|
|
171
198
|
// Delete a tax rate
|
|
172
|
-
|
|
199
|
+
app.delete('/:id', auth, async (c) => {
|
|
173
200
|
const taxRate = await TaxRate.findOne({
|
|
174
201
|
where: {
|
|
175
|
-
id: req.
|
|
176
|
-
livemode:
|
|
202
|
+
id: c.req.param('id'),
|
|
203
|
+
livemode: c.get('livemode'),
|
|
177
204
|
},
|
|
178
205
|
});
|
|
179
206
|
|
|
@@ -188,33 +215,7 @@ router.delete('/:id', auth, async (req, res) => {
|
|
|
188
215
|
|
|
189
216
|
await taxRate.destroy();
|
|
190
217
|
|
|
191
|
-
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
// Find matching tax rate for a given location and tax code
|
|
195
|
-
router.post('/match', auth, async (req, res) => {
|
|
196
|
-
const schema = Joi.object({
|
|
197
|
-
country: Joi.string().length(2).uppercase().required(),
|
|
198
|
-
state: Joi.string().max(50).empty('').optional(),
|
|
199
|
-
postal_code: Joi.string().max(20).empty('').optional(),
|
|
200
|
-
tax_code: Joi.string().max(20).empty('').optional(),
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
const { error, value: data } = schema.validate(req.body, { stripUnknown: true });
|
|
204
|
-
if (error) {
|
|
205
|
-
res.status(400).json({ error: error.message });
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const matchedRate = await TaxRate.findMatchingRate({
|
|
210
|
-
country: data.country,
|
|
211
|
-
state: data.state,
|
|
212
|
-
postalCode: data.postal_code,
|
|
213
|
-
taxCode: data.tax_code,
|
|
214
|
-
livemode: req.livemode,
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
res.json(matchedRate);
|
|
218
|
+
return c.json({ deleted: true, id: c.req.param('id') });
|
|
218
219
|
});
|
|
219
220
|
|
|
220
|
-
export default
|
|
221
|
+
export default app;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Phase 3 (express→hono) — hono fork of routes/tool.ts. Sub-app with
|
|
2
|
+
// routes relative to /api/tool (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 dayjs from 'dayjs';
|
|
6
|
+
import { Hono } from 'hono';
|
|
7
|
+
import Joi from 'joi';
|
|
8
|
+
|
|
9
|
+
import { getUrl } from '@blocklet/sdk/lib/component';
|
|
10
|
+
import { joinURL } from 'ufo';
|
|
11
|
+
import { authenticate } from '../../middlewares/hono/security';
|
|
12
|
+
import { formatToShortUrl } from '../../libs/url';
|
|
13
|
+
import { CheckoutSession } from '../../store/models';
|
|
14
|
+
import logger from '../../libs/logger';
|
|
15
|
+
|
|
16
|
+
const loginAuth = authenticate<CheckoutSession>({ component: false, ensureLogin: true, mine: true });
|
|
17
|
+
|
|
18
|
+
const app = new Hono();
|
|
19
|
+
|
|
20
|
+
const shortUrlSchema = Joi.object({
|
|
21
|
+
url: Joi.string().uri().required(),
|
|
22
|
+
timeoutMin: Joi.number().optional(),
|
|
23
|
+
maxVisits: Joi.number().optional(),
|
|
24
|
+
}).unknown(true);
|
|
25
|
+
|
|
26
|
+
app.get('/short-url', loginAuth, async (c) => {
|
|
27
|
+
const query = c.req.query();
|
|
28
|
+
const { error } = shortUrlSchema.validate(query);
|
|
29
|
+
if (error) {
|
|
30
|
+
return c.json({ error: 'Invalid parameters', message: error.message }, 400);
|
|
31
|
+
}
|
|
32
|
+
const { url, timeoutMin = 60, maxVisits = 5 } = query;
|
|
33
|
+
const timeoutMinNumber = Number(timeoutMin);
|
|
34
|
+
const validUntil = dayjs()
|
|
35
|
+
.add(timeoutMinNumber > 60 ? 60 : timeoutMinNumber, 'minutes')
|
|
36
|
+
.format('YYYY-MM-DDTHH:mm:ss+00:00');
|
|
37
|
+
|
|
38
|
+
const shortUrl = await formatToShortUrl({ url: url as string, validUntil, maxVisits: Number(maxVisits) });
|
|
39
|
+
|
|
40
|
+
return c.json({ url: shortUrl });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
app.get('/short-connect-url', async (c) => {
|
|
44
|
+
const query = c.req.query();
|
|
45
|
+
const { error } = shortUrlSchema.validate(query);
|
|
46
|
+
if (error) {
|
|
47
|
+
return c.json({ error: 'Invalid parameters', message: error.message }, 400);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { url, timeoutMin = 60, maxVisits = 5 } = query;
|
|
51
|
+
const serverUrl = getUrl();
|
|
52
|
+
|
|
53
|
+
const allowedEndpoint = joinURL(new URL(serverUrl).origin, '.well-known/service/gen-simple-access-key');
|
|
54
|
+
|
|
55
|
+
if (!url?.toString().startsWith(allowedEndpoint)) {
|
|
56
|
+
logger.error('Client try to generate short connect url from invalid service', { url });
|
|
57
|
+
return c.json({ error: 'Just support generate short connect url from gen-simple-access-key service' }, 400);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const timeoutMinNumber = Number(timeoutMin);
|
|
61
|
+
const validUntil = dayjs()
|
|
62
|
+
.add(timeoutMinNumber > 60 ? 60 : timeoutMinNumber, 'minutes')
|
|
63
|
+
.format('YYYY-MM-DDTHH:mm:ss+00:00');
|
|
64
|
+
|
|
65
|
+
const shortUrl = await formatToShortUrl({ url: url as string, validUntil, maxVisits: Number(maxVisits) });
|
|
66
|
+
return c.json({ url: shortUrl });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export default app;
|
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Phase 3 (express→hono) — hono fork of routes/usage-records.ts. Sub-app with
|
|
2
|
+
// routes relative to /api/usage-records (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 { Context, Hono } from 'hono';
|
|
3
6
|
import Joi from 'joi';
|
|
4
7
|
import pick from 'lodash/pick';
|
|
5
8
|
import { Op } from 'sequelize';
|
|
6
9
|
|
|
7
|
-
import { forwardUsageRecordToStripe } from '
|
|
8
|
-
import { createListParamSchema, getOrder } from '
|
|
9
|
-
import dayjs from '
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
10
|
+
import { forwardUsageRecordToStripe } from '../../integrations/stripe/resource';
|
|
11
|
+
import { createListParamSchema, getOrder } from '../../libs/api';
|
|
12
|
+
import dayjs from '../../libs/dayjs';
|
|
13
|
+
import { getLock } from '../../libs/lock';
|
|
14
|
+
import logger from '../../libs/logger';
|
|
15
|
+
import { authenticate } from '../../middlewares/hono/security';
|
|
16
|
+
import { usageRecordQueue } from '../../queues/usage-record';
|
|
17
|
+
import { Invoice, Price, Subscription, SubscriptionItem, UsageRecord } from '../../store/models';
|
|
15
18
|
|
|
16
|
-
const
|
|
19
|
+
const app = new Hono();
|
|
17
20
|
const auth = authenticate<UsageRecord>({ component: true, roles: ['owner', 'admin'] });
|
|
18
21
|
|
|
19
22
|
const UsageReportScheme = Joi.object({
|
|
@@ -25,24 +28,25 @@ const UsageReportScheme = Joi.object({
|
|
|
25
28
|
}).unknown(true);
|
|
26
29
|
|
|
27
30
|
// @link https://stripe.com/docs/api/usage_records/create
|
|
28
|
-
|
|
29
|
-
const
|
|
31
|
+
app.post('/', auth, async (c) => {
|
|
32
|
+
const body = c.get('sanitizedBody') ?? {};
|
|
33
|
+
const { error } = UsageReportScheme.validate(body);
|
|
30
34
|
if (error) {
|
|
31
|
-
return
|
|
35
|
+
return c.json({ error: error.message }, 400);
|
|
32
36
|
}
|
|
33
|
-
const raw: Partial<UsageRecord> = pick(
|
|
37
|
+
const raw: Partial<UsageRecord> = pick(body, ['timestamp', 'quantity', 'subscription_item_id']);
|
|
34
38
|
const item = await SubscriptionItem.findByPk(raw.subscription_item_id);
|
|
35
39
|
if (!item) {
|
|
36
|
-
return
|
|
40
|
+
return c.json({ error: `SubscriptionItem not found: ${raw.subscription_item_id}` }, 400);
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
const subscription = await Subscription.findByPk(item.subscription_id);
|
|
40
44
|
if (!subscription) {
|
|
41
|
-
return
|
|
45
|
+
return c.json({ error: `Subscription not found: ${item.subscription_id}` }, 400);
|
|
42
46
|
}
|
|
43
47
|
if (raw.timestamp) {
|
|
44
48
|
if (raw.timestamp < subscription.current_period_start || raw.timestamp > subscription.current_period_end) {
|
|
45
|
-
return
|
|
49
|
+
return c.json({ error: '`timestamp` must be within the current period' }, 400);
|
|
46
50
|
}
|
|
47
51
|
}
|
|
48
52
|
|
|
@@ -57,14 +61,14 @@ router.post('/', auth, async (req, res) => {
|
|
|
57
61
|
logger.info('Usage reporting', {
|
|
58
62
|
subscriptionId: item.subscription_id,
|
|
59
63
|
subscriptionItemId: item.id,
|
|
60
|
-
body
|
|
64
|
+
body,
|
|
61
65
|
});
|
|
62
66
|
|
|
63
67
|
let doc = await UsageRecord.findOne({
|
|
64
68
|
where: { timestamp: raw.timestamp, subscription_item_id: raw.subscription_item_id },
|
|
65
69
|
});
|
|
66
70
|
|
|
67
|
-
const action =
|
|
71
|
+
const action = body.action || 'increment';
|
|
68
72
|
if (doc) {
|
|
69
73
|
if (doc.billed) {
|
|
70
74
|
logger.info('UsageRecord updated', {
|
|
@@ -72,7 +76,7 @@ router.post('/', auth, async (req, res) => {
|
|
|
72
76
|
timestamp: raw.timestamp,
|
|
73
77
|
newQuantity: raw.quantity,
|
|
74
78
|
});
|
|
75
|
-
return
|
|
79
|
+
return c.json({ error: 'UsageRecord is immutable because already billed' }, 400);
|
|
76
80
|
}
|
|
77
81
|
if (action === 'increment') {
|
|
78
82
|
await doc.increment('quantity', { by: raw.quantity });
|
|
@@ -88,9 +92,10 @@ router.post('/', auth, async (req, res) => {
|
|
|
88
92
|
subscriptionId: subscription.id,
|
|
89
93
|
action,
|
|
90
94
|
});
|
|
91
|
-
return
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
return c.json(
|
|
96
|
+
{ error: 'UsageRecord action must be `increment` for subscriptions with billing_thresholds' },
|
|
97
|
+
400
|
|
98
|
+
);
|
|
94
99
|
}
|
|
95
100
|
await doc.update({ quantity: raw.quantity });
|
|
96
101
|
logger.info('UsageRecord updated', {
|
|
@@ -101,7 +106,7 @@ router.post('/', auth, async (req, res) => {
|
|
|
101
106
|
});
|
|
102
107
|
}
|
|
103
108
|
} else {
|
|
104
|
-
raw.livemode =
|
|
109
|
+
raw.livemode = c.get('livemode');
|
|
105
110
|
doc = await UsageRecord.create(raw as UsageRecord);
|
|
106
111
|
}
|
|
107
112
|
|
|
@@ -127,10 +132,10 @@ router.post('/', auth, async (req, res) => {
|
|
|
127
132
|
action,
|
|
128
133
|
});
|
|
129
134
|
await doc.reload();
|
|
130
|
-
return
|
|
135
|
+
return c.json(doc);
|
|
131
136
|
} catch (err) {
|
|
132
137
|
logger.error('Error in usage-records', { error: err });
|
|
133
|
-
return
|
|
138
|
+
return c.json({ error: (err as any).message || 'UsageRecord creation failed' }, 400);
|
|
134
139
|
} finally {
|
|
135
140
|
lock.release();
|
|
136
141
|
}
|
|
@@ -149,21 +154,21 @@ const schema = createListParamSchema<{
|
|
|
149
154
|
},
|
|
150
155
|
100
|
|
151
156
|
);
|
|
152
|
-
|
|
153
|
-
const { page, pageSize, ...query } = await schema.validateAsync(req.query, { stripUnknown: true });
|
|
157
|
+
app.get('/summary', auth, async (c) => {
|
|
158
|
+
const { page, pageSize, ...query } = await schema.validateAsync(c.req.query(), { stripUnknown: true });
|
|
154
159
|
|
|
155
160
|
try {
|
|
156
161
|
const item = await SubscriptionItem.findByPk(query.subscription_item_id, {
|
|
157
162
|
include: [{ model: Price, as: 'price' }],
|
|
158
163
|
});
|
|
159
164
|
if (!item) {
|
|
160
|
-
return
|
|
165
|
+
return c.json({ error: `SubscriptionItem not found: ${query.subscription_item_id}` }, 400);
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
const { rows, count } = await Invoice.findAndCountAll({
|
|
164
169
|
where: { subscription_id: item.subscription_id },
|
|
165
170
|
attributes: ['id', 'period_end', 'period_start'],
|
|
166
|
-
order: getOrder(req.query, [['created_at', 'DESC']]),
|
|
171
|
+
order: getOrder(c.req.query(), [['created_at', 'DESC']]),
|
|
167
172
|
offset: (page - 1) * pageSize,
|
|
168
173
|
limit: pageSize,
|
|
169
174
|
});
|
|
@@ -190,10 +195,10 @@ router.get('/summary', auth, async (req, res) => {
|
|
|
190
195
|
})
|
|
191
196
|
);
|
|
192
197
|
|
|
193
|
-
|
|
198
|
+
return c.json({ count, list, paging: { page, pageSize } });
|
|
194
199
|
} catch (err) {
|
|
195
200
|
logger.error(err);
|
|
196
|
-
|
|
201
|
+
return c.json({ count: 0, list: [], paging: { page, pageSize } });
|
|
197
202
|
}
|
|
198
203
|
});
|
|
199
204
|
|
|
@@ -207,19 +212,19 @@ const UsageRecordScheme = Joi.object({
|
|
|
207
212
|
}).unknown(true);
|
|
208
213
|
|
|
209
214
|
export function createUsageRecordQueryFn(doc?: Subscription) {
|
|
210
|
-
return async (
|
|
211
|
-
const { error, value: query } = UsageRecordScheme.validate(req.query, { stripUnknown: true });
|
|
215
|
+
return async (c: Context) => {
|
|
216
|
+
const { error, value: query } = UsageRecordScheme.validate(c.req.query(), { stripUnknown: true });
|
|
212
217
|
if (error) {
|
|
213
|
-
return
|
|
218
|
+
return c.json({ error: `usage record request query invalid: ${error.message}` }, 400);
|
|
214
219
|
}
|
|
215
220
|
try {
|
|
216
221
|
const item = await SubscriptionItem.findByPk(query.subscription_item_id);
|
|
217
222
|
if (!item) {
|
|
218
|
-
return
|
|
223
|
+
return c.json({ error: `SubscriptionItem not found: ${query.subscription_item_id}` }, 400);
|
|
219
224
|
}
|
|
220
225
|
const subscription = doc || (await Subscription.findByPk(item.subscription_id));
|
|
221
226
|
if (!subscription) {
|
|
222
|
-
return
|
|
227
|
+
return c.json({ error: `Subscription not found: ${item.subscription_id}` }, 400);
|
|
223
228
|
}
|
|
224
229
|
|
|
225
230
|
const { rows: list, count } = await UsageRecord.findAndCountAll({
|
|
@@ -233,14 +238,14 @@ export function createUsageRecordQueryFn(doc?: Subscription) {
|
|
|
233
238
|
order: [['created_at', 'ASC']],
|
|
234
239
|
});
|
|
235
240
|
|
|
236
|
-
|
|
241
|
+
return c.json({ count, list });
|
|
237
242
|
} catch (err) {
|
|
238
243
|
logger.error(err);
|
|
239
|
-
|
|
244
|
+
return c.json({ count: 0, list: [] });
|
|
240
245
|
}
|
|
241
246
|
};
|
|
242
247
|
}
|
|
243
248
|
|
|
244
|
-
|
|
249
|
+
app.get('/', auth, createUsageRecordQueryFn());
|
|
245
250
|
|
|
246
|
-
export default
|
|
251
|
+
export default app;
|