feat(commerce): replace mock purchases with real API calls

CLN-02: getPurchases() now calls GET /marketplace/orders;
requestRefund() calls POST /marketplace/orders/:id/refund.
Removed MOCK_PURCHASES constant. MSW handler updated.
This commit is contained in:
senke 2026-02-22 17:44:29 +01:00
parent 834fa1f979
commit 8e9431fe93
2 changed files with 95 additions and 46 deletions

View file

@ -273,6 +273,7 @@ export const handlersMarketplace = [
);
}),
// GET /marketplace/orders — user's orders (commerceService.getPurchases)
http.get('*/api/v1/marketplace/orders', () => {
return HttpResponse.json({
success: true,
@ -280,16 +281,33 @@ export const handlersMarketplace = [
{
id: 'order-1',
status: 'completed',
total_amount: 29.99,
total: 29.99,
created_at: '2024-01-01T00:00:00Z',
items: [
{
id: 'item-1',
product_id: 'prod-1',
title: 'Cyberpunk Drum Kit',
price: 29.99,
},
],
},
{
id: 'order-2',
status: 'completed',
total_amount: 49.99,
total: 49.99,
created_at: '2024-01-15T00:00:00Z',
items: [
{
id: 'item-2',
product_id: 'prod-2',
title: 'Ethereal Pads Vol. 1',
price: 49.99,
},
],
},
],
});
}),

View file

@ -1,6 +1,22 @@
import { apiClient } from './api/client';
import { apiClient } from '@/services/api/client';
import { Purchase, Product } from '../types';
interface OrderItemApi {
id?: string;
product_id: string;
price: number;
title?: string;
}
interface OrderApi {
id: string;
status: string;
total_amount?: number;
total?: number;
created_at: string;
items?: OrderItemApi[];
}
interface SellerSale {
order_id: string;
product_id: string;
@ -36,51 +52,63 @@ function formatRelativeDate(dateStr: string): string {
return d.toLocaleDateString();
}
const MOCK_PURCHASES: Purchase[] = [
{
id: 'p1',
orderId: 'ORD-9921',
date: '2023-10-24',
price: 29.99,
status: 'completed',
downloadUrl: '#',
license: { id: 'l1', name: 'Standard', price: 29.99, features: [] },
product: {
id: 'prod1',
title: 'Cyberpunk 2077 Drums',
type: 'pack',
price: 29.99,
currency: 'USD',
rating: 5,
coverUrl: 'https://picsum.photos/id/120/100/100',
author: 'Neon Audio',
} as Partial<Product> as Product,
},
{
id: 'p2',
orderId: 'ORD-9850',
date: '2023-10-15',
price: 49.99,
status: 'completed',
downloadUrl: '#',
license: { id: 'l2', name: 'Premium', price: 49.99, features: [] },
product: {
id: 'prod2',
title: 'Ethereal Pads Vol. 1',
type: 'pack',
price: 49.99,
currency: 'USD',
rating: 4,
coverUrl: 'https://picsum.photos/id/140/100/100',
author: 'Soundscapes',
} as Partial<Product> as Product,
},
];
function mapOrderToPurchases(order: OrderApi): Purchase[] {
const orderId = typeof order.id === 'string' ? order.id : String(order.id);
const dateStr = order.created_at
? new Date(order.created_at).toISOString().slice(0, 10)
: '';
const items = order.items ?? [];
if (items.length === 0) {
const total = order.total_amount ?? order.total ?? 0;
return [
{
id: orderId,
orderId,
date: dateStr,
price: total,
status: order.status,
downloadUrl: '#',
license: { id: 'lic-default', name: 'Standard', price: total, features: [] },
product: {
id: 'unknown',
title: 'Order',
type: 'pack',
price: total,
currency: 'USD',
coverUrl: '',
} as Partial<Product> as Product,
},
];
}
return items.map((item, idx) => {
const productId = item.product_id ?? 'unknown';
const price = item.price ?? 0;
const title = item.title ?? productId;
return {
id: `${orderId}-${item.id ?? idx}`,
orderId,
date: dateStr,
price,
status: order.status,
downloadUrl: '#',
license: { id: `lic-${productId}`, name: 'Standard', price, features: [] },
product: {
id: productId,
title,
type: 'pack',
price,
currency: 'USD',
coverUrl: '',
} as Partial<Product> as Product,
};
});
}
export const commerceService = {
getPurchases: async () => {
await new Promise((resolve) => setTimeout(resolve, 600));
return MOCK_PURCHASES;
getPurchases: async (): Promise<Purchase[]> => {
const response = await apiClient.get<OrderApi[]>('/marketplace/orders');
const orders = Array.isArray(response.data) ? response.data : [];
return orders.flatMap(mapOrderToPurchases);
},
// v0.401 M3: Real API call to GET /sell/sales
@ -128,8 +156,11 @@ export const commerceService = {
return (data as { top_products?: TopProductStats[] })?.top_products ?? [];
},
requestRefund: async (_orderId: string, _reason: string) => {
await new Promise((resolve) => setTimeout(resolve, 800));
requestRefund: async (orderId: string, reason: string) => {
await apiClient.post(`/marketplace/orders/${orderId}/refund`, {
reason: reason || 'Requested by customer',
details: '',
});
return { success: true };
},
};