diff --git a/src/app/core/services/checkout-countdown.service.spec.ts b/src/app/core/services/checkout-countdown.service.spec.ts index c104e28..212579c 100644 --- a/src/app/core/services/checkout-countdown.service.spec.ts +++ b/src/app/core/services/checkout-countdown.service.spec.ts @@ -54,4 +54,16 @@ describe('CheckoutCountdownService', () => { expect(service.remainingSeconds()).toBeNull(); }); + + it('keeps the active countdown when a partial checkout response omits timing fields', () => { + service.synchronize({ + expires_at: null, + expires_in_seconds: 10, + server_time: '2026-08-27T15:00:00.000Z', + }); + + service.synchronize({}); + + expect(service.remainingSeconds()).toBe(10); + }); }); diff --git a/src/app/core/services/checkout-countdown.service.ts b/src/app/core/services/checkout-countdown.service.ts index 6038230..3141fd3 100644 --- a/src/app/core/services/checkout-countdown.service.ts +++ b/src/app/core/services/checkout-countdown.service.ts @@ -16,9 +16,13 @@ export class CheckoutCountdownService implements OnDestroy { readonly remainingSeconds = this.remainingSecondsState.asReadonly(); - synchronize(timing: CheckoutTiming): void { + synchronize(timing: Partial): void { const remainingSeconds = this.resolveRemainingSeconds(timing); + if (remainingSeconds === undefined) { + return; + } + if (remainingSeconds === null) { this.clear(); return; @@ -43,9 +47,15 @@ export class CheckoutCountdownService implements OnDestroy { this.clear(); } - private resolveRemainingSeconds(timing: CheckoutTiming): number | null { - const expiresAt = timing.expires_at ? Date.parse(timing.expires_at) : Number.NaN; - const serverTime = Date.parse(timing.server_time); + private resolveRemainingSeconds(timing: Partial): number | null | undefined { + if (timing.expires_at === null && timing.expires_in_seconds === null) { + return null; + } + + const expiresAt = + typeof timing.expires_at === 'string' ? Date.parse(timing.expires_at) : Number.NaN; + const serverTime = + typeof timing.server_time === 'string' ? Date.parse(timing.server_time) : Number.NaN; if (Number.isFinite(expiresAt) && Number.isFinite(serverTime)) { return Math.max(0, Math.ceil((expiresAt - serverTime) / 1_000)); @@ -62,7 +72,7 @@ export class CheckoutCountdownService implements OnDestroy { return Math.max(0, Math.ceil((expiresAt - Date.now()) / 1_000)); } - return null; + return undefined; } private updateRemainingSeconds(): void { 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 c9d0913..2802068 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 @@ -177,7 +177,6 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.stopQrPolling(); this.stopTransferPolling(); - this.checkoutCountdownService.clear(); } private mapPurchaseItemToMock(item: PurchaseDetailItemResponse): CartItemMock { @@ -287,6 +286,7 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { const tenant = this.tenantService.tenant(); const purchaseId = this.createdPurchaseId(); if (!tenant || !purchaseId) { + this.checkoutCountdownService.clear(); return true; } @@ -297,6 +297,7 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { await firstValueFrom(this.cartService.loadCart()); this.createdPurchaseId.set(null); this.createdPurchase.set(null); + this.checkoutCountdownService.clear(); this.navigationStarted = true; return true; @@ -310,6 +311,7 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { }); this.createdPurchaseId.set(null); this.createdPurchase.set(null); + this.checkoutCountdownService.clear(); this.navigationStarted = true; return true; } @@ -671,6 +673,7 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { this.navigationStarted = true; this.stopQrPolling(); this.stopTransferPolling(); + this.checkoutCountdownService.clear(); void this.router.navigate(['/checkout/status', purchaseId]); } @@ -792,6 +795,7 @@ export class CheckoutPageComponent implements OnInit, OnDestroy { this.navigationStarted = true; this.stopQrPolling(); this.stopTransferPolling(); + this.checkoutCountdownService.clear(); void this.router.navigate(['/checkout/status', purchaseId], { queryParams: { status: 'expired' }, });