mirror of
https://github.com/MAGICGrants/campaign-site.git
synced 2026-01-08 20:08:05 -05:00
Prevent same invoice from being processed by webhook handler twice
This commit is contained in:
@@ -43,7 +43,19 @@ type WebhookBody = Record<string, any> & {
|
||||
}
|
||||
|
||||
async function handleFundingRequiredApiDonation(body: WebhookBody) {
|
||||
if (!body.metadata) return
|
||||
if (!body.metadata || JSON.stringify(body.metadata) === '{}') return
|
||||
|
||||
const existingDonation = await prisma.donation.findFirst({
|
||||
where: { btcPayInvoiceId: body.invoiceId },
|
||||
})
|
||||
|
||||
if (existingDonation) {
|
||||
log(
|
||||
'warn',
|
||||
`[BTCPay webhook] Attempted to process already processed invoice ${body.invoiceId}.`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle payment methods like "BTC-LightningNetwork" if added in the future
|
||||
const cryptoCode = body.paymentMethod.includes('-')
|
||||
@@ -79,6 +91,18 @@ async function handleFundingRequiredApiDonation(body: WebhookBody) {
|
||||
async function handleDonationOrMembership(body: WebhookBody) {
|
||||
if (!body.metadata || JSON.stringify(body.metadata) === '{}') return
|
||||
|
||||
const existingDonation = await prisma.donation.findFirst({
|
||||
where: { btcPayInvoiceId: body.invoiceId },
|
||||
})
|
||||
|
||||
if (existingDonation) {
|
||||
log(
|
||||
'warn',
|
||||
`[BTCPay webhook] Attempted to process already processed invoice ${body.invoiceId}.`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const termToMembershipExpiresAt = {
|
||||
monthly: dayjs().add(1, 'month').toDate(),
|
||||
annually: dayjs().add(1, 'year').toDate(),
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
stripe as _stripe,
|
||||
strapiApi,
|
||||
} from '../../server/services'
|
||||
import { DonationMetadata, StrapiCreatePointBody } from '../../server/types'
|
||||
import { DonationMetadata } from '../../server/types'
|
||||
import { sendDonationConfirmationEmail } from './mailing'
|
||||
import { getPointsBalance, givePointsToUser } from './perks'
|
||||
import { NET_DONATION_AMOUNT_WITH_POINTS_RATE, POINTS_PER_USD } from '../../config'
|
||||
@@ -26,6 +26,18 @@ async function handleDonationOrNonRecurringMembership(paymentIntent: Stripe.Paym
|
||||
if (JSON.stringify(metadata) === '{}') return
|
||||
if (metadata.isSubscription === 'true') return
|
||||
|
||||
const existingDonation = await prisma.donation.findFirst({
|
||||
where: { stripePaymentIntentId: paymentIntent.id },
|
||||
})
|
||||
|
||||
if (existingDonation) {
|
||||
log(
|
||||
'warn',
|
||||
`[Stripe webhook] Attempted to process already processed payment intent ${paymentIntent.id}.`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Skip this event if intent is still not fully paid
|
||||
if (paymentIntent.amount_received !== paymentIntent.amount) return
|
||||
|
||||
@@ -138,12 +150,21 @@ async function handleRecurringMembership(invoice: Stripe.Invoice) {
|
||||
|
||||
if (!invoiceLine) {
|
||||
log(
|
||||
'info',
|
||||
'warn',
|
||||
`[/api/stripe/${metadata.fundSlug}-webhook] Line not fund for invoice ${invoice.id}. Skipping.`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const existingDonation = await prisma.donation.findFirst({
|
||||
where: { stripeInvoiceId: invoice.id },
|
||||
})
|
||||
|
||||
if (existingDonation) {
|
||||
log('warn', `[Stripe webhook] Attempted to process already processed invoice ${invoice.id}.`)
|
||||
return
|
||||
}
|
||||
|
||||
const shouldGivePointsBack = metadata.givePointsBack === 'true'
|
||||
const grossFiatAmount = invoice.total / 100
|
||||
const netFiatAmount = shouldGivePointsBack
|
||||
|
||||
Reference in New Issue
Block a user