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 737c0c0..61cf098 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,7 +21,6 @@ describe('CheckoutPageComponent payment validation', () => { cancelPurchase: ReturnType; generatePaymentIntent: ReturnType; getPurchase: ReturnType; - submitPurchaseForReview: ReturnType; withCustomLoading: ReturnType; }; let cartServiceStub: { @@ -61,7 +60,6 @@ describe('CheckoutPageComponent payment validation', () => { qr_data: { qr_code: 'qr-value' }, }), getPurchase: vi.fn().mockResolvedValue({ status: 'pending_payment' }), - submitPurchaseForReview: vi.fn().mockResolvedValue({ status: 'pending_payment' }), withCustomLoading: vi.fn(), }; checkoutServiceStub.withCustomLoading.mockReturnValue(checkoutServiceStub); @@ -196,38 +194,36 @@ describe('CheckoutPageComponent payment validation', () => { expect(checkoutServiceStub.getPurchase).not.toHaveBeenCalled(); }); - it('checks a transfer once and redirects to purchase status while pending', async () => { + it('checks the purchase detail once when the transfer was made', async () => { + checkoutServiceStub.getPurchase.mockResolvedValue({ status: 'pending_payment' }); const { component } = createComponent(); component.selectedPaymentMethod.set('transfer'); await component.onComplete(); - expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledTimes(1); - expect(component.transferValidationStatus()).toBe('pending'); + expect(checkoutServiceStub.getPurchase).toHaveBeenCalledWith('tenant-test', 25); + expect(component.transferValidationStatus()).toBe('error'); expect(cartServiceStub.clearCart).not.toHaveBeenCalled(); - expect(routerStub.navigate).toHaveBeenCalledWith(['/checkout/status', 25]); + expect(routerStub.navigate).not.toHaveBeenCalled(); await vi.advanceTimersByTimeAsync(30_000); - expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledTimes(1); - - await component.onComplete(); - expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledTimes(1); + expect(checkoutServiceStub.getPurchase).toHaveBeenCalledTimes(1); }); it('navigates after a transfer is confirmed as paid', async () => { - checkoutServiceStub.submitPurchaseForReview.mockResolvedValue({ status: 'paid' }); + checkoutServiceStub.getPurchase.mockResolvedValue({ status: 'paid' }); const { component } = createComponent(); component.selectedPaymentMethod.set('transfer'); await component.onComplete(); - expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledWith('tenant-test', 25); + expect(checkoutServiceStub.getPurchase).toHaveBeenCalledWith('tenant-test', 25); expect(cartServiceStub.clearCart).not.toHaveBeenCalled(); expect(routerStub.navigate).toHaveBeenCalledWith(['/checkout/status', 25]); }); it('shows a retryable state when transfer validation fails', async () => { vi.spyOn(console, 'error').mockImplementation(() => undefined); - checkoutServiceStub.submitPurchaseForReview.mockRejectedValue(new Error('network error')); + checkoutServiceStub.getPurchase.mockRejectedValue(new Error('network error')); const { component } = createComponent(); component.selectedPaymentMethod.set('transfer'); 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 116203a..59c6286 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 @@ -448,20 +448,16 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { try { const purchase = await this.checkoutService .withCustomLoading() - .submitPurchaseForReview(tenant.codigo, purchaseId); - - if (purchase.status === 'paid' || purchase.status === 'pending_payment') { - if (purchase.status === 'pending_payment') { - this.transferValidationStatus.set('pending'); - } + .getPurchase(tenant.codigo, purchaseId); + if (purchase.status === 'paid') { this.navigateToPurchaseStatus(purchaseId); return; } this.transferValidationStatus.set('error'); } catch (error) { - console.error('Failed to submit purchase for review:', error); + console.error('Failed to validate transfer payment:', error); this.transferValidationStatus.set('error'); } } diff --git a/src/app/features/store/pages/checkout-page/checkout-page.models.ts b/src/app/features/store/pages/checkout-page/checkout-page.models.ts index a932052..b9d2e86 100644 --- a/src/app/features/store/pages/checkout-page/checkout-page.models.ts +++ b/src/app/features/store/pages/checkout-page/checkout-page.models.ts @@ -3,7 +3,7 @@ import { FormControl, FormGroup } from '@angular/forms'; export type PaymentMethod = 'qr' | 'transfer'; export type TransferField = 'cvu' | 'alias'; export type QrPaymentStatus = 'idle' | 'waiting' | 'timed_out' | 'failed'; -export type TransferValidationStatus = 'idle' | 'checking' | 'pending' | 'error'; +export type TransferValidationStatus = 'idle' | 'checking' | 'error'; export interface PaymentMethodOption { id: PaymentMethod; diff --git a/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.html b/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.html index 1808163..46167a6 100644 --- a/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.html +++ b/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.html @@ -1,5 +1,5 @@
- @if (validationStatus() === 'pending' || validationStatus() === 'error') { + @if (validationStatus() === 'error') {
} + + @if (validationStatus() === 'checking') { +
+ + Verificando pago +
+ } } diff --git a/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.scss b/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.scss index def5cf0..e937bd4 100644 --- a/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.scss +++ b/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.scss @@ -1,4 +1,5 @@ .payment-panel__card { + position: relative; width: 100%; max-width: 320px; min-height: 400px; @@ -13,6 +14,40 @@ text-align: center; } +.payment-verification { + position: absolute; + inset: 0; + z-index: 2; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.45rem; + border-radius: inherit; + background: rgba(255, 255, 255, 0.82); + + &__spinner { + width: 30px; + height: 30px; + border: 4px solid rgba(17, 17, 17, 0.2); + border-top-color: #111111; + border-radius: 50%; + animation: payment-verification-spin 0.75s linear infinite; + } + + &__message { + color: #111111; + font-size: 0.72rem; + font-weight: 700; + } +} + +@keyframes payment-verification-spin { + to { + transform: rotate(360deg); + } +} + .payment-panel__title { max-width: 18rem; margin: 0; diff --git a/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.spec.ts b/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.spec.ts new file mode 100644 index 0000000..ec21f16 --- /dev/null +++ b/src/app/features/store/pages/checkout-page/components/checkout-payment-transfer/checkout-payment-transfer.component.spec.ts @@ -0,0 +1,50 @@ +import { getTestBed, TestBed } from '@angular/core/testing'; +import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing'; +import { beforeAll, describe, expect, it } from 'vitest'; + +import { CheckoutPaymentTransferComponent } from './checkout-payment-transfer.component'; + +describe('CheckoutPaymentTransferComponent', () => { + beforeAll(() => { + try { + getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting()); + } catch { + // Test environment may already be initialized by another setup entrypoint. + } + }); + + it('renders the payment verification overlay while checking the transfer', async () => { + await TestBed.configureTestingModule({ + imports: [CheckoutPaymentTransferComponent], + }).compileComponents(); + + const fixture = TestBed.createComponent(CheckoutPaymentTransferComponent); + fixture.componentRef.setInput('validationStatus', 'checking'); + fixture.detectChanges(); + + const overlay = fixture.nativeElement.querySelector('.payment-verification') as HTMLElement; + expect(overlay).not.toBeNull(); + expect(overlay.textContent).toContain('Verificando pago'); + expect(overlay.querySelector('.payment-verification__spinner')).not.toBeNull(); + }); + + it('shows the QR payment error and WhatsApp action when validation fails', async () => { + await TestBed.configureTestingModule({ + imports: [CheckoutPaymentTransferComponent], + }).compileComponents(); + + const fixture = TestBed.createComponent(CheckoutPaymentTransferComponent); + fixture.componentRef.setInput('validationStatus', 'error'); + fixture.componentRef.setInput('paymentAmount', 300000); + fixture.componentRef.setInput('whatsappUrl', 'https://wa.me/543412602222'); + fixture.detectChanges(); + + const element = fixture.nativeElement as HTMLElement; + const whatsapp = Array.from(element.querySelectorAll('button')).find((button) => + button.textContent?.includes('WhatsApp'), + ); + expect(element.textContent).toMatch(/No pudimos verificar el pago de \$\s*300\.000\./); + expect(whatsapp).toBeDefined(); + expect(element.querySelector('.payment-verification')).toBeNull(); + }); +});