feat(checkout): refactor purchase completion flow and clear cart state
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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`, {
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user