Add per-quantity TPago payment links for multi-ticket checkout.
Each ticket quantity (1-5) can have its own TPago link so checkout, emails, and admin config use the correct fixed-amount URL.
This commit is contained in:
@@ -259,6 +259,10 @@ async function migrate() {
|
||||
id TEXT PRIMARY KEY,
|
||||
tpago_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
tpago_link TEXT,
|
||||
tpago_link_2 TEXT,
|
||||
tpago_link_3 TEXT,
|
||||
tpago_link_4 TEXT,
|
||||
tpago_link_5 TEXT,
|
||||
tpago_instructions TEXT,
|
||||
tpago_instructions_es TEXT,
|
||||
bank_transfer_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -283,6 +287,13 @@ async function migrate() {
|
||||
await (db as any).run(sql`ALTER TABLE payment_options ADD COLUMN allow_duplicate_bookings INTEGER NOT NULL DEFAULT 0`);
|
||||
} catch (e) { /* column may already exist */ }
|
||||
|
||||
// Add per-quantity TPago link columns to payment_options if they don't exist
|
||||
for (const col of ['tpago_link_2', 'tpago_link_3', 'tpago_link_4', 'tpago_link_5']) {
|
||||
try {
|
||||
await (db as any).run(sql.raw(`ALTER TABLE payment_options ADD COLUMN ${col} TEXT`));
|
||||
} catch (e) { /* column may already exist */ }
|
||||
}
|
||||
|
||||
// Event payment overrides table
|
||||
await (db as any).run(sql`
|
||||
CREATE TABLE IF NOT EXISTS event_payment_overrides (
|
||||
@@ -290,6 +301,10 @@ async function migrate() {
|
||||
event_id TEXT NOT NULL REFERENCES events(id),
|
||||
tpago_enabled INTEGER,
|
||||
tpago_link TEXT,
|
||||
tpago_link_2 TEXT,
|
||||
tpago_link_3 TEXT,
|
||||
tpago_link_4 TEXT,
|
||||
tpago_link_5 TEXT,
|
||||
tpago_instructions TEXT,
|
||||
tpago_instructions_es TEXT,
|
||||
bank_transfer_enabled INTEGER,
|
||||
@@ -309,6 +324,13 @@ async function migrate() {
|
||||
)
|
||||
`);
|
||||
|
||||
// Add per-quantity TPago link columns to event_payment_overrides if they don't exist
|
||||
for (const col of ['tpago_link_2', 'tpago_link_3', 'tpago_link_4', 'tpago_link_5']) {
|
||||
try {
|
||||
await (db as any).run(sql.raw(`ALTER TABLE event_payment_overrides ADD COLUMN ${col} TEXT`));
|
||||
} catch (e) { /* column may already exist */ }
|
||||
}
|
||||
|
||||
await (db as any).run(sql`
|
||||
CREATE TABLE IF NOT EXISTS contacts (
|
||||
id TEXT PRIMARY KEY,
|
||||
@@ -702,6 +724,10 @@ async function migrate() {
|
||||
id UUID PRIMARY KEY,
|
||||
tpago_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
tpago_link VARCHAR(500),
|
||||
tpago_link_2 VARCHAR(500),
|
||||
tpago_link_3 VARCHAR(500),
|
||||
tpago_link_4 VARCHAR(500),
|
||||
tpago_link_5 VARCHAR(500),
|
||||
tpago_instructions TEXT,
|
||||
tpago_instructions_es TEXT,
|
||||
bank_transfer_enabled INTEGER NOT NULL DEFAULT 0,
|
||||
@@ -726,12 +752,23 @@ async function migrate() {
|
||||
await (db as any).execute(sql`ALTER TABLE payment_options ADD COLUMN allow_duplicate_bookings INTEGER NOT NULL DEFAULT 0`);
|
||||
} catch (e) { /* column may already exist */ }
|
||||
|
||||
// Add per-quantity TPago link columns to payment_options if they don't exist
|
||||
for (const col of ['tpago_link_2', 'tpago_link_3', 'tpago_link_4', 'tpago_link_5']) {
|
||||
try {
|
||||
await (db as any).execute(sql.raw(`ALTER TABLE payment_options ADD COLUMN ${col} VARCHAR(500)`));
|
||||
} catch (e) { /* column may already exist */ }
|
||||
}
|
||||
|
||||
await (db as any).execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS event_payment_overrides (
|
||||
id UUID PRIMARY KEY,
|
||||
event_id UUID NOT NULL REFERENCES events(id),
|
||||
tpago_enabled INTEGER,
|
||||
tpago_link VARCHAR(500),
|
||||
tpago_link_2 VARCHAR(500),
|
||||
tpago_link_3 VARCHAR(500),
|
||||
tpago_link_4 VARCHAR(500),
|
||||
tpago_link_5 VARCHAR(500),
|
||||
tpago_instructions TEXT,
|
||||
tpago_instructions_es TEXT,
|
||||
bank_transfer_enabled INTEGER,
|
||||
@@ -751,6 +788,13 @@ async function migrate() {
|
||||
)
|
||||
`);
|
||||
|
||||
// Add per-quantity TPago link columns to event_payment_overrides if they don't exist
|
||||
for (const col of ['tpago_link_2', 'tpago_link_3', 'tpago_link_4', 'tpago_link_5']) {
|
||||
try {
|
||||
await (db as any).execute(sql.raw(`ALTER TABLE event_payment_overrides ADD COLUMN ${col} VARCHAR(500)`));
|
||||
} catch (e) { /* column may already exist */ }
|
||||
}
|
||||
|
||||
await (db as any).execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS contacts (
|
||||
id UUID PRIMARY KEY,
|
||||
|
||||
@@ -135,6 +135,10 @@ export const sqlitePaymentOptions = sqliteTable('payment_options', {
|
||||
// TPago configuration
|
||||
tpagoEnabled: integer('tpago_enabled', { mode: 'boolean' }).notNull().default(false),
|
||||
tpagoLink: text('tpago_link'),
|
||||
tpagoLink2: text('tpago_link_2'),
|
||||
tpagoLink3: text('tpago_link_3'),
|
||||
tpagoLink4: text('tpago_link_4'),
|
||||
tpagoLink5: text('tpago_link_5'),
|
||||
tpagoInstructions: text('tpago_instructions'),
|
||||
tpagoInstructionsEs: text('tpago_instructions_es'),
|
||||
// Bank Transfer configuration
|
||||
@@ -166,6 +170,10 @@ export const sqliteEventPaymentOverrides = sqliteTable('event_payment_overrides'
|
||||
// Override flags (null means use global)
|
||||
tpagoEnabled: integer('tpago_enabled', { mode: 'boolean' }),
|
||||
tpagoLink: text('tpago_link'),
|
||||
tpagoLink2: text('tpago_link_2'),
|
||||
tpagoLink3: text('tpago_link_3'),
|
||||
tpagoLink4: text('tpago_link_4'),
|
||||
tpagoLink5: text('tpago_link_5'),
|
||||
tpagoInstructions: text('tpago_instructions'),
|
||||
tpagoInstructionsEs: text('tpago_instructions_es'),
|
||||
bankTransferEnabled: integer('bank_transfer_enabled', { mode: 'boolean' }),
|
||||
@@ -467,6 +475,10 @@ export const pgPaymentOptions = pgTable('payment_options', {
|
||||
id: uuid('id').primaryKey(),
|
||||
tpagoEnabled: pgInteger('tpago_enabled').notNull().default(0),
|
||||
tpagoLink: varchar('tpago_link', { length: 500 }),
|
||||
tpagoLink2: varchar('tpago_link_2', { length: 500 }),
|
||||
tpagoLink3: varchar('tpago_link_3', { length: 500 }),
|
||||
tpagoLink4: varchar('tpago_link_4', { length: 500 }),
|
||||
tpagoLink5: varchar('tpago_link_5', { length: 500 }),
|
||||
tpagoInstructions: pgText('tpago_instructions'),
|
||||
tpagoInstructionsEs: pgText('tpago_instructions_es'),
|
||||
bankTransferEnabled: pgInteger('bank_transfer_enabled').notNull().default(0),
|
||||
@@ -492,6 +504,10 @@ export const pgEventPaymentOverrides = pgTable('event_payment_overrides', {
|
||||
eventId: uuid('event_id').notNull().references(() => pgEvents.id),
|
||||
tpagoEnabled: pgInteger('tpago_enabled'),
|
||||
tpagoLink: varchar('tpago_link', { length: 500 }),
|
||||
tpagoLink2: varchar('tpago_link_2', { length: 500 }),
|
||||
tpagoLink3: varchar('tpago_link_3', { length: 500 }),
|
||||
tpagoLink4: varchar('tpago_link_4', { length: 500 }),
|
||||
tpagoLink5: varchar('tpago_link_5', { length: 500 }),
|
||||
tpagoInstructions: pgText('tpago_instructions'),
|
||||
tpagoInstructionsEs: pgText('tpago_instructions_es'),
|
||||
bankTransferEnabled: pgInteger('bank_transfer_enabled'),
|
||||
|
||||
@@ -748,6 +748,10 @@ export const emailService = {
|
||||
const defaults = {
|
||||
tpagoEnabled: false,
|
||||
tpagoLink: null,
|
||||
tpagoLink2: null,
|
||||
tpagoLink3: null,
|
||||
tpagoLink4: null,
|
||||
tpagoLink5: null,
|
||||
tpagoInstructions: null,
|
||||
tpagoInstructionsEs: null,
|
||||
bankTransferEnabled: false,
|
||||
@@ -766,6 +770,10 @@ export const emailService = {
|
||||
return {
|
||||
tpagoEnabled: overrides?.tpagoEnabled ?? global.tpagoEnabled,
|
||||
tpagoLink: overrides?.tpagoLink ?? global.tpagoLink,
|
||||
tpagoLink2: overrides?.tpagoLink2 ?? global.tpagoLink2,
|
||||
tpagoLink3: overrides?.tpagoLink3 ?? global.tpagoLink3,
|
||||
tpagoLink4: overrides?.tpagoLink4 ?? global.tpagoLink4,
|
||||
tpagoLink5: overrides?.tpagoLink5 ?? global.tpagoLink5,
|
||||
tpagoInstructions: overrides?.tpagoInstructions ?? global.tpagoInstructions,
|
||||
tpagoInstructionsEs: overrides?.tpagoInstructionsEs ?? global.tpagoInstructionsEs,
|
||||
bankTransferEnabled: overrides?.bankTransferEnabled ?? global.bankTransferEnabled,
|
||||
@@ -885,7 +893,9 @@ export const emailService = {
|
||||
|
||||
// Add payment-method specific variables
|
||||
if (payment.provider === 'tpago') {
|
||||
variables.tpagoLink = paymentConfig.tpagoLink || '';
|
||||
// Select the TPago link matching the number of tickets (1-5), falling back to the base link
|
||||
const tpagoLinkKey = ticketCount <= 1 ? 'tpagoLink' : `tpagoLink${Math.min(ticketCount, 5)}`;
|
||||
variables.tpagoLink = paymentConfig[tpagoLinkKey] || paymentConfig.tpagoLink || '';
|
||||
} else {
|
||||
// Bank transfer
|
||||
variables.bankName = paymentConfig.bankName || '';
|
||||
|
||||
@@ -18,6 +18,10 @@ const booleanOrNumber = z.union([z.boolean(), z.number()]).transform((val) => {
|
||||
const updatePaymentOptionsSchema = z.object({
|
||||
tpagoEnabled: booleanOrNumber.optional(),
|
||||
tpagoLink: z.string().optional().nullable(),
|
||||
tpagoLink2: z.string().optional().nullable(),
|
||||
tpagoLink3: z.string().optional().nullable(),
|
||||
tpagoLink4: z.string().optional().nullable(),
|
||||
tpagoLink5: z.string().optional().nullable(),
|
||||
tpagoInstructions: z.string().optional().nullable(),
|
||||
tpagoInstructionsEs: z.string().optional().nullable(),
|
||||
bankTransferEnabled: booleanOrNumber.optional(),
|
||||
@@ -40,6 +44,10 @@ const updatePaymentOptionsSchema = z.object({
|
||||
const updateEventOverridesSchema = z.object({
|
||||
tpagoEnabled: booleanOrNumber.optional().nullable(),
|
||||
tpagoLink: z.string().optional().nullable(),
|
||||
tpagoLink2: z.string().optional().nullable(),
|
||||
tpagoLink3: z.string().optional().nullable(),
|
||||
tpagoLink4: z.string().optional().nullable(),
|
||||
tpagoLink5: z.string().optional().nullable(),
|
||||
tpagoInstructions: z.string().optional().nullable(),
|
||||
tpagoInstructionsEs: z.string().optional().nullable(),
|
||||
bankTransferEnabled: booleanOrNumber.optional().nullable(),
|
||||
@@ -68,6 +76,10 @@ paymentOptionsRouter.get('/', requireAuth(['admin']), async (c) => {
|
||||
paymentOptions: {
|
||||
tpagoEnabled: false,
|
||||
tpagoLink: null,
|
||||
tpagoLink2: null,
|
||||
tpagoLink3: null,
|
||||
tpagoLink4: null,
|
||||
tpagoLink5: null,
|
||||
tpagoInstructions: null,
|
||||
tpagoInstructionsEs: null,
|
||||
bankTransferEnabled: false,
|
||||
@@ -171,6 +183,10 @@ paymentOptionsRouter.get('/event/:eventId', async (c) => {
|
||||
const defaults = {
|
||||
tpagoEnabled: false,
|
||||
tpagoLink: null,
|
||||
tpagoLink2: null,
|
||||
tpagoLink3: null,
|
||||
tpagoLink4: null,
|
||||
tpagoLink5: null,
|
||||
tpagoInstructions: null,
|
||||
tpagoInstructionsEs: null,
|
||||
bankTransferEnabled: false,
|
||||
@@ -193,6 +209,10 @@ paymentOptionsRouter.get('/event/:eventId', async (c) => {
|
||||
const merged = {
|
||||
tpagoEnabled: overrides?.tpagoEnabled ?? global.tpagoEnabled,
|
||||
tpagoLink: overrides?.tpagoLink ?? global.tpagoLink,
|
||||
tpagoLink2: overrides?.tpagoLink2 ?? global.tpagoLink2,
|
||||
tpagoLink3: overrides?.tpagoLink3 ?? global.tpagoLink3,
|
||||
tpagoLink4: overrides?.tpagoLink4 ?? global.tpagoLink4,
|
||||
tpagoLink5: overrides?.tpagoLink5 ?? global.tpagoLink5,
|
||||
tpagoInstructions: overrides?.tpagoInstructions ?? global.tpagoInstructions,
|
||||
tpagoInstructionsEs: overrides?.tpagoInstructionsEs ?? global.tpagoInstructionsEs,
|
||||
bankTransferEnabled: overrides?.bankTransferEnabled ?? global.bankTransferEnabled,
|
||||
|
||||
@@ -631,11 +631,21 @@ ticketsRouter.get('/:id', async (c) => {
|
||||
(db as any).select().from(payments).where(eq((payments as any).ticketId, id))
|
||||
);
|
||||
|
||||
// Count how many tickets belong to this booking (for per-quantity payment links)
|
||||
let bookingTicketCount = 1;
|
||||
if (ticket.bookingId) {
|
||||
const bookingTickets = await dbAll<any>(
|
||||
(db as any).select().from(tickets).where(eq((tickets as any).bookingId, ticket.bookingId))
|
||||
);
|
||||
bookingTicketCount = bookingTickets.length || 1;
|
||||
}
|
||||
|
||||
return c.json({
|
||||
ticket: {
|
||||
...ticket,
|
||||
event,
|
||||
payment,
|
||||
bookingTicketCount,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user