@qazuor/qzpay-drizzle 1.7.8 → 1.9.0
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/dist/index.cjs +322 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +103 -4
- package/dist/index.d.ts +103 -4
- package/dist/index.js +315 -18
- package/dist/index.js.map +1 -1
- package/dist/schema/index.cjs +101 -1
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +1496 -535
- package/dist/schema/index.d.ts +1496 -535
- package/dist/schema/index.js +98 -2
- package/dist/schema/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -130,13 +130,13 @@ function firstOrThrow(results, entityType, id) {
|
|
|
130
130
|
}
|
|
131
131
|
async function updateWithVersionHelper(db, table, id, expectedVersion, updateData, options) {
|
|
132
132
|
const { entityType, entityId, includeSoftDeleted = false, transform } = options;
|
|
133
|
-
const { and:
|
|
133
|
+
const { and: and20, eq: eq19, isNull: isNull13 } = await import('drizzle-orm');
|
|
134
134
|
const { generateVersion: generateVersion2 } = await Promise.resolve().then(() => (init_optimistic_locking(), optimistic_locking_exports));
|
|
135
|
-
const conditions = [
|
|
135
|
+
const conditions = [eq19(table.id, id), eq19(table.version, expectedVersion)];
|
|
136
136
|
if (!includeSoftDeleted && "deletedAt" in table) {
|
|
137
137
|
conditions.push(isNull13(table.deletedAt));
|
|
138
138
|
}
|
|
139
|
-
const whereClause =
|
|
139
|
+
const whereClause = and20(...conditions);
|
|
140
140
|
if (!whereClause) {
|
|
141
141
|
throw new Error("Failed to build WHERE clause for version update");
|
|
142
142
|
}
|
|
@@ -150,7 +150,7 @@ async function updateWithVersionHelper(db, table, id, expectedVersion, updateDat
|
|
|
150
150
|
result = await db.update(table).set(dataWithVersion).where(whereClause).returning();
|
|
151
151
|
} catch (_error) {
|
|
152
152
|
try {
|
|
153
|
-
const existsQuery = await db.select().from(table).where(includeSoftDeleted ?
|
|
153
|
+
const existsQuery = await db.select().from(table).where(includeSoftDeleted ? eq19(table.id, id) : and20(eq19(table.id, id), isNull13(table.deletedAt))).limit(1);
|
|
154
154
|
if (existsQuery.length > 0) {
|
|
155
155
|
throw new exports.QZPayOptimisticLockError(entityType, entityId);
|
|
156
156
|
}
|
|
@@ -159,7 +159,7 @@ async function updateWithVersionHelper(db, table, id, expectedVersion, updateDat
|
|
|
159
159
|
throw new exports.QZPayEntityNotFoundError(entityType, entityId);
|
|
160
160
|
}
|
|
161
161
|
if (result.length === 0) {
|
|
162
|
-
const existsQuery = await db.select().from(table).where(includeSoftDeleted ?
|
|
162
|
+
const existsQuery = await db.select().from(table).where(includeSoftDeleted ? eq19(table.id, id) : and20(eq19(table.id, id), isNull13(table.deletedAt))).limit(1);
|
|
163
163
|
if (existsQuery.length > 0) {
|
|
164
164
|
throw new exports.QZPayOptimisticLockError(entityType, entityId);
|
|
165
165
|
}
|
|
@@ -1107,6 +1107,48 @@ function mapCorePromoCodeUpdateToDrizzle(update) {
|
|
|
1107
1107
|
return result;
|
|
1108
1108
|
}
|
|
1109
1109
|
|
|
1110
|
+
// src/mappers/subscription-polling-job.mapper.ts
|
|
1111
|
+
var POLLING_JOB_DEFAULTS = {
|
|
1112
|
+
initialDelayMs: 3e4,
|
|
1113
|
+
maxAttempts: 60
|
|
1114
|
+
};
|
|
1115
|
+
function mapDrizzlePollingJobToCore(row) {
|
|
1116
|
+
return {
|
|
1117
|
+
id: row.id,
|
|
1118
|
+
subscriptionId: row.subscriptionId,
|
|
1119
|
+
provider: row.provider,
|
|
1120
|
+
providerResourceId: row.providerResourceId,
|
|
1121
|
+
resourceType: row.resourceType,
|
|
1122
|
+
status: row.status,
|
|
1123
|
+
attempts: row.attempts,
|
|
1124
|
+
maxAttempts: row.maxAttempts,
|
|
1125
|
+
nextPollAt: row.nextPollAt,
|
|
1126
|
+
lastPolledAt: row.lastPolledAt ?? null,
|
|
1127
|
+
lastProviderStatus: row.lastProviderStatus ?? null,
|
|
1128
|
+
lastError: row.lastError ?? null,
|
|
1129
|
+
metadata: row.metadata ?? {},
|
|
1130
|
+
createdAt: row.createdAt,
|
|
1131
|
+
updatedAt: row.updatedAt,
|
|
1132
|
+
completedAt: row.completedAt ?? null,
|
|
1133
|
+
version: row.version
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
function mapPollingScheduleInputToDrizzleInsert(input) {
|
|
1137
|
+
const initialDelayMs = input.initialDelayMs ?? POLLING_JOB_DEFAULTS.initialDelayMs;
|
|
1138
|
+
const nextPollAt = new Date(Date.now() + initialDelayMs);
|
|
1139
|
+
return {
|
|
1140
|
+
subscriptionId: input.subscriptionId,
|
|
1141
|
+
provider: input.provider,
|
|
1142
|
+
providerResourceId: input.providerResourceId,
|
|
1143
|
+
resourceType: input.resourceType ?? "subscription",
|
|
1144
|
+
status: "pending",
|
|
1145
|
+
attempts: 0,
|
|
1146
|
+
maxAttempts: input.maxAttempts ?? POLLING_JOB_DEFAULTS.maxAttempts,
|
|
1147
|
+
nextPollAt,
|
|
1148
|
+
metadata: input.metadata ?? {}
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1110
1152
|
// src/mappers/subscription.mapper.ts
|
|
1111
1153
|
function mapDrizzleSubscriptionToCore(drizzle3) {
|
|
1112
1154
|
const providerSubscriptionIds = {};
|
|
@@ -1890,6 +1932,93 @@ var billingPrices = pgCore.pgTable(
|
|
|
1890
1932
|
);
|
|
1891
1933
|
var billingPriceInsertSchema = drizzleZod.createInsertSchema(billingPrices);
|
|
1892
1934
|
var billingPriceSelectSchema = drizzleZod.createSelectSchema(billingPrices);
|
|
1935
|
+
var billingSubscriptionPollingJobs = pgCore.pgTable(
|
|
1936
|
+
"billing_subscription_polling_jobs",
|
|
1937
|
+
{
|
|
1938
|
+
id: pgCore.uuid("id").primaryKey().defaultRandom(),
|
|
1939
|
+
subscriptionId: pgCore.uuid("subscription_id").notNull().references(() => billingSubscriptions.id, { onDelete: "cascade" }),
|
|
1940
|
+
/**
|
|
1941
|
+
* Provider identifier, e.g. `mercadopago`. Future-proofed so
|
|
1942
|
+
* Stripe or other adapters can opt in to polling if needed.
|
|
1943
|
+
*/
|
|
1944
|
+
provider: pgCore.varchar("provider", { length: 50 }).notNull(),
|
|
1945
|
+
/**
|
|
1946
|
+
* Provider-side resource id (e.g. MP preapproval id, MP preference
|
|
1947
|
+
* id) that the poller uses to query the provider's REST endpoint.
|
|
1948
|
+
* Interpretation depends on {@link resourceType}.
|
|
1949
|
+
*/
|
|
1950
|
+
providerResourceId: pgCore.varchar("provider_resource_id", { length: 255 }).notNull(),
|
|
1951
|
+
/**
|
|
1952
|
+
* Provider-agnostic classification of the polled resource.
|
|
1953
|
+
* - `subscription`: recurring authorization (MP preapproval, Stripe subscription).
|
|
1954
|
+
* - `one_time_payment`: deferred-payment checkout session whose
|
|
1955
|
+
* payment id isn't known yet (MP preference one-time, Stripe
|
|
1956
|
+
* checkout.session in payment mode). The poller's adapter search
|
|
1957
|
+
* call resolves the actual payment when the user completes the flow.
|
|
1958
|
+
*
|
|
1959
|
+
* Default `'subscription'` keeps backward compatibility for pre-existing
|
|
1960
|
+
* rows enqueued before the column was introduced.
|
|
1961
|
+
*/
|
|
1962
|
+
resourceType: pgCore.varchar("resource_type", { length: 20 }).notNull().default("subscription"),
|
|
1963
|
+
/**
|
|
1964
|
+
* Job lifecycle status.
|
|
1965
|
+
* - `pending`: cron will pick up when due
|
|
1966
|
+
* - `succeeded`: provider returned a terminal authorized state
|
|
1967
|
+
* - `failed`: provider returned cancelled/rejected
|
|
1968
|
+
* - `timeout`: max_attempts reached without resolution
|
|
1969
|
+
* - `cancelled`: cancelled externally (e.g. user cancelled sub mid-flight)
|
|
1970
|
+
*/
|
|
1971
|
+
status: pgCore.varchar("status", { length: 20 }).notNull().default("pending"),
|
|
1972
|
+
attempts: pgCore.integer("attempts").notNull().default(0),
|
|
1973
|
+
maxAttempts: pgCore.integer("max_attempts").notNull().default(60),
|
|
1974
|
+
nextPollAt: pgCore.timestamp("next_poll_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1975
|
+
lastPolledAt: pgCore.timestamp("last_polled_at", { withTimezone: true }),
|
|
1976
|
+
/**
|
|
1977
|
+
* Raw provider status from the last poll (e.g. `pending`,
|
|
1978
|
+
* `authorized`). Useful for diagnostics without joining logs.
|
|
1979
|
+
*/
|
|
1980
|
+
lastProviderStatus: pgCore.varchar("last_provider_status", { length: 50 }),
|
|
1981
|
+
/**
|
|
1982
|
+
* Last error message (truncated server-side to avoid bloat).
|
|
1983
|
+
* Set when a poll attempt fails (network, 4xx, 5xx).
|
|
1984
|
+
*/
|
|
1985
|
+
lastError: pgCore.text("last_error"),
|
|
1986
|
+
metadata: pgCore.jsonb("metadata").notNull().default({}),
|
|
1987
|
+
createdAt: pgCore.timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1988
|
+
updatedAt: pgCore.timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
1989
|
+
completedAt: pgCore.timestamp("completed_at", { withTimezone: true }),
|
|
1990
|
+
/**
|
|
1991
|
+
* Optimistic lock token. Updated on every state change. Workers
|
|
1992
|
+
* must include this in their UPDATE WHERE clause to detect
|
|
1993
|
+
* concurrent modifications.
|
|
1994
|
+
*/
|
|
1995
|
+
version: pgCore.uuid("version").notNull().defaultRandom()
|
|
1996
|
+
},
|
|
1997
|
+
(table) => ({
|
|
1998
|
+
/**
|
|
1999
|
+
* Partial index that supports the cron's "find due pending
|
|
2000
|
+
* jobs" query. Only rows still pending are scanned, so the
|
|
2001
|
+
* per-tick cost is O(k) where k = #pending jobs (NOT a full
|
|
2002
|
+
* table scan that would grow with historical job count).
|
|
2003
|
+
*/
|
|
2004
|
+
dueIdx: pgCore.index("idx_polling_jobs_due").on(table.nextPollAt).where(drizzleOrm.sql`status = 'pending'`),
|
|
2005
|
+
/**
|
|
2006
|
+
* Lookup by subscription, e.g. when a webhook arrives and we
|
|
2007
|
+
* need to mark the active polling job as `succeeded`.
|
|
2008
|
+
*/
|
|
2009
|
+
subscriptionIdx: pgCore.index("idx_polling_jobs_subscription").on(table.subscriptionId),
|
|
2010
|
+
/**
|
|
2011
|
+
* At most one ACTIVE polling job per subscription. Prevents
|
|
2012
|
+
* duplicate enqueuing if `schedulePolling()` is called twice
|
|
2013
|
+
* concurrently. Historical jobs (succeeded/failed/timeout/
|
|
2014
|
+
* cancelled) are NOT constrained — multiple terminal rows per
|
|
2015
|
+
* subscription are allowed for audit trail.
|
|
2016
|
+
*/
|
|
2017
|
+
oneActivePerSubscriptionIdx: pgCore.uniqueIndex("idx_polling_jobs_one_active_per_sub").on(table.subscriptionId).where(drizzleOrm.sql`status = 'pending'`)
|
|
2018
|
+
})
|
|
2019
|
+
);
|
|
2020
|
+
var billingSubscriptionPollingJobInsertSchema = drizzleZod.createInsertSchema(billingSubscriptionPollingJobs);
|
|
2021
|
+
var billingSubscriptionPollingJobSelectSchema = drizzleZod.createSelectSchema(billingSubscriptionPollingJobs);
|
|
1893
2022
|
var billingUsageRecords = pgCore.pgTable(
|
|
1894
2023
|
"billing_usage_records",
|
|
1895
2024
|
{
|
|
@@ -1991,7 +2120,14 @@ var billingSubscriptionsRelations = drizzleOrm.relations(billingSubscriptions, (
|
|
|
1991
2120
|
payments: many(billingPayments),
|
|
1992
2121
|
invoices: many(billingInvoices),
|
|
1993
2122
|
usageRecords: many(billingUsageRecords),
|
|
1994
|
-
addons: many(billingSubscriptionAddons)
|
|
2123
|
+
addons: many(billingSubscriptionAddons),
|
|
2124
|
+
pollingJobs: many(billingSubscriptionPollingJobs)
|
|
2125
|
+
}));
|
|
2126
|
+
var billingSubscriptionPollingJobsRelations = drizzleOrm.relations(billingSubscriptionPollingJobs, ({ one }) => ({
|
|
2127
|
+
subscription: one(billingSubscriptions, {
|
|
2128
|
+
fields: [billingSubscriptionPollingJobs.subscriptionId],
|
|
2129
|
+
references: [billingSubscriptions.id]
|
|
2130
|
+
})
|
|
1995
2131
|
}));
|
|
1996
2132
|
var billingPaymentsRelations = drizzleOrm.relations(billingPayments, ({ one, many }) => ({
|
|
1997
2133
|
customer: one(billingCustomers, {
|
|
@@ -2188,6 +2324,7 @@ var qzpaySchema = {
|
|
|
2188
2324
|
billingPromoCodes,
|
|
2189
2325
|
billingPromoCodeUsage,
|
|
2190
2326
|
billingSubscriptions,
|
|
2327
|
+
billingSubscriptionPollingJobs,
|
|
2191
2328
|
billingUsageRecords,
|
|
2192
2329
|
billingVendors,
|
|
2193
2330
|
billingVendorPayouts,
|
|
@@ -2212,6 +2349,7 @@ var qzpaySchema = {
|
|
|
2212
2349
|
billingPromoCodesRelations,
|
|
2213
2350
|
billingPromoCodeUsageRelations,
|
|
2214
2351
|
billingSubscriptionsRelations,
|
|
2352
|
+
billingSubscriptionPollingJobsRelations,
|
|
2215
2353
|
billingUsageRecordsRelations,
|
|
2216
2354
|
billingVendorsRelations,
|
|
2217
2355
|
billingVendorPayoutsRelations
|
|
@@ -4700,6 +4838,119 @@ var QZPayPromoCodesRepository = class {
|
|
|
4700
4838
|
}
|
|
4701
4839
|
};
|
|
4702
4840
|
init_base_repository();
|
|
4841
|
+
var PG_UNIQUE_VIOLATION = "23505";
|
|
4842
|
+
function isUniqueViolation(error) {
|
|
4843
|
+
if (typeof error !== "object" || error === null) {
|
|
4844
|
+
return false;
|
|
4845
|
+
}
|
|
4846
|
+
const direct = error;
|
|
4847
|
+
if (direct.code === PG_UNIQUE_VIOLATION) {
|
|
4848
|
+
return true;
|
|
4849
|
+
}
|
|
4850
|
+
const cause = error.cause;
|
|
4851
|
+
if (cause && typeof cause === "object") {
|
|
4852
|
+
const innerCode = cause.code;
|
|
4853
|
+
if (innerCode === PG_UNIQUE_VIOLATION) {
|
|
4854
|
+
return true;
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4857
|
+
return false;
|
|
4858
|
+
}
|
|
4859
|
+
var QZPaySubscriptionPollingJobsRepository = class {
|
|
4860
|
+
constructor(db) {
|
|
4861
|
+
this.db = db;
|
|
4862
|
+
}
|
|
4863
|
+
/**
|
|
4864
|
+
* Insert a new pending polling job.
|
|
4865
|
+
*
|
|
4866
|
+
* Returns `null` if a partial-unique violation is raised — i.e.,
|
|
4867
|
+
* there is already a `pending` job for the same subscription. The
|
|
4868
|
+
* caller can then choose to read the existing job and treat it as
|
|
4869
|
+
* the source of truth.
|
|
4870
|
+
*/
|
|
4871
|
+
async create(input) {
|
|
4872
|
+
try {
|
|
4873
|
+
const result = await this.db.insert(billingSubscriptionPollingJobs).values(input).returning();
|
|
4874
|
+
return firstOrNull(result);
|
|
4875
|
+
} catch (error) {
|
|
4876
|
+
if (isUniqueViolation(error)) {
|
|
4877
|
+
return null;
|
|
4878
|
+
}
|
|
4879
|
+
throw error;
|
|
4880
|
+
}
|
|
4881
|
+
}
|
|
4882
|
+
/**
|
|
4883
|
+
* Find a polling job by id.
|
|
4884
|
+
*/
|
|
4885
|
+
async findById(id) {
|
|
4886
|
+
const result = await this.db.select().from(billingSubscriptionPollingJobs).where(drizzleOrm.eq(billingSubscriptionPollingJobs.id, id)).limit(1);
|
|
4887
|
+
return firstOrNull(result);
|
|
4888
|
+
}
|
|
4889
|
+
/**
|
|
4890
|
+
* Find the active (`pending`) polling job for a subscription, if any.
|
|
4891
|
+
* Returns the single active job because the partial-unique index
|
|
4892
|
+
* enforces at most one.
|
|
4893
|
+
*/
|
|
4894
|
+
async findActiveBySubscriptionId(subscriptionId) {
|
|
4895
|
+
const result = await this.db.select().from(billingSubscriptionPollingJobs).where(
|
|
4896
|
+
drizzleOrm.and(drizzleOrm.eq(billingSubscriptionPollingJobs.subscriptionId, subscriptionId), drizzleOrm.eq(billingSubscriptionPollingJobs.status, "pending"))
|
|
4897
|
+
).limit(1);
|
|
4898
|
+
return firstOrNull(result);
|
|
4899
|
+
}
|
|
4900
|
+
/**
|
|
4901
|
+
* Fetch up to `limit` pending jobs whose `next_poll_at <= now`,
|
|
4902
|
+
* ordered by `next_poll_at` ascending so most-overdue jobs are
|
|
4903
|
+
* processed first.
|
|
4904
|
+
*/
|
|
4905
|
+
async findDuePending(now, limit) {
|
|
4906
|
+
const safeLimit = Math.max(1, Math.min(limit, 200));
|
|
4907
|
+
return this.db.select().from(billingSubscriptionPollingJobs).where(drizzleOrm.and(drizzleOrm.eq(billingSubscriptionPollingJobs.status, "pending"), drizzleOrm.lte(billingSubscriptionPollingJobs.nextPollAt, now))).orderBy(drizzleOrm.asc(billingSubscriptionPollingJobs.nextPollAt)).limit(safeLimit);
|
|
4908
|
+
}
|
|
4909
|
+
/**
|
|
4910
|
+
* Optimistic-locked update.
|
|
4911
|
+
*
|
|
4912
|
+
* The UPDATE only fires when both `id` AND `version` match the row
|
|
4913
|
+
* the caller previously read. On success the row's `version` is
|
|
4914
|
+
* rotated to a fresh uuid (server-side via gen_random_uuid()) and
|
|
4915
|
+
* `updated_at` is bumped. Returns `null` when another worker has
|
|
4916
|
+
* already moved the row (the version no longer matches).
|
|
4917
|
+
*/
|
|
4918
|
+
async tryLockedUpdate(params) {
|
|
4919
|
+
const updates = {
|
|
4920
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
4921
|
+
};
|
|
4922
|
+
if (params.status !== void 0) {
|
|
4923
|
+
updates.status = params.status;
|
|
4924
|
+
}
|
|
4925
|
+
if (params.lastPolledAt !== void 0) {
|
|
4926
|
+
updates.lastPolledAt = params.lastPolledAt;
|
|
4927
|
+
}
|
|
4928
|
+
if (params.lastProviderStatus !== void 0) {
|
|
4929
|
+
updates.lastProviderStatus = params.lastProviderStatus;
|
|
4930
|
+
}
|
|
4931
|
+
if (params.lastError !== void 0) {
|
|
4932
|
+
updates.lastError = params.lastError;
|
|
4933
|
+
}
|
|
4934
|
+
if (params.nextPollAt !== void 0) {
|
|
4935
|
+
updates.nextPollAt = params.nextPollAt;
|
|
4936
|
+
}
|
|
4937
|
+
if (params.completedAt !== void 0) {
|
|
4938
|
+
updates.completedAt = params.completedAt;
|
|
4939
|
+
}
|
|
4940
|
+
const attemptsExpr = params.incrementAttemptsBy && params.incrementAttemptsBy !== 0 ? drizzleOrm.sql`${billingSubscriptionPollingJobs.attempts} + ${params.incrementAttemptsBy}` : void 0;
|
|
4941
|
+
const result = await this.db.update(billingSubscriptionPollingJobs).set({
|
|
4942
|
+
...updates,
|
|
4943
|
+
// Rotate the version token on every successful update so
|
|
4944
|
+
// a subsequent UPDATE with the previous token fails.
|
|
4945
|
+
version: drizzleOrm.sql`gen_random_uuid()`,
|
|
4946
|
+
...attemptsExpr ? { attempts: attemptsExpr } : {}
|
|
4947
|
+
}).where(
|
|
4948
|
+
drizzleOrm.and(drizzleOrm.eq(billingSubscriptionPollingJobs.id, params.id), drizzleOrm.eq(billingSubscriptionPollingJobs.version, params.expectedVersion))
|
|
4949
|
+
).returning();
|
|
4950
|
+
return firstOrNull(result);
|
|
4951
|
+
}
|
|
4952
|
+
};
|
|
4953
|
+
init_base_repository();
|
|
4703
4954
|
var QZPaySubscriptionsRepository = class {
|
|
4704
4955
|
constructor(db) {
|
|
4705
4956
|
this.db = db;
|
|
@@ -5767,6 +6018,7 @@ var QZPayDrizzleStorageAdapter = class {
|
|
|
5767
6018
|
checkoutsRepo;
|
|
5768
6019
|
customersRepo;
|
|
5769
6020
|
subscriptionsRepo;
|
|
6021
|
+
subscriptionPollingJobsRepo;
|
|
5770
6022
|
paymentsRepo;
|
|
5771
6023
|
paymentMethodsRepo;
|
|
5772
6024
|
invoicesRepo;
|
|
@@ -5782,6 +6034,7 @@ var QZPayDrizzleStorageAdapter = class {
|
|
|
5782
6034
|
checkouts;
|
|
5783
6035
|
customers;
|
|
5784
6036
|
subscriptions;
|
|
6037
|
+
subscriptionPollingJobs;
|
|
5785
6038
|
payments;
|
|
5786
6039
|
paymentMethods;
|
|
5787
6040
|
invoices;
|
|
@@ -5799,6 +6052,7 @@ var QZPayDrizzleStorageAdapter = class {
|
|
|
5799
6052
|
this.checkoutsRepo = new QZPayCheckoutsRepository(this.db);
|
|
5800
6053
|
this.customersRepo = new QZPayCustomersRepository(typedDb);
|
|
5801
6054
|
this.subscriptionsRepo = new QZPaySubscriptionsRepository(typedDb);
|
|
6055
|
+
this.subscriptionPollingJobsRepo = new QZPaySubscriptionPollingJobsRepository(this.db);
|
|
5802
6056
|
this.paymentsRepo = new QZPayPaymentsRepository(this.db);
|
|
5803
6057
|
this.paymentMethodsRepo = new QZPayPaymentMethodsRepository(this.db);
|
|
5804
6058
|
this.invoicesRepo = new QZPayInvoicesRepository(typedDb);
|
|
@@ -5813,6 +6067,7 @@ var QZPayDrizzleStorageAdapter = class {
|
|
|
5813
6067
|
this.checkouts = this.createCheckoutStorage();
|
|
5814
6068
|
this.customers = this.createCustomerStorage();
|
|
5815
6069
|
this.subscriptions = this.createSubscriptionStorage();
|
|
6070
|
+
this.subscriptionPollingJobs = this.createSubscriptionPollingJobStorage();
|
|
5816
6071
|
this.payments = this.createPaymentStorage();
|
|
5817
6072
|
this.paymentMethods = this.createPaymentMethodStorage();
|
|
5818
6073
|
this.invoices = this.createInvoiceStorage();
|
|
@@ -5949,6 +6204,48 @@ var QZPayDrizzleStorageAdapter = class {
|
|
|
5949
6204
|
}
|
|
5950
6205
|
};
|
|
5951
6206
|
}
|
|
6207
|
+
// ==================== Subscription Polling Job Storage ====================
|
|
6208
|
+
createSubscriptionPollingJobStorage() {
|
|
6209
|
+
const repo = this.subscriptionPollingJobsRepo;
|
|
6210
|
+
return {
|
|
6211
|
+
async create(input) {
|
|
6212
|
+
if (!input.provider) {
|
|
6213
|
+
throw new Error(
|
|
6214
|
+
'QZPaySchedulePollingInput.provider is required when calling subscriptionPollingJobs.create. Pass the configured payment adapter provider (e.g., "mercadopago") explicitly.'
|
|
6215
|
+
);
|
|
6216
|
+
}
|
|
6217
|
+
const drizzleInput = mapPollingScheduleInputToDrizzleInsert({ ...input, provider: input.provider });
|
|
6218
|
+
const row = await repo.create(drizzleInput);
|
|
6219
|
+
return row ? mapDrizzlePollingJobToCore(row) : null;
|
|
6220
|
+
},
|
|
6221
|
+
async update(input) {
|
|
6222
|
+
const row = await repo.tryLockedUpdate({
|
|
6223
|
+
id: input.id,
|
|
6224
|
+
expectedVersion: input.expectedVersion,
|
|
6225
|
+
...input.status !== void 0 ? { status: input.status } : {},
|
|
6226
|
+
...input.incrementAttemptsBy !== void 0 ? { incrementAttemptsBy: input.incrementAttemptsBy } : {},
|
|
6227
|
+
...input.lastPolledAt !== void 0 ? { lastPolledAt: input.lastPolledAt } : {},
|
|
6228
|
+
...input.lastProviderStatus !== void 0 ? { lastProviderStatus: input.lastProviderStatus } : {},
|
|
6229
|
+
...input.lastError !== void 0 ? { lastError: input.lastError } : {},
|
|
6230
|
+
...input.nextPollAt !== void 0 ? { nextPollAt: input.nextPollAt } : {},
|
|
6231
|
+
...input.completedAt !== void 0 ? { completedAt: input.completedAt } : {}
|
|
6232
|
+
});
|
|
6233
|
+
return row ? mapDrizzlePollingJobToCore(row) : null;
|
|
6234
|
+
},
|
|
6235
|
+
async findById(id) {
|
|
6236
|
+
const row = await repo.findById(id);
|
|
6237
|
+
return row ? mapDrizzlePollingJobToCore(row) : null;
|
|
6238
|
+
},
|
|
6239
|
+
async findActiveBySubscriptionId(subscriptionId) {
|
|
6240
|
+
const row = await repo.findActiveBySubscriptionId(subscriptionId);
|
|
6241
|
+
return row ? mapDrizzlePollingJobToCore(row) : null;
|
|
6242
|
+
},
|
|
6243
|
+
async findDuePending(now, limit) {
|
|
6244
|
+
const rows = await repo.findDuePending(now, limit);
|
|
6245
|
+
return rows.map(mapDrizzlePollingJobToCore);
|
|
6246
|
+
}
|
|
6247
|
+
};
|
|
6248
|
+
}
|
|
5952
6249
|
// ==================== Payment Storage ====================
|
|
5953
6250
|
createPaymentStorage() {
|
|
5954
6251
|
const repo = this.paymentsRepo;
|
|
@@ -6504,9 +6801,9 @@ async function runMigrations(config) {
|
|
|
6504
6801
|
if (verbose) {
|
|
6505
6802
|
console.log("[QZPay] Starting database migrations...");
|
|
6506
6803
|
}
|
|
6507
|
-
const
|
|
6804
|
+
const sql22 = postgres2__default.default(connectionUrl, { max: 1 });
|
|
6508
6805
|
try {
|
|
6509
|
-
const db = postgresJs.drizzle(
|
|
6806
|
+
const db = postgresJs.drizzle(sql22);
|
|
6510
6807
|
await migrator.migrate(db, {
|
|
6511
6808
|
migrationsFolder
|
|
6512
6809
|
});
|
|
@@ -6517,14 +6814,14 @@ async function runMigrations(config) {
|
|
|
6517
6814
|
console.error("[QZPay] Migration failed:", error);
|
|
6518
6815
|
throw error;
|
|
6519
6816
|
} finally {
|
|
6520
|
-
await
|
|
6817
|
+
await sql22.end();
|
|
6521
6818
|
}
|
|
6522
6819
|
}
|
|
6523
6820
|
async function hasPendingMigrations(connectionUrl) {
|
|
6524
|
-
const
|
|
6821
|
+
const sql22 = postgres2__default.default(connectionUrl, { max: 1 });
|
|
6525
6822
|
try {
|
|
6526
|
-
postgresJs.drizzle(
|
|
6527
|
-
const result = await
|
|
6823
|
+
postgresJs.drizzle(sql22);
|
|
6824
|
+
const result = await sql22`
|
|
6528
6825
|
SELECT EXISTS (
|
|
6529
6826
|
SELECT FROM information_schema.tables
|
|
6530
6827
|
WHERE table_name = 'drizzle_migrations'
|
|
@@ -6535,24 +6832,24 @@ async function hasPendingMigrations(connectionUrl) {
|
|
|
6535
6832
|
}
|
|
6536
6833
|
return false;
|
|
6537
6834
|
} finally {
|
|
6538
|
-
await
|
|
6835
|
+
await sql22.end();
|
|
6539
6836
|
}
|
|
6540
6837
|
}
|
|
6541
6838
|
async function ensureDatabase(config) {
|
|
6542
6839
|
const { connectionUrl, databaseName } = config;
|
|
6543
|
-
const
|
|
6840
|
+
const sql22 = postgres2__default.default(connectionUrl, { max: 1 });
|
|
6544
6841
|
try {
|
|
6545
|
-
const result = await
|
|
6842
|
+
const result = await sql22`
|
|
6546
6843
|
SELECT 1 FROM pg_database WHERE datname = ${databaseName}
|
|
6547
6844
|
`;
|
|
6548
6845
|
if (result.length === 0) {
|
|
6549
|
-
await
|
|
6846
|
+
await sql22.unsafe(`CREATE DATABASE "${databaseName}"`);
|
|
6550
6847
|
console.log(`[QZPay] Created database: ${databaseName}`);
|
|
6551
6848
|
return true;
|
|
6552
6849
|
}
|
|
6553
6850
|
return false;
|
|
6554
6851
|
} finally {
|
|
6555
|
-
await
|
|
6852
|
+
await sql22.end();
|
|
6556
6853
|
}
|
|
6557
6854
|
}
|
|
6558
6855
|
|
|
@@ -6787,6 +7084,7 @@ async function executeTransaction(db, options, fn) {
|
|
|
6787
7084
|
return withTransaction(db, executeFn);
|
|
6788
7085
|
}
|
|
6789
7086
|
|
|
7087
|
+
exports.POLLING_JOB_DEFAULTS = POLLING_JOB_DEFAULTS;
|
|
6790
7088
|
exports.QZPAY_DRIZZLE_SCHEMA_VERSION = QZPAY_DRIZZLE_SCHEMA_VERSION;
|
|
6791
7089
|
exports.QZPAY_PAGINATION_DEFAULTS = QZPAY_PAGINATION_DEFAULTS;
|
|
6792
7090
|
exports.QZPayAddonsRepository = QZPayAddonsRepository;
|
|
@@ -6802,6 +7100,7 @@ exports.QZPayPaymentsRepository = QZPayPaymentsRepository;
|
|
|
6802
7100
|
exports.QZPayPlansRepository = QZPayPlansRepository;
|
|
6803
7101
|
exports.QZPayPricesRepository = QZPayPricesRepository;
|
|
6804
7102
|
exports.QZPayPromoCodesRepository = QZPayPromoCodesRepository;
|
|
7103
|
+
exports.QZPaySubscriptionPollingJobsRepository = QZPaySubscriptionPollingJobsRepository;
|
|
6805
7104
|
exports.QZPaySubscriptionsRepository = QZPaySubscriptionsRepository;
|
|
6806
7105
|
exports.QZPayUsageRecordsRepository = QZPayUsageRecordsRepository;
|
|
6807
7106
|
exports.QZPayVendorsRepository = QZPayVendorsRepository;
|
|
@@ -6879,6 +7178,10 @@ exports.billingSubscriptionAddonSelectSchema = billingSubscriptionAddonSelectSch
|
|
|
6879
7178
|
exports.billingSubscriptionAddons = billingSubscriptionAddons;
|
|
6880
7179
|
exports.billingSubscriptionAddonsRelations = billingSubscriptionAddonsRelations;
|
|
6881
7180
|
exports.billingSubscriptionInsertSchema = billingSubscriptionInsertSchema;
|
|
7181
|
+
exports.billingSubscriptionPollingJobInsertSchema = billingSubscriptionPollingJobInsertSchema;
|
|
7182
|
+
exports.billingSubscriptionPollingJobSelectSchema = billingSubscriptionPollingJobSelectSchema;
|
|
7183
|
+
exports.billingSubscriptionPollingJobs = billingSubscriptionPollingJobs;
|
|
7184
|
+
exports.billingSubscriptionPollingJobsRelations = billingSubscriptionPollingJobsRelations;
|
|
6882
7185
|
exports.billingSubscriptionSelectSchema = billingSubscriptionSelectSchema;
|
|
6883
7186
|
exports.billingSubscriptions = billingSubscriptions;
|
|
6884
7187
|
exports.billingSubscriptionsRelations = billingSubscriptionsRelations;
|
|
@@ -6963,6 +7266,7 @@ exports.mapDrizzleLimitToCore = mapDrizzleLimitToCore;
|
|
|
6963
7266
|
exports.mapDrizzlePaymentMethodToCore = mapDrizzlePaymentMethodToCore;
|
|
6964
7267
|
exports.mapDrizzlePaymentToCore = mapDrizzlePaymentToCore;
|
|
6965
7268
|
exports.mapDrizzlePlanToCore = mapDrizzlePlanToCore;
|
|
7269
|
+
exports.mapDrizzlePollingJobToCore = mapDrizzlePollingJobToCore;
|
|
6966
7270
|
exports.mapDrizzlePriceToCore = mapDrizzlePriceToCore;
|
|
6967
7271
|
exports.mapDrizzlePromoCodeToCore = mapDrizzlePromoCodeToCore;
|
|
6968
7272
|
exports.mapDrizzleSubscriptionAddonToCore = mapDrizzleSubscriptionAddonToCore;
|
|
@@ -6970,6 +7274,7 @@ exports.mapDrizzleSubscriptionToCore = mapDrizzleSubscriptionToCore;
|
|
|
6970
7274
|
exports.mapDrizzleUsageRecordToCore = mapDrizzleUsageRecordToCore;
|
|
6971
7275
|
exports.mapDrizzleVendorPayoutToCore = mapDrizzleVendorPayoutToCore;
|
|
6972
7276
|
exports.mapDrizzleVendorToCore = mapDrizzleVendorToCore;
|
|
7277
|
+
exports.mapPollingScheduleInputToDrizzleInsert = mapPollingScheduleInputToDrizzleInsert;
|
|
6973
7278
|
exports.normalizePaginationOptions = normalizePaginationOptions;
|
|
6974
7279
|
exports.offsetToPage = offsetToPage;
|
|
6975
7280
|
exports.onlyDeleted = onlyDeleted;
|