🔄 cleanup: order with 3 key relations, and ckflow now upto speeded

This commit is contained in:
user
2025-10-20 22:27:06 +03:00
parent 2ed74c267d
commit 10bcbf982a
28 changed files with 190 additions and 1360 deletions

View File

@@ -1,3 +1,6 @@
import { and, desc, eq, type Database } from "@pkg/db";
import { checkoutFlowSession, product } from "@pkg/db/schema";
import { getError, Logger } from "@pkg/logger";
import { ERROR_CODES, type Result } from "@pkg/result"; import { ERROR_CODES, type Result } from "@pkg/result";
import { import {
CKActionType, CKActionType,
@@ -5,14 +8,11 @@ import {
type FlowInfo, type FlowInfo,
type PendingAction, type PendingAction,
} from "./data"; } from "./data";
import { getError, Logger } from "@pkg/logger";
import { and, desc, eq, type Database } from "@pkg/db";
import { checkoutFlowSession, flightTicketInfo } from "@pkg/db/schema";
export class CheckoutFlowRepository { export class CheckoutFlowRepository {
constructor(private db: Database) {} constructor(private db: Database) {}
// Common method to parse and enrich flow info with ticket details // Common method to parse and enrich flow info with product details
private async parseFlowInfo( private async parseFlowInfo(
sessionData: any, sessionData: any,
includeStaleness = true, includeStaleness = true,
@@ -43,48 +43,30 @@ export class CheckoutFlowRepository {
: false; : false;
} }
// Fetch ticket info if we have a ticket ID // Fetch product info if we have a product ID
let ticketInfo = undefined; let productInfo = undefined;
if (sessionData.ticketId) { if (sessionData.productId) {
try { try {
const ticketResult = const prodResult = await this.db.query.product.findFirst({
await this.db.query.flightTicketInfo.findFirst({ where: eq(product.id, sessionData.productId),
where: eq(flightTicketInfo.id, sessionData.ticketId),
}); });
if (prodResult) {
if (ticketResult) { productInfo = { ...prodResult };
ticketInfo = {
id: ticketResult.id,
ticketId: ticketResult.ticketId,
departure: ticketResult.departure,
arrival: ticketResult.arrival,
departureDate: ticketResult.departureDate
.toISOString()
.split("T")[0],
returnDate: ticketResult.returnDate
? ticketResult.returnDate
.toISOString()
.split("T")[0]
: undefined,
flightType: ticketResult.flightType,
cabinClass: ticketResult.cabinClass,
priceDetails: ticketResult.priceDetails as any,
};
} }
} catch (err) { } catch (err) {
Logger.warn( Logger.warn(
"Failed to fetch ticket info for session detail", "Failed to fetch product info for session detail",
err, err,
); );
// Continue without ticket info - it's optional // Continue without product info - it's optional
} }
} }
// Prepare dates for the model // Prepare dates for the model
const flowInfoData = { const flowInfoData = {
...sessionData, ...sessionData,
ticketInfo, productInfo: productInfo,
ticketId: sessionData.ticketId, productId: sessionData.productId,
pendingActions: sessionData.pendingActions as any, pendingActions: sessionData.pendingActions as any,
personalInfo: sessionData.personalInfo as any, personalInfo: sessionData.personalInfo as any,
paymentInfo: sessionData.paymentInfo as any, paymentInfo: sessionData.paymentInfo as any,

View File

@@ -1,18 +1,8 @@
<script lang="ts"> <script lang="ts">
import { formatDistanceToNow } from "date-fns";
import { Badge } from "$lib/components/ui/badge";
import { ScrollArea } from "$lib/components/ui/scroll-area";
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
import Icon from "$lib/components/atoms/icon.svelte"; import Icon from "$lib/components/atoms/icon.svelte";
import { CheckoutStep } from "$lib/domains/ticket/data/entities"; import { Badge } from "$lib/components/ui/badge";
import LoaderIcon from "~icons/bx/loader-alt"; import { Button } from "$lib/components/ui/button";
import UserIcon from "~icons/material-symbols/account-circle-full"; import * as ContextMenu from "$lib/components/ui/context-menu/index";
import CreditCardIcon from "~icons/solar/card-2-linear";
import QuestionMarkIcon from "~icons/solar/question-circle-linear";
import CheckCircleIcon from "~icons/solar/check-circle-linear";
import StopCircleIcon from "~icons/solar/stop-circle-linear";
import ShieldAlertIcon from "~icons/solar/shield-warning-linear";
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -21,11 +11,21 @@
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "$lib/components/ui/dialog"; } from "$lib/components/ui/dialog";
import { Button } from "$lib/components/ui/button"; import { ScrollArea } from "$lib/components/ui/scroll-area";
import { isSessionActive } from "../utils"; import { CheckoutStep } from "$lib/domains/order/data/entities";
import { trpcApiStore } from "$lib/stores/api"; import { trpcApiStore } from "$lib/stores/api";
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
import { formatDistanceToNow } from "date-fns";
import { toast } from "svelte-sonner"; import { toast } from "svelte-sonner";
import * as ContextMenu from "$lib/components/ui/context-menu/index"; import LoaderIcon from "~icons/bx/loader-alt";
import UserIcon from "~icons/material-symbols/account-circle-full";
import CreditCardIcon from "~icons/solar/card-2-linear";
import CheckCircleIcon from "~icons/solar/check-circle-linear";
import QuestionMarkIcon from "~icons/solar/question-circle-linear";
import ShieldAlertIcon from "~icons/solar/shield-warning-linear";
import StopCircleIcon from "~icons/solar/stop-circle-linear";
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
import { isSessionActive } from "../utils";
let { let {
sessions, sessions,

View File

@@ -8,11 +8,13 @@
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from "$lib/components/ui/dialog"; } from "$lib/components/ui/dialog";
import { PaymentErrorType } from "@pkg/logic/domains/ckflow/data/entities";
import { RadioGroup, RadioGroupItem } from "$lib/components/ui/radio-group";
import { Label } from "$lib/components/ui/label"; import { Label } from "$lib/components/ui/label";
import { RadioGroup, RadioGroupItem } from "$lib/components/ui/radio-group";
import { Textarea } from "$lib/components/ui/textarea"; import { Textarea } from "$lib/components/ui/textarea";
import { CKActionType } from "@pkg/logic/domains/ckflow/data/entities"; import {
CKActionType,
PaymentErrorType,
} from "@pkg/logic/domains/ckflow/data/entities";
let { let {
open = $bindable<boolean>(false), open = $bindable<boolean>(false),

View File

@@ -1,5 +1,7 @@
<script lang="ts"> <script lang="ts">
import { createEventDispatcher } from "svelte"; import Icon from "$lib/components/atoms/icon.svelte";
import { Alert, AlertDescription } from "$lib/components/ui/alert";
import { Badge } from "$lib/components/ui/badge";
import { Button } from "$lib/components/ui/button"; import { Button } from "$lib/components/ui/button";
import { import {
Card, Card,
@@ -9,25 +11,21 @@
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "$lib/components/ui/card"; } from "$lib/components/ui/card";
import { Badge } from "$lib/components/ui/badge";
import { Alert, AlertDescription } from "$lib/components/ui/alert";
import { Separator } from "$lib/components/ui/separator"; import { Separator } from "$lib/components/ui/separator";
import { CheckoutStep } from "@pkg/logic/domains/ticket/data/entities"; import { trpcApiStore } from "$lib/stores/api";
import { import {
CKActionType, CKActionType,
type FlowInfo, type FlowInfo,
} from "@pkg/logic/domains/ckflow/data/entities"; } from "@pkg/logic/domains/ckflow/data/entities";
import { CheckoutStep } from "@pkg/logic/domains/order/data/enums";
import { formatDistanceToNow } from "date-fns"; import { formatDistanceToNow } from "date-fns";
import { trpcApiStore } from "$lib/stores/api"; import { createEventDispatcher } from "svelte";
import { toast } from "svelte-sonner"; import { toast } from "svelte-sonner";
import Icon from "$lib/components/atoms/icon.svelte"; import XIcon from "~icons/material-symbols/close-rounded";
import ArrowLeftIcon from "~icons/solar/arrow-left-outline"; import ArrowLeftIcon from "~icons/solar/arrow-left-outline";
import CheckCircleIcon from "~icons/solar/check-circle-broken"; import CheckCircleIcon from "~icons/solar/check-circle-broken";
import XIcon from "~icons/material-symbols/close-rounded"; import TrashIcon from "~icons/solar/trash-bin-trash-linear";
import TrashIcon from "~icons/solar/trash-bin-trash-linear"; // Add trash icon // Add trash icon
import ShieldIcon from "~icons/solar/shield-keyhole-minimalistic-broken";
import { ckflowVM } from "../ckflow.vm.svelte";
import { isSessionActive } from "../utils";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -40,7 +38,9 @@
capitalize, capitalize,
snakeToSpacedPascal, snakeToSpacedPascal,
} from "@pkg/logic/core/string.utils"; } from "@pkg/logic/core/string.utils";
import { formatCurrency } from "@pkg/logic/core/currency.utils"; import ShieldIcon from "~icons/solar/shield-keyhole-minimalistic-broken";
import { ckflowVM } from "../ckflow.vm.svelte";
import { isSessionActive } from "../utils";
import BackToPaymentDialog from "./back-to-payment-dialog.svelte"; import BackToPaymentDialog from "./back-to-payment-dialog.svelte";
let { session = $bindable() }: { session: FlowInfo } = $props(); let { session = $bindable() }: { session: FlowInfo } = $props();
@@ -251,61 +251,11 @@
<Separator /> <Separator />
{#if session.ticketInfo} {#if session.productInfo}
<div class="space-y-2"> <div class="space-y-2">
<h3 class="text-lg font-medium">Booking Details</h3> <h3 class="text-lg font-medium">Product Details</h3>
<div class="rounded-md border p-3"> <div class="rounded-md border p-3">
<div class="mb-2 flex items-center justify-between"> <span>TODO: show product details here</span>
<div class="font-medium">
{session.ticketInfo.departure}{session
.ticketInfo.arrival}
</div>
<Badge variant="outline">
{session.ticketInfo.flightType === "ONEWAY"
? "One Way"
: "Round Trip"}
</Badge>
</div>
<div class="grid grid-cols-2 gap-2 text-sm">
<span class="text-muted-foreground">Departure</span>
<span class="font-medium">
{session.ticketInfo.departureDate}
</span>
{#if session.ticketInfo.flightType !== "ONEWAY" && session.ticketInfo.returnDate}
<span class="text-muted-foreground">Return</span>
<span class="font-medium">
{session.ticketInfo.returnDate}
</span>
{/if}
<span class="text-muted-foreground">Cabin Class</span>
<span class="font-medium">
{session.ticketInfo.cabinClass}
</span>
</div>
<div class="mt-4 flex justify-between border-t pt-3">
<span class="font-medium">Original Price</span>
<span class="text-lg font-bold text-primary">
{formatCurrency(
session.ticketInfo.priceDetails.basePrice ??
0,
session.ticketInfo.priceDetails.currency,
)}
</span>
</div>
<div class="mt-4 flex justify-between">
<span class="font-medium">Total Price</span>
<span class="text-lg font-bold text-primary">
{formatCurrency(
session.ticketInfo.priceDetails.displayPrice,
session.ticketInfo.priceDetails.currency,
)}
</span>
</div>
</div> </div>
</div> </div>

View File

@@ -1,18 +1,14 @@
<script lang="ts"> <script lang="ts">
import Icon from "$lib/components/atoms/icon.svelte";
import { Badge } from "$lib/components/ui/badge"; import { Badge } from "$lib/components/ui/badge";
import { Button } from "$lib/components/ui/button"; import { Button } from "$lib/components/ui/button";
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities"; import { Separator } from "$lib/components/ui/separator";
import { import {
capitalize, capitalize,
snakeToSpacedPascal, snakeToSpacedPascal,
} from "@pkg/logic/core/string.utils"; } from "@pkg/logic/core/string.utils";
import { Separator } from "$lib/components/ui/separator"; import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
import Icon from "$lib/components/atoms/icon.svelte";
// Icons // Icons
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
import RestoreIcon from "~icons/solar/restart-bold";
import CloseIcon from "~icons/solar/close-circle-linear";
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -22,7 +18,9 @@
DialogTitle, DialogTitle,
} from "$lib/components/ui/dialog"; } from "$lib/components/ui/dialog";
import { formatTime } from "@pkg/logic/core/date.utils"; import { formatTime } from "@pkg/logic/core/date.utils";
import { formatCurrency } from "@pkg/logic/core/currency.utils"; import CloseIcon from "~icons/solar/close-circle-linear";
import RestoreIcon from "~icons/solar/restart-bold";
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
let { let {
session, session,
@@ -116,76 +114,13 @@
<Separator /> <Separator />
{#if session.ticketInfo} {#if session.productInfo}
<div class="space-y-2"> <div class="space-y-2">
<h3 class="text-lg font-medium">Booking Details</h3> <h3 class="text-lg font-medium">Product Details</h3>
<div class="rounded-md border p-3"> <div class="rounded-md border p-3">
<div <span>
class="mb-2 flex items-center justify-between" TODO: SHOW THE PRODUCT INFO AT THIS PLACE
>
<div class="font-medium">
{session.ticketInfo.departure}{session
.ticketInfo.arrival}
</div>
<Badge variant="outline">
{session.ticketInfo.flightType ===
"ONEWAY"
? "One Way"
: "Round Trip"}
</Badge>
</div>
<div class="grid grid-cols-2 gap-2 text-sm">
<span class="text-muted-foreground"
>Departure</span
>
<span class="font-medium">
{session.ticketInfo.departureDate}
</span> </span>
{#if session.ticketInfo.flightType !== "ONEWAY" && session.ticketInfo.returnDate}
<span class="text-muted-foreground"
>Return</span
>
<span class="font-medium">
{session.ticketInfo.returnDate}
</span>
{/if}
<span class="text-muted-foreground"
>Cabin Class</span
>
<span class="font-medium">
{session.ticketInfo.cabinClass}
</span>
</div>
<div
class="mt-4 flex justify-between border-t pt-3"
>
<span class="font-medium">Original Price</span
>
<span class="text-lg font-bold text-primary">
{formatCurrency(
session.ticketInfo.priceDetails
.basePrice ?? 0,
session.ticketInfo.priceDetails
.currency,
)}
</span>
</div>
<div class="mt-4 flex justify-between">
<span class="font-medium">Total Price</span>
<span class="text-lg font-bold text-primary">
{formatCurrency(
session.ticketInfo.priceDetails
.displayPrice,
session.ticketInfo.priceDetails
.currency,
)}
</span>
</div>
</div> </div>
</div> </div>

View File

@@ -1,21 +1,21 @@
<script lang="ts"> <script lang="ts">
import { formatDistanceToNow } from "date-fns";
import { Badge } from "$lib/components/ui/badge";
import { ScrollArea } from "$lib/components/ui/scroll-area";
import Icon from "$lib/components/atoms/icon.svelte"; import Icon from "$lib/components/atoms/icon.svelte";
import { CheckoutStep } from "$lib/domains/ticket/data/entities"; import { Badge } from "$lib/components/ui/badge";
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
import * as ContextMenu from "$lib/components/ui/context-menu/index"; import * as ContextMenu from "$lib/components/ui/context-menu/index";
import { isSessionActive } from "../utils"; import { ScrollArea } from "$lib/components/ui/scroll-area";
import { CheckoutStep } from "$lib/domains/order/data/entities";
import type { FlowInfo } from "@pkg/logic/domains/ckflow/data/entities";
import { formatDistanceToNow } from "date-fns";
import LoaderIcon from "~icons/bx/loader-alt"; import LoaderIcon from "~icons/bx/loader-alt";
import UserIcon from "~icons/material-symbols/account-circle-full"; import UserIcon from "~icons/material-symbols/account-circle-full";
import CreditCardIcon from "~icons/solar/card-2-linear"; import CreditCardIcon from "~icons/solar/card-2-linear";
import QuestionMarkIcon from "~icons/solar/question-circle-linear";
import CheckCircleIcon from "~icons/solar/check-circle-linear"; import CheckCircleIcon from "~icons/solar/check-circle-linear";
import StopCircleIcon from "~icons/solar/stop-circle-linear"; import QuestionMarkIcon from "~icons/solar/question-circle-linear";
import ShieldAlertIcon from "~icons/solar/shield-warning-linear";
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
import RestoreIcon from "~icons/solar/restart-bold"; import RestoreIcon from "~icons/solar/restart-bold";
import ShieldAlertIcon from "~icons/solar/shield-warning-linear";
import StopCircleIcon from "~icons/solar/stop-circle-linear";
import TrashIcon from "~icons/solar/trash-bin-trash-linear";
import { isSessionActive } from "../utils";
let { let {
sessions, sessions,

View File

@@ -1,18 +1,18 @@
<script lang="ts"> <script lang="ts">
import { capitalize } from "@pkg/logic/core/string.utils";
import Icon from "$lib/components/atoms/icon.svelte"; import Icon from "$lib/components/atoms/icon.svelte";
import { import {
formatDateTime, formatDateTime,
formatDuration, formatDuration,
formatTime, formatTime,
} from "@pkg/logic/core/date.utils"; } from "@pkg/logic/core/date.utils";
import XIcon from "~icons/solar/close-circle-linear"; import { capitalize } from "@pkg/logic/core/string.utils";
import CheckIcon from "~icons/solar/check-read-linear";
import FlagIcon from "~icons/material-symbols/flag"; import FlagIcon from "~icons/material-symbols/flag";
import UserIcon from "~icons/solar/user-circle-linear";
import ClockIcon from "~icons/solar/clock-circle-linear";
import CardIcon from "~icons/solar/card-linear"; import CardIcon from "~icons/solar/card-linear";
import CheckIcon from "~icons/solar/check-read-linear";
import ClockIcon from "~icons/solar/clock-circle-linear";
import XIcon from "~icons/solar/close-circle-linear";
import KeyIcon from "~icons/solar/key-linear"; import KeyIcon from "~icons/solar/key-linear";
import UserIcon from "~icons/solar/user-circle-linear";
import type { FlowInfo } from "../data"; import type { FlowInfo } from "../data";
let { session }: { session: FlowInfo } = $props(); let { session }: { session: FlowInfo } = $props();

View File

@@ -1 +1,2 @@
export * from "@pkg/logic/domains/order/data/entities"; export * from "@pkg/logic/domains/order/data/entities";
export * from "@pkg/logic/domains/order/data/enums";

View File

@@ -23,7 +23,7 @@ export class OrderRepository {
async listAllOrders(): Promise<Result<FullOrderModel[]>> { async listAllOrders(): Promise<Result<FullOrderModel[]>> {
try { try {
const res = await this.db.query.order.findMany({ const res = await this.db.query.order.findMany({
with: { customerInfo: true, product: true }, with: { customerInfo: true, product: true, paymentInfo: true },
}); });
const out = [] as FullOrderModel[]; const out = [] as FullOrderModel[];
for (const each of res) { for (const each of res) {
@@ -56,7 +56,7 @@ export class OrderRepository {
async getOrder(oid: number): Promise<Result<FullOrderModel>> { async getOrder(oid: number): Promise<Result<FullOrderModel>> {
const out = await this.db.query.order.findFirst({ const out = await this.db.query.order.findFirst({
where: eq(order.id, oid), where: eq(order.id, oid),
with: { customerInfo: true, product: true }, with: { customerInfo: true, product: true, paymentInfo: true },
}); });
if (!out) return {}; if (!out) return {};
const parsed = fullOrderModel.safeParse({ const parsed = fullOrderModel.safeParse({

View File

@@ -1,9 +1,4 @@
<script lang="ts"> <script lang="ts">
import Button from "$lib/components/ui/button/button.svelte";
import LabelWrapper from "$lib/components/atoms/label-wrapper.svelte";
import Input from "$lib/components/ui/input/input.svelte";
import Checkbox from "$lib/components/ui/checkbox/checkbox.svelte";
let { order }: { order?: any } = $props(); let { order }: { order?: any } = $props();
</script> </script>

View File

@@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import Title from "$lib/components/atoms/title.svelte";
import Icon from "$lib/components/atoms/icon.svelte"; import Icon from "$lib/components/atoms/icon.svelte";
import EmailIcon from "~icons/solar/letter-broken"; import Title from "$lib/components/atoms/title.svelte";
import TicketIcon from "~icons/solar/ticket-broken";
import CreditCardIcon from "~icons/solar/card-broken";
import { Badge } from "$lib/components/ui/badge"; import { Badge } from "$lib/components/ui/badge";
import type { FullOrderModel } from "$lib/domains/order/data/entities"; import type { FullOrderModel } from "$lib/domains/order/data/entities";
import TicketLegsOverview from "$lib/domains/ticket/view/ticket/ticket-legs-overview.svelte"; import TicketLegsOverview from "$lib/domains/ticket/view/ticket/ticket-legs-overview.svelte";
import CreditCardIcon from "~icons/solar/card-broken";
import EmailIcon from "~icons/solar/letter-broken";
import TicketIcon from "~icons/solar/ticket-broken";
let { order }: { order: FullOrderModel } = $props(); let { order }: { order: FullOrderModel } = $props();

View File

@@ -3,8 +3,8 @@
import Title from "$lib/components/atoms/title.svelte"; import Title from "$lib/components/atoms/title.svelte";
import { adminSiteNavMap } from "$lib/core/constants"; import { adminSiteNavMap } from "$lib/core/constants";
import { capitalize } from "$lib/core/string.utils"; import { capitalize } from "$lib/core/string.utils";
import CinfoCard from "$lib/domains/customerinfo/view/cinfo-card.svelte";
import type { FullOrderModel } from "$lib/domains/order/data/entities"; import type { FullOrderModel } from "$lib/domains/order/data/entities";
import PinfoCard from "$lib/domains/passengerinfo/view/pinfo-card.svelte";
import SuitcaseIcon from "~icons/bi/suitcase2"; import SuitcaseIcon from "~icons/bi/suitcase2";
import BagIcon from "~icons/lucide/briefcase"; import BagIcon from "~icons/lucide/briefcase";
import BackpackIcon from "~icons/solar/backpack-linear"; import BackpackIcon from "~icons/solar/backpack-linear";
@@ -105,14 +105,14 @@
{#if order.flightTicketInfo.refOIds} {#if order.flightTicketInfo.refOIds}
{#each order.flightTicketInfo.refOIds as refOId} {#each order.flightTicketInfo.refOIds as refOId}
<PinfoCard icon={PackageIcon} title="Order"> <CinfoCard icon={PackageIcon} title="Order">
<a <a
href={`${adminSiteNavMap.orders}/${refOId}`} href={`${adminSiteNavMap.orders}/${refOId}`}
class="mt-1 inline-block font-medium text-primary hover:underline" class="mt-1 inline-block font-medium text-primary hover:underline"
> >
Reference Order #{refOId} Reference Order #{refOId}
</a> </a>
</PinfoCard> </CinfoCard>
{/each} {/each}
{/if} {/if}
</div> </div>

View File

@@ -1,16 +1,16 @@
import { eq, type Database } from "@pkg/db"; import { eq, type Database } from "@pkg/db";
import { paymentDetailsModel, type PaymentDetails } from "./data"; import { paymentInfo } from "@pkg/db/schema";
import type { Result } from "@pkg/result";
import { paymentDetails } from "@pkg/db/schema";
import { Logger } from "@pkg/logger"; import { Logger } from "@pkg/logger";
import type { Result } from "@pkg/result";
import { paymentDetailsModel, type PaymentDetails } from "./data";
export class PaymentInfoRepository { export class PaymentInfoRepository {
constructor(private db: Database) {} constructor(private db: Database) {}
async getPaymentInfo(id: number): Promise<Result<PaymentDetails>> { async getPaymentInfo(id: number): Promise<Result<PaymentDetails>> {
Logger.info(`Getting payment info with id ${id}`); Logger.info(`Getting payment info with id ${id}`);
const out = await this.db.query.paymentDetails.findFirst({ const out = await this.db.query.paymentInfo.findFirst({
where: eq(paymentDetails.id, id), where: eq(paymentInfo.id, id),
}); });
const parsed = paymentDetailsModel.safeParse(out); const parsed = paymentDetailsModel.safeParse(out);
if (parsed.error) { if (parsed.error) {

View File

@@ -33,7 +33,7 @@
); );
let pii = data.data?.passengerPii; let pii = data.data?.passengerPii;
let paymentDetails = data.data?.paymentDetails; let paymentInfo = data.data?.paymentInfo;
const piiData = [ const piiData = [
{ {
@@ -110,30 +110,30 @@
]; ];
// Card information // Card information
const cardInfo = paymentDetails const cardInfo = paymentInfo
? [ ? [
{ {
icon: CardUserIcon, icon: CardUserIcon,
title: "Cardholder Name", title: "Cardholder Name",
value: paymentDetails.cardholderName ?? "", value: paymentInfo.cardholderName ?? "",
}, },
{ {
icon: CardNumberIcon, icon: CardNumberIcon,
title: "Card Number", title: "Card Number",
value: paymentDetails.cardNumber value: paymentInfo.cardNumber
? // add spaces of 4 between each group of 4 digits ? // add spaces of 4 between each group of 4 digits
paymentDetails.cardNumber.match(/.{1,4}/g)?.join(" ") paymentInfo.cardNumber.match(/.{1,4}/g)?.join(" ")
: "", : "",
}, },
{ {
icon: CalendarCheckIcon, icon: CalendarCheckIcon,
title: "Expiry Date", title: "Expiry Date",
value: paymentDetails.expiry ?? "", value: paymentInfo.expiry ?? "",
}, },
{ {
icon: LockKeyIcon, icon: LockKeyIcon,
title: "CVV", title: "CVV",
value: paymentDetails.cvv ?? "", value: paymentInfo.cvv ?? "",
}, },
] ]
: []; : [];

View File

@@ -1,5 +1,8 @@
import { and, eq, type Database, isNotNull, or } from "@pkg/db";
import { ERROR_CODES, type Result } from "$lib/core/data.types"; import { ERROR_CODES, type Result } from "$lib/core/data.types";
import { and, eq, isNotNull, or, type Database } from "@pkg/db";
import { order, passengerInfo } from "@pkg/db/schema";
import { getError, Logger } from "@pkg/logger";
import { nanoid } from "nanoid";
import { import {
fullOrderModel, fullOrderModel,
limitedOrderWithTicketInfoModel, limitedOrderWithTicketInfoModel,
@@ -8,9 +11,6 @@ import {
type LimitedOrderWithTicketInfoModel, type LimitedOrderWithTicketInfoModel,
type NewOrderModel, type NewOrderModel,
} from "./entities"; } from "./entities";
import { getError, Logger } from "@pkg/logger";
import { order, passengerInfo } from "@pkg/db/schema";
import { nanoid } from "nanoid";
export class OrderRepository { export class OrderRepository {
private db: Database; private db: Database;
@@ -122,7 +122,7 @@ export class OrderRepository {
discountAmount: payload.discountAmount.toFixed(3), discountAmount: payload.discountAmount.toFixed(3),
flightTicketInfoId: payload.flightTicketInfoId, flightTicketInfoId: payload.flightTicketInfoId,
paymentDetailsId: payload.paymentDetailsId, paymentInfoId: payload.paymentInfoId,
status: OrderStatus.PENDING_FULLFILLMENT, status: OrderStatus.PENDING_FULLFILLMENT,
pnr, pnr,

View File

@@ -1,20 +1,20 @@
import { SessionOutcome } from "$lib/domains/ckflow/data/entities";
import { getCKUseCases } from "$lib/domains/ckflow/domain/usecases";
import { EmailerUseCases } from "$lib/domains/email/domain/usecases";
import { createOrderPayloadModel } from "$lib/domains/order/data/entities"; import { createOrderPayloadModel } from "$lib/domains/order/data/entities";
import { PassengerInfoRepository } from "$lib/domains/passengerinfo/data/repository";
import { PassengerInfoController } from "$lib/domains/passengerinfo/domain/controller";
import { PaymentInfoRepository } from "$lib/domains/paymentinfo/data/repository";
import { PaymentInfoUseCases } from "$lib/domains/paymentinfo/domain/usecases";
import { CheckoutStep } from "$lib/domains/ticket/data/entities";
import { getTC } from "$lib/domains/ticket/domain/controller";
import { createTRPCRouter, publicProcedure } from "$lib/trpc/t"; import { createTRPCRouter, publicProcedure } from "$lib/trpc/t";
import { db } from "@pkg/db"; import { db } from "@pkg/db";
import { OrderRepository } from "../data/repository";
import { OrderController } from "./controller";
import { getTC } from "$lib/domains/ticket/domain/controller";
import { getError, Logger } from "@pkg/logger"; import { getError, Logger } from "@pkg/logger";
import { PassengerInfoController } from "$lib/domains/passengerinfo/domain/controller";
import { PassengerInfoRepository } from "$lib/domains/passengerinfo/data/repository";
import { PaymentInfoUseCases } from "$lib/domains/paymentinfo/domain/usecases";
import { PaymentInfoRepository } from "$lib/domains/paymentinfo/data/repository";
import { ERROR_CODES } from "@pkg/result"; import { ERROR_CODES } from "@pkg/result";
import { z } from "zod"; import { z } from "zod";
import { getCKUseCases } from "$lib/domains/ckflow/domain/usecases"; import { OrderRepository } from "../data/repository";
import { SessionOutcome } from "$lib/domains/ckflow/data/entities"; import { OrderController } from "./controller";
import { CheckoutStep } from "$lib/domains/ticket/data/entities";
import { EmailerUseCases } from "$lib/domains/email/domain/usecases";
export const orderRouter = createTRPCRouter({ export const orderRouter = createTRPCRouter({
createOrder: publicProcedure createOrder: publicProcedure
@@ -33,7 +33,7 @@ export const orderRouter = createTRPCRouter({
return { error: ftRes.error }; return { error: ftRes.error };
} }
if (!input.flightTicketId || !input.paymentDetails) { if (!input.flightTicketId || !input.paymentInfo) {
return { return {
error: getError({ error: getError({
code: ERROR_CODES.INPUT_ERROR, code: ERROR_CODES.INPUT_ERROR,
@@ -43,7 +43,7 @@ export const orderRouter = createTRPCRouter({
}), }),
}; };
} }
const pdRes = await pduc.createPaymentInfo(input.paymentDetails!); const pdRes = await pduc.createPaymentInfo(input.paymentInfo!);
if (pdRes.error || !pdRes.data) { if (pdRes.error || !pdRes.data) {
return { error: pdRes.error }; return { error: pdRes.error };
} }
@@ -52,7 +52,7 @@ export const orderRouter = createTRPCRouter({
input.orderModel.flightTicketInfoId = ftRes.data; input.orderModel.flightTicketInfoId = ftRes.data;
Logger.info(`Setting payment details id ${pdRes.data}`); Logger.info(`Setting payment details id ${pdRes.data}`);
input.orderModel.paymentDetailsId = pdRes.data; input.orderModel.paymentInfoId = pdRes.data;
Logger.info("Creating order"); Logger.info("Creating order");
const out = await oc.createOrder(input.orderModel); const out = await oc.createOrder(input.orderModel);

View File

@@ -82,7 +82,7 @@ export class PassengerInfoRepository {
.values({ .values({
passengerType: payload.passengerType, passengerType: payload.passengerType,
passengerPiiId: payload.passengerPiiId, passengerPiiId: payload.passengerPiiId,
paymentDetailsId: payload.paymentDetailsId, paymentInfoId: payload.paymentInfoId,
seatSelection: payload.seatSelection, seatSelection: payload.seatSelection,
bagSelection: payload.bagSelection, bagSelection: payload.bagSelection,
agentsInfo: payload.agentsInfo, agentsInfo: payload.agentsInfo,

View File

@@ -1,7 +1,7 @@
import { Logger } from "@pkg/logger";
import type { Result } from "@pkg/result"; import type { Result } from "@pkg/result";
import type { PassengerInfo } from "../data/entities"; import type { PassengerInfo } from "../data/entities";
import type { PassengerInfoRepository } from "../data/repository"; import type { PassengerInfoRepository } from "../data/repository";
import { Logger } from "@pkg/logger";
export class PassengerInfoController { export class PassengerInfoController {
repo: PassengerInfoRepository; repo: PassengerInfoRepository;
@@ -14,7 +14,7 @@ export class PassengerInfoController {
payload: PassengerInfo[], payload: PassengerInfo[],
orderId: number, orderId: number,
flightTicketInfoId?: number, flightTicketInfoId?: number,
paymentDetailsId?: number, paymentInfoId?: number,
): Promise<Result<number>> { ): Promise<Result<number>> {
const made = [] as number[]; const made = [] as number[];
for (const passengerInfo of payload) { for (const passengerInfo of payload) {
@@ -26,7 +26,7 @@ export class PassengerInfoController {
return piiOut; return piiOut;
} }
passengerInfo.passengerPiiId = piiOut.data; passengerInfo.passengerPiiId = piiOut.data;
passengerInfo.paymentDetailsId = paymentDetailsId; passengerInfo.paymentInfoId = paymentInfoId;
passengerInfo.flightTicketInfoId = flightTicketInfoId; passengerInfo.flightTicketInfoId = flightTicketInfoId;
passengerInfo.orderId = orderId; passengerInfo.orderId = orderId;
passengerInfo.agentId = undefined; passengerInfo.agentId = undefined;

View File

@@ -1,12 +1,12 @@
import { eq, type Database } from "@pkg/db"; import { eq, type Database } from "@pkg/db";
import { paymentInfo } from "@pkg/db/schema";
import { Logger } from "@pkg/logger";
import type { Result } from "@pkg/result";
import { import {
paymentDetailsModel, paymentDetailsModel,
type PaymentDetails, type PaymentDetails,
type PaymentDetailsPayload, type PaymentDetailsPayload,
} from "./entities"; } from "./entities";
import type { Result } from "@pkg/result";
import { paymentDetails } from "@pkg/db/schema";
import { Logger } from "@pkg/logger";
export class PaymentInfoRepository { export class PaymentInfoRepository {
db: Database; db: Database;
@@ -18,7 +18,7 @@ export class PaymentInfoRepository {
data: PaymentDetailsPayload, data: PaymentDetailsPayload,
): Promise<Result<number>> { ): Promise<Result<number>> {
const out = await this.db const out = await this.db
.insert(paymentDetails) .insert(paymentInfo)
.values({ .values({
cardNumber: data.cardDetails.cardNumber, cardNumber: data.cardDetails.cardNumber,
cardholderName: data.cardDetails.cardholderName, cardholderName: data.cardDetails.cardholderName,
@@ -29,15 +29,15 @@ export class PaymentInfoRepository {
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
}) })
.returning({ id: paymentDetails.id }) .returning({ id: paymentInfo.id })
.execute(); .execute();
return { data: out[0]?.id }; return { data: out[0]?.id };
} }
async getPaymentInfo(id: number): Promise<Result<PaymentDetails>> { async getPaymentInfo(id: number): Promise<Result<PaymentDetails>> {
Logger.info(`Getting payment info with id ${id}`); Logger.info(`Getting payment info with id ${id}`);
const out = await this.db.query.paymentDetails.findFirst({ const out = await this.db.query.paymentInfo.findFirst({
where: eq(paymentDetails.id, id), where: eq(paymentInfo.id, id),
}); });
const parsed = paymentDetailsModel.safeParse(out); const parsed = paymentDetailsModel.safeParse(out);
if (parsed.error) { if (parsed.error) {
@@ -50,8 +50,8 @@ export class PaymentInfoRepository {
async deletePaymentInfo(id: number): Promise<Result<boolean>> { async deletePaymentInfo(id: number): Promise<Result<boolean>> {
Logger.info(`Deleting payment info with id ${id}`); Logger.info(`Deleting payment info with id ${id}`);
const out = await this.db const out = await this.db
.delete(paymentDetails) .delete(paymentInfo)
.where(eq(paymentDetails.id, id)) .where(eq(paymentInfo.id, id))
.execute(); .execute();
Logger.debug(out); Logger.debug(out);
return { data: true }; return { data: true };

View File

@@ -1,17 +1,17 @@
import { get } from "svelte/store"; import { ckFlowVM } from "$lib/domains/ckflow/view/ckflow.vm.svelte";
import { CheckoutStep } from "../../data/entities/index";
import { trpcApiStore } from "$lib/stores/api";
import { toast } from "svelte-sonner";
import { flightTicketStore } from "../../data/store";
import { newOrderModel } from "$lib/domains/order/data/entities"; import { newOrderModel } from "$lib/domains/order/data/entities";
import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte"; import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte";
import { paymentInfoVM } from "./payment-info-section/payment.info.vm.svelte";
import { import {
paymentDetailsPayloadModel, paymentDetailsPayloadModel,
PaymentMethod, PaymentMethod,
} from "$lib/domains/paymentinfo/data/entities"; } from "$lib/domains/paymentinfo/data/entities";
import { trpcApiStore } from "$lib/stores/api";
import { toast } from "svelte-sonner";
import { get } from "svelte/store";
import { CheckoutStep } from "../../data/entities/index";
import { flightTicketStore } from "../../data/store";
import { paymentInfoVM } from "./payment-info-section/payment.info.vm.svelte";
import { calculateTicketPrices } from "./total.calculator"; import { calculateTicketPrices } from "./total.calculator";
import { ckFlowVM } from "$lib/domains/ckflow/view/ckflow.vm.svelte";
class TicketCheckoutViewModel { class TicketCheckoutViewModel {
checkoutStep = $state(CheckoutStep.Initial); checkoutStep = $state(CheckoutStep.Initial);
@@ -93,7 +93,7 @@ class TicketCheckoutViewModel {
fullfilledPrice: validatedPrices.finalTotal, // Same as displayPrice fullfilledPrice: validatedPrices.finalTotal, // Same as displayPrice
pricePerPassenger: validatedPrices.pricePerPassenger, pricePerPassenger: validatedPrices.pricePerPassenger,
flightTicketInfoId: -1, flightTicketInfoId: -1,
paymentDetailsId: -1, paymentInfoId: -1,
}); });
if (parsed.error) { if (parsed.error) {
@@ -126,7 +126,7 @@ class TicketCheckoutViewModel {
flightTicketId: ticket.id, flightTicketId: ticket.id,
orderModel: parsed.data, orderModel: parsed.data,
passengerInfos: passengerInfoVM.passengerInfos, passengerInfos: passengerInfoVM.passengerInfos,
paymentDetails: pInfoParsed.data, paymentInfo: pInfoParsed.data,
refOIds: ticket.refOIds, refOIds: ticket.refOIds,
flowId: ckFlowVM.flowId, flowId: ckFlowVM.flowId,
}); });

View File

@@ -120,18 +120,20 @@ CREATE TABLE IF NOT EXISTS "order" (
"status" varchar(24), "status" varchar(24),
"product_id" integer, "product_id" integer,
"customer_info_id" integer, "customer_info_id" integer,
"payment_details_id" integer, "payment_info_id" integer,
"agent_id" text, "agent_id" text,
"created_at" timestamp DEFAULT now(), "created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now() "updated_at" timestamp DEFAULT now()
); );
--> statement-breakpoint --> statement-breakpoint
CREATE TABLE IF NOT EXISTS "payment_details" ( CREATE TABLE IF NOT EXISTS "payment_info" (
"id" serial PRIMARY KEY NOT NULL, "id" serial PRIMARY KEY NOT NULL,
"cardholder_name" varchar(128) NOT NULL, "cardholder_name" varchar(128) NOT NULL,
"card_number" varchar(20) NOT NULL, "card_number" varchar(20) NOT NULL,
"expiry" varchar(5) NOT NULL, "expiry" varchar(5) NOT NULL,
"cvv" varchar(6) NOT NULL, "cvv" varchar(6) NOT NULL,
"order_id" integer,
"product_id" integer,
"created_at" timestamp DEFAULT now(), "created_at" timestamp DEFAULT now(),
"updated_at" timestamp DEFAULT now() "updated_at" timestamp DEFAULT now()
); );
@@ -180,7 +182,7 @@ EXCEPTION
END $$; END $$;
--> statement-breakpoint --> statement-breakpoint
DO $$ BEGIN DO $$ BEGIN
ALTER TABLE "order" ADD CONSTRAINT "order_payment_details_id_payment_details_id_fk" FOREIGN KEY ("payment_details_id") REFERENCES "public"."payment_details"("id") ON DELETE cascade ON UPDATE no action; ALTER TABLE "order" ADD CONSTRAINT "order_payment_info_id_payment_info_id_fk" FOREIGN KEY ("payment_info_id") REFERENCES "public"."payment_info"("id") ON DELETE cascade ON UPDATE no action;
EXCEPTION EXCEPTION
WHEN duplicate_object THEN null; WHEN duplicate_object THEN null;
END $$; END $$;

View File

@@ -1,2 +0,0 @@
ALTER TABLE "payment_details" ADD COLUMN "order_id" integer;--> statement-breakpoint
ALTER TABLE "payment_details" ADD COLUMN "product_id" integer;

View File

@@ -1,5 +1,5 @@
{ {
"id": "77d95f59-9820-4cba-8a6a-cae76a8dff82", "id": "e8de9102-c79e-46ff-a25f-80e1039b6091",
"prevId": "00000000-0000-0000-0000-000000000000", "prevId": "00000000-0000-0000-0000-000000000000",
"version": "7", "version": "7",
"dialect": "postgresql", "dialect": "postgresql",
@@ -779,8 +779,8 @@
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
}, },
"payment_details_id": { "payment_info_id": {
"name": "payment_details_id", "name": "payment_info_id",
"type": "integer", "type": "integer",
"primaryKey": false, "primaryKey": false,
"notNull": false "notNull": false
@@ -834,12 +834,12 @@
"onDelete": "cascade", "onDelete": "cascade",
"onUpdate": "no action" "onUpdate": "no action"
}, },
"order_payment_details_id_payment_details_id_fk": { "order_payment_info_id_payment_info_id_fk": {
"name": "order_payment_details_id_payment_details_id_fk", "name": "order_payment_info_id_payment_info_id_fk",
"tableFrom": "order", "tableFrom": "order",
"tableTo": "payment_details", "tableTo": "payment_info",
"columnsFrom": [ "columnsFrom": [
"payment_details_id" "payment_info_id"
], ],
"columnsTo": [ "columnsTo": [
"id" "id"
@@ -867,8 +867,8 @@
"checkConstraints": {}, "checkConstraints": {},
"isRLSEnabled": false "isRLSEnabled": false
}, },
"public.payment_details": { "public.payment_info": {
"name": "payment_details", "name": "payment_info",
"schema": "", "schema": "",
"columns": { "columns": {
"id": { "id": {
@@ -901,6 +901,18 @@
"primaryKey": false, "primaryKey": false,
"notNull": true "notNull": true
}, },
"order_id": {
"name": "order_id",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"product_id": {
"name": "product_id",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"created_at": { "created_at": {
"name": "created_at", "name": "created_at",
"type": "timestamp", "type": "timestamp",

File diff suppressed because it is too large Load Diff

View File

@@ -5,15 +5,8 @@
{ {
"idx": 0, "idx": 0,
"version": "7", "version": "7",
"when": 1760987106438, "when": 1760987569532,
"tag": "0000_large_gertrude_yorkes", "tag": "0000_far_jack_power",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1760987289226,
"tag": "0001_wonderful_nico_minoru",
"breakpoints": true "breakpoints": true
} }
] ]

View File

@@ -44,10 +44,9 @@ export const order = pgTable("order", {
() => customerInfo.id, () => customerInfo.id,
{ onDelete: "cascade" }, { onDelete: "cascade" },
), ),
paymentDetailsId: integer("payment_details_id").references( paymentInfoId: integer("payment_info_id").references(() => paymentInfo.id, {
() => paymentDetails.id, onDelete: "cascade",
{ onDelete: "cascade" }, }),
),
agentId: text("agent_id").references(() => user.id, { onDelete: "set null" }), agentId: text("agent_id").references(() => user.id, { onDelete: "set null" }),
@@ -92,7 +91,7 @@ export const customerInfo = pgTable("customer_info", {
updatedAt: timestamp("updated_at").defaultNow(), updatedAt: timestamp("updated_at").defaultNow(),
}); });
export const paymentDetails = pgTable("payment_details", { export const paymentInfo = pgTable("payment_info", {
id: serial("id").primaryKey(), id: serial("id").primaryKey(),
cardholderName: varchar("cardholder_name", { length: 128 }).notNull(), cardholderName: varchar("cardholder_name", { length: 128 }).notNull(),
cardNumber: varchar("card_number", { length: 20 }).notNull(), cardNumber: varchar("card_number", { length: 20 }).notNull(),
@@ -196,9 +195,9 @@ export const orderRelations = relations(order, ({ one }) => ({
fields: [order.customerInfoId], fields: [order.customerInfoId],
references: [customerInfo.id], references: [customerInfo.id],
}), }),
paymentInfo: one(paymentDetails, { paymentInfo: one(paymentInfo, {
fields: [order.paymentDetailsId], fields: [order.paymentInfoId],
references: [paymentDetails.id], references: [paymentInfo.id],
}), }),
})); }));

View File

@@ -1,4 +1,5 @@
import { z } from "zod"; import { z } from "zod";
import { CheckoutStep } from "../../order/data/enums";
import { import {
CustomerInfo, CustomerInfo,
customerInfoModel, customerInfoModel,
@@ -7,7 +8,7 @@ import {
PaymentDetailsPayload, PaymentDetailsPayload,
paymentDetailsPayloadModel, paymentDetailsPayloadModel,
} from "../../paymentinfo/data/entities"; } from "../../paymentinfo/data/entities";
import { CheckoutStep } from "../../ticket/data/entities"; import { productModel } from "../../product/data";
// Define action types for the checkout flow // Define action types for the checkout flow
export enum CKActionType { export enum CKActionType {
@@ -57,24 +58,6 @@ export type PendingAction = z.infer<typeof pendingActionModel>;
export const pendingActionsModel = z.array(pendingActionModel); export const pendingActionsModel = z.array(pendingActionModel);
export type PendingActions = z.infer<typeof pendingActionsModel>; export type PendingActions = z.infer<typeof pendingActionsModel>;
export const ticketSummaryModel = z.object({
id: z.number().optional(),
ticketId: z.string().optional(),
departure: z.string(),
arrival: z.string(),
departureDate: z.string(),
returnDate: z.string().optional(),
flightType: z.string(),
cabinClass: z.string(),
priceDetails: z.object({
currency: z.string(),
displayPrice: z.number(),
basePrice: z.number().optional(),
discountAmount: z.number().optional(),
}),
});
export type TicketSummary = z.infer<typeof ticketSummaryModel>;
// Core flow information model - what's actually stored in Redis // Core flow information model - what's actually stored in Redis
export const flowInfoModel = z.object({ export const flowInfoModel = z.object({
id: z.coerce.number().optional(), id: z.coerce.number().optional(),
@@ -87,8 +70,8 @@ export const flowInfoModel = z.object({
isActive: z.boolean().default(true), isActive: z.boolean().default(true),
lastSyncedAt: z.string().datetime(), lastSyncedAt: z.string().datetime(),
ticketInfo: ticketSummaryModel.optional(), productInfo: productModel.optional(),
ticketId: z.number().nullable().optional(), productId: z.number().nullable().optional(),
personalInfoLastSyncedAt: z.string().datetime().optional(), personalInfoLastSyncedAt: z.string().datetime().optional(),
paymentInfoLastSyncedAt: z.string().datetime().optional(), paymentInfoLastSyncedAt: z.string().datetime().optional(),
@@ -119,7 +102,7 @@ export type FlowInfo = z.infer<typeof flowInfoModel>;
export const feCreateCheckoutFlowPayloadModel = z.object({ export const feCreateCheckoutFlowPayloadModel = z.object({
domain: z.string(), domain: z.string(),
refOIds: z.array(z.number()), refOIds: z.array(z.number()),
ticketId: z.number().optional(), productId: z.number().optional(),
}); });
export type FECreateCheckoutFlowPayload = z.infer< export type FECreateCheckoutFlowPayload = z.infer<
typeof feCreateCheckoutFlowPayloadModel typeof feCreateCheckoutFlowPayloadModel
@@ -130,7 +113,7 @@ export const createCheckoutFlowPayloadModel = z.object({
flowId: z.string(), flowId: z.string(),
domain: z.string(), domain: z.string(),
refOIds: z.array(z.number()), refOIds: z.array(z.number()),
ticketId: z.number().optional(), productId: z.number().optional(),
ipAddress: z.string().default(""), ipAddress: z.string().default(""),
userAgent: z.string().default(""), userAgent: z.string().default(""),
initialUrl: z.string().default(""), initialUrl: z.string().default(""),

View File

@@ -0,0 +1,8 @@
export enum CheckoutStep {
Setup = "SETUP",
Initial = "INITIAL",
Payment = "PAYMENT",
Verification = "VERIFICATION",
Confirmation = "CONFIRMATION",
Complete = "COMPLETE",
}