From 9bb3374ba0443492d78df44b3966a681933c9639 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Wed, 29 Jul 2026 09:20:17 -0300 Subject: [PATCH] feat: add submitPurchaseForReview method and update checkout flow to handle purchase review status --- src/app/core/services/checkout.service.ts | 19 ++++++++++++++++ .../checkout-page.component.spec.ts | 22 ++++++++----------- .../checkout-page/checkout-page.component.ts | 19 ++++++++-------- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/app/core/services/checkout.service.ts b/src/app/core/services/checkout.service.ts index 1b1accd..fdb9d08 100644 --- a/src/app/core/services/checkout.service.ts +++ b/src/app/core/services/checkout.service.ts @@ -201,6 +201,25 @@ export class CheckoutService { }; } + async submitPurchaseForReview( + tenantCode: string, + purchaseId: number, + ): Promise { + const response = await firstValueFrom( + this.http.post<{ data?: PurchaseStatusResponse } | PurchaseStatusResponse>( + `${environment.url}tenants/${tenantCode}/compras/${purchaseId}/review`, + {}, + ), + ); + + const purchase = this.extractResponseData(response); + if (!purchase) { + throw new Error('Error al enviar la compra a revisi\u00f3n.'); + } + + return { status: purchase.status ?? null }; + } + async cancelPurchase(tenantCode: string, purchaseId: number): Promise { const response = await firstValueFrom( this.http.post<{ data?: PurchaseStatusResponse } | PurchaseStatusResponse>( diff --git a/src/app/features/store/pages/checkout-page/checkout-page.component.spec.ts b/src/app/features/store/pages/checkout-page/checkout-page.component.spec.ts index 4924c6f..b9f4845 100644 --- a/src/app/features/store/pages/checkout-page/checkout-page.component.spec.ts +++ b/src/app/features/store/pages/checkout-page/checkout-page.component.spec.ts @@ -21,6 +21,7 @@ describe('CheckoutPageComponent payment validation', () => { cancelPurchase: ReturnType; generatePaymentIntent: ReturnType; getPurchase: ReturnType; + submitPurchaseForReview: ReturnType; }; let cartServiceStub: { cart: ReturnType; @@ -58,6 +59,7 @@ describe('CheckoutPageComponent payment validation', () => { qr_data: { qr_code: 'qr-value' }, }), getPurchase: vi.fn().mockResolvedValue({ status: 'pending_payment' }), + submitPurchaseForReview: vi.fn().mockResolvedValue({ status: 'in_review' }), }; cartServiceStub = { cart: signal({ @@ -189,25 +191,19 @@ describe('CheckoutPageComponent payment validation', () => { expect(checkoutServiceStub.getPurchase).not.toHaveBeenCalled(); }); - it('shows the verification error without redirecting while a transfer is pending', async () => { + it('submits a transfer for review and navigates to its status page', async () => { const { component } = createComponent(); component.selectedPaymentMethod.set('transfer'); await component.onComplete(); - expect(checkoutServiceStub.getPurchase).toHaveBeenCalledTimes(1); - expect(component.transferValidationStatus()).toBe('pending'); + + expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledWith('tenant-test', 25); expect(cartServiceStub.clearCart).not.toHaveBeenCalled(); - expect(routerStub.navigate).not.toHaveBeenCalled(); - - await vi.advanceTimersByTimeAsync(30_000); - expect(checkoutServiceStub.getPurchase).toHaveBeenCalledTimes(1); - - await component.onComplete(); - expect(checkoutServiceStub.getPurchase).toHaveBeenCalledTimes(2); + expect(routerStub.navigate).toHaveBeenCalledWith(['/checkout/status', 25]); }); it('navigates after a transfer is confirmed as paid', async () => { - checkoutServiceStub.getPurchase.mockResolvedValue({ status: 'paid' }); + checkoutServiceStub.submitPurchaseForReview.mockResolvedValue({ status: 'paid' }); const { component } = createComponent(); component.selectedPaymentMethod.set('transfer'); @@ -219,7 +215,7 @@ describe('CheckoutPageComponent payment validation', () => { it('shows a retryable state when transfer validation fails', async () => { vi.spyOn(console, 'error').mockImplementation(() => undefined); - checkoutServiceStub.getPurchase.mockRejectedValue(new Error('network error')); + checkoutServiceStub.submitPurchaseForReview.mockRejectedValue(new Error('network error')); const { component } = createComponent(); component.selectedPaymentMethod.set('transfer'); @@ -365,7 +361,7 @@ describe('CheckoutPageComponent payment validation', () => { expect(component.qrPaymentStatus()).toBe('waiting'); }); - it.each(['paid', 'cancelled', 'rejected', 'expired'])( + it.each(['in_review', 'paid', 'cancelled', 'rejected', 'expired'])( 'redirects a %s purchase to its status page', async (status) => { routeQueryParamMap = convertToParamMap({ purchase: 25 }); diff --git a/src/app/features/store/pages/checkout-page/checkout-page.component.ts b/src/app/features/store/pages/checkout-page/checkout-page.component.ts index 35f0296..d802a94 100644 --- a/src/app/features/store/pages/checkout-page/checkout-page.component.ts +++ b/src/app/features/store/pages/checkout-page/checkout-page.component.ts @@ -438,21 +438,19 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { this.transferValidationStatus.set('checking'); try { - const purchase = await this.checkoutService.getPurchase(tenant.codigo, purchaseId); + const purchase = await this.checkoutService.submitPurchaseForReview( + tenant.codigo, + purchaseId, + ); - if (purchase.status === 'paid') { - this.handleConfirmedPayment(purchaseId); + if (purchase.status === 'in_review' || purchase.status === 'paid') { + this.navigateToPurchaseStatus(purchaseId); return; } - if (purchase.status === 'pending_payment') { - this.transferValidationStatus.set('pending'); - return; - } - - this.transferValidationStatus.set('pending'); + this.transferValidationStatus.set('error'); } catch (error) { - console.error('Failed to validate transfer payment:', error); + console.error('Failed to submit purchase for review:', error); this.transferValidationStatus.set('error'); } } @@ -580,6 +578,7 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { const purchase = await this.checkoutService.getPurchase(tenant.codigo, purchaseId); if ( + purchase.status === 'in_review' || purchase.status === 'paid' || purchase.status === 'cancelled' || purchase.status === 'rejected' ||