feat(checkout): refactor purchase completion flow and clear cart state

This commit is contained in:
2026-07-07 15:36:01 -03:00
parent 05ada5f613
commit e8d71bfd03
8 changed files with 41 additions and 14 deletions

View File

@@ -34,7 +34,8 @@ describe('CartService', () => {
beforeEach(() => { beforeEach(() => {
tenantServiceMock = { tenantServiceMock = {
getTenantApiUrl: vi.fn().mockReturnValue('http://api.test/tenants/acme') getTenantApiUrl: vi.fn().mockReturnValue('http://api.test/tenants/acme'),
tenant: vi.fn().mockReturnValue({ codigo: 'acme' })
}; };
TestBed.configureTestingModule({ TestBed.configureTestingModule({
@@ -74,6 +75,18 @@ describe('CartService', () => {
req.flush({ data: mockCart }); req.flush({ data: mockCart });
}); });
it('should clear cart state locally', () => {
service.clearCart();
expect(service.cart()).toEqual({
id: null,
tenant_codigo: 'acme',
status: 'active',
items: [],
subtotal: '0.00'
});
});
it('should add item and update signal', () => { it('should add item and update signal', () => {
service.addItem(10, 2).subscribe((res) => { service.addItem(10, 2).subscribe((res) => {
expect(res.data).toEqual(mockCart); expect(res.data).toEqual(mockCart);

View File

@@ -23,6 +23,16 @@ export class CartService {
return this.tenantService.getTenantApiUrl(); return this.tenantService.getTenantApiUrl();
} }
clearCart(): void {
this.cartState.set({
id: null,
tenant_codigo: this.tenantService.tenant()?.codigo ?? '',
status: 'active',
items: [],
subtotal: '0.00'
});
}
loadCart(): Observable<Cart> { loadCart(): Observable<Cart> {
return this.http return this.http
.get<ApiResponse<Cart>>(`${this.tenantApiUrl}/cart`, { .get<ApiResponse<Cart>>(`${this.tenantApiUrl}/cart`, {

View File

@@ -13,7 +13,6 @@ export interface CreatePurchasePayload {
} }
export interface PurchaseStatusResponse { export interface PurchaseStatusResponse {
payment_status: string | null;
status: string | null; status: string | null;
} }
@@ -47,9 +46,9 @@ export class CheckoutService {
return response; return response;
} }
async finalizePurchase(tenantCode: string, purchaseId: number): Promise<PurchaseStatusResponse> { async completePurchase(tenantCode: string, purchaseId: number): Promise<PurchaseStatusResponse> {
const response = await firstValueFrom( const response = await firstValueFrom(
this.http.post<{ data?: PurchaseStatusResponse } | PurchaseStatusResponse>(`${environment.url}tenants/${tenantCode}/compras/${purchaseId}/finalize`, {}) this.http.post<{ data?: PurchaseStatusResponse } | PurchaseStatusResponse>(`${environment.url}tenants/${tenantCode}/compras/${purchaseId}/complete`, {})
); );
const purchase = this.extractResponseData<PurchaseStatusResponse>(response); const purchase = this.extractResponseData<PurchaseStatusResponse>(response);
@@ -59,7 +58,6 @@ export class CheckoutService {
} }
return { return {
payment_status: purchase.payment_status ?? null,
status: purchase.status ?? null status: purchase.status ?? null
}; };
} }
@@ -76,7 +74,6 @@ export class CheckoutService {
} }
return { return {
payment_status: purchase.payment_status ?? null,
status: purchase.status ?? null status: purchase.status ?? null
}; };
} }

View File

@@ -21,7 +21,7 @@
(paymentMethodChange)="selectPaymentMethod($event)" (paymentMethodChange)="selectPaymentMethod($event)"
(copyTransferValue)="copyTransferValue($event.field, $event.value)" (copyTransferValue)="copyTransferValue($event.field, $event.value)"
(cancelStep)="onCancel()" (cancelStep)="onCancel()"
(finalize)="onFinalize()" (complete)="onComplete()"
/> />
</app-step> </app-step>
</app-stepper> </app-stepper>

View File

@@ -241,13 +241,20 @@ export class CheckoutPageComponent implements OnInit {
} }
} }
protected async onFinalize(): Promise<void> { protected async onComplete(): Promise<void> {
const purchaseId = this.createdPurchaseId(); const purchaseId = this.createdPurchaseId();
const tenant = this.tenantService.tenant();
if (!purchaseId) { if (!purchaseId || !tenant) {
return; return;
} }
void this.router.navigate(['/checkout/status', purchaseId]); try {
await this.checkoutService.completePurchase(tenant.codigo, purchaseId);
this.cartService.clearCart();
void this.router.navigate(['/checkout/status', purchaseId]);
} catch (error) {
console.error('Failed to complete purchase:', error);
}
} }
} }

View File

@@ -142,7 +142,7 @@
type="button" type="button"
hostClass="checkout-payment__action-btn" hostClass="checkout-payment__action-btn"
[disabled]="isGeneratingIntent()" [disabled]="isGeneratingIntent()"
(click)="finalize.emit()" (click)="complete.emit()"
> >
Finalizar compra Finalizar compra
</app-button> </app-button>

View File

@@ -25,7 +25,7 @@ export class CheckoutPaymentStepComponent {
readonly paymentMethodChange = output<PaymentMethod>(); readonly paymentMethodChange = output<PaymentMethod>();
readonly copyTransferValue = output<{ field: TransferField; value: string }>(); readonly copyTransferValue = output<{ field: TransferField; value: string }>();
readonly cancelStep = output<void>(); readonly cancelStep = output<void>();
readonly finalize = output<void>(); readonly complete = output<void>();
protected selectPaymentMethod(method: PaymentMethod): void { protected selectPaymentMethod(method: PaymentMethod): void {
this.paymentMethodChange.emit(method); this.paymentMethodChange.emit(method);

View File

@@ -85,11 +85,11 @@ export class PurchaseStatusPageComponent implements OnInit, OnDestroy {
} }
private resolveStatus(purchase: PurchaseStatusResponse): PurchaseStatusView { private resolveStatus(purchase: PurchaseStatusResponse): PurchaseStatusView {
if (purchase.payment_status === 'approved' || purchase.status === 'paid') { if (purchase.status === 'paid') {
return 'approved'; return 'approved';
} }
if (purchase.payment_status === 'rejected' || purchase.status === 'cancelled') { if (purchase.status === 'rejected' || purchase.status === 'cancelled') {
return 'rejected'; return 'rejected';
} }