mirror of
https://github.com/xtr-dev/payload-billing.git
synced 2025-12-10 10:53:23 +00:00
fix: Implement true atomic optimistic locking and enhance type safety
🔒 Critical Race Condition Fixes: - Add version field to payment schema for atomic updates - Implement true optimistic locking using PayloadCMS updateMany with version checks - Eliminate race condition window between conflict check and update - Auto-increment version in beforeChange hooks 🛡️ Type Safety Improvements: - Replace 'any' type with proper ProviderData<T> generic - Maintain type safety throughout payment provider operations - Enhanced intellisense and compile-time error detection ⚡ Performance & Reliability: - Atomic version-based locking prevents lost updates - Proper conflict detection with detailed logging - Graceful handling of concurrent modifications - Version field hidden from admin UI but tracked internally 🔧 Configuration Validation: - All critical validation moved to provider initialization - Early failure detection prevents runtime issues - Clear error messages for configuration problems 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -106,6 +106,15 @@ export function createPaymentsCollection(pluginConfig: BillingPluginConfig): Col
|
||||
hasMany: true,
|
||||
relationTo: extractSlug(pluginConfig.collections?.refunds || defaults.refundsCollection) as CollectionSlug,
|
||||
},
|
||||
{
|
||||
name: 'version',
|
||||
type: 'number',
|
||||
admin: {
|
||||
hidden: true, // Hide from admin UI
|
||||
},
|
||||
defaultValue: 1,
|
||||
required: true,
|
||||
},
|
||||
]
|
||||
if (overrides?.fields) {
|
||||
fields = overrides?.fields({defaultFields: fields})
|
||||
@@ -129,6 +138,9 @@ export function createPaymentsCollection(pluginConfig: BillingPluginConfig): Col
|
||||
beforeChange: [
|
||||
async ({ data, operation, req }) => {
|
||||
if (operation === 'create') {
|
||||
// Initialize version for new payments
|
||||
data.version = 1
|
||||
|
||||
// Validate amount format
|
||||
if (data.amount && !Number.isInteger(data.amount)) {
|
||||
throw new Error('Amount must be an integer (in cents)')
|
||||
@@ -143,6 +155,15 @@ export function createPaymentsCollection(pluginConfig: BillingPluginConfig): Col
|
||||
}
|
||||
|
||||
await initProviderPayment(req.payload, data)
|
||||
} else if (operation === 'update') {
|
||||
// Auto-increment version for updates (if not already set by optimistic locking)
|
||||
if (!data.version) {
|
||||
const currentDoc = await req.payload.findByID({
|
||||
collection: extractSlug(pluginConfig.collections?.payments || defaults.paymentsCollection),
|
||||
id: req.id as any
|
||||
})
|
||||
data.version = (currentDoc.version || 1) + 1
|
||||
}
|
||||
}
|
||||
},
|
||||
] satisfies CollectionBeforeChangeHook<Payment>[],
|
||||
|
||||
Reference in New Issue
Block a user