feat: add expires_at to PurchaseDetailResponse and update checkout logic for pending payments

This commit is contained in:
2026-08-03 17:04:07 -03:00
parent 2487546a75
commit f289e9e2e4
3 changed files with 32 additions and 8 deletions

View File

@@ -61,6 +61,7 @@ export interface PurchaseDetailResponse extends PurchaseStatusResponse {
user_id: number;
created_at: string | null;
payment_method: string | null;
expires_at: string | null;
dni: string | null;
transfer_payer_dni: string | null;
telefono: string | null;

View File

@@ -60,7 +60,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' }),
submitPurchaseForReview: vi.fn().mockResolvedValue({ status: 'pending_payment' }),
withCustomLoading: vi.fn(),
};
checkoutServiceStub.withCustomLoading.mockReturnValue(checkoutServiceStub);
@@ -199,20 +199,20 @@ describe('CheckoutPageComponent payment validation', () => {
component.selectedPaymentMethod.set('transfer');
await component.onComplete();
expect(checkoutServiceStub.getPurchase).toHaveBeenCalledTimes(1);
expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledTimes(1);
expect(component.transferValidationStatus()).toBe('pending');
expect(cartServiceStub.clearCart).not.toHaveBeenCalled();
expect(routerStub.navigate).toHaveBeenCalledWith(['/checkout/status', 25]);
await vi.advanceTimersByTimeAsync(30_000);
expect(checkoutServiceStub.getPurchase).toHaveBeenCalledTimes(1);
expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledTimes(1);
await component.onComplete();
expect(checkoutServiceStub.getPurchase).toHaveBeenCalledTimes(1);
expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledTimes(1);
});
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');
@@ -375,7 +375,7 @@ describe('CheckoutPageComponent payment validation', () => {
expect(component.qrPaymentStatus()).toBe('waiting');
});
it.each(['in_review', 'paid', 'cancelled', 'rejected', 'expired'])(
it.each(['paid', 'cancelled', 'rejected', 'expired'])(
'redirects a %s purchase to its status page',
async (status) => {
routeQueryParamMap = convertToParamMap({ purchase: 25 });
@@ -395,6 +395,25 @@ describe('CheckoutPageComponent payment validation', () => {
},
);
it('redirects a submitted pending payment purchase to its status page', async () => {
routeQueryParamMap = convertToParamMap({ purchase: 25 });
checkoutServiceStub.getPurchase.mockResolvedValue({
id: 25,
status: 'pending_payment',
payment_method: 'transfer',
expires_at: null,
transfer_payer_dni: null,
items: [],
subtotal: '100.00',
total: '100.00',
});
createComponent();
await Promise.resolve();
expect(routerStub.navigate).toHaveBeenCalledWith(['/checkout/status', 25]);
});
it('updates a purchase item while editing and refreshes checkout totals', async () => {
const updatedPurchase = {
id: 25,

View File

@@ -437,7 +437,11 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
.withCustomLoading()
.submitPurchaseForReview(tenant.codigo, purchaseId);
if (purchase.status === 'paid') {
if (purchase.status === 'paid' || purchase.status === 'pending_payment') {
if (purchase.status === 'pending_payment') {
this.transferValidationStatus.set('pending');
}
this.navigateToPurchaseStatus(purchaseId);
return;
}
@@ -576,7 +580,7 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
.getPurchase(tenant.codigo, purchaseId);
if (
purchase.status === 'in_review' ||
(purchase.status === 'pending_payment' && purchase.expires_at === null) ||
purchase.status === 'paid' ||
purchase.status === 'cancelled' ||
purchase.status === 'rejected' ||