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
|
@@ -2,7 +2,9 @@ import type { LiteralUnion } from 'type-fest';
|
|
|
2
2
|
|
|
3
3
|
import { BN } from '@ocap/util';
|
|
4
4
|
import { Op } from 'sequelize';
|
|
5
|
-
import {
|
|
5
|
+
import { paymentReloadSubscriptionJobs } from '../libs/env';
|
|
6
|
+
import { systemFindAll, systemFindByPk, systemFindOne } from '../store/scoped';
|
|
7
|
+
import { createEvent, reportAuditFailure } from '../libs/audit';
|
|
6
8
|
import { ensurePassportRevoked } from '../integrations/blocklet/passport';
|
|
7
9
|
// eslint-disable-next-line import/no-cycle
|
|
8
10
|
import { batchHandleStripeSubscriptions } from '../integrations/stripe/resource';
|
|
@@ -13,7 +15,7 @@ import { events } from '../libs/event';
|
|
|
13
15
|
import { getLock } from '../libs/lock';
|
|
14
16
|
import logger from '../libs/logger';
|
|
15
17
|
import { getGasPayerExtra, isDelegationSufficientForPayment } from '../libs/payment';
|
|
16
|
-
import createQueue from '../libs/queue';
|
|
18
|
+
import createQueue, { assertJobObjectTenant } from '../libs/queue';
|
|
17
19
|
import { getStatementDescriptor } from '../libs/session';
|
|
18
20
|
import { NonRetryableError } from '../libs/error';
|
|
19
21
|
import {
|
|
@@ -83,7 +85,7 @@ async function attachQuotesForInvoice(
|
|
|
83
85
|
return { lineItems, quotes: [] };
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
const currency = (await PaymentCurrency
|
|
88
|
+
const currency = (await systemFindByPk(PaymentCurrency, currencyId, {
|
|
87
89
|
include: [{ model: PaymentMethod, as: 'payment_method' }],
|
|
88
90
|
})) as PaymentCurrency & { payment_method: PaymentMethod };
|
|
89
91
|
if (!currency) {
|
|
@@ -178,22 +180,24 @@ const doHandleSubscriptionInvoice = async ({
|
|
|
178
180
|
metadata?: Record<string, any>;
|
|
179
181
|
}) => {
|
|
180
182
|
// Do we still have the customer
|
|
181
|
-
const customer = await Customer
|
|
183
|
+
const customer = await systemFindByPk(Customer, subscription.customer_id);
|
|
182
184
|
if (!customer) {
|
|
183
185
|
logger.warn(`Customer ${subscription.customer_id} not found for subscription: ${subscription.id}`);
|
|
184
186
|
return null;
|
|
185
187
|
}
|
|
188
|
+
assertJobObjectTenant(customer);
|
|
186
189
|
|
|
187
190
|
// Do we still have the currency
|
|
188
|
-
const currency = await PaymentCurrency
|
|
191
|
+
const currency = await systemFindByPk(PaymentCurrency, subscription.currency_id);
|
|
189
192
|
if (!currency) {
|
|
190
193
|
logger.warn(`Currency ${subscription.currency_id} not found for subscription: ${subscription.id}`);
|
|
191
194
|
return null;
|
|
192
195
|
}
|
|
196
|
+
assertJobObjectTenant(currency);
|
|
193
197
|
|
|
194
198
|
// Do not proceed if previous invoice is not immutable
|
|
195
199
|
if (reason === 'cycle') {
|
|
196
|
-
const previous = await Invoice
|
|
200
|
+
const previous = await systemFindOne(Invoice, {
|
|
197
201
|
where: {
|
|
198
202
|
subscription_id: subscription.id,
|
|
199
203
|
period_start: subscription.current_period_start,
|
|
@@ -203,7 +207,7 @@ const doHandleSubscriptionInvoice = async ({
|
|
|
203
207
|
});
|
|
204
208
|
let existOverdraftProtection;
|
|
205
209
|
if (previous) {
|
|
206
|
-
existOverdraftProtection = await Invoice
|
|
210
|
+
existOverdraftProtection = await systemFindOne(Invoice, {
|
|
207
211
|
where: {
|
|
208
212
|
subscription_id: subscription.id,
|
|
209
213
|
billing_reason: 'overdraft_protection',
|
|
@@ -220,7 +224,7 @@ const doHandleSubscriptionInvoice = async ({
|
|
|
220
224
|
|
|
221
225
|
// check if invoice already created for this reason
|
|
222
226
|
if (['cycle', 'cancel', 'recover'].includes(reason)) {
|
|
223
|
-
const exist = await Invoice
|
|
227
|
+
const exist = await systemFindOne(Invoice, {
|
|
224
228
|
where: {
|
|
225
229
|
subscription_id: subscription.id,
|
|
226
230
|
period_start: start,
|
|
@@ -235,7 +239,7 @@ const doHandleSubscriptionInvoice = async ({
|
|
|
235
239
|
}
|
|
236
240
|
|
|
237
241
|
// expand subscription items
|
|
238
|
-
const subscriptionItems = await SubscriptionItem
|
|
242
|
+
const subscriptionItems = await systemFindAll(SubscriptionItem, { where: { subscription_id: subscription.id } });
|
|
239
243
|
let expandedItems = await Price.expand(
|
|
240
244
|
subscriptionItems.map((x) => ({ id: x.id, price_id: x.price_id, quantity: x.quantity })),
|
|
241
245
|
{ product: true }
|
|
@@ -251,7 +255,7 @@ const doHandleSubscriptionInvoice = async ({
|
|
|
251
255
|
createEvent('Subscription', 'usage.report.empty', subscription, {
|
|
252
256
|
usageReportStart,
|
|
253
257
|
usageReportEnd,
|
|
254
|
-
}).catch(
|
|
258
|
+
}).catch(reportAuditFailure);
|
|
255
259
|
logger.info('create usage report empty event', {
|
|
256
260
|
subscriptionId: subscription.id,
|
|
257
261
|
usageReportStart,
|
|
@@ -542,11 +546,12 @@ export async function handleSubscriptionInvoice(args: Parameters<typeof doHandle
|
|
|
542
546
|
}
|
|
543
547
|
|
|
544
548
|
const handleSubscriptionBeforeCancel = async (subscription: Subscription) => {
|
|
545
|
-
const paymentCurrency = await PaymentCurrency
|
|
549
|
+
const paymentCurrency = await systemFindByPk(PaymentCurrency, subscription.currency_id);
|
|
546
550
|
if (!paymentCurrency) {
|
|
547
551
|
logger.warn('Payment currency not found for subscription', { subscription: subscription.id });
|
|
548
552
|
return;
|
|
549
553
|
}
|
|
554
|
+
assertJobObjectTenant(paymentCurrency);
|
|
550
555
|
if (paymentCurrency.isCredit()) {
|
|
551
556
|
logger.info('Skip invoice creation for credit subscription', { subscription: subscription.id });
|
|
552
557
|
return;
|
|
@@ -592,12 +597,12 @@ const handleSubscriptionWhenActive = async (subscription: Subscription) => {
|
|
|
592
597
|
|
|
593
598
|
// Check if this is a credit subscription
|
|
594
599
|
const isCredit = await subscription.isConsumesCredit();
|
|
595
|
-
const paymentCurrency = await PaymentCurrency
|
|
600
|
+
const paymentCurrency = await systemFindByPk(PaymentCurrency, subscription.currency_id);
|
|
596
601
|
|
|
597
602
|
if (isCredit && paymentCurrency?.isCredit()) {
|
|
598
603
|
// For credit subscriptions, check credit availability instead of creating invoices
|
|
599
|
-
const customer = await Customer
|
|
600
|
-
const paymentMethod = await PaymentMethod
|
|
604
|
+
const customer = await systemFindByPk(Customer, subscription.customer_id);
|
|
605
|
+
const paymentMethod = await systemFindByPk(PaymentMethod, subscription.default_payment_method_id);
|
|
601
606
|
|
|
602
607
|
if (!customer || !paymentMethod || !paymentCurrency) {
|
|
603
608
|
logger.warn('Credit subscription cycle skipped due to missing dependencies', {
|
|
@@ -757,7 +762,7 @@ const handleFinalInvoicePayment = async (
|
|
|
757
762
|
paymentCurrency: PaymentCurrency
|
|
758
763
|
) => {
|
|
759
764
|
// Check if there's any unpaid final metered invoice
|
|
760
|
-
const lastInvoice = await Invoice
|
|
765
|
+
const lastInvoice = await systemFindOne(Invoice, {
|
|
761
766
|
where: {
|
|
762
767
|
subscription_id: subscription.id,
|
|
763
768
|
billing_reason: 'subscription_cancel',
|
|
@@ -775,7 +780,7 @@ const handleFinalInvoicePayment = async (
|
|
|
775
780
|
}
|
|
776
781
|
|
|
777
782
|
// Check payment status and handle accordingly
|
|
778
|
-
const paymentIntent = await PaymentIntent
|
|
783
|
+
const paymentIntent = await systemFindByPk(PaymentIntent, lastInvoice.payment_intent_id);
|
|
779
784
|
if (!paymentIntent) {
|
|
780
785
|
logger.warn('PaymentIntent not found for final invoice', {
|
|
781
786
|
subscription: subscription.id,
|
|
@@ -783,6 +788,7 @@ const handleFinalInvoicePayment = async (
|
|
|
783
788
|
});
|
|
784
789
|
return;
|
|
785
790
|
}
|
|
791
|
+
assertJobObjectTenant(paymentIntent);
|
|
786
792
|
|
|
787
793
|
// If payment already succeeded, skip processing
|
|
788
794
|
if (paymentIntent.status === 'succeeded') {
|
|
@@ -794,7 +800,7 @@ const handleFinalInvoicePayment = async (
|
|
|
794
800
|
return;
|
|
795
801
|
}
|
|
796
802
|
|
|
797
|
-
const customer = await Customer
|
|
803
|
+
const customer = await systemFindByPk(Customer, subscription.customer_id);
|
|
798
804
|
if (!customer) {
|
|
799
805
|
logger.warn('Final invoice settlement skipped because customer not found', {
|
|
800
806
|
subscription: subscription.id,
|
|
@@ -802,6 +808,7 @@ const handleFinalInvoicePayment = async (
|
|
|
802
808
|
});
|
|
803
809
|
return;
|
|
804
810
|
}
|
|
811
|
+
assertJobObjectTenant(customer);
|
|
805
812
|
|
|
806
813
|
logger.info('Found unpaid final invoice, checking user delegation balance', {
|
|
807
814
|
subscription: subscription.id,
|
|
@@ -834,7 +841,7 @@ const handleFinalInvoicePayment = async (
|
|
|
834
841
|
};
|
|
835
842
|
|
|
836
843
|
export const handleStakeSlashAfterCancel = async (subscription: Subscription, forceSlash: boolean = false) => {
|
|
837
|
-
const invoice = await Invoice
|
|
844
|
+
const invoice = await systemFindByPk(Invoice, subscription.latest_invoice_id);
|
|
838
845
|
if (!invoice || (invoice.status !== 'uncollectible' && !forceSlash)) {
|
|
839
846
|
logger.warn('Stake slashing aborted because invoice status', {
|
|
840
847
|
subscription: subscription.id,
|
|
@@ -843,7 +850,7 @@ export const handleStakeSlashAfterCancel = async (subscription: Subscription, fo
|
|
|
843
850
|
});
|
|
844
851
|
return;
|
|
845
852
|
}
|
|
846
|
-
const currency = await PaymentCurrency
|
|
853
|
+
const currency = await systemFindByPk(PaymentCurrency, invoice.currency_id || subscription.currency_id);
|
|
847
854
|
if (!currency) {
|
|
848
855
|
logger.warn('Stake slashing aborted because currency not found', {
|
|
849
856
|
subscription: subscription.id,
|
|
@@ -852,7 +859,8 @@ export const handleStakeSlashAfterCancel = async (subscription: Subscription, fo
|
|
|
852
859
|
});
|
|
853
860
|
return;
|
|
854
861
|
}
|
|
855
|
-
|
|
862
|
+
assertJobObjectTenant(currency);
|
|
863
|
+
const method = await systemFindByPk(PaymentMethod, currency.payment_method_id);
|
|
856
864
|
if (!method || method.type !== 'arcblock') {
|
|
857
865
|
logger.warn('Stake slashing aborted because payment method not arcblock', {
|
|
858
866
|
subscription: subscription.id,
|
|
@@ -861,7 +869,7 @@ export const handleStakeSlashAfterCancel = async (subscription: Subscription, fo
|
|
|
861
869
|
});
|
|
862
870
|
return;
|
|
863
871
|
}
|
|
864
|
-
const customer = await Customer
|
|
872
|
+
const customer = await systemFindByPk(Customer, subscription.customer_id);
|
|
865
873
|
if (!customer) {
|
|
866
874
|
logger.warn('Stake slashing aborted because customer not found', {
|
|
867
875
|
subscription: subscription.id,
|
|
@@ -870,7 +878,8 @@ export const handleStakeSlashAfterCancel = async (subscription: Subscription, fo
|
|
|
870
878
|
});
|
|
871
879
|
return;
|
|
872
880
|
}
|
|
873
|
-
|
|
881
|
+
assertJobObjectTenant(customer);
|
|
882
|
+
const paymentIntent = await systemFindByPk(PaymentIntent, invoice.payment_intent_id);
|
|
874
883
|
if (!paymentIntent) {
|
|
875
884
|
logger.warn('Stake slashing aborted because payment intent not found', {
|
|
876
885
|
subscription: subscription.id,
|
|
@@ -878,6 +887,7 @@ export const handleStakeSlashAfterCancel = async (subscription: Subscription, fo
|
|
|
878
887
|
});
|
|
879
888
|
return;
|
|
880
889
|
}
|
|
890
|
+
assertJobObjectTenant(paymentIntent);
|
|
881
891
|
|
|
882
892
|
// Use lock to prevent race condition with payment queue
|
|
883
893
|
const lock = getLock(`payment-${paymentIntent.id}`);
|
|
@@ -1023,7 +1033,7 @@ export const handleStakeSlashAfterCancel = async (subscription: Subscription, fo
|
|
|
1023
1033
|
};
|
|
1024
1034
|
|
|
1025
1035
|
const ensureReturnStake = async (subscription: Subscription, paymentCurrencyId?: string, stakingAddress?: string) => {
|
|
1026
|
-
const paymentCurrency = await PaymentCurrency
|
|
1036
|
+
const paymentCurrency = await systemFindByPk(PaymentCurrency, paymentCurrencyId || subscription.currency_id);
|
|
1027
1037
|
if (!paymentCurrency) {
|
|
1028
1038
|
logger.warn('Stake return skipped because no payment currency', {
|
|
1029
1039
|
subscription: subscription.id,
|
|
@@ -1031,7 +1041,9 @@ const ensureReturnStake = async (subscription: Subscription, paymentCurrencyId?:
|
|
|
1031
1041
|
});
|
|
1032
1042
|
return;
|
|
1033
1043
|
}
|
|
1034
|
-
|
|
1044
|
+
assertJobObjectTenant(paymentCurrency);
|
|
1045
|
+
const paymentMethod = await systemFindByPk(
|
|
1046
|
+
PaymentMethod,
|
|
1035
1047
|
paymentCurrency.payment_method_id || subscription.default_payment_method_id
|
|
1036
1048
|
);
|
|
1037
1049
|
if (paymentMethod?.type !== 'arcblock') {
|
|
@@ -1066,7 +1078,7 @@ const ensureReturnStake = async (subscription: Subscription, paymentCurrencyId?:
|
|
|
1066
1078
|
return;
|
|
1067
1079
|
}
|
|
1068
1080
|
if (result.return_amount !== '0') {
|
|
1069
|
-
const invoice = await Invoice
|
|
1081
|
+
const invoice = await systemFindOne(Invoice, {
|
|
1070
1082
|
where: {
|
|
1071
1083
|
billing_reason: 'stake',
|
|
1072
1084
|
subscription_id: subscription.id,
|
|
@@ -1122,7 +1134,7 @@ const ensureReturnStake = async (subscription: Subscription, paymentCurrencyId?:
|
|
|
1122
1134
|
};
|
|
1123
1135
|
|
|
1124
1136
|
const slashStakeOnCancel = async (subscription: Subscription) => {
|
|
1125
|
-
const paymentMethod = await PaymentMethod
|
|
1137
|
+
const paymentMethod = await systemFindByPk(PaymentMethod, subscription.default_payment_method_id);
|
|
1126
1138
|
if (paymentMethod?.type !== 'arcblock') {
|
|
1127
1139
|
logger.warn('Stake slashing skipped because payment method not arcblock', {
|
|
1128
1140
|
subscription: subscription.id,
|
|
@@ -1130,7 +1142,7 @@ const slashStakeOnCancel = async (subscription: Subscription) => {
|
|
|
1130
1142
|
});
|
|
1131
1143
|
return;
|
|
1132
1144
|
}
|
|
1133
|
-
const customer = await Customer
|
|
1145
|
+
const customer = await systemFindByPk(Customer, subscription.customer_id);
|
|
1134
1146
|
if (!customer) {
|
|
1135
1147
|
logger.warn('Stake slashing skipped because customer not found', {
|
|
1136
1148
|
subscription: subscription.id,
|
|
@@ -1138,7 +1150,8 @@ const slashStakeOnCancel = async (subscription: Subscription) => {
|
|
|
1138
1150
|
});
|
|
1139
1151
|
return;
|
|
1140
1152
|
}
|
|
1141
|
-
|
|
1153
|
+
assertJobObjectTenant(customer);
|
|
1154
|
+
const currency = await systemFindByPk(PaymentCurrency, subscription.currency_id);
|
|
1142
1155
|
if (!currency) {
|
|
1143
1156
|
logger.warn('Stake slashing skipped because currency not found', {
|
|
1144
1157
|
subscription: subscription.id,
|
|
@@ -1146,6 +1159,7 @@ const slashStakeOnCancel = async (subscription: Subscription) => {
|
|
|
1146
1159
|
});
|
|
1147
1160
|
return;
|
|
1148
1161
|
}
|
|
1162
|
+
assertJobObjectTenant(currency);
|
|
1149
1163
|
const address = await getSubscriptionStakeAddress(subscription, customer.did, paymentMethod);
|
|
1150
1164
|
const result = await getSubscriptionStakeSlashSetup(subscription, address, paymentMethod);
|
|
1151
1165
|
const stakeEnough = await checkRemainingStake(paymentMethod, currency, address, result.return_amount);
|
|
@@ -1169,7 +1183,7 @@ const slashStakeOnCancel = async (subscription: Subscription) => {
|
|
|
1169
1183
|
}
|
|
1170
1184
|
|
|
1171
1185
|
// FIXME: handle exist one more invoices
|
|
1172
|
-
const invoice = await Invoice
|
|
1186
|
+
const invoice = await systemFindOne(Invoice, {
|
|
1173
1187
|
where: {
|
|
1174
1188
|
subscription_id: subscription.id,
|
|
1175
1189
|
billing_reason: 'slash_stake',
|
|
@@ -1235,13 +1249,14 @@ const ensureRefundOnCancel = async (subscription: Subscription) => {
|
|
|
1235
1249
|
});
|
|
1236
1250
|
return;
|
|
1237
1251
|
}
|
|
1238
|
-
const lastInvoice = await Invoice
|
|
1252
|
+
const lastInvoice = await systemFindByPk(Invoice, subscription.latest_invoice_id);
|
|
1239
1253
|
if (!lastInvoice) {
|
|
1240
1254
|
logger.warn('Refund skipped because no latest invoice', {
|
|
1241
1255
|
subscription: subscription.id,
|
|
1242
1256
|
});
|
|
1243
1257
|
return;
|
|
1244
1258
|
}
|
|
1259
|
+
assertJobObjectTenant(lastInvoice);
|
|
1245
1260
|
|
|
1246
1261
|
const result = await getSubscriptionRefundSetup(subscription, subscription.cancel_at, lastInvoice.currency_id);
|
|
1247
1262
|
if (result.remainingUnused === '0') {
|
|
@@ -1251,7 +1266,7 @@ const ensureRefundOnCancel = async (subscription: Subscription) => {
|
|
|
1251
1266
|
});
|
|
1252
1267
|
return;
|
|
1253
1268
|
}
|
|
1254
|
-
const currency = await PaymentCurrency
|
|
1269
|
+
const currency = await systemFindByPk(PaymentCurrency, lastInvoice?.currency_id);
|
|
1255
1270
|
const item = await Refund.create({
|
|
1256
1271
|
type: 'refund',
|
|
1257
1272
|
livemode: subscription.livemode,
|
|
@@ -1299,7 +1314,7 @@ const ensureReturnOverdraftProtectionStake = async (subscription: Subscription,
|
|
|
1299
1314
|
});
|
|
1300
1315
|
return;
|
|
1301
1316
|
}
|
|
1302
|
-
const paymentCurrency = await PaymentCurrency
|
|
1317
|
+
const paymentCurrency = await systemFindByPk(PaymentCurrency, paymentCurrencyId || subscription.currency_id);
|
|
1303
1318
|
if (!paymentCurrency) {
|
|
1304
1319
|
logger.warn('Return overdraft protection stake skipped because currency not found', {
|
|
1305
1320
|
subscription: subscription.id,
|
|
@@ -1307,7 +1322,8 @@ const ensureReturnOverdraftProtectionStake = async (subscription: Subscription,
|
|
|
1307
1322
|
});
|
|
1308
1323
|
return;
|
|
1309
1324
|
}
|
|
1310
|
-
|
|
1325
|
+
assertJobObjectTenant(paymentCurrency);
|
|
1326
|
+
const paymentMethod = await systemFindByPk(PaymentMethod, paymentCurrency.payment_method_id);
|
|
1311
1327
|
if (!paymentMethod) {
|
|
1312
1328
|
logger.warn('Return overdraft protection stake skipped because payment method not found', {
|
|
1313
1329
|
subscription: subscription.id,
|
|
@@ -1315,6 +1331,7 @@ const ensureReturnOverdraftProtectionStake = async (subscription: Subscription,
|
|
|
1315
1331
|
});
|
|
1316
1332
|
return;
|
|
1317
1333
|
}
|
|
1334
|
+
assertJobObjectTenant(paymentMethod);
|
|
1318
1335
|
if (paymentMethod?.type !== 'arcblock') {
|
|
1319
1336
|
logger.info('Return overdraft protection stake skipped because payment method is not arcblock', {
|
|
1320
1337
|
subscription: subscription.id,
|
|
@@ -1346,7 +1363,7 @@ const ensureSlashOverdraftProtectionStake = async (subscription: Subscription) =
|
|
|
1346
1363
|
});
|
|
1347
1364
|
return;
|
|
1348
1365
|
}
|
|
1349
|
-
const paymentCurrency = await PaymentCurrency
|
|
1366
|
+
const paymentCurrency = await systemFindByPk(PaymentCurrency, subscription.currency_id);
|
|
1350
1367
|
if (!paymentCurrency) {
|
|
1351
1368
|
logger.warn('Slash overdraft protection stake skipped because currency not found', {
|
|
1352
1369
|
subscription: subscription.id,
|
|
@@ -1354,7 +1371,8 @@ const ensureSlashOverdraftProtectionStake = async (subscription: Subscription) =
|
|
|
1354
1371
|
});
|
|
1355
1372
|
return;
|
|
1356
1373
|
}
|
|
1357
|
-
|
|
1374
|
+
assertJobObjectTenant(paymentCurrency);
|
|
1375
|
+
const paymentMethod = await systemFindByPk(PaymentMethod, paymentCurrency.payment_method_id);
|
|
1358
1376
|
if (!paymentMethod) {
|
|
1359
1377
|
logger.warn('Slash overdraft protection stake skipped because payment method not found', {
|
|
1360
1378
|
subscription: subscription.id,
|
|
@@ -1362,6 +1380,7 @@ const ensureSlashOverdraftProtectionStake = async (subscription: Subscription) =
|
|
|
1362
1380
|
});
|
|
1363
1381
|
return;
|
|
1364
1382
|
}
|
|
1383
|
+
assertJobObjectTenant(paymentMethod);
|
|
1365
1384
|
if (paymentMethod?.type !== 'arcblock') {
|
|
1366
1385
|
logger.info('Slash overdraft protection stake skipped because payment method is not arcblock', {
|
|
1367
1386
|
subscription: subscription.id,
|
|
@@ -1386,11 +1405,12 @@ const ensureSlashOverdraftProtectionStake = async (subscription: Subscription) =
|
|
|
1386
1405
|
export const handleSubscription = async (job: SubscriptionJob) => {
|
|
1387
1406
|
logger.info('handle subscription', job);
|
|
1388
1407
|
|
|
1389
|
-
const subscription = await Subscription
|
|
1408
|
+
const subscription = await systemFindByPk(Subscription, job.subscriptionId);
|
|
1390
1409
|
if (!subscription) {
|
|
1391
1410
|
logger.warn('Subscription not found', { subscription: job.subscriptionId });
|
|
1392
1411
|
return;
|
|
1393
1412
|
}
|
|
1413
|
+
assertJobObjectTenant(subscription);
|
|
1394
1414
|
if (EXPECTED_SUBSCRIPTION_STATUS.includes(subscription.status) === false) {
|
|
1395
1415
|
logger.warn('Subscription status not expected', { subscription: subscription.id, status: subscription.status });
|
|
1396
1416
|
return;
|
|
@@ -1468,7 +1488,7 @@ export const subscriptionQueue = createQueue<SubscriptionJob>({
|
|
|
1468
1488
|
* Checks for subscriptions that may have missed billing periods during downtime
|
|
1469
1489
|
*/
|
|
1470
1490
|
export const handleCreditSubscriptionRecovery = async () => {
|
|
1471
|
-
const lock = getLock('creditSubscriptionRecovery');
|
|
1491
|
+
const lock = getLock('creditSubscriptionRecovery', { scope: 'global' });
|
|
1472
1492
|
if (lock.locked) {
|
|
1473
1493
|
return;
|
|
1474
1494
|
}
|
|
@@ -1478,7 +1498,7 @@ export const handleCreditSubscriptionRecovery = async () => {
|
|
|
1478
1498
|
await lock.acquire();
|
|
1479
1499
|
|
|
1480
1500
|
// Find active credit subscriptions that might need recovery
|
|
1481
|
-
const creditSubscriptions = await Subscription
|
|
1501
|
+
const creditSubscriptions = await systemFindAll(Subscription, {
|
|
1482
1502
|
where: {
|
|
1483
1503
|
status: ['active', 'trialing'],
|
|
1484
1504
|
},
|
|
@@ -1565,7 +1585,7 @@ export const handleCreditSubscriptionRecovery = async () => {
|
|
|
1565
1585
|
};
|
|
1566
1586
|
|
|
1567
1587
|
export const startSubscriptionQueue = async () => {
|
|
1568
|
-
const lock = getLock('startSubscriptionQueue');
|
|
1588
|
+
const lock = getLock('startSubscriptionQueue', { scope: 'global' });
|
|
1569
1589
|
if (lock.locked) {
|
|
1570
1590
|
return;
|
|
1571
1591
|
}
|
|
@@ -1576,7 +1596,7 @@ export const startSubscriptionQueue = async () => {
|
|
|
1576
1596
|
// First handle credit subscription recovery
|
|
1577
1597
|
await handleCreditSubscriptionRecovery();
|
|
1578
1598
|
|
|
1579
|
-
const subscriptions = await Subscription
|
|
1599
|
+
const subscriptions = await systemFindAll(Subscription, {
|
|
1580
1600
|
where: {
|
|
1581
1601
|
status: EXPECTED_SUBSCRIPTION_STATUS,
|
|
1582
1602
|
},
|
|
@@ -1603,7 +1623,7 @@ export const startSubscriptionQueue = async () => {
|
|
|
1603
1623
|
return;
|
|
1604
1624
|
}
|
|
1605
1625
|
logger.info('add subscription job', { subscription: x.id, action: 'cycle' });
|
|
1606
|
-
await addSubscriptionJob(x, 'cycle',
|
|
1626
|
+
await addSubscriptionJob(x, 'cycle', paymentReloadSubscriptionJobs());
|
|
1607
1627
|
})
|
|
1608
1628
|
);
|
|
1609
1629
|
|
|
@@ -1624,10 +1644,11 @@ export const slashStakeQueue = createQueue({
|
|
|
1624
1644
|
name: 'slashStake',
|
|
1625
1645
|
onJob: async (job) => {
|
|
1626
1646
|
const { subscriptionId } = job;
|
|
1627
|
-
const subscription = await Subscription
|
|
1647
|
+
const subscription = await systemFindByPk(Subscription, subscriptionId);
|
|
1628
1648
|
if (!subscription) {
|
|
1629
1649
|
return;
|
|
1630
1650
|
}
|
|
1651
|
+
assertJobObjectTenant(subscription);
|
|
1631
1652
|
await slashStakeOnCancel(subscription);
|
|
1632
1653
|
},
|
|
1633
1654
|
options: {
|
|
@@ -1643,10 +1664,11 @@ export const returnStakeQueue = createQueue({
|
|
|
1643
1664
|
name: 'returnStake',
|
|
1644
1665
|
onJob: async (job) => {
|
|
1645
1666
|
const { subscriptionId, stakingAddress, paymentCurrencyId } = job;
|
|
1646
|
-
const subscription = await Subscription
|
|
1667
|
+
const subscription = await systemFindByPk(Subscription, subscriptionId);
|
|
1647
1668
|
if (!subscription) {
|
|
1648
1669
|
return;
|
|
1649
1670
|
}
|
|
1671
|
+
assertJobObjectTenant(subscription);
|
|
1650
1672
|
await ensureReturnStake(subscription, paymentCurrencyId, stakingAddress);
|
|
1651
1673
|
},
|
|
1652
1674
|
options: {
|
|
@@ -1662,10 +1684,11 @@ export const subscriptionCancelRefund = createQueue({
|
|
|
1662
1684
|
name: 'subscription-cancel-refund',
|
|
1663
1685
|
onJob: async (job) => {
|
|
1664
1686
|
const { subscriptionId } = job;
|
|
1665
|
-
const subscription = await Subscription
|
|
1687
|
+
const subscription = await systemFindByPk(Subscription, subscriptionId);
|
|
1666
1688
|
if (!subscription) {
|
|
1667
1689
|
return;
|
|
1668
1690
|
}
|
|
1691
|
+
assertJobObjectTenant(subscription);
|
|
1669
1692
|
await ensureRefundOnCancel(subscription);
|
|
1670
1693
|
},
|
|
1671
1694
|
options: {
|
|
@@ -1681,10 +1704,11 @@ export const returnOverdraftProtectionQueue = createQueue({
|
|
|
1681
1704
|
name: 'returnOverdraftProtection',
|
|
1682
1705
|
onJob: async (job) => {
|
|
1683
1706
|
const { subscriptionId, paymentCurrencyId } = job;
|
|
1684
|
-
const subscription = await Subscription
|
|
1707
|
+
const subscription = await systemFindByPk(Subscription, subscriptionId);
|
|
1685
1708
|
if (!subscription) {
|
|
1686
1709
|
return;
|
|
1687
1710
|
}
|
|
1711
|
+
assertJobObjectTenant(subscription);
|
|
1688
1712
|
await ensureReturnOverdraftProtectionStake(subscription, paymentCurrencyId);
|
|
1689
1713
|
},
|
|
1690
1714
|
options: {
|
|
@@ -1700,10 +1724,11 @@ export const slashOverdraftProtectionQueue = createQueue({
|
|
|
1700
1724
|
name: 'slashOverdraftProtection',
|
|
1701
1725
|
onJob: async (job) => {
|
|
1702
1726
|
const { subscriptionId } = job;
|
|
1703
|
-
const subscription = await Subscription
|
|
1727
|
+
const subscription = await systemFindByPk(Subscription, subscriptionId);
|
|
1704
1728
|
if (!subscription) {
|
|
1705
1729
|
return;
|
|
1706
1730
|
}
|
|
1731
|
+
assertJobObjectTenant(subscription);
|
|
1707
1732
|
await ensureSlashOverdraftProtectionStake(subscription);
|
|
1708
1733
|
},
|
|
1709
1734
|
options: {
|
|
@@ -1791,7 +1816,7 @@ events.on('customer.subscription.recovered', async (subscription: Subscription)
|
|
|
1791
1816
|
logger.info('subscription cancel job replaced after recover', { subscription: subscription.id });
|
|
1792
1817
|
await subscriptionQueue.delete(`cancel-${subscription.id}`);
|
|
1793
1818
|
await subscriptionQueue.delete(subscription.id);
|
|
1794
|
-
const doc = await Subscription
|
|
1819
|
+
const doc = await systemFindByPk(Subscription, subscription.id);
|
|
1795
1820
|
await handleSubscriptionAfterRecover(doc!);
|
|
1796
1821
|
});
|
|
1797
1822
|
|
|
@@ -1837,10 +1862,11 @@ events.on('customer.subscription.upgraded', async (subscription: Subscription) =
|
|
|
1837
1862
|
});
|
|
1838
1863
|
|
|
1839
1864
|
events.on('customer.stake.revoked', async ({ subscriptionId, tx }: { subscriptionId: string; tx: any }) => {
|
|
1840
|
-
const subscription = await Subscription
|
|
1865
|
+
const subscription = await systemFindByPk(Subscription, subscriptionId);
|
|
1841
1866
|
if (!subscription) {
|
|
1842
1867
|
return;
|
|
1843
1868
|
}
|
|
1869
|
+
assertJobObjectTenant(subscription);
|
|
1844
1870
|
|
|
1845
1871
|
const { address } = tx.tx.itxJson;
|
|
1846
1872
|
if (address === subscription.overdraft_protection?.payment_details?.arcblock?.staking?.address) {
|
|
@@ -1893,12 +1919,13 @@ events.on('customer.stake.revoked', async ({ subscriptionId, tx }: { subscriptio
|
|
|
1893
1919
|
events.on('setup_intent.succeeded', async (setupIntent: SetupIntent) => {
|
|
1894
1920
|
logger.info('setup intent succeeded', { setupIntent: setupIntent.id });
|
|
1895
1921
|
if (setupIntent.metadata?.from_currency && setupIntent?.metadata?.subscription_id) {
|
|
1896
|
-
const subscription = await Subscription
|
|
1922
|
+
const subscription = await systemFindByPk(Subscription, setupIntent.metadata.subscription_id);
|
|
1897
1923
|
if (!subscription) {
|
|
1898
1924
|
logger.info('skip return stake because no subscription found', { setupIntent: setupIntent.id });
|
|
1899
1925
|
return;
|
|
1900
1926
|
}
|
|
1901
|
-
|
|
1927
|
+
assertJobObjectTenant(subscription);
|
|
1928
|
+
const stakingInvoice = await systemFindOne(Invoice, {
|
|
1902
1929
|
where: {
|
|
1903
1930
|
subscription_id: subscription.id,
|
|
1904
1931
|
billing_reason: 'stake',
|
|
@@ -1984,11 +2011,12 @@ type SlippagePreCheckJob = {
|
|
|
1984
2011
|
async function handleSlippagePreCheck(job: SlippagePreCheckJob): Promise<void> {
|
|
1985
2012
|
const { subscriptionId, renewalTime } = job;
|
|
1986
2013
|
|
|
1987
|
-
const subscription = await Subscription
|
|
2014
|
+
const subscription = await systemFindByPk(Subscription, subscriptionId);
|
|
1988
2015
|
if (!subscription) {
|
|
1989
2016
|
logger.warn('Slippage pre-check: Subscription not found', { subscriptionId });
|
|
1990
2017
|
return;
|
|
1991
2018
|
}
|
|
2019
|
+
assertJobObjectTenant(subscription);
|
|
1992
2020
|
|
|
1993
2021
|
// Skip if subscription is not in active status
|
|
1994
2022
|
if (!['active', 'trialing'].includes(subscription.status)) {
|
|
@@ -2007,17 +2035,19 @@ async function handleSlippagePreCheck(job: SlippagePreCheckJob): Promise<void> {
|
|
|
2007
2035
|
}
|
|
2008
2036
|
|
|
2009
2037
|
// Get payment currency and payment method
|
|
2010
|
-
const paymentCurrency = await PaymentCurrency
|
|
2038
|
+
const paymentCurrency = await systemFindByPk(PaymentCurrency, subscription.currency_id);
|
|
2011
2039
|
if (!paymentCurrency) {
|
|
2012
2040
|
logger.info('Slippage pre-check: Payment currency not found', { subscriptionId });
|
|
2013
2041
|
return;
|
|
2014
2042
|
}
|
|
2043
|
+
assertJobObjectTenant(paymentCurrency);
|
|
2015
2044
|
|
|
2016
|
-
const paymentMethod = await PaymentMethod
|
|
2045
|
+
const paymentMethod = await systemFindByPk(PaymentMethod, paymentCurrency.payment_method_id);
|
|
2017
2046
|
if (!paymentMethod) {
|
|
2018
2047
|
logger.info('Slippage pre-check: Payment method not found', { subscriptionId });
|
|
2019
2048
|
return;
|
|
2020
2049
|
}
|
|
2050
|
+
assertJobObjectTenant(paymentMethod);
|
|
2021
2051
|
|
|
2022
2052
|
// Get current exchange rate
|
|
2023
2053
|
try {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { BN } from '@ocap/util';
|
|
2
2
|
import logger from '../libs/logger';
|
|
3
|
-
import createQueue from '../libs/queue';
|
|
3
|
+
import createQueue, { assertJobObjectTenant } from '../libs/queue';
|
|
4
4
|
import { getAccountState, transferTokenFromCustomer, getCustomerTokenBalance } from '../integrations/arcblock/token';
|
|
5
5
|
import { CreditTransaction, CreditGrant, Customer, MeterEvent, PaymentCurrency, Subscription } from '../store/models';
|
|
6
|
+
import { systemFindAll, systemFindByPk } from '../store/scoped';
|
|
6
7
|
|
|
7
8
|
type TokenTransferJob = {
|
|
8
9
|
creditTransactionId: string;
|
|
@@ -30,11 +31,12 @@ type ValidationResult =
|
|
|
30
31
|
* Validate transfer job and fetch required data
|
|
31
32
|
*/
|
|
32
33
|
async function validateAndFetchData(job: TokenTransferJob): Promise<ValidationResult> {
|
|
33
|
-
const creditTransaction = await CreditTransaction
|
|
34
|
+
const creditTransaction = await systemFindByPk(CreditTransaction, job.creditTransactionId);
|
|
34
35
|
if (!creditTransaction) {
|
|
35
36
|
logger.warn('CreditTransaction not found', { creditTransactionId: job.creditTransactionId });
|
|
36
37
|
return { valid: false };
|
|
37
38
|
}
|
|
39
|
+
assertJobObjectTenant(creditTransaction);
|
|
38
40
|
|
|
39
41
|
// Check if already transferred
|
|
40
42
|
if (creditTransaction.transfer_status === 'completed') {
|
|
@@ -42,11 +44,12 @@ async function validateAndFetchData(job: TokenTransferJob): Promise<ValidationRe
|
|
|
42
44
|
return { valid: false };
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
const creditGrant = await CreditGrant
|
|
47
|
+
const creditGrant = await systemFindByPk(CreditGrant, job.creditGrantId);
|
|
46
48
|
if (!creditGrant) {
|
|
47
49
|
logger.warn('CreditGrant not found', { creditGrantId: job.creditGrantId });
|
|
48
50
|
return { valid: false };
|
|
49
51
|
}
|
|
52
|
+
assertJobObjectTenant(creditGrant);
|
|
50
53
|
|
|
51
54
|
const customer = await Customer.findByPkOrDid(job.customerDid);
|
|
52
55
|
if (!customer) {
|
|
@@ -54,21 +57,23 @@ async function validateAndFetchData(job: TokenTransferJob): Promise<ValidationRe
|
|
|
54
57
|
return { valid: false };
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
const paymentCurrency = await PaymentCurrency
|
|
60
|
+
const paymentCurrency = await systemFindByPk(PaymentCurrency, job.paymentCurrencyId);
|
|
58
61
|
if (!paymentCurrency) {
|
|
59
62
|
logger.warn('PaymentCurrency not found', { paymentCurrencyId: job.paymentCurrencyId });
|
|
60
63
|
return { valid: false };
|
|
61
64
|
}
|
|
65
|
+
assertJobObjectTenant(paymentCurrency);
|
|
62
66
|
|
|
63
|
-
const meterEvent = await MeterEvent
|
|
67
|
+
const meterEvent = await systemFindByPk(MeterEvent, job.meterEventId);
|
|
64
68
|
if (!meterEvent) {
|
|
65
69
|
logger.warn('MeterEvent not found', { meterEventId: job.meterEventId });
|
|
66
70
|
return { valid: false };
|
|
67
71
|
}
|
|
72
|
+
assertJobObjectTenant(meterEvent);
|
|
68
73
|
|
|
69
74
|
let subscription: Subscription | undefined;
|
|
70
75
|
if (job.subscriptionId) {
|
|
71
|
-
subscription = (await Subscription
|
|
76
|
+
subscription = (await systemFindByPk(Subscription, job.subscriptionId)) || undefined;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
return {
|
|
@@ -239,7 +244,7 @@ tokenTransferQueue.on('finished', ({ id, job }) => {
|
|
|
239
244
|
tokenTransferQueue.on('failed', async ({ id, job, error }) => {
|
|
240
245
|
logger.error('Token transfer job failed after all retries', { id, job, error: error.message });
|
|
241
246
|
|
|
242
|
-
const creditTransaction = await CreditTransaction
|
|
247
|
+
const creditTransaction = await systemFindByPk(CreditTransaction, job.creditTransactionId);
|
|
243
248
|
|
|
244
249
|
if (creditTransaction && creditTransaction.transfer_status !== 'completed') {
|
|
245
250
|
await creditTransaction.update({
|
|
@@ -295,7 +300,7 @@ export async function startTokenTransferQueue(): Promise<void> {
|
|
|
295
300
|
logger.info('Token transfer queue started');
|
|
296
301
|
|
|
297
302
|
// Process any pending transfers on startup
|
|
298
|
-
const pendingTransactions = await CreditTransaction
|
|
303
|
+
const pendingTransactions = await systemFindAll(CreditTransaction, {
|
|
299
304
|
where: {
|
|
300
305
|
transfer_status: 'pending',
|
|
301
306
|
},
|
|
@@ -309,8 +314,8 @@ export async function startTokenTransferQueue(): Promise<void> {
|
|
|
309
314
|
await Promise.all(
|
|
310
315
|
pendingTransactions.map(async (transaction) => {
|
|
311
316
|
try {
|
|
312
|
-
const customer = (await Customer
|
|
313
|
-
const creditGrant = (await CreditGrant
|
|
317
|
+
const customer = (await systemFindByPk(Customer, transaction.customer_id))!;
|
|
318
|
+
const creditGrant = (await systemFindByPk(CreditGrant, transaction.credit_grant_id))!;
|
|
314
319
|
|
|
315
320
|
await addTokenTransferJob({
|
|
316
321
|
creditTransactionId: transaction.id,
|