import { Component, ElementRef, inject, ViewChild } from '@angular/core'; import { PaymentService } from '../../core/services/carnet/payment.service'; import { CommonModule } from '@angular/common'; import { Fees } from '../../core/models/carnet/fee'; import { CarnetService } from '../../core/services/carnet/carnet.service'; import { ApiErrorHandlerService } from '../../core/services/common/api-error-handler.service'; import { NavigationService } from '../../core/services/common/navigation.service'; import { NotificationService } from '../../core/services/common/notification.service'; import { StorageService } from '../../core/services/common/storage.service'; import { finalize } from 'rxjs'; import { AngularMaterialModule } from '../../shared/module/angular-material.module'; import { PaymentDetail } from '../../core/models/carnet/payment-details'; // This is necessary to tell TypeScript that a global 'paypal' object exists. declare var paypal: any; @Component({ selector: 'app-checkout', imports: [CommonModule, AngularMaterialModule], templateUrl: './checkout.component.html', styleUrl: './checkout.component.scss' }) export class CheckoutComponent { paymentCompleted: boolean = false; paymentError: boolean = false; isLoading: boolean = false; paymentDetails: any = null; currentApplicationDetails: { headerid: number, applicationName: string, applicationType: string } | null = null; changeInProgress: boolean = false; estimatedFees: Fees = {}; @ViewChild('paypalcontrol', { static: true }) paypalElement!: ElementRef; orderDetails: PaymentDetail = {}; orderTotal = 0.00; currency = 'USD'; private paymentService = inject(PaymentService); private notificationService = inject(NotificationService); private errorHandler = inject(ApiErrorHandlerService); private storageService = inject(StorageService); private navigationService = inject(NavigationService); private carnetService = inject(CarnetService); constructor() { this.currentApplicationDetails = this.storageService.get<{ headerid: number, applicationName: string, applicationType: string }>('currentapplication') this.orderDetails.headerid = this.currentApplicationDetails?.headerid; this.orderDetails.applicationName = this.currentApplicationDetails?.applicationName; this.orderDetails.description = ` 'Carnet Application - ${this.currentApplicationDetails?.headerid} - ${this.currentApplicationDetails?.applicationName}` } ngOnInit(): void { if (this.currentApplicationDetails?.headerid) { this.carnetService.getEstimatedFees(this.currentApplicationDetails?.headerid).subscribe({ next: (data: Fees) => { if (data) { this.estimatedFees = data; this.calculateTotal(); } }, error: (error: any) => { let errorMessage = this.errorHandler.handleApiError(error, 'Failed to get estimated fees'); this.notificationService.showError(errorMessage); console.error('Error getting estimated fees:', error); } }); } } ngAfterViewInit(): void { this.initConfig(); } initConfig(): void { paypal .Buttons({ style: { layout: 'vertical', // or 'horizontal' color: 'gold', shape: 'pill', label: 'pay', height: 35 // Height in pixels (default is ~35-45px) // 'paypal' – Standard PayPal button [defoult] // 'checkout' – "Checkout with PayPal" // 'buynow' – "Buy Now with PayPal" // 'pay' – "Pay with PayPal" // 'installment' – Displays installment information // 'subscribe' – For subscriptions // 'donate' – For donations // pay : Pay with [PayPal] // buynow : [PayPal] Buy Now // Error: Expected style.height to be between 25px and 55px - got 10px // width width usually doesn’t need to be set or must be reasonable. }, // 1. Call your server to set up the transaction createOrder: async (data: any, actions: any) => { try { // Call the createOrder method from your service const orderId = await this.paymentService.createOrder(this.orderDetails); this.orderDetails.orderId = orderId; return orderId; } catch (error) { this.notificationService.showError('Failed to initiate the payment'); console.error('Error creating order:', error); return; } }, // 2. Call your server to finalize the transaction onApprove: async (data: any, actions: any) => { try { // data.orderID is the ID of the transaction from PayPal await this.paymentService.completePayment(this.orderDetails); // You can now redirect the user or update the UI this.changeInProgress = true; this.carnetService.submitApplication(this.currentApplicationDetails?.headerid!).pipe(finalize(() => { this.changeInProgress = false; })).subscribe({ next: () => { this.notificationService.showSuccess('Application submitted successfully'); this.storageService.removeItem('currentapplication'); this.navigationService.navigate(["home"]); }, error: (error) => { this.notificationService.showError('Failed to submit application'); console.error('Error submitting the application', error); } }); } catch (error) { this.notificationService.showError('Failed to complete the payment'); console.error('Error completing the payment:', error); return; } }, // 3. Handle errors onError: (error: any) => { this.notificationService.showError('Payment transaction failed'); this.orderDetails.paymentStatus = 'Failed'; this.orderDetails.paymentErrorDetails = JSON.stringify(error); this.paymentService.logTransactionDetails(this.orderDetails); console.error('An error occurred during the transaction:', error); }, // 4. Handle cancellation onCancel: (data: any) => { this.orderDetails.paymentStatus = 'Cancelled'; if (this.orderDetails.orderId) { this.paymentService.logTransactionDetails(this.orderDetails); } this.onCancel(); } }).render(this.paypalElement.nativeElement) .catch((error: any) => { console.error('Failed to render PayPal Buttons:', error); }); } calculateTotal(): void { const validFees = Object.values(this.estimatedFees).filter( (fee): fee is number => typeof fee === 'number' ); this.orderTotal = validFees.reduce((total, fee) => total + fee, 0); this.orderDetails.price = this.orderTotal?.toString() ?? '0.00'; } onCancel(): void { this.storageService.removeItem('currentapplication'); let route: string = 'edit-carnet'; if (this.currentApplicationDetails?.applicationType === 'duplicate') { route = 'duplicate-carnet'; } else if (this.currentApplicationDetails?.applicationType === 'extend') { route = 'extend-carnet'; } else if (this.currentApplicationDetails?.applicationType === 'additional') { route = 'additional-sets'; } this.navigationService.navigate([route, this.currentApplicationDetails?.headerid], { state: { isEditMode: true }, queryParams: { applicationname: this.currentApplicationDetails?.applicationName, return: 'shipping' } }) } }