Skip to content

Commit 4c1c79d

Browse files
committed
feat: enhance order and payment types, add checkout currency handling, and update order status display
1 parent 5c3705d commit 4c1c79d

6 files changed

Lines changed: 211 additions & 51 deletions

File tree

src/lib/server/api/commercify.ts

Lines changed: 173 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import type {
66
CreateProductVariantInput,
77
Currency,
88
Order,
9+
OrderStatus,
910
OrderSummary,
1011
PaginatedData,
12+
PaymentStatus,
1113
Product,
1214
ProductVariant,
1315
UpdateCategoryInput,
@@ -43,9 +45,15 @@ import {
4345
type CategoryDTO,
4446
type CreateCategoryRequest,
4547
type UpdateCategoryRequest,
46-
type UserDTO
48+
type UserDTO,
49+
type OrderDTO,
50+
type OrderStatus as ApiOrderStatus,
51+
type AppliedDiscountDTO,
52+
type CompleteCheckoutRequest,
53+
type CheckoutCompleteResponse,
54+
type OrderSummaryDTO
4755
} from './types';
48-
import type { Address, Checkout, CheckoutItem } from '$lib/types/checkout';
56+
import type { Address, Checkout, CheckoutItem, CompleteCheckoutInput } from '$lib/types/checkout';
4957
import type { CreateDiscount, Discount } from '$lib/types/discount';
5058
import type { CommercifyUser } from '$lib/types/user';
5159

@@ -94,7 +102,7 @@ export class CommercifyClient {
94102
try {
95103
const headers: Record<string, string> = {
96104
'Content-Type': 'application/json',
97-
'Origin': EnvironmentConfig.getOriginUrl(),
105+
Origin: EnvironmentConfig.getOriginUrl(),
98106
...(options.headers as Record<string, string>)
99107
};
100108

@@ -114,11 +122,11 @@ export class CommercifyClient {
114122
headers['Cookie'] = this.cookies;
115123
}
116124

117-
console.log('Making request to:', url, {
118-
headers: Object.keys(headers),
119-
hasAuthHeader: !!headers['Authorization'],
120-
origin: headers['Origin']
121-
});
125+
// console.log('Making request to:', url, {
126+
// headers: Object.keys(headers),
127+
// hasAuthHeader: !!headers['Authorization'],
128+
// origin: headers['Origin']
129+
// });
122130

123131
const response = await fetch(url, {
124132
headers,
@@ -591,8 +599,6 @@ export class CommercifyClient {
591599
})) || []
592600
};
593601

594-
console.log('Api request body:', apiData);
595-
596602
try {
597603
const response = await this.request<ResponseDTO<ProductDTO>>('/admin/products', {
598604
method: 'POST',
@@ -1015,11 +1021,27 @@ export class CommercifyClient {
10151021
/**
10161022
* Get or create a checkout session for the current user/session
10171023
*/
1018-
async getOrCreateCheckout(): Promise<{ success: boolean; data?: Checkout; error?: string }> {
1024+
async getOrCreateCheckout(
1025+
currency: string
1026+
): Promise<{ success: boolean; data?: Checkout; error?: string }> {
1027+
if (!currency) {
1028+
console.warn('No currency provided for checkout session');
1029+
return {
1030+
success: false,
1031+
error: 'Currency is required to get or create a checkout session'
1032+
};
1033+
}
1034+
1035+
const params = new URLSearchParams();
1036+
params.append('currency', currency);
1037+
10191038
try {
1020-
const response = await this.request<ResponseDTO<CheckoutDTO>>('/checkout', {
1021-
method: 'GET'
1022-
});
1039+
const response = await this.request<ResponseDTO<CheckoutDTO>>(
1040+
`/checkout?${params.toString()}`,
1041+
{
1042+
method: 'GET'
1043+
}
1044+
);
10231045

10241046
if (!response.success || !response.data) {
10251047
return {
@@ -1058,11 +1080,12 @@ export class CommercifyClient {
10581080
},
10591081
subtotal: dto.total_amount,
10601082
totalAmount: dto.final_amount,
1083+
shippingCost: dto.shipping_cost,
10611084
currency: dto.currency,
10621085
shippingDetails: dto.shipping_option
10631086
? this.mapShippingDetails(dto.shipping_option)
10641087
: undefined,
1065-
discountDetails: this.mapDiscountDetails(dto),
1088+
discountDetails: this.mapDiscountDetails(dto.applied_discount),
10661089
paymentProvider: dto.payment_provider,
10671090
status: dto.status,
10681091
createdAt: dto.created_at,
@@ -1083,30 +1106,28 @@ export class CommercifyClient {
10831106
};
10841107
}
10851108

1086-
private mapDiscountDetails(dto: CheckoutDTO) {
1087-
if (dto.discount_amount === 0 || !dto.applied_discount) return undefined;
1109+
private mapDiscountDetails(discount?: AppliedDiscountDTO) {
1110+
if (!discount) return undefined;
10881111

10891112
const validTypes = ['percentage', 'fixed'];
1090-
if (!validTypes.includes(dto.applied_discount.type)) {
1091-
console.warn(`Invalid discount type: ${dto.applied_discount.type}. Defaulting to 'fixed'.`);
1092-
dto.applied_discount.type = 'fixed';
1113+
if (!validTypes.includes(discount.type)) {
1114+
console.warn(`Invalid discount type: ${discount.type}. Defaulting to 'fixed'.`);
1115+
discount.type = 'fixed';
10931116
}
10941117

10951118
const validMethods = ['basket', 'item'];
10961119

1097-
if (!validMethods.includes(dto.applied_discount.method)) {
1098-
console.warn(
1099-
`Invalid discount method: ${dto.applied_discount.method}. Defaulting to 'basket'.`
1100-
);
1101-
dto.applied_discount.method = 'basket';
1120+
if (!validMethods.includes(discount.method)) {
1121+
console.warn(`Invalid discount method: ${discount.method}. Defaulting to 'basket'.`);
1122+
discount.method = 'basket';
11021123
}
11031124

11041125
return {
1105-
code: dto.applied_discount.code,
1106-
amount: dto.applied_discount.amount,
1107-
value: dto.applied_discount.value,
1108-
type: dto.applied_discount.type as 'percentage' | 'fixed',
1109-
method: dto.applied_discount.method as 'basket' | 'item'
1126+
code: discount.code,
1127+
amount: discount.amount,
1128+
value: discount.value,
1129+
type: discount.type as 'percentage' | 'fixed',
1130+
method: discount.method as 'basket' | 'item'
11101131
};
11111132
}
11121133

@@ -1154,7 +1175,6 @@ export class CommercifyClient {
11541175
body: JSON.stringify(data)
11551176
});
11561177

1157-
console.log('Checkout item added:', response.data?.items);
11581178
if (!response.success || !response.data) {
11591179
return {
11601180
success: false,
@@ -1495,10 +1515,129 @@ export class CommercifyClient {
14951515
/**
14961516
* Convert the current checkout session to an order
14971517
*/
1498-
async convertCheckoutToOrder(): Promise<{ success: boolean; data?: Order; error?: string }> {
1518+
async convertCheckoutToOrder(input: CompleteCheckoutInput): Promise<{
1519+
success: boolean;
1520+
data?: {
1521+
order: OrderSummary;
1522+
actionRequired: boolean;
1523+
redirect_url: string | null;
1524+
};
1525+
error?: string;
1526+
}> {
1527+
console.log('Converting checkout to order with input:', input);
1528+
1529+
const requestBody: CompleteCheckoutRequest = {
1530+
payment_provider: input.provider,
1531+
payment_data: {}
1532+
};
1533+
1534+
if (input.provider === 'stripe') {
1535+
requestBody.payment_data.card_details = {
1536+
card_number: input.cardDetails!.cardNumber,
1537+
expiry_month: parseInt(input.cardDetails!.expiryMonth, 10),
1538+
expiry_year: parseInt(input.cardDetails!.expiryYear, 10),
1539+
cvv: input.cardDetails!.cvc,
1540+
cardholder_name: 'N/A' // Placeholder, not used in API
1541+
};
1542+
} else if (input.provider === 'mobilepay') {
1543+
requestBody.payment_data = {
1544+
phone_number: input.phoneNumber!
1545+
};
1546+
}
1547+
1548+
console.log('Complete checkout request body:', requestBody);
1549+
1550+
try {
1551+
const response = await this.request<ResponseDTO<CheckoutCompleteResponse>>(
1552+
'/checkout/complete',
1553+
{
1554+
method: 'POST',
1555+
body: JSON.stringify(requestBody)
1556+
}
1557+
);
1558+
1559+
if (!response.success || !response.data) {
1560+
return {
1561+
success: false,
1562+
error: response.error || 'Failed to remove discount code'
1563+
};
1564+
}
1565+
1566+
return {
1567+
success: true,
1568+
data: {
1569+
order: this.mapOrderSummary(response.data.order),
1570+
actionRequired: response.data.action_required || false,
1571+
redirect_url: response.data.redirect_url || null
1572+
}
1573+
};
1574+
} catch (error) {
1575+
console.error(
1576+
'Error removing checkout discount:',
1577+
error instanceof Error ? error.message : String(error)
1578+
);
1579+
1580+
return {
1581+
success: false,
1582+
error: 'Failed to remove discount code'
1583+
};
1584+
}
1585+
}
1586+
1587+
mapOrder(dto: OrderDTO): Order {
14991588
return {
1500-
success: false,
1501-
error: 'This method is not implemented yet. Please check back later.'
1589+
id: dto.id.toString(),
1590+
checkoutId: dto.checkout_id?.toString() || 'N/A',
1591+
subtotal: dto.total_amount,
1592+
orderNumber: dto.order_number,
1593+
status: dto.status as OrderStatus,
1594+
totalAmount: dto.total_amount,
1595+
currency: dto.currency,
1596+
shippingAddress: this.mapAddress(dto.shipping_address),
1597+
billingAddress: this.mapAddress(dto.billing_address),
1598+
shippingCost: dto.shipping_cost,
1599+
customerDetails: {
1600+
fullName: dto.customer.full_name,
1601+
email: dto.customer.email,
1602+
phone: dto.customer.phone
1603+
},
1604+
items: dto.items.map((item) => ({
1605+
productName: item.product_name,
1606+
variantName: item.variant_name,
1607+
sku: item.sku,
1608+
price: item.unit_price,
1609+
quantity: item.quantity,
1610+
subtotal: item.total_price,
1611+
image: item.image_url === '' ? undefined : item.image_url
1612+
})),
1613+
shippingDetails: dto.shipping_details
1614+
? this.mapShippingDetails(dto.shipping_details)
1615+
: undefined,
1616+
discountDetails: this.mapDiscountDetails(dto.discount_details),
1617+
paymentProvider: dto.payment_details.provider,
1618+
createdAt: dto.created_at,
1619+
updatedAt: dto.updated_at
1620+
};
1621+
}
1622+
1623+
mapOrderSummary(dto: OrderSummaryDTO): OrderSummary {
1624+
return {
1625+
id: dto.id.toString(),
1626+
orderNumber: dto.order_number,
1627+
orderStatus: dto.status as OrderStatus,
1628+
totalAmount: {
1629+
amount: dto.total_amount,
1630+
currency: dto.currency
1631+
},
1632+
itemsCount: dto.order_lines_amount,
1633+
checkoutId: dto.checkout_id?.toString(),
1634+
customer: {
1635+
name: dto.customer.full_name,
1636+
email: dto.customer.email
1637+
},
1638+
paymentStatus: dto.payment_status as PaymentStatus,
1639+
createdAt: dto.created_at,
1640+
updatedAt: dto.updated_at
15021641
};
15031642
}
15041643

src/lib/server/api/types.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -434,14 +434,16 @@ export interface OrderDTO {
434434
shipping_details: ShippingOptionDTO;
435435
discount_details: AppliedDiscountDTO;
436436
customer: CustomerDetailsDTO;
437-
checkout_id?: string;
437+
checkout_id: string;
438438
created_at: string;
439439
updated_at: string;
440440
}
441441
export interface OrderSummaryDTO {
442442
id: number /* uint */;
443443
order_number: string;
444+
checkout_id: string;
444445
user_id: number /* uint */;
446+
customer: CustomerDetailsDTO;
445447
status: OrderStatus;
446448
payment_status: PaymentStatus;
447449
total_amount: number /* float64 */; // Subtotal (items only)
@@ -474,6 +476,7 @@ export interface OrderItemDTO {
474476
quantity: number /* int */;
475477
unit_price: number /* float64 */;
476478
total_price: number /* float64 */;
479+
image_url?: string;
477480
created_at: string;
478481
updated_at: string;
479482
}
@@ -517,15 +520,6 @@ export interface OrderSearchRequest {
517520
end_date?: string;
518521
pagination: PaginationDTO;
519522
}
520-
/**
521-
* ProcessPaymentRequest represents the data needed to process a payment
522-
*/
523-
export interface ProcessPaymentRequest {
524-
payment_method: PaymentMethod;
525-
payment_provider: PaymentProvider;
526-
card_details?: any /* service.CardDetails */;
527-
phone_number?: string;
528-
}
529523
/**
530524
* OrderStatus represents the status of an order
531525
*/

src/lib/types/checkout.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,22 @@ export interface Checkout {
4242
};
4343
subtotal: number;
4444
totalAmount: number;
45+
shippingCost: number;
4546
currency: string;
4647
paymentProvider?: string; // e.g., 'stripe', 'mobilepay'
4748
status: string; // e.g., 'pending', 'paid', 'shipped', 'completed', 'cancelled'
4849
createdAt: string;
4950
updatedAt: string;
5051
}
52+
53+
export interface CompleteCheckoutInput {
54+
provider: 'stripe' | 'mobilepay'; // e.g., 'stripe', 'mobilepay'
55+
phoneNumber?: string; // Required for mobilepay, optional for others
56+
// Required for stripe
57+
cardDetails?: {
58+
cardNumber: string; // e.g., '4242 4242 4242 4242'
59+
expiryMonth: string; // e.g., '12'
60+
expiryYear: string; // e.g., '2025'
61+
cvc: string; // e.g., '123'
62+
};
63+
}

src/lib/types/order.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
import type { Checkout, CheckoutItem } from './checkout';
22
import type { Price } from './common';
3-
import type { PaymentDetails } from './payment';
3+
import type { PaymentDetails, PaymentStatus } from './payment';
44

55
export interface OrderItem extends CheckoutItem {}
66

77
export interface Order extends Checkout {
88
orderNumber: string;
99
items: OrderItem[];
1010
checkoutId: string; // Reference to the original checkout session
11-
status: 'pending' | 'paid' | 'shipped' | 'completed' | 'cancelled';
11+
status: OrderStatus;
1212
paymentDetails?: PaymentDetails;
1313
}
1414

1515
export interface OrderSummary {
16+
id: string; // Unique identifier for the order
1617
orderNumber: string;
1718
totalAmount: Price; // Total amount for the order
18-
status: 'pending' | 'paid' | 'shipped' | 'completed' | 'cancelled';
19-
paymentStatus: 'pending' | 'paid' | 'failed'; // Status of the payment
19+
orderStatus: OrderStatus;
20+
paymentStatus: PaymentStatus; // Status of the payment
2021
createdAt: string; // ISO date string
2122
updatedAt: string; // ISO date string
2223
itemsCount: number; // Number of items in the order
@@ -26,3 +27,10 @@ export interface OrderSummary {
2627
email: string; // Customer email
2728
};
2829
}
30+
31+
export type OrderStatus = string;
32+
export const OrderStatusPending: OrderStatus = 'pending';
33+
export const OrderStatusPaid: OrderStatus = 'paid';
34+
export const OrderStatusShipped: OrderStatus = 'shipped';
35+
export const OrderStatusCancelled: OrderStatus = 'cancelled';
36+
export const OrderStatusCompleted: OrderStatus = 'completed';

0 commit comments

Comments
 (0)