From 79de7910d425b089139ed68f145e2398198e9836 Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Fri, 21 Nov 2025 17:31:20 +0100 Subject: [PATCH] fix: fetch payment sessions from database for persistence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test provider was using an in-memory Map to store payment sessions, which caused "Payment session not found" errors in several scenarios: 1. Next.js hot reload clearing the memory 2. Different execution contexts (API routes vs Payload admin) 3. Server restarts losing all sessions This fix updates all three test provider endpoints (UI, process, status) to fetch payment data from the database when not found in memory: - Tries in-memory session first (fast path) - Falls back to database query by providerId - Creates and caches session from database payment - Handles both string and object collection configurations This makes the built-in test UI work reliably out of the box, without requiring users to implement custom session management. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- package.json | 2 +- src/providers/test.ts | 126 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index d840489..cc2879c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xtr-dev/payload-billing", - "version": "0.1.20", + "version": "0.1.21", "description": "PayloadCMS plugin for billing and payment provider integrations with tracking and local testing", "license": "MIT", "type": "module", diff --git a/src/providers/test.ts b/src/providers/test.ts index 1341beb..a2b412f 100644 --- a/src/providers/test.ts +++ b/src/providers/test.ts @@ -242,10 +242,15 @@ export const testProvider = (testConfig: TestProviderConfig) => { { path: '/payload-billing/test/payment/:id', method: 'get', - handler: (req) => { + handler: async (req) => { // Extract payment ID from URL path const urlParts = req.url?.split('/') || [] - const paymentId = urlParts[urlParts.length - 1] + let paymentId = urlParts[urlParts.length - 1] + + // Remove query parameters if present + if (paymentId?.includes('?')) { + paymentId = paymentId.split('?')[0] + } if (!paymentId) { return new Response(JSON.stringify({ error: 'Payment ID required' }), { @@ -263,7 +268,41 @@ export const testProvider = (testConfig: TestProviderConfig) => { }) } - const session = testPaymentSessions.get(paymentId) + // Try to get session from memory first (for backward compatibility) + let session = testPaymentSessions.get(paymentId) + + // If not in memory, fetch from database + if (!session && req.payload) { + try { + const paymentsConfig = pluginConfig.collections?.payments + const paymentSlug = typeof paymentsConfig === 'string' ? paymentsConfig : (paymentsConfig?.slug || 'payments') + const result = await req.payload.find({ + collection: paymentSlug, + where: { + providerId: { + equals: paymentId + } + }, + limit: 1 + }) + + if (result.docs && result.docs.length > 0) { + const payment = result.docs[0] as Payment + // Create session from database payment + session = { + id: paymentId, + payment: payment, + createdAt: new Date(payment.createdAt || Date.now()), + status: 'pending' as PaymentOutcome + } + // Store in memory for future requests + testPaymentSessions.set(paymentId, session) + } + } catch (error) { + console.error('Error fetching payment from database:', error) + } + } + if (!session) { return new Response(JSON.stringify({ error: 'Payment session not found' }), { status: 404, @@ -322,7 +361,41 @@ export const testProvider = (testConfig: TestProviderConfig) => { const { paymentId, scenarioId, method } = validation.data! - const session = testPaymentSessions.get(paymentId) + // Try to get session from memory first + let session = testPaymentSessions.get(paymentId) + + // If not in memory, fetch from database + if (!session && req.payload) { + try { + const paymentsConfig = pluginConfig.collections?.payments + const paymentSlug = typeof paymentsConfig === 'string' ? paymentsConfig : (paymentsConfig?.slug || 'payments') + const result = await req.payload.find({ + collection: paymentSlug, + where: { + providerId: { + equals: paymentId + } + }, + limit: 1 + }) + + if (result.docs && result.docs.length > 0) { + const payment = result.docs[0] as Payment + // Create session from database payment + session = { + id: paymentId, + payment: payment, + createdAt: new Date(payment.createdAt || Date.now()), + status: 'pending' as PaymentOutcome + } + // Store in memory for future requests + testPaymentSessions.set(paymentId, session) + } + } catch (error) { + console.error('Error fetching payment from database:', error) + } + } + if (!session) { return new Response(JSON.stringify({ error: 'Payment session not found' }), { status: 404, @@ -398,10 +471,15 @@ export const testProvider = (testConfig: TestProviderConfig) => { { path: '/payload-billing/test/status/:id', method: 'get', - handler: (req) => { + handler: async (req) => { // Extract payment ID from URL path const urlParts = req.url?.split('/') || [] - const paymentId = urlParts[urlParts.length - 1] + let paymentId = urlParts[urlParts.length - 1] + + // Remove query parameters if present + if (paymentId?.includes('?')) { + paymentId = paymentId.split('?')[0] + } if (!paymentId) { return new Response(JSON.stringify({ error: 'Payment ID required' }), { @@ -419,7 +497,41 @@ export const testProvider = (testConfig: TestProviderConfig) => { }) } - const session = testPaymentSessions.get(paymentId) + // Try to get session from memory first + let session = testPaymentSessions.get(paymentId) + + // If not in memory, fetch from database + if (!session && req.payload) { + try { + const paymentsConfig = pluginConfig.collections?.payments + const paymentSlug = typeof paymentsConfig === 'string' ? paymentsConfig : (paymentsConfig?.slug || 'payments') + const result = await req.payload.find({ + collection: paymentSlug, + where: { + providerId: { + equals: paymentId + } + }, + limit: 1 + }) + + if (result.docs && result.docs.length > 0) { + const payment = result.docs[0] as Payment + // Create session from database payment + session = { + id: paymentId, + payment: payment, + createdAt: new Date(payment.createdAt || Date.now()), + status: 'pending' as PaymentOutcome + } + // Store in memory for future requests + testPaymentSessions.set(paymentId, session) + } + } catch (error) { + console.error('Error fetching payment from database:', error) + } + } + if (!session) { return new Response(JSON.stringify({ error: 'Payment session not found' }), { status: 404,