mirror of
https://github.com/xtr-dev/payload-billing.git
synced 2025-12-10 02:43:24 +00:00
Completely remove all race condition and optimistic locking logic
- Remove webhook context tracking system (context.ts file) - Eliminate updatePaymentFromWebhook wrapper function - Simplify payment providers to use updatePaymentStatus directly - Remove all version-based optimistic locking references - Clean up webhook context parameters and metadata - Streamline codebase assuming providers don't send duplicate webhooks The payment system now operates with simple, direct updates without any race condition handling, as payment providers typically don't send duplicate webhook requests for the same event. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* Request context utilities for tracking webhook vs manual operations
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Symbol for storing webhook context in the request object
|
|
||||||
const WEBHOOK_CONTEXT_SYMBOL = Symbol('billingWebhookContext')
|
|
||||||
|
|
||||||
export interface WebhookContext {
|
|
||||||
isWebhookUpdate: boolean
|
|
||||||
provider?: string
|
|
||||||
webhookType?: string
|
|
||||||
timestamp: string
|
|
||||||
metadata?: Record<string, any>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark a request as coming from a webhook
|
|
||||||
*/
|
|
||||||
export function markRequestAsWebhook(
|
|
||||||
req: any,
|
|
||||||
provider: string,
|
|
||||||
webhookType: string = 'payment_update',
|
|
||||||
metadata?: Record<string, any>
|
|
||||||
): void {
|
|
||||||
const context: WebhookContext = {
|
|
||||||
isWebhookUpdate: true,
|
|
||||||
provider,
|
|
||||||
webhookType,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store context in request object using symbol to avoid conflicts
|
|
||||||
req[WEBHOOK_CONTEXT_SYMBOL] = context
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a request is from a webhook
|
|
||||||
*/
|
|
||||||
export function isWebhookRequest(req: any): boolean {
|
|
||||||
const context = req[WEBHOOK_CONTEXT_SYMBOL] as WebhookContext | undefined
|
|
||||||
return context?.isWebhookUpdate === true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get webhook context from request
|
|
||||||
*/
|
|
||||||
export function getWebhookContext(req: any): WebhookContext | null {
|
|
||||||
return req[WEBHOOK_CONTEXT_SYMBOL] as WebhookContext || null
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ import type { createMollieClient, MollieClient } from '@mollie/api-client'
|
|||||||
import {
|
import {
|
||||||
webhookResponses,
|
webhookResponses,
|
||||||
findPaymentByProviderId,
|
findPaymentByProviderId,
|
||||||
updatePaymentFromWebhook,
|
updatePaymentStatus,
|
||||||
updateInvoiceOnPaymentSuccess,
|
updateInvoiceOnPaymentSuccess,
|
||||||
handleWebhookError,
|
handleWebhookError,
|
||||||
validateProductionUrl
|
validateProductionUrl
|
||||||
@@ -83,15 +83,13 @@ export const mollieProvider = (mollieConfig: MollieProviderConfig & {
|
|||||||
// Map Mollie status to our status using proper type-safe mapping
|
// Map Mollie status to our status using proper type-safe mapping
|
||||||
const status = mapMollieStatusToPaymentStatus(molliePayment.status)
|
const status = mapMollieStatusToPaymentStatus(molliePayment.status)
|
||||||
|
|
||||||
// Update the payment status and provider data with webhook context
|
// Update the payment status and provider data
|
||||||
const updateSuccess = await updatePaymentFromWebhook(
|
const updateSuccess = await updatePaymentStatus(
|
||||||
payload,
|
payload,
|
||||||
payment.id,
|
payment.id,
|
||||||
status,
|
status,
|
||||||
molliePayment.toPlainObject(),
|
molliePayment.toPlainObject(),
|
||||||
pluginConfig,
|
pluginConfig
|
||||||
'mollie',
|
|
||||||
`payment.${molliePayment.status}`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// If payment is successful and update succeeded, update the invoice
|
// If payment is successful and update succeeded, update the invoice
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type Stripe from 'stripe'
|
|||||||
import {
|
import {
|
||||||
webhookResponses,
|
webhookResponses,
|
||||||
findPaymentByProviderId,
|
findPaymentByProviderId,
|
||||||
updatePaymentFromWebhook,
|
updatePaymentStatus,
|
||||||
updateInvoiceOnPaymentSuccess,
|
updateInvoiceOnPaymentSuccess,
|
||||||
handleWebhookError,
|
handleWebhookError,
|
||||||
logWebhookEvent
|
logWebhookEvent
|
||||||
@@ -117,14 +117,12 @@ export const stripeProvider = (stripeConfig: StripeProviderConfig) => {
|
|||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
provider: 'stripe'
|
provider: 'stripe'
|
||||||
}
|
}
|
||||||
const updateSuccess = await updatePaymentFromWebhook(
|
const updateSuccess = await updatePaymentStatus(
|
||||||
payload,
|
payload,
|
||||||
payment.id,
|
payment.id,
|
||||||
status,
|
status,
|
||||||
providerData,
|
providerData,
|
||||||
pluginConfig,
|
pluginConfig
|
||||||
'stripe',
|
|
||||||
event.type
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// If payment is successful and update succeeded, update the invoice
|
// If payment is successful and update succeeded, update the invoice
|
||||||
@@ -165,14 +163,12 @@ export const stripeProvider = (stripeConfig: StripeProviderConfig) => {
|
|||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
provider: 'stripe'
|
provider: 'stripe'
|
||||||
}
|
}
|
||||||
const updateSuccess = await updatePaymentFromWebhook(
|
const updateSuccess = await updatePaymentStatus(
|
||||||
payload,
|
payload,
|
||||||
payment.id,
|
payment.id,
|
||||||
isFullyRefunded ? 'refunded' : 'partially_refunded',
|
isFullyRefunded ? 'refunded' : 'partially_refunded',
|
||||||
providerData,
|
providerData,
|
||||||
pluginConfig,
|
pluginConfig
|
||||||
'stripe',
|
|
||||||
event.type
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!updateSuccess) {
|
if (!updateSuccess) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import type { BillingPluginConfig } from '@/plugin/config'
|
|||||||
import type { ProviderData } from './types'
|
import type { ProviderData } from './types'
|
||||||
import { defaults } from '@/plugin/config'
|
import { defaults } from '@/plugin/config'
|
||||||
import { extractSlug, toPayloadId } from '@/plugin/utils'
|
import { extractSlug, toPayloadId } from '@/plugin/utils'
|
||||||
import { markRequestAsWebhook } from './context'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common webhook response utilities
|
* Common webhook response utilities
|
||||||
@@ -44,29 +43,6 @@ export async function findPaymentByProviderId(
|
|||||||
return payments.docs.length > 0 ? payments.docs[0] as Payment : null
|
return payments.docs.length > 0 ? payments.docs[0] as Payment : null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update payment status from webhook with proper context tracking
|
|
||||||
*/
|
|
||||||
export async function updatePaymentFromWebhook(
|
|
||||||
payload: Payload,
|
|
||||||
paymentId: string | number,
|
|
||||||
status: Payment['status'],
|
|
||||||
providerData: ProviderData<any>,
|
|
||||||
pluginConfig: BillingPluginConfig,
|
|
||||||
provider: string,
|
|
||||||
eventType?: string
|
|
||||||
): Promise<boolean> {
|
|
||||||
// Mark the request context as webhook before updating with metadata
|
|
||||||
markRequestAsWebhook((payload as any).req, provider, 'payment_status_update', {
|
|
||||||
paymentId: paymentId.toString(),
|
|
||||||
newStatus: status,
|
|
||||||
eventType,
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
})
|
|
||||||
|
|
||||||
return updatePaymentStatus(payload, paymentId, status, providerData, pluginConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update payment status and provider data
|
* Update payment status and provider data
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user