feat(checkout): enhance payment submission with error handling and polling logic
This commit is contained in:
@@ -23,6 +23,7 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
prepareItemEditing: ReturnType<typeof vi.fn>;
|
prepareItemEditing: ReturnType<typeof vi.fn>;
|
||||||
cancelPurchase: ReturnType<typeof vi.fn>;
|
cancelPurchase: ReturnType<typeof vi.fn>;
|
||||||
generatePaymentIntent: ReturnType<typeof vi.fn>;
|
generatePaymentIntent: ReturnType<typeof vi.fn>;
|
||||||
|
submitPurchaseForReview: ReturnType<typeof vi.fn>;
|
||||||
getPurchase: ReturnType<typeof vi.fn>;
|
getPurchase: ReturnType<typeof vi.fn>;
|
||||||
withCustomLoading: ReturnType<typeof vi.fn>;
|
withCustomLoading: ReturnType<typeof vi.fn>;
|
||||||
};
|
};
|
||||||
@@ -69,6 +70,7 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
generatePaymentIntent: vi.fn().mockResolvedValue({
|
generatePaymentIntent: vi.fn().mockResolvedValue({
|
||||||
qr_data: { qr_code: 'qr-value' },
|
qr_data: { qr_code: 'qr-value' },
|
||||||
}),
|
}),
|
||||||
|
submitPurchaseForReview: vi.fn().mockResolvedValue({ status: 'pending_payment' }),
|
||||||
getPurchase: vi.fn().mockResolvedValue({ status: 'pending_payment' }),
|
getPurchase: vi.fn().mockResolvedValue({ status: 'pending_payment' }),
|
||||||
withCustomLoading: vi.fn(),
|
withCustomLoading: vi.fn(),
|
||||||
};
|
};
|
||||||
@@ -218,8 +220,9 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
const { component } = createComponent();
|
const { component } = createComponent();
|
||||||
component.selectedPaymentMethod.set('transfer');
|
component.selectedPaymentMethod.set('transfer');
|
||||||
|
|
||||||
component.onComplete();
|
await component.onComplete();
|
||||||
expect(component.transferValidationStatus()).toBe('checking');
|
expect(component.transferValidationStatus()).toBe('checking');
|
||||||
|
expect(checkoutServiceStub.submitPurchaseForReview).toHaveBeenCalledWith('tenant-test', 25);
|
||||||
expect(checkoutServiceStub.getPurchase).not.toHaveBeenCalled();
|
expect(checkoutServiceStub.getPurchase).not.toHaveBeenCalled();
|
||||||
|
|
||||||
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
||||||
@@ -241,7 +244,7 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
const { component } = createComponent();
|
const { component } = createComponent();
|
||||||
component.selectedPaymentMethod.set('transfer');
|
component.selectedPaymentMethod.set('transfer');
|
||||||
|
|
||||||
component.onComplete();
|
await component.onComplete();
|
||||||
await vi.advanceTimersByTimeAsync(3_000);
|
await vi.advanceTimersByTimeAsync(3_000);
|
||||||
|
|
||||||
expect(checkoutServiceStub.getPurchase).toHaveBeenCalledWith('tenant-test', 25);
|
expect(checkoutServiceStub.getPurchase).toHaveBeenCalledWith('tenant-test', 25);
|
||||||
@@ -255,7 +258,7 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
const { component } = createComponent();
|
const { component } = createComponent();
|
||||||
component.selectedPaymentMethod.set('transfer');
|
component.selectedPaymentMethod.set('transfer');
|
||||||
|
|
||||||
component.onComplete();
|
await component.onComplete();
|
||||||
await vi.advanceTimersByTimeAsync(12_000);
|
await vi.advanceTimersByTimeAsync(12_000);
|
||||||
|
|
||||||
expect(checkoutServiceStub.getPurchase).toHaveBeenCalledTimes(4);
|
expect(checkoutServiceStub.getPurchase).toHaveBeenCalledTimes(4);
|
||||||
@@ -264,6 +267,20 @@ describe('CheckoutPageComponent payment validation', () => {
|
|||||||
expect(routerStub.navigate).not.toHaveBeenCalled();
|
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 () => {
|
it('cancels transfer polling when the payment method changes or the component is destroyed', async () => {
|
||||||
const first = createComponent();
|
const first = createComponent();
|
||||||
first.component.selectedPaymentMethod.set('transfer');
|
first.component.selectedPaymentMethod.set('transfer');
|
||||||
|
|||||||
@@ -514,7 +514,7 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onComplete(): void {
|
protected async onComplete(): Promise<void> {
|
||||||
const purchaseId = this.createdPurchaseId();
|
const purchaseId = this.createdPurchaseId();
|
||||||
const tenant = this.tenantService.tenant();
|
const tenant = this.tenantService.tenant();
|
||||||
|
|
||||||
@@ -533,7 +533,29 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
|
|||||||
this.transferPollingAttempts = 0;
|
this.transferPollingAttempts = 0;
|
||||||
|
|
||||||
const runId = this.transferPollingRunId;
|
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 {
|
private scheduleTransferPoll(runId: number): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user