EventBuzz_Console/src/services/api/publicEventTickets.ts

239 lines
6.8 KiB
TypeScript

import { API_BASE_URL } from './client';
type ApiEnvelope<T> = { success?: boolean; data?: T; error?: { message?: string } };
async function parse<T>(res: Response): Promise<{ data?: T; error?: string }> {
let body: unknown;
try {
body = await res.json();
} catch {
return { error: res.statusText || 'Network error' };
}
const api = body as ApiEnvelope<T>;
if (api.success === true && api.data !== undefined) return { data: api.data };
return { error: api.error?.message || 'Request failed' };
}
function url(path: string) {
const base = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL;
const p = path.startsWith('/') ? path : `/${path}`;
return `${base}${p}`;
}
export type PublicTicketCatalogItem = {
_id: string;
name: string;
description: string;
keyPoints: string[];
basePriceMinor: number;
currency: string;
maxPerOrder: number | null;
requiresBillingState: boolean;
registrationFormId: string | null;
registrationFormFields: PublicFormField[] | null;
};
export type PublicFormField = {
label?: string;
type?: string;
required?: boolean;
options?: string[];
allowOther?: boolean;
otherLabel?: string;
placeholder?: string;
mapsTo?: string;
};
export type TicketQuote = {
ticketId: string;
quantity: number;
billingStateCode: string | null;
baseMinor: number;
taxMinor: number;
totalMinor: number;
/** Present on manual UPI quotes — Eventbuzz platform fee (paise). */
platformFeeMinor?: number;
/** Manual quote only — for UPI `pn` field. */
ticketName?: string;
eventName?: string;
taxLines: { label: string; rateBps: number; amountMinor: number }[];
currency: string;
};
export type PublicCheckoutOptions = {
onlineAvailable: boolean;
onlineGateways?: Array<{
id: string;
provider: string;
label: string;
isActive: boolean;
isPrimary: boolean;
testMode: boolean;
ticketCheckoutSupported: boolean;
}>;
manualUpi: {
manualQrMode: 'upload' | 'dynamic';
eventName: string;
upiQrImageUrl: string;
upiId: string;
payeeName: string;
instructions: string;
platformFeeInr: number;
} | null;
};
export type CheckoutOrderResponse = {
keyId: string;
orderId: string;
amount: number;
currency: string;
transactionId: string;
checkoutSource: string;
};
export const publicEventTicketsApi = {
async getEventSummary(eventId: string): Promise<{
name: string;
subtitle?: string;
startDate?: string;
endDate?: string;
location?: string;
locationType?: string;
image?: string;
}> {
const res = await fetch(url(`/public/events/${eventId}/summary`), { method: 'GET' });
const out = await parse<{
name: string;
subtitle?: string;
startDate?: string;
endDate?: string;
location?: string;
locationType?: string;
image?: string;
}>(res);
if (out.error) throw new Error(out.error);
return out.data!;
},
async listTickets(eventId: string): Promise<PublicTicketCatalogItem[]> {
const res = await fetch(url(`/public/events/${eventId}/tickets`), { method: 'GET' });
const out = await parse<{ tickets: PublicTicketCatalogItem[] }>(res);
if (out.error) throw new Error(out.error);
return out.data?.tickets ?? [];
},
async checkoutOptions(eventId: string): Promise<PublicCheckoutOptions> {
const res = await fetch(url(`/public/events/${eventId}/tickets/checkout-options`), { method: 'GET' });
const out = await parse<PublicCheckoutOptions>(res);
if (out.error) throw new Error(out.error);
return out.data!;
},
async quote(
eventId: string,
payload: { ticketId: string; quantity: number; billingStateCode?: string },
): Promise<TicketQuote> {
const res = await fetch(url(`/public/events/${eventId}/tickets/quote`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const out = await parse<TicketQuote>(res);
if (out.error) throw new Error(out.error);
return out.data!;
},
async manualQuote(
eventId: string,
payload: { ticketId: string; quantity: number; billingStateCode?: string },
): Promise<TicketQuote> {
const res = await fetch(url(`/public/events/${eventId}/tickets/manual-quote`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const out = await parse<TicketQuote>(res);
if (out.error) throw new Error(out.error);
return out.data!;
},
async checkout(
eventId: string,
payload: {
ticketId: string;
quantity: number;
billingStateCode?: string;
purchaserEmail: string;
sourceFormId?: string;
attendees: Array<{
firstName: string;
lastName: string;
email: string;
phone?: string;
organization?: string;
designation?: string;
displayName?: string;
formData?: Record<string, unknown>;
}>;
expectedTotalMinor: number;
idempotencyKey?: string;
orgGatewayId?: string;
},
): Promise<CheckoutOrderResponse> {
const res = await fetch(url(`/public/events/${eventId}/tickets/checkout`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const out = await parse<CheckoutOrderResponse>(res);
if (out.error) throw new Error(out.error);
return out.data!;
},
async verifyPayment(
eventId: string,
payload: { orderId: string; paymentId: string; signature: string },
): Promise<void> {
const res = await fetch(url(`/public/events/${eventId}/payment/verify`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const out = await parse<{ verified: boolean }>(res);
if (out.error) throw new Error(out.error);
},
async manualSubmit(
eventId: string,
payload: {
ticketId: string;
quantity: number;
billingStateCode?: string;
purchaserEmail: string;
sourceFormId?: string;
attendees: Array<{
firstName: string;
lastName: string;
email: string;
phone?: string;
organization?: string;
designation?: string;
displayName?: string;
formData?: Record<string, unknown>;
}>;
expectedTotalMinor: number;
screenshotDataUrl: string;
buyerNote?: string;
},
): Promise<{ orderId: string; message: string }> {
const res = await fetch(url(`/public/events/${eventId}/tickets/manual-submit`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const out = await parse<{ orderId: string; message: string }>(res);
if (out.error) throw new Error(out.error);
return out.data!;
},
};