mirror of
https://github.com/xtr-dev/payload-billing.git
synced 2025-12-10 02:43:24 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 79de7910d4 | |||
| bb5ba83bc3 | |||
| 79166f7edf | |||
| 6de405d07f | |||
| 7c0b42e35d | |||
| 25b340d818 | |||
| 46bec6bd2e |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xtr-dev/payload-billing",
|
||||
"version": "0.1.16",
|
||||
"version": "0.1.21",
|
||||
"description": "PayloadCMS plugin for billing and payment provider integrations with tracking and local testing",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
||||
@@ -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.'
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,18 @@ export function createPaymentsCollection(pluginConfig: BillingPluginConfig): Col
|
||||
useAsTitle: 'id',
|
||||
},
|
||||
fields,
|
||||
defaultPopulate: {
|
||||
id: true,
|
||||
provider: true,
|
||||
status: true,
|
||||
amount: true,
|
||||
currency: true,
|
||||
description: true,
|
||||
checkoutUrl: true,
|
||||
providerId: true,
|
||||
metadata: true,
|
||||
providerData: true,
|
||||
},
|
||||
hooks: {
|
||||
afterChange: [
|
||||
async ({ doc, operation, req, previousDoc }) => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
@@ -492,7 +604,10 @@ export const testProvider = (testConfig: TestProviderConfig) => {
|
||||
|
||||
// Set provider ID and data
|
||||
payment.providerId = testPaymentId
|
||||
const paymentUrl = `${baseUrl}/api/payload-billing/test/payment/${testPaymentId}`
|
||||
// Use custom UI route if specified, otherwise use built-in UI endpoint
|
||||
const paymentUrl = testConfig.customUiRoute
|
||||
? `${baseUrl}${testConfig.customUiRoute}/${testPaymentId}`
|
||||
: `${baseUrl}/api/payload-billing/test/payment/${testPaymentId}`
|
||||
const providerData: ProviderData = {
|
||||
raw: {
|
||||
id: testPaymentId,
|
||||
|
||||
Reference in New Issue
Block a user