fix: resolve plugin initialization failure in Next.js API routes

Use Symbol.for() instead of Symbol() for plugin singleton storage to ensure
plugin state persists across different module loading contexts (admin panel,
API routes, server components).

This fixes the "Billing plugin not initialized" error that occurred when
calling payload.create() from Next.js API routes, server components, or
server actions.

Changes:
- Plugin singleton now uses Symbol.for('@xtr-dev/payload-billing')
- Provider singletons (stripe, mollie, test) use global symbols
- Enhanced error message with troubleshooting guidance

Fixes #1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-21 15:35:12 +01:00
parent 25b340d818
commit 7c0b42e35d
5 changed files with 6 additions and 5 deletions

View File

@@ -7,7 +7,8 @@ export const initProviderPayment = async (payload: Payload, payment: Partial<Pay
if (!billing) {
throw new Error(
'Billing plugin not initialized. Make sure the billingPlugin is properly configured in your Payload config and that Payload has finished initializing.'
'Billing plugin not initialized. Make sure the billingPlugin is properly configured in your Payload config and that Payload has finished initializing. ' +
'If you are calling this from a Next.js API route or Server Component, ensure you are using getPayload() with the same config instance used in your Payload configuration.'
)
}

View File

@@ -4,7 +4,7 @@ import type { Config, Payload } from 'payload'
import { createSingleton } from './singleton'
import type { PaymentProvider } from '../providers/index'
const singleton = createSingleton(Symbol('billingPlugin'))
const singleton = createSingleton(Symbol.for('@xtr-dev/payload-billing'))
type BillingPlugin = {
config: BillingPluginConfig

View File

@@ -14,7 +14,7 @@ import {
import { formatAmountForProvider, isValidAmount, isValidCurrencyCode } from './currency'
import { createContextLogger } from '../utils/logger'
const symbol = Symbol('mollie')
const symbol = Symbol.for('@xtr-dev/payload-billing/mollie')
export type MollieProviderConfig = Parameters<typeof createMollieClient>[0]
/**

View File

@@ -14,7 +14,7 @@ import {
import { isValidAmount, isValidCurrencyCode } from './currency'
import { createContextLogger } from '../utils/logger'
const symbol = Symbol('stripe')
const symbol = Symbol.for('@xtr-dev/payload-billing/stripe')
export interface StripeProviderConfig {
secretKey: string

View File

@@ -6,7 +6,7 @@ import { handleWebhookError, logWebhookEvent } from './utils'
import { isValidAmount, isValidCurrencyCode } from './currency'
import { createContextLogger } from '../utils/logger'
const TestModeWarningSymbol = Symbol('TestModeWarning')
const TestModeWarningSymbol = Symbol.for('@xtr-dev/payload-billing/test-mode-warning')
const hasGivenTestModeWarning = () => TestModeWarningSymbol in globalThis
const setTestModeWarning = () => ((<any>globalThis)[TestModeWarningSymbol] = true)