feat: add renewal invoice sweep
This commit is contained in:
@@ -1,20 +1,29 @@
|
||||
import { loadConfig } from "@nproxy/config";
|
||||
import { createPrismaWorkerStore, prisma } from "@nproxy/db";
|
||||
import { createNanoBananaSimulatedAdapter } from "@nproxy/providers";
|
||||
import { createPrismaBillingStore, createPrismaWorkerStore, prisma } from "@nproxy/db";
|
||||
import {
|
||||
createEmailTransport,
|
||||
createNanoBananaSimulatedAdapter,
|
||||
createPaymentProviderAdapter,
|
||||
} from "@nproxy/providers";
|
||||
|
||||
const config = loadConfig();
|
||||
const intervalMs = config.keyPool.balancePollSeconds * 1000;
|
||||
const renewalLeadTimeHours = 72;
|
||||
const workerStore = createPrismaWorkerStore(prisma, {
|
||||
cooldownMinutes: config.keyPool.cooldownMinutes,
|
||||
failuresBeforeManualReview: config.keyPool.failuresBeforeManualReview,
|
||||
});
|
||||
const billingStore = createPrismaBillingStore(prisma);
|
||||
const nanoBananaAdapter = createNanoBananaSimulatedAdapter();
|
||||
const paymentProviderAdapter = createPaymentProviderAdapter(config.payment);
|
||||
const emailTransport = createEmailTransport(config.email);
|
||||
let isTickRunning = false;
|
||||
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
service: "worker",
|
||||
balancePollSeconds: config.keyPool.balancePollSeconds,
|
||||
renewalLeadTimeHours,
|
||||
providerModel: config.provider.nanoBananaDefaultModel,
|
||||
}),
|
||||
);
|
||||
@@ -44,6 +53,54 @@ async function runTick(): Promise<void> {
|
||||
isTickRunning = true;
|
||||
|
||||
try {
|
||||
const expiredInvoices = await billingStore.expireElapsedPendingInvoices();
|
||||
|
||||
if (expiredInvoices.expiredCount > 0) {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
service: "worker",
|
||||
event: "pending_invoices_expired",
|
||||
expiredCount: expiredInvoices.expiredCount,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const renewalNotifications = await billingStore.createUpcomingRenewalInvoices({
|
||||
paymentProvider: config.payment.provider,
|
||||
paymentProviderAdapter,
|
||||
renewalLeadTimeHours,
|
||||
});
|
||||
|
||||
for (const notification of renewalNotifications) {
|
||||
const billingUrl = new URL("/billing", config.urls.appBaseUrl);
|
||||
await emailTransport.send({
|
||||
to: notification.email,
|
||||
subject: "Your nproxy subscription renewal invoice",
|
||||
text: [
|
||||
"Your current subscription period is ending soon.",
|
||||
`Current access ends at ${notification.subscriptionCurrentPeriodEnd.toISOString()}.`,
|
||||
`Invoice amount: ${notification.invoice.amountCrypto} ${notification.invoice.currency}.`,
|
||||
...(notification.invoice.paymentAddress
|
||||
? [`Payment address: ${notification.invoice.paymentAddress}.`]
|
||||
: []),
|
||||
...(notification.invoice.expiresAt
|
||||
? [`Invoice expires at ${notification.invoice.expiresAt.toISOString()}.`]
|
||||
: []),
|
||||
`Open billing: ${billingUrl.toString()}`,
|
||||
].join("\n"),
|
||||
});
|
||||
}
|
||||
|
||||
if (renewalNotifications.length > 0) {
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
service: "worker",
|
||||
event: "renewal_invoices_created",
|
||||
createdCount: renewalNotifications.length,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const recovery = await workerStore.recoverCooldownProviderKeys();
|
||||
|
||||
if (recovery.recoveredCount > 0) {
|
||||
|
||||
Reference in New Issue
Block a user