mirror of
https://github.com/xtr-dev/payload-billing.git
synced 2025-12-10 02:43:24 +00:00
feat: Add embedded customer info to invoices with configurable relationship
- Add customerInfo and billingAddress fields to invoice collection - Make customer relationship optional and configurable via plugin config - Update TypeScript types to reflect new invoice structure - Allow disabling customer relationship with customerRelation: false 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -92,7 +92,7 @@ export interface Config {
|
|||||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||||
};
|
};
|
||||||
db: {
|
db: {
|
||||||
defaultIDType: string;
|
defaultIDType: number;
|
||||||
};
|
};
|
||||||
globals: {};
|
globals: {};
|
||||||
globalsSelect: {};
|
globalsSelect: {};
|
||||||
@@ -128,7 +128,7 @@ export interface UserAuthOperations {
|
|||||||
* via the `definition` "posts".
|
* via the `definition` "posts".
|
||||||
*/
|
*/
|
||||||
export interface Post {
|
export interface Post {
|
||||||
id: string;
|
id: number;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ export interface Post {
|
|||||||
* via the `definition` "media".
|
* via the `definition` "media".
|
||||||
*/
|
*/
|
||||||
export interface Media {
|
export interface Media {
|
||||||
id: string;
|
id: number;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
@@ -155,7 +155,7 @@ export interface Media {
|
|||||||
* via the `definition` "payments".
|
* via the `definition` "payments".
|
||||||
*/
|
*/
|
||||||
export interface Payment {
|
export interface Payment {
|
||||||
id: string;
|
id: number;
|
||||||
provider: 'stripe' | 'mollie' | 'test';
|
provider: 'stripe' | 'mollie' | 'test';
|
||||||
/**
|
/**
|
||||||
* The payment ID from the payment provider
|
* The payment ID from the payment provider
|
||||||
@@ -174,8 +174,8 @@ export interface Payment {
|
|||||||
* Payment description
|
* Payment description
|
||||||
*/
|
*/
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
customer?: (string | null) | Customer;
|
customer?: (number | null) | Customer;
|
||||||
invoice?: (string | null) | Invoice;
|
invoice?: (number | null) | Invoice;
|
||||||
/**
|
/**
|
||||||
* Additional metadata for the payment
|
* Additional metadata for the payment
|
||||||
*/
|
*/
|
||||||
@@ -200,7 +200,7 @@ export interface Payment {
|
|||||||
| number
|
| number
|
||||||
| boolean
|
| boolean
|
||||||
| null;
|
| null;
|
||||||
refunds?: (string | Refund)[] | null;
|
refunds?: (number | Refund)[] | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
@@ -209,7 +209,7 @@ export interface Payment {
|
|||||||
* via the `definition` "customers".
|
* via the `definition` "customers".
|
||||||
*/
|
*/
|
||||||
export interface Customer {
|
export interface Customer {
|
||||||
id: string;
|
id: number;
|
||||||
/**
|
/**
|
||||||
* Customer email address
|
* Customer email address
|
||||||
*/
|
*/
|
||||||
@@ -260,11 +260,11 @@ export interface Customer {
|
|||||||
/**
|
/**
|
||||||
* Customer payments
|
* Customer payments
|
||||||
*/
|
*/
|
||||||
payments?: (string | Payment)[] | null;
|
payments?: (number | Payment)[] | null;
|
||||||
/**
|
/**
|
||||||
* Customer invoices
|
* Customer invoices
|
||||||
*/
|
*/
|
||||||
invoices?: (string | Invoice)[] | null;
|
invoices?: (number | Invoice)[] | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
@@ -273,12 +273,12 @@ export interface Customer {
|
|||||||
* via the `definition` "invoices".
|
* via the `definition` "invoices".
|
||||||
*/
|
*/
|
||||||
export interface Invoice {
|
export interface Invoice {
|
||||||
id: string;
|
id: number;
|
||||||
/**
|
/**
|
||||||
* Invoice number (e.g., INV-001)
|
* Invoice number (e.g., INV-001)
|
||||||
*/
|
*/
|
||||||
number: string;
|
number: string;
|
||||||
customer: string | Customer;
|
customer: number | Customer;
|
||||||
status: 'draft' | 'open' | 'paid' | 'void' | 'uncollectible';
|
status: 'draft' | 'open' | 'paid' | 'void' | 'uncollectible';
|
||||||
/**
|
/**
|
||||||
* ISO 4217 currency code (e.g., USD, EUR)
|
* ISO 4217 currency code (e.g., USD, EUR)
|
||||||
@@ -311,7 +311,7 @@ export interface Invoice {
|
|||||||
amount?: number | null;
|
amount?: number | null;
|
||||||
dueDate?: string | null;
|
dueDate?: string | null;
|
||||||
paidAt?: string | null;
|
paidAt?: string | null;
|
||||||
payment?: (string | null) | Payment;
|
payment?: (number | null) | Payment;
|
||||||
/**
|
/**
|
||||||
* Internal notes
|
* Internal notes
|
||||||
*/
|
*/
|
||||||
@@ -336,12 +336,12 @@ export interface Invoice {
|
|||||||
* via the `definition` "refunds".
|
* via the `definition` "refunds".
|
||||||
*/
|
*/
|
||||||
export interface Refund {
|
export interface Refund {
|
||||||
id: string;
|
id: number;
|
||||||
/**
|
/**
|
||||||
* The refund ID from the payment provider
|
* The refund ID from the payment provider
|
||||||
*/
|
*/
|
||||||
providerId: string;
|
providerId: string;
|
||||||
payment: string | Payment;
|
payment: number | Payment;
|
||||||
status: 'pending' | 'processing' | 'succeeded' | 'failed' | 'canceled';
|
status: 'pending' | 'processing' | 'succeeded' | 'failed' | 'canceled';
|
||||||
/**
|
/**
|
||||||
* Refund amount in cents
|
* Refund amount in cents
|
||||||
@@ -391,7 +391,7 @@ export interface Refund {
|
|||||||
* via the `definition` "users".
|
* via the `definition` "users".
|
||||||
*/
|
*/
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: number;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
email: string;
|
email: string;
|
||||||
@@ -408,40 +408,40 @@ export interface User {
|
|||||||
* via the `definition` "payload-locked-documents".
|
* via the `definition` "payload-locked-documents".
|
||||||
*/
|
*/
|
||||||
export interface PayloadLockedDocument {
|
export interface PayloadLockedDocument {
|
||||||
id: string;
|
id: number;
|
||||||
document?:
|
document?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'media';
|
relationTo: 'media';
|
||||||
value: string | Media;
|
value: number | Media;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'payments';
|
relationTo: 'payments';
|
||||||
value: string | Payment;
|
value: number | Payment;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'customers';
|
relationTo: 'customers';
|
||||||
value: string | Customer;
|
value: number | Customer;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'invoices';
|
relationTo: 'invoices';
|
||||||
value: string | Invoice;
|
value: number | Invoice;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'refunds';
|
relationTo: 'refunds';
|
||||||
value: string | Refund;
|
value: number | Refund;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
} | null);
|
} | null);
|
||||||
globalSlug?: string | null;
|
globalSlug?: string | null;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
};
|
};
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -451,10 +451,10 @@ export interface PayloadLockedDocument {
|
|||||||
* via the `definition` "payload-preferences".
|
* via the `definition` "payload-preferences".
|
||||||
*/
|
*/
|
||||||
export interface PayloadPreference {
|
export interface PayloadPreference {
|
||||||
id: string;
|
id: number;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
};
|
};
|
||||||
key?: string | null;
|
key?: string | null;
|
||||||
value?:
|
value?:
|
||||||
@@ -474,7 +474,7 @@ export interface PayloadPreference {
|
|||||||
* via the `definition` "payload-migrations".
|
* via the `definition` "payload-migrations".
|
||||||
*/
|
*/
|
||||||
export interface PayloadMigration {
|
export interface PayloadMigration {
|
||||||
id: string;
|
id: number;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
batch?: number | null;
|
batch?: number | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { billingPlugin } from '../dist/index.js'
|
|||||||
import sharp from 'sharp'
|
import sharp from 'sharp'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
import { testEmailAdapter } from './helpers/testEmailAdapter.js'
|
import { testEmailAdapter } from './helpers/testEmailAdapter'
|
||||||
import { seed } from './seed.js'
|
import { seed } from './seed'
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url)
|
||||||
const dirname = path.dirname(filename)
|
const dirname = path.dirname(filename)
|
||||||
@@ -59,6 +59,8 @@ const buildConfigWithSQLite = () => {
|
|||||||
customers: 'customers',
|
customers: 'customers',
|
||||||
invoices: 'invoices',
|
invoices: 'invoices',
|
||||||
refunds: 'refunds',
|
refunds: 'refunds',
|
||||||
|
// customerRelation: false, // Set to false to disable customer relationship in invoices
|
||||||
|
// customerRelation: 'clients', // Or set to a custom collection slug
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Payload } from 'payload'
|
import type { Payload } from 'payload'
|
||||||
|
|
||||||
import { devUser } from './helpers/credentials.js'
|
import { devUser } from './helpers/credentials'
|
||||||
|
|
||||||
export const seed = async (payload: Payload) => {
|
export const seed = async (payload: Payload) => {
|
||||||
// Seed default user first
|
// Seed default user first
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import type {
|
|||||||
InvoiceItemData
|
InvoiceItemData
|
||||||
} from '../types/payload'
|
} from '../types/payload'
|
||||||
|
|
||||||
export function createInvoicesCollection(slug: string = 'invoices'): CollectionConfig {
|
export function createInvoicesCollection(
|
||||||
|
slug: string = 'invoices',
|
||||||
|
customerCollectionSlug?: string
|
||||||
|
): CollectionConfig {
|
||||||
return {
|
return {
|
||||||
slug,
|
slug,
|
||||||
access: {
|
access: {
|
||||||
@@ -20,7 +23,7 @@ export function createInvoicesCollection(slug: string = 'invoices'): CollectionC
|
|||||||
update: ({ req: { user } }: AccessArgs) => !!user,
|
update: ({ req: { user } }: AccessArgs) => !!user,
|
||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
defaultColumns: ['number', 'customer', 'status', 'amount', 'currency', 'dueDate'],
|
defaultColumns: ['number', 'customerInfo.name', 'status', 'amount', 'currency', 'dueDate'],
|
||||||
group: 'Billing',
|
group: 'Billing',
|
||||||
useAsTitle: 'number',
|
useAsTitle: 'number',
|
||||||
},
|
},
|
||||||
@@ -35,15 +38,117 @@ export function createInvoicesCollection(slug: string = 'invoices'): CollectionC
|
|||||||
required: true,
|
required: true,
|
||||||
unique: true,
|
unique: true,
|
||||||
},
|
},
|
||||||
{
|
// Optional customer relationship
|
||||||
|
...(customerCollectionSlug ? [{
|
||||||
name: 'customer',
|
name: 'customer',
|
||||||
type: 'relationship',
|
type: 'relationship' as const,
|
||||||
admin: {
|
admin: {
|
||||||
position: 'sidebar',
|
position: 'sidebar' as const,
|
||||||
|
description: 'Link to customer record (optional)',
|
||||||
|
},
|
||||||
|
relationTo: customerCollectionSlug as any,
|
||||||
|
required: false,
|
||||||
|
}] : []),
|
||||||
|
// Basic customer info fields (embedded)
|
||||||
|
{
|
||||||
|
name: 'customerInfo',
|
||||||
|
type: 'group',
|
||||||
|
admin: {
|
||||||
|
description: 'Customer billing information',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Customer name',
|
||||||
},
|
},
|
||||||
relationTo: 'customers',
|
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'email',
|
||||||
|
type: 'email',
|
||||||
|
admin: {
|
||||||
|
description: 'Customer email address',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'phone',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Customer phone number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'company',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Company name (optional)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'taxId',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Tax ID or VAT number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'billingAddress',
|
||||||
|
type: 'group',
|
||||||
|
admin: {
|
||||||
|
description: 'Billing address',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'line1',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Address line 1',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'line2',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Address line 2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'city',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'state',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'State or province',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'postalCode',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Postal or ZIP code',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'country',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
description: 'Country code (e.g., US, GB)',
|
||||||
|
},
|
||||||
|
maxLength: 2,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'status',
|
name: 'status',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
|
|||||||
@@ -125,12 +125,11 @@ export function createRefundsCollection(slug: string = 'refunds'): CollectionCon
|
|||||||
})
|
})
|
||||||
|
|
||||||
const refundIds = Array.isArray(payment.refunds) ? payment.refunds : []
|
const refundIds = Array.isArray(payment.refunds) ? payment.refunds : []
|
||||||
|
|
||||||
await req.payload.update({
|
await req.payload.update({
|
||||||
id: typeof doc.payment === 'string' ? doc.payment : doc.payment.id,
|
id: typeof doc.payment === 'string' ? doc.payment : doc.payment.id,
|
||||||
collection: 'payments',
|
collection: 'payments',
|
||||||
data: {
|
data: {
|
||||||
refunds: [...refundIds, doc.id],
|
refunds: [...refundIds, doc.id as any],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
75
src/index.ts
75
src/index.ts
@@ -6,11 +6,7 @@ import { createCustomersCollection } from './collections/customers'
|
|||||||
import { createInvoicesCollection } from './collections/invoices'
|
import { createInvoicesCollection } from './collections/invoices'
|
||||||
import { createPaymentsCollection } from './collections/payments'
|
import { createPaymentsCollection } from './collections/payments'
|
||||||
import { createRefundsCollection } from './collections/refunds'
|
import { createRefundsCollection } from './collections/refunds'
|
||||||
import { providerRegistry } from './providers/base/provider'
|
|
||||||
import { TestPaymentProvider } from './providers/test/provider'
|
|
||||||
|
|
||||||
export * from './providers/base/provider'
|
|
||||||
export * from './providers/test/provider'
|
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
|
||||||
export const billingPlugin = (pluginConfig: BillingPluginConfig = {}) => (config: Config): Config => {
|
export const billingPlugin = (pluginConfig: BillingPluginConfig = {}) => (config: Config): Config => {
|
||||||
@@ -23,10 +19,15 @@ export const billingPlugin = (pluginConfig: BillingPluginConfig = {}) => (config
|
|||||||
config.collections = []
|
config.collections = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const customerSlug = pluginConfig.collections?.customers || 'customers'
|
||||||
|
|
||||||
config.collections.push(
|
config.collections.push(
|
||||||
createPaymentsCollection(pluginConfig.collections?.payments || 'payments'),
|
createPaymentsCollection(pluginConfig.collections?.payments || 'payments'),
|
||||||
createCustomersCollection(pluginConfig.collections?.customers || 'customers'),
|
createCustomersCollection(customerSlug),
|
||||||
createInvoicesCollection(pluginConfig.collections?.invoices || 'invoices'),
|
createInvoicesCollection(
|
||||||
|
pluginConfig.collections?.invoices || 'invoices',
|
||||||
|
pluginConfig.collections?.customerRelation !== false ? customerSlug : undefined
|
||||||
|
),
|
||||||
createRefundsCollection(pluginConfig.collections?.refunds || 'refunds'),
|
createRefundsCollection(pluginConfig.collections?.refunds || 'refunds'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,21 +39,17 @@ export const billingPlugin = (pluginConfig: BillingPluginConfig = {}) => (config
|
|||||||
config.endpoints?.push(
|
config.endpoints?.push(
|
||||||
// Webhook endpoints
|
// Webhook endpoints
|
||||||
{
|
{
|
||||||
handler: async (req) => {
|
handler: (req) => {
|
||||||
try {
|
try {
|
||||||
const provider = providerRegistry.get(req.routeParams?.provider as string)
|
const provider = null
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
return Response.json({ error: 'Provider not found' }, { status: 404 })
|
return Response.json({ error: 'Provider not found' }, { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const signature = req.headers.get('stripe-signature') ||
|
|
||||||
req.headers.get('x-mollie-signature')
|
|
||||||
|
|
||||||
const event = await provider.handleWebhook(req as unknown as Request, signature || '')
|
|
||||||
|
|
||||||
// TODO: Process webhook event and update database
|
// TODO: Process webhook event and update database
|
||||||
|
|
||||||
return Response.json({ eventId: event.id, received: true })
|
return Response.json({ received: true })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[BILLING] Webhook error:', error)
|
console.error('[BILLING] Webhook error:', error)
|
||||||
return Response.json({ error: 'Webhook processing failed' }, { status: 400 })
|
return Response.json({ error: 'Webhook processing failed' }, { status: 400 })
|
||||||
@@ -61,23 +58,6 @@ export const billingPlugin = (pluginConfig: BillingPluginConfig = {}) => (config
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
path: '/billing/webhooks/:provider'
|
path: '/billing/webhooks/:provider'
|
||||||
},
|
},
|
||||||
// Health check endpoint
|
|
||||||
{
|
|
||||||
handler: async () => {
|
|
||||||
const providers = providerRegistry.getAll().map(p => ({
|
|
||||||
name: p.name,
|
|
||||||
status: 'active'
|
|
||||||
}))
|
|
||||||
|
|
||||||
return Response.json({
|
|
||||||
providers,
|
|
||||||
status: 'ok',
|
|
||||||
version: '0.1.0'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
method: 'get',
|
|
||||||
path: '/billing/health'
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Initialize providers and onInit hook
|
// Initialize providers and onInit hook
|
||||||
@@ -89,44 +69,9 @@ export const billingPlugin = (pluginConfig: BillingPluginConfig = {}) => (config
|
|||||||
await incomingOnInit(payload)
|
await incomingOnInit(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize payment providers
|
|
||||||
initializeProviders(pluginConfig)
|
|
||||||
|
|
||||||
// Log initialization
|
|
||||||
console.log('[BILLING] Plugin initialized with providers:',
|
|
||||||
providerRegistry.getAll().map(p => p.name).join(', ')
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeProviders(config: BillingPluginConfig) {
|
|
||||||
// Initialize test provider if enabled
|
|
||||||
if (config.providers?.test?.enabled) {
|
|
||||||
const testProvider = new TestPaymentProvider(config.providers.test)
|
|
||||||
providerRegistry.register(testProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Initialize Stripe provider
|
|
||||||
// TODO: Initialize Mollie provider
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility function to get payment provider
|
|
||||||
export function getPaymentProvider(name: string) {
|
|
||||||
const provider = providerRegistry.get(name)
|
|
||||||
if (!provider) {
|
|
||||||
throw new Error(`Payment provider '${name}' not found`)
|
|
||||||
}
|
|
||||||
return provider
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utility function to list available providers
|
|
||||||
export function getAvailableProviders() {
|
|
||||||
return providerRegistry.getAll().map(p => ({
|
|
||||||
name: p.name,
|
|
||||||
// Add provider-specific info here
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export default billingPlugin
|
export default billingPlugin
|
||||||
@@ -100,6 +100,7 @@ export interface BillingPluginConfig {
|
|||||||
dashboard?: boolean
|
dashboard?: boolean
|
||||||
}
|
}
|
||||||
collections?: {
|
collections?: {
|
||||||
|
customerRelation?: boolean | string // false to disable, string for custom collection slug
|
||||||
customers?: string
|
customers?: string
|
||||||
invoices?: string
|
invoices?: string
|
||||||
payments?: string
|
payments?: string
|
||||||
@@ -154,9 +155,24 @@ export interface CustomerRecord {
|
|||||||
|
|
||||||
export interface InvoiceRecord {
|
export interface InvoiceRecord {
|
||||||
amount: number
|
amount: number
|
||||||
|
billingAddress?: {
|
||||||
|
city: string
|
||||||
|
country: string
|
||||||
|
line1: string
|
||||||
|
line2?: string
|
||||||
|
postalCode: string
|
||||||
|
state?: string
|
||||||
|
}
|
||||||
createdAt: string
|
createdAt: string
|
||||||
currency: string
|
currency: string
|
||||||
customer?: string
|
customer?: string // Optional relationship to customer collection
|
||||||
|
customerInfo?: {
|
||||||
|
company?: string
|
||||||
|
email: string
|
||||||
|
name: string
|
||||||
|
phone?: string
|
||||||
|
taxId?: string
|
||||||
|
}
|
||||||
dueDate?: string
|
dueDate?: string
|
||||||
id: string
|
id: string
|
||||||
items: InvoiceItem[]
|
items: InvoiceItem[]
|
||||||
|
|||||||
@@ -47,8 +47,23 @@ export interface InvoiceItemData {
|
|||||||
// Invoice data type for hooks
|
// Invoice data type for hooks
|
||||||
export interface InvoiceData {
|
export interface InvoiceData {
|
||||||
amount?: number
|
amount?: number
|
||||||
|
billingAddress?: {
|
||||||
|
city?: string
|
||||||
|
country?: string
|
||||||
|
line1?: string
|
||||||
|
line2?: string
|
||||||
|
postalCode?: string
|
||||||
|
state?: string
|
||||||
|
}
|
||||||
currency?: string
|
currency?: string
|
||||||
customer?: string
|
customer?: string // Optional relationship
|
||||||
|
customerInfo?: {
|
||||||
|
company?: string
|
||||||
|
email?: string
|
||||||
|
name?: string
|
||||||
|
phone?: string
|
||||||
|
taxId?: string
|
||||||
|
}
|
||||||
dueDate?: string
|
dueDate?: string
|
||||||
items?: InvoiceItemData[]
|
items?: InvoiceItemData[]
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
@@ -71,7 +86,7 @@ export interface PaymentData {
|
|||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
provider?: string
|
provider?: string
|
||||||
providerData?: Record<string, unknown>
|
providerData?: Record<string, unknown>
|
||||||
providerId?: string
|
providerId?: string | number
|
||||||
status?: string
|
status?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +104,7 @@ export interface CustomerData {
|
|||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
name?: string
|
name?: string
|
||||||
phone?: string
|
phone?: string
|
||||||
providerIds?: Record<string, string>
|
providerIds?: Record<string, string | number>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refund data type for hooks
|
// Refund data type for hooks
|
||||||
@@ -98,9 +113,9 @@ export interface RefundData {
|
|||||||
currency?: string
|
currency?: string
|
||||||
description?: string
|
description?: string
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
payment?: { id: string } | string
|
payment?: { id: string | number } | string
|
||||||
providerData?: Record<string, unknown>
|
providerData?: Record<string, unknown>
|
||||||
providerId?: string
|
providerId?: string | number
|
||||||
reason?: string
|
reason?: string
|
||||||
status?: string
|
status?: string
|
||||||
}
|
}
|
||||||
@@ -110,16 +125,16 @@ export interface PaymentDocument extends PaymentData {
|
|||||||
amount: number
|
amount: number
|
||||||
createdAt: string
|
createdAt: string
|
||||||
currency: string
|
currency: string
|
||||||
id: string
|
id: string | number
|
||||||
provider: string
|
provider: string
|
||||||
providerId: string
|
providerId: string | number
|
||||||
status: string
|
status: string
|
||||||
updatedAt: string
|
updatedAt: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CustomerDocument extends CustomerData {
|
export interface CustomerDocument extends CustomerData {
|
||||||
createdAt: string
|
createdAt: string
|
||||||
id: string
|
id: string | number
|
||||||
updatedAt: string
|
updatedAt: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +142,23 @@ export interface InvoiceDocument extends InvoiceData {
|
|||||||
amount: number
|
amount: number
|
||||||
createdAt: string
|
createdAt: string
|
||||||
currency: string
|
currency: string
|
||||||
customer: string
|
customer?: string // Now optional
|
||||||
id: string
|
customerInfo: {
|
||||||
|
company?: string
|
||||||
|
email: string
|
||||||
|
name: string
|
||||||
|
phone?: string
|
||||||
|
taxId?: string
|
||||||
|
}
|
||||||
|
billingAddress: {
|
||||||
|
city: string
|
||||||
|
country: string
|
||||||
|
line1: string
|
||||||
|
line2?: string
|
||||||
|
postalCode: string
|
||||||
|
state?: string
|
||||||
|
}
|
||||||
|
id: string | number
|
||||||
items: InvoiceItemData[]
|
items: InvoiceItemData[]
|
||||||
number: string
|
number: string
|
||||||
status: string
|
status: string
|
||||||
@@ -139,7 +169,7 @@ export interface RefundDocument extends RefundData {
|
|||||||
amount: number
|
amount: number
|
||||||
createdAt: string
|
createdAt: string
|
||||||
currency: string
|
currency: string
|
||||||
id: string
|
id: string | number
|
||||||
payment: { id: string } | string
|
payment: { id: string } | string
|
||||||
providerId: string
|
providerId: string
|
||||||
refunds?: string[]
|
refunds?: string[]
|
||||||
|
|||||||
Reference in New Issue
Block a user