1- import { NextRequest , NextResponse } from " next/server" ;
1+ import { NextRequest , NextResponse } from ' next/server' ;
22
3- import { MoneyValueError } from " @/db/queries/shop/orders" ;
4- import { resolveLocaleAndCurrency } from " @/lib/shop/request-locale" ;
3+ import { MoneyValueError } from ' @/db/queries/shop/orders' ;
4+ import { resolveLocaleAndCurrency } from ' @/lib/shop/request-locale' ;
55
6- import { rehydrateCartItems } from "@/lib/services/products" ;
7- import { cartRehydratePayloadSchema } from "@/lib/validation/shop" ;
8- import { InvalidPayloadError , PriceConfigError } from "@/lib/services/errors" ;
6+ import { rehydrateCartItems } from '@/lib/services/products' ;
7+ import { cartRehydratePayloadSchema } from '@/lib/validation/shop' ;
8+ import { InvalidPayloadError , PriceConfigError } from '@/lib/services/errors' ;
9+ import { logError } from '@/lib/logging' ;
910
1011function normalizeCartPayload ( body : unknown ) {
11- if ( ! body || typeof body !== " object" ) return body ;
12+ if ( ! body || typeof body !== ' object' ) return body ;
1213 const { items, ...rest } = body as { items ?: unknown } ;
1314
1415 if ( ! Array . isArray ( items ) ) return body ;
1516
1617 return {
1718 ...rest ,
18- items : items . map ( ( item ) => {
19- if ( ! item || typeof item !== " object" ) return item ;
19+ items : items . map ( item => {
20+ if ( ! item || typeof item !== ' object' ) return item ;
2021 const { quantity, ...itemRest } = item as { quantity ?: unknown } ;
2122 const normalizedQuantity =
22- typeof quantity === " string" && quantity . trim ( ) . length > 0
23+ typeof quantity === ' string' && quantity . trim ( ) . length > 0
2324 ? Number ( quantity )
2425 : quantity ;
2526
@@ -28,73 +29,72 @@ function normalizeCartPayload(body: unknown) {
2829 } ;
2930}
3031
32+ function jsonError (
33+ status : number ,
34+ code : string ,
35+ message : string ,
36+ details ?: unknown
37+ ) {
38+ return NextResponse . json (
39+ { error : { code, message, ...( details ? { details } : { } ) } } ,
40+ { status }
41+ ) ;
42+ }
43+
3144export async function POST ( request : NextRequest ) {
3245 let body : unknown ;
3346
3447 try {
3548 body = await request . json ( ) ;
3649 } catch {
37- return NextResponse . json (
38- { error : "Unable to process cart data." } ,
39- { status : 400 }
40- ) ;
50+ return jsonError ( 400 , 'INVALID_PAYLOAD' , 'Unable to process cart data.' ) ;
4151 }
4252
4353 const normalizedBody = normalizeCartPayload ( body ) ;
4454 const parsedPayload = cartRehydratePayloadSchema . safeParse ( normalizedBody ) ;
4555
4656 if ( ! parsedPayload . success ) {
47- return NextResponse . json (
48- { error : "Invalid cart payload" , details : parsedPayload . error . format ( ) } ,
49- { status : 400 }
50- ) ;
57+ return jsonError ( 400 , 'INVALID_PAYLOAD' , 'Invalid cart payload' , {
58+ issues : parsedPayload . error . format ( ) ,
59+ } ) ;
5160 }
5261
5362 const { currency } = resolveLocaleAndCurrency ( request ) ;
5463
55-
5664 try {
5765 const { items } = parsedPayload . data ;
5866 const parsedResult = await rehydrateCartItems ( items , currency ) ;
5967 return NextResponse . json ( parsedResult ) ;
6068 } catch ( error ) {
61- console . error ( "Cart rehydrate failed" , error ) ;
69+ logError ( 'cart_rehydrate_failed' , error ) ;
6270
71+ // Missing price for locale currency is a CONTRACT error, not a 422.
6372 if ( error instanceof PriceConfigError ) {
64- return NextResponse . json (
65- {
66- code : error . code ,
67- message : error . message ,
68- details : { productId : error . productId , currency : error . currency } ,
69- } ,
70- { status : 422 }
71- ) ;
73+ return jsonError ( 400 , error . code , error . message , {
74+ productId : error . productId ,
75+ currency : error . currency ,
76+ } ) ;
7277 }
78+
79+ // DB misconfiguration / invalid stored money: treat as 500 (server fault),
80+ // but keep stable code for diagnostics.
7381 if ( error instanceof MoneyValueError ) {
74- return NextResponse . json (
82+ return jsonError (
83+ 500 ,
84+ 'PRICE_CONFIG_ERROR' ,
85+ 'Invalid price configuration for one or more products.' ,
7586 {
76- code : "PRICE_CONFIG_ERROR" ,
77- message : "Invalid price configuration for one or more products." ,
78- details : {
79- productId : error . productId ,
80- field : error . field ,
81- rawValue : error . rawValue ,
82- } ,
83- } ,
84- { status : 500 }
87+ productId : error . productId ,
88+ field : error . field ,
89+ rawValue : error . rawValue ,
90+ }
8591 ) ;
8692 }
8793
8894 if ( error instanceof InvalidPayloadError ) {
89- return NextResponse . json (
90- { code : error . code , message : error . message } ,
91- { status : 400 }
92- ) ;
95+ return jsonError ( 400 , error . code , error . message ) ;
9396 }
9497
95- return NextResponse . json (
96- { error : "Unable to rehydrate cart." } ,
97- { status : 500 }
98- ) ;
98+ return jsonError ( 500 , 'INTERNAL_ERROR' , 'Unable to rehydrate cart.' ) ;
9999 }
100100}
0 commit comments