feat(checkout): enhance payment submission with error handling and polling logic

This commit is contained in:
2026-08-20 15:53:33 -03:00
parent d0b1607ff4
commit cfa1091116
2 changed files with 44 additions and 5 deletions

View File

@@ -23,6 +23,7 @@ describe('CheckoutPageComponent payment validation', () => {
prepareItemEditing: ReturnType<typeof vi.fn>;
cancelPurchase: ReturnType<typeof vi.fn>;
generatePaymentIntent: ReturnType<typeof vi.fn>;
submitPurchaseForReview: ReturnType<typeof vi.fn>;
getPurchase: ReturnType<typeof vi.fn>;
withCustomLoading: ReturnType<typeof vi.fn>;
};
@@ -69,6 +70,7 @@ describe('CheckoutPageComponent payment validation', () => {
generatePaymentIntent: vi.fn().mockResolvedValue({
qr_data: { qr_code: 'qr-value' },
}),
submitPurchaseForReview: vi.fn().mockResolvedValue({ status: 'pending_payment' }),
getPurchase: vi.fn().mockResolvedValue({ status: 'pending_payment' }),
withCustomLoading: vi.fn(),
};
@@ -218,8 +220,9 @@ describe('CheckoutPageComponent payment validation', () => {
const { component } = createComponent();
component.selectedPaymentMethod.set('transfer');
component.onComplete();
await component.onComplete();
expect(component.transferValidationStatus()).toBe('checking');
expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledWith('tenant-test', 25);
expect(checkoutServiceStub.getPurchase).not.toHaveBeenCalled();
for (let attempt = 1; attempt <= 3; attempt += 1) {
@@ -241,7 +244,7 @@ describe('CheckoutPageComponent payment validation', () => {
const { component } = createComponent();
component.selectedPaymentMethod.set('transfer');
component.onComplete();
await component.onComplete();
await vi.advanceTimersByTimeAsync(3_000);
expect(checkoutServiceStub.getPurchase).toHaveBeenCalledWith('tenant-test', 25);
@@ -255,7 +258,7 @@ describe('CheckoutPageComponent payment validation', () => {
const { component } = createComponent();
component.selectedPaymentMethod.set('transfer');
component.onComplete();
await component.onComplete();
await vi.advanceTimersByTimeAsync(12_000);
expect(checkoutServiceStub.getPurchase).toHaveBeenCalledTimes(4);
@@ -264,6 +267,20 @@ describe('CheckoutPageComponent payment validation', () => {
expect(routerStub.navigate).not.toHaveBeenCalled();
});
it('does not poll when submitting a transfer for review fails', async () => {
vi.spyOn(console, 'error').mockImplementation(() => undefined);
checkoutServiceStub.submitPurchaseForReview.mockRejectedValue(new Error('network error'));
const { component } = createComponent();
component.selectedPaymentMethod.set('transfer');
await component.onComplete();
await vi.advanceTimersByTimeAsync(12_000);
expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledWith('tenant-test', 25);
expect(checkoutServiceStub.getPurchase).not.toHaveBeenCalled();
expect(component.transferValidationStatus()).toBe('error');
});
it('cancels transfer polling when the payment method changes or the component is destroyed', async () => {
const first = createComponent();
first.component.selectedPaymentMethod.set('transfer');

View File

@@ -514,7 +514,7 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
}
}
protected onComplete(): void {
protected async onComplete(): Promise<void> {
const purchaseId = this.createdPurchaseId();
const tenant = this.tenantService.tenant();
@@ -533,7 +533,29 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
this.transferPollingAttempts = 0;
const runId = this.transferPollingRunId;
this.scheduleTransferPoll(runId);
try {
const purchase = await this.checkoutService
.withCustomLoading()
.submitPurchaseForReview(tenant.codigo, purchaseId);
if (runId !== this.transferPollingRunId) {
return;
}
if (purchase.status === 'paid') {
this.navigateToPurchaseStatus(purchaseId);
return;
}
this.scheduleTransferPoll(runId);
} catch (error) {
console.error('Failed to submit transfer payment for review:', error);
if (runId === this.transferPollingRunId) {
this.transferValidationStatus.set('error');
}
}
}
private scheduleTransferPoll(runId: number): void {