update payment method, remove stripe, paypal, amanapay
Build & Push / Pipeline Tests (push) Failing after 59s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Failing after 48s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
Build & Push / Pipeline Tests (push) Failing after 59s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Failing after 48s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
This commit is contained in:
@@ -63,7 +63,6 @@ function fmtDate(value?: Date | string | null) {
|
||||
function paymentMethodLabel(method?: string | null) {
|
||||
if (method === 'BANK_TRANSFER') return 'Bank transfer'
|
||||
if (method === 'CHECK') return 'Check'
|
||||
if (method === 'STRIPE') return 'Online card payment'
|
||||
return method ?? 'Manual payment'
|
||||
}
|
||||
|
||||
@@ -410,7 +409,7 @@ export async function createManualCheckout(companyId: string, employeeId: string
|
||||
if (scheduled.legacySubscriptionInvoice) {
|
||||
await tx.subscriptionInvoice.update({
|
||||
where: { id: scheduled.legacySubscriptionInvoice.id },
|
||||
data: { paymentProvider: 'MANUAL', stripeCheckoutSessionId: null },
|
||||
data: { paymentProvider: 'MANUAL' },
|
||||
})
|
||||
}
|
||||
await createBillingEvent(tx, {
|
||||
@@ -538,163 +537,6 @@ export async function createManualCheckout(companyId: string, employeeId: string
|
||||
})
|
||||
}
|
||||
|
||||
export async function createCanonicalStripeCheckoutInvoice(data: {
|
||||
companyId: string
|
||||
subscriptionId: string
|
||||
plan: 'STARTER' | 'GROWTH' | 'PRO' | 'ENTERPRISE'
|
||||
billingPeriod: 'MONTHLY' | 'ANNUAL'
|
||||
amount: number
|
||||
currency: 'MAD'
|
||||
stripeCheckoutSessionId: string
|
||||
dueAt: Date
|
||||
}) {
|
||||
const account = await ensurePrimaryBillingAccount(data.companyId)
|
||||
const platformBillingSettings = await getPlatformBillingSettings()
|
||||
const tax = calculateTaxForAccount(data.amount, account, platformBillingSettings.taxRate)
|
||||
return prisma.$transaction(async (tx: any) => {
|
||||
const duplicate = await tx.billingInvoice.findFirst({
|
||||
where: { metadata: { path: ['stripeCheckoutSessionId'], equals: data.stripeCheckoutSessionId } },
|
||||
})
|
||||
if (duplicate) return duplicate
|
||||
const subscription = await tx.subscription.findUniqueOrThrow({ where: { id: data.subscriptionId } })
|
||||
const now = new Date()
|
||||
const isRenewal = subscription.status === 'ACTIVE' && subscription.currentPeriodEnd && subscription.currentPeriodEnd > now
|
||||
const renewalKey = isRenewal ? `${subscription.id}:${subscription.currentPeriodEnd!.toISOString()}` : null
|
||||
if (renewalKey) {
|
||||
const scheduled = await tx.billingInvoice.findUnique({
|
||||
where: { renewalKey },
|
||||
include: { legacySubscriptionInvoice: true },
|
||||
})
|
||||
if (scheduled) {
|
||||
if (!PAYABLE_INVOICE_STATUSES.includes(scheduled.status)) {
|
||||
throw new ConflictError('The renewal invoice is not payable')
|
||||
}
|
||||
if (scheduled.amountDue !== tax.totalAmount || scheduled.requestedPlan !== data.plan || scheduled.requestedBillingPeriod !== data.billingPeriod) {
|
||||
throw new ConflictError('An existing renewal invoice must be resolved before changing the renewal terms')
|
||||
}
|
||||
const invoice = await tx.billingInvoice.update({
|
||||
where: { id: scheduled.id },
|
||||
data: {
|
||||
collectionMethod: 'STRIPE',
|
||||
paymentProvider: 'STRIPE',
|
||||
metadata: {
|
||||
...((scheduled.metadata as Record<string, unknown>) ?? {}),
|
||||
source: 'stripe_checkout',
|
||||
stripeCheckoutSessionId: data.stripeCheckoutSessionId,
|
||||
},
|
||||
},
|
||||
})
|
||||
if (scheduled.legacySubscriptionInvoice) {
|
||||
await tx.subscriptionInvoice.update({
|
||||
where: { id: scheduled.legacySubscriptionInvoice.id },
|
||||
data: { paymentProvider: 'STRIPE', stripeCheckoutSessionId: data.stripeCheckoutSessionId },
|
||||
})
|
||||
} else {
|
||||
await tx.subscriptionInvoice.create({
|
||||
data: {
|
||||
companyId: data.companyId,
|
||||
subscriptionId: data.subscriptionId,
|
||||
requestedPlan: data.plan,
|
||||
requestedBillingPeriod: data.billingPeriod,
|
||||
amount: tax.totalAmount,
|
||||
currency: data.currency,
|
||||
status: 'PENDING',
|
||||
paymentProvider: 'STRIPE',
|
||||
stripeCheckoutSessionId: data.stripeCheckoutSessionId,
|
||||
billingInvoiceId: invoice.id,
|
||||
dueAt: scheduled.dueAt ?? data.dueAt,
|
||||
},
|
||||
})
|
||||
}
|
||||
await createBillingEvent(tx, {
|
||||
billingAccountId: account.id,
|
||||
invoiceId: invoice.id,
|
||||
subscriptionId: data.subscriptionId,
|
||||
companyId: data.companyId,
|
||||
eventType: 'stripe_checkout.created',
|
||||
source: 'customer',
|
||||
payload: { stripeCheckoutSessionId: data.stripeCheckoutSessionId, reusedRenewalInvoice: true },
|
||||
})
|
||||
return invoice
|
||||
}
|
||||
}
|
||||
const invoiceSequence = await getNextInvoiceSequence(tx)
|
||||
const invoiceNumber = buildSequentialInvoiceNumber(invoiceSequence, now)
|
||||
const invoice = await tx.billingInvoice.create({
|
||||
data: {
|
||||
billingAccountId: account.id,
|
||||
companyId: data.companyId,
|
||||
subscriptionId: data.subscriptionId,
|
||||
invoiceNumber,
|
||||
invoiceSequence,
|
||||
invoiceType: isRenewal ? 'SUBSCRIPTION_RENEWAL' : 'SUBSCRIPTION_INITIAL',
|
||||
status: 'OPEN',
|
||||
currency: data.currency,
|
||||
subtotalAmount: data.amount,
|
||||
taxAmount: tax.taxAmount,
|
||||
totalAmount: tax.totalAmount,
|
||||
amountDue: tax.totalAmount,
|
||||
invoiceDate: now,
|
||||
dueAt: data.dueAt,
|
||||
finalizedAt: now,
|
||||
billingName: account.legalName,
|
||||
billingEmail: account.billingEmail,
|
||||
billingAddress: account.billingAddress ?? undefined,
|
||||
paymentProvider: 'STRIPE',
|
||||
collectionMethod: 'STRIPE',
|
||||
requestedPlan: data.plan,
|
||||
requestedBillingPeriod: data.billingPeriod,
|
||||
renewalKey,
|
||||
isSubscriptionBlocking: true,
|
||||
metadata: { source: 'stripe_checkout', stripeCheckoutSessionId: data.stripeCheckoutSessionId },
|
||||
lineItems: {
|
||||
create: [
|
||||
{
|
||||
subscriptionId: data.subscriptionId,
|
||||
plan: data.plan,
|
||||
type: 'SUBSCRIPTION_FEE',
|
||||
description: `${data.plan} subscription — ${data.billingPeriod}`,
|
||||
quantity: 1,
|
||||
unitAmount: data.amount,
|
||||
amount: data.amount,
|
||||
currency: data.currency,
|
||||
periodStart: isRenewal ? subscription.currentPeriodEnd : now,
|
||||
periodEnd: isRenewal ? addBillingPeriod(subscription.currentPeriodEnd!, data.billingPeriod) : addBillingPeriod(now, data.billingPeriod),
|
||||
},
|
||||
...taxLineItem(tax, data.currency),
|
||||
],
|
||||
},
|
||||
...(taxRecordCreate(account, tax) ? { taxRecords: taxRecordCreate(account, tax) } : {}),
|
||||
},
|
||||
})
|
||||
await tx.subscriptionInvoice.create({
|
||||
data: {
|
||||
companyId: data.companyId,
|
||||
subscriptionId: data.subscriptionId,
|
||||
requestedPlan: data.plan,
|
||||
requestedBillingPeriod: data.billingPeriod,
|
||||
amount: tax.totalAmount,
|
||||
currency: data.currency,
|
||||
status: 'PENDING',
|
||||
paymentProvider: 'STRIPE',
|
||||
stripeCheckoutSessionId: data.stripeCheckoutSessionId,
|
||||
billingInvoiceId: invoice.id,
|
||||
dueAt: data.dueAt,
|
||||
},
|
||||
})
|
||||
await createBillingEvent(tx, {
|
||||
billingAccountId: account.id,
|
||||
invoiceId: invoice.id,
|
||||
subscriptionId: data.subscriptionId,
|
||||
companyId: data.companyId,
|
||||
eventType: 'stripe_checkout.created',
|
||||
source: 'customer',
|
||||
payload: { stripeCheckoutSessionId: data.stripeCheckoutSessionId },
|
||||
})
|
||||
return invoice
|
||||
})
|
||||
}
|
||||
|
||||
export async function finalizeCanonicalOnlinePayment(legacyInvoiceId: string, providerPaymentId?: string) {
|
||||
let collectionsCaseId: string | null = null
|
||||
const handled = await prisma.$transaction(async (tx: any) => {
|
||||
@@ -711,7 +553,7 @@ export async function finalizeCanonicalOnlinePayment(legacyInvoiceId: string, pr
|
||||
data: {
|
||||
invoiceId: invoice.id,
|
||||
billingAccountId: invoice.billingAccountId,
|
||||
providerPaymentId: providerPaymentId ?? legacy.stripeCheckoutSessionId ?? legacy.providerInvoiceId,
|
||||
providerPaymentId: providerPaymentId ?? legacy.providerInvoiceId,
|
||||
channel: 'ONLINE',
|
||||
status: 'SUCCEEDED',
|
||||
amount: invoice.amountDue,
|
||||
@@ -833,7 +675,7 @@ export async function finalizeCanonicalOnlinePayment(legacyInvoiceId: string, pr
|
||||
invoice: invoiceLabel,
|
||||
amountPaid: invoice.amountPaid || paymentAttempt?.amount || invoice.totalAmount,
|
||||
currency: invoice.currency,
|
||||
paymentType: invoice.collectionMethod ?? invoice.paymentProvider ?? 'STRIPE',
|
||||
paymentType: invoice.collectionMethod ?? invoice.paymentProvider ?? 'BANK_TRANSFER',
|
||||
paymentReference: paymentAttempt?.providerPaymentId ?? null,
|
||||
paidAt: invoice.paidAt,
|
||||
plan: invoice.requestedPlan ?? invoice.subscription?.plan ?? null,
|
||||
@@ -854,7 +696,7 @@ export async function finalizeCanonicalOnlinePayment(legacyInvoiceId: string, pr
|
||||
invoiceId: invoice.id,
|
||||
amountPaid: invoice.amountPaid || paymentAttempt?.amount || invoice.totalAmount,
|
||||
currency: invoice.currency,
|
||||
paymentType: invoice.collectionMethod ?? invoice.paymentProvider ?? 'STRIPE',
|
||||
paymentType: invoice.collectionMethod ?? invoice.paymentProvider ?? 'BANK_TRANSFER',
|
||||
paymentReference: paymentAttempt?.providerPaymentId ?? null,
|
||||
subscriptionStart: periodStart,
|
||||
subscriptionEnd: periodEnd,
|
||||
@@ -889,7 +731,7 @@ export async function recordCanonicalOnlinePaymentFailure(
|
||||
data: {
|
||||
invoiceId: invoice.id,
|
||||
billingAccountId: invoice.billingAccountId,
|
||||
providerPaymentId: legacy.stripeCheckoutSessionId ?? legacy.providerInvoiceId,
|
||||
providerPaymentId: legacy.providerInvoiceId,
|
||||
channel: 'ONLINE',
|
||||
status: 'FAILED',
|
||||
amount: invoice.amountDue,
|
||||
@@ -997,7 +839,7 @@ export async function getCanonicalInvoices(companyId: string) {
|
||||
currency: invoice.currency,
|
||||
status: invoice.status,
|
||||
paymentProvider: invoice.paymentProvider,
|
||||
collectionMethod: invoice.paymentProvider === 'STRIPE' ? 'STRIPE' : 'BANK_TRANSFER',
|
||||
collectionMethod: 'BANK_TRANSFER',
|
||||
requestedPlan: invoice.requestedPlan,
|
||||
requestedBillingPeriod: invoice.requestedBillingPeriod,
|
||||
dueAt: invoice.dueAt,
|
||||
|
||||
Reference in New Issue
Block a user