Skip to content

Commit b77fce9

Browse files
committed
feat(payments): Enhance payment handling with response mappers and cache invalidation
1 parent 435cc73 commit b77fce9

7 files changed

Lines changed: 135 additions & 23 deletions

File tree

bun.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"@lucide/svelte": "^0.515.0",
1919
"@sveltejs/adapter-auto": "^6.0.1",
2020
"@sveltejs/adapter-node": "^5.2.13",
21-
"@sveltejs/kit": "^2.22.4",
21+
"@sveltejs/kit": "^2.22.5",
2222
"@sveltejs/vite-plugin-svelte": "^5.1.0",
2323
"@tailwindcss/typography": "^0.5.16",
2424
"@tailwindcss/vite": "^4.1.11",
@@ -40,7 +40,7 @@
4040
"vite": "^6.3.5"
4141
},
4242
"dependencies": {
43-
"commercify-api-client": "^1.2.10",
43+
"commercify-api-client": "^1.2.11",
4444
"lucide-svelte": "^0.515.0",
4545
"zod": "3.25.67"
4646
}

src/lib/mappers/order.mapper.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,32 @@ export const mapOrderSummary = (dto: OrderSummaryDTO): OrderSummary => {
3737
};
3838
};
3939

40+
export const orderSummaryResponseMapper = (
41+
dto: ResponseDTO<OrderSummaryDTO>
42+
): { data: OrderSummary | null; success: boolean; error?: string } => {
43+
if (!dto || !dto.data) {
44+
return {
45+
data: null,
46+
success: false,
47+
error: 'No order summary data available'
48+
};
49+
}
50+
51+
if (dto.error) {
52+
return {
53+
data: null,
54+
success: false,
55+
error: dto.error
56+
};
57+
}
58+
59+
return {
60+
data: mapOrderSummary(dto.data),
61+
success: dto.success,
62+
error: dto.error
63+
};
64+
};
65+
4066
export const orderListSummaryResponseMapper = (
4167
dto: ListResponseDTO<OrderSummaryDTO>
4268
): { data: PaginatedData<OrderSummary>; success: boolean; error?: string } => {
@@ -119,6 +145,8 @@ export const orderMapper = (dto: OrderDTO): Order => {
119145
// Extract payment details from transactions
120146
const paymentDetails = extractPaymentDetailsFromTransactions(dto.payment_transactions);
121147

148+
console.log('Payment Details:', paymentDetails);
149+
122150
return {
123151
id: dto.id.toString(),
124152
checkoutId: dto.checkout_id?.toString() || 'N/A',

src/lib/mappers/payments.mapper.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { ResponseDTO } from 'commercify-api-client';
2+
3+
export const paymentResponseMapper = (
4+
dto: ResponseDTO<string>
5+
): { data: string | null; success: boolean; error?: string } => {
6+
if (!dto || !dto.message) {
7+
return {
8+
data: null,
9+
success: false,
10+
error: 'No payment data available'
11+
};
12+
}
13+
14+
if (dto.error) {
15+
return {
16+
data: null,
17+
success: false,
18+
error: dto.error
19+
};
20+
}
21+
22+
return {
23+
data: dto.message,
24+
success: dto.success,
25+
error: dto.error
26+
};
27+
};

src/lib/server/commercify/api.ts

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ import {
4242
currenyListMapper,
4343
userResponseMapper,
4444
orderListSummaryResponseMapper,
45-
loginMapper
45+
loginMapper,
46+
orderSummaryResponseMapper
4647
} from '$lib/mappers';
4748
import { OrderCache, ProductCache } from '$lib/cache';
4849
import type { CreateProductInput, UpdateCategoryInput, UpdateProductInput } from '$lib/types';
4950
import { categoryResponseMapper } from '$lib/mappers/category.mapper';
51+
import { paymentResponseMapper } from '$lib/mappers/payments.mapper';
5052

5153
/**
5254
* Cached API client wrapper that adds caching to API operations
@@ -252,20 +254,59 @@ export class CachedCommercifyApiClient {
252254
),
253255

254256
updateOrderStatus: async (id: string, status: UpdateOrderStatusRequest) => {
255-
const result = await this.client.orders.updateOrderStatus(id, status, orderResponseMapper);
257+
const result = await this.client.orders.updateOrderStatus(
258+
id,
259+
status,
260+
orderSummaryResponseMapper
261+
);
262+
// Invalidate both specific order and order lists cache
256263
CacheInvalidator.invalidateOrder(id);
264+
await CacheInvalidator.invalidateOrderLists();
257265
return result;
258266
}
259267
};
260268
}
261269

262270
get payments() {
263271
return {
264-
capture: (paymentId: string, options: CapturePaymentRequest) =>
265-
this.client.admin.capturePayment(paymentId, options),
266-
cancel: (paymentId: string) => this.client.admin.cancelPayment(paymentId),
267-
refund: (paymentId: string, options: RefundPaymentRequest) =>
268-
this.client.admin.refundPayment(paymentId, options)
272+
capture: async (paymentId: string, options: CapturePaymentRequest, orderId?: string) => {
273+
const result = await this.client.admin.capturePayment(
274+
paymentId,
275+
options,
276+
paymentResponseMapper
277+
);
278+
// Invalidate order lists cache since payment status affects order listings
279+
await CacheInvalidator.invalidateOrderLists();
280+
// If order ID is provided, also invalidate specific order cache
281+
if (orderId) {
282+
CacheInvalidator.invalidateOrder(orderId);
283+
}
284+
return result;
285+
},
286+
cancel: async (paymentId: string, orderId?: string) => {
287+
const result = await this.client.admin.cancelPayment(paymentId, paymentResponseMapper);
288+
// Invalidate order lists cache since payment status affects order listings
289+
await CacheInvalidator.invalidateOrderLists();
290+
// If order ID is provided, also invalidate specific order cache
291+
if (orderId) {
292+
CacheInvalidator.invalidateOrder(orderId);
293+
}
294+
return result;
295+
},
296+
refund: async (paymentId: string, options: RefundPaymentRequest, orderId?: string) => {
297+
const result = await this.client.admin.refundPayment(
298+
paymentId,
299+
options,
300+
paymentResponseMapper
301+
);
302+
// Invalidate order lists cache since payment status affects order listings
303+
await CacheInvalidator.invalidateOrderLists();
304+
// If order ID is provided, also invalidate specific order cache
305+
if (orderId) {
306+
CacheInvalidator.invalidateOrder(orderId);
307+
}
308+
return result;
309+
}
269310
};
270311
}
271312

src/lib/server/commercify/cache.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,18 @@ export class CacheInvalidator {
220220
* Invalidates order cache
221221
*/
222222
static invalidateOrder(id: string): void {
223+
// Client-side cache invalidation
223224
OrderCache.invalidateOrder(id);
225+
// Server-side cache invalidation
226+
serverCache.invalidate(`order:${id}`);
227+
}
228+
229+
/**
230+
* Invalidates all order-related caches (lists, etc.)
231+
*/
232+
static async invalidateOrderLists(): Promise<void> {
233+
// Server-side cache invalidation
234+
serverCache.invalidatePattern('^orders:');
224235
}
225236

226237
/**
@@ -286,9 +297,7 @@ export class CacheHelpers {
286297
/**
287298
* Creates a higher-order function to handle mutations with cache invalidation
288299
*/
289-
static withCacheInvalidation<T>(
290-
invalidationFn: () => void | Promise<void>
291-
) {
300+
static withCacheInvalidation<T>(invalidationFn: () => void | Promise<void>) {
292301
return async (operation: () => Promise<T>): Promise<T> => {
293302
const result = await operation();
294303
await invalidationFn();

src/routes/admin/orders/[id]/+page.server.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,24 @@ export const load: PageServerLoad = async ({ params, locals, depends }) => {
3838
};
3939

4040
export const actions: Actions = {
41-
capture: async ({ request, locals }) => {
41+
capture: async ({ request, locals, params }) => {
4242
const { commercify } = locals;
4343
const data = await request.formData();
4444
const paymentId = data.get('paymentId') as string;
45+
const orderId = params.id;
4546

4647
if (!paymentId) {
4748
return fail(400, { error: 'Payment ID is required' });
4849
}
4950

5051
// Note: The API captures payment by orderId, but we validate paymentId from the form
51-
const result = await commercify.payments.capture(paymentId, {
52-
isFull: true
53-
});
52+
const result = await commercify.payments.capture(
53+
paymentId,
54+
{
55+
is_full: true
56+
},
57+
orderId
58+
);
5459

5560
if (!result.success) {
5661
return fail(400, {
@@ -64,16 +69,17 @@ export const actions: Actions = {
6469
};
6570
},
6671

67-
refund: async ({ locals, request }) => {
72+
refund: async ({ locals, request, params }) => {
6873
const { commercify } = locals;
6974
const data = await request.formData();
7075
const paymentId = data.get('paymentId') as string;
76+
const orderId = params.id;
7177

7278
if (!paymentId) {
7379
return fail(400, { error: 'Order ID is required' });
7480
}
7581

76-
const result = await commercify.payments.refund(paymentId, { isFull: true });
82+
const result = await commercify.payments.refund(paymentId, { is_full: true }, orderId);
7783

7884
if (!result.success) {
7985
return fail(400, {
@@ -87,16 +93,17 @@ export const actions: Actions = {
8793
};
8894
},
8995

90-
cancel: async ({ request, locals }) => {
96+
cancel: async ({ request, locals, params }) => {
9197
const { commercify } = locals;
9298
const data = await request.formData();
9399
const paymentId = data.get('paymentId') as string;
100+
const orderId = params.id;
94101

95102
if (!paymentId) {
96103
return fail(400, { error: 'Order ID is required' });
97104
}
98105

99-
const result = await commercify.payments.cancel(paymentId);
106+
const result = await commercify.payments.cancel(paymentId, orderId);
100107

101108
if (!result.success) {
102109
return fail(400, {

0 commit comments

Comments
 (0)