import type { CollectionConfig } from 'payload' import type { AccessArgs, CollectionAfterChangeHook, CollectionBeforeChangeHook, PaymentData, PaymentDocument } from '../types/payload' export function createPaymentsCollection(slug: string = 'payments'): CollectionConfig { return { slug, access: { create: ({ req: { user } }: AccessArgs) => !!user, delete: ({ req: { user } }: AccessArgs) => !!user, read: ({ req: { user } }: AccessArgs) => !!user, update: ({ req: { user } }: AccessArgs) => !!user, }, admin: { defaultColumns: ['id', 'provider', 'status', 'amount', 'currency', 'createdAt'], group: 'Billing', useAsTitle: 'id', }, fields: [ { name: 'provider', type: 'select', admin: { position: 'sidebar', }, options: [ { label: 'Stripe', value: 'stripe' }, { label: 'Mollie', value: 'mollie' }, { label: 'Test', value: 'test' }, ], required: true, }, { name: 'providerId', type: 'text', admin: { description: 'The payment ID from the payment provider', }, label: 'Provider Payment ID', required: true, unique: true, }, { name: 'status', type: 'select', admin: { position: 'sidebar', }, options: [ { label: 'Pending', value: 'pending' }, { label: 'Processing', value: 'processing' }, { label: 'Succeeded', value: 'succeeded' }, { label: 'Failed', value: 'failed' }, { label: 'Canceled', value: 'canceled' }, { label: 'Refunded', value: 'refunded' }, { label: 'Partially Refunded', value: 'partially_refunded' }, ], required: true, }, { name: 'amount', type: 'number', admin: { description: 'Amount in cents (e.g., 2000 = $20.00)', }, min: 1, required: true, }, { name: 'currency', type: 'text', admin: { description: 'ISO 4217 currency code (e.g., USD, EUR)', }, maxLength: 3, required: true, }, { name: 'description', type: 'text', admin: { description: 'Payment description', }, }, { name: 'customer', type: 'relationship', admin: { position: 'sidebar', }, relationTo: 'customers', }, { name: 'invoice', type: 'relationship', admin: { position: 'sidebar', }, relationTo: 'invoices', }, { name: 'metadata', type: 'json', admin: { description: 'Additional metadata for the payment', }, }, { name: 'providerData', type: 'json', admin: { description: 'Raw data from the payment provider', readOnly: true, }, }, { name: 'refunds', type: 'relationship', admin: { position: 'sidebar', readOnly: true, }, hasMany: true, relationTo: 'refunds', }, ], hooks: { afterChange: [ ({ doc, operation, req }: CollectionAfterChangeHook) => { if (operation === 'create') { req.payload.logger.info(`Payment created: ${doc.id} (${doc.provider})`) } }, ], beforeChange: [ ({ data, operation }: CollectionBeforeChangeHook) => { if (operation === 'create') { // Validate amount format if (data.amount && !Number.isInteger(data.amount)) { throw new Error('Amount must be an integer (in cents)') } // Validate currency format if (data.currency) { data.currency = data.currency.toUpperCase() if (!/^[A-Z]{3}$/.test(data.currency)) { throw new Error('Currency must be a 3-letter ISO code') } } } }, ], }, timestamps: true, } }