Preview:
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { Md5 } from 'ts-md5';
import { environment } from '../../environments/environment';
import { OrderService } from '../Services/order.service';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UserService } from '../Services/userprofile.service';
import { CartItemViewModel, OrderViewModel } from '../shared/order';
import { PaymentViewModel } from '../shared/payment';
import { PaymentService } from '../Services/payment.service';

declare global {
  interface Window {
    payfast_do_onsite_payment: (param1: any, callback: any) => any;
  }
}

@Component({
  selector: 'app-payfast',
  standalone: true,
  imports: [],
  templateUrl: './payfast.component.html',
  styleUrl: './payfast.component.css'
})
export class PayfastComponent implements OnInit {
  memberId!: number;

  constructor(private router : Router, private orderService : OrderService, private paymentService : PaymentService , private userService: UserService, private formBuilder: FormBuilder, private snackBar: MatSnackBar) {
    
  }

  ngOnInit(): void {
    this.fetchMemberId();
  }

  getSignature(data : Map<string, string>) : string {
    let tmp = new URLSearchParams();
    data.forEach((v, k)=> {
      tmp.append(k, v)
    });
    let queryString = tmp.toString();
    let sig = Md5.hashStr(queryString);
    return sig;
  }

  async doOnSitePayment() {
    await this.fetchMemberId();

    let onSiteUserData = new Map<string, string>();
    onSiteUserData.set("merchant_id", "10033427")
    onSiteUserData.set("merchant_key", "mu83ipbgas9p7")

    onSiteUserData.set('return_url', window.location.origin + '/payment-success')
    onSiteUserData.set('cancel_url', window.location.origin + '/payment-cancel')

    // Gather required user data from orderService or other sources
    const userData = this.orderService.getUserData();
    onSiteUserData.set("email_address", userData.email);
    
    // Set amount and item_name
    this.orderService.getTotalAmount().subscribe(amount => {
      onSiteUserData.set('amount', amount.toString());
      onSiteUserData.set('item_name', 'Cart Purchase');

      // Optional passphrase for added security
      onSiteUserData.set('passphrase', 'HelloWorldHello'); // Use if you have a passphrase

      let signature = this.getSignature(onSiteUserData);
      onSiteUserData.set('signature', signature);

      let formData = new FormData();
      onSiteUserData.forEach((val, key) => {
        formData.append(key, val);
      });

      fetch(environment.payfastOnsiteEndpoint, {
        method: 'POST',
        body: formData,
        redirect: 'follow'
      })
      .then(response => response.json())
      .then(respJson => {
          let uuid = respJson['uuid'];
          window.payfast_do_onsite_payment({ 'uuid': uuid }, (res: any) => {
            if (res == true) {
              this.createOrder().then((orderResponse) => {
                this.createPayment(orderResponse);
                this.snackBar.open('Payment Successful', 'Close', { duration: 5000 });
              });         
            } else {
              this.snackBar.open('Payment Failed', 'Close', { duration: 5000 });              
            }
          });
        })
        .catch(error => {
          console.error('Error processing payment:', error);
          this.router.navigate(['/cancel']);
        });
      });
    }

    //order
    private fetchMemberId() {
      const user = localStorage.getItem('User');
      if (user) {
        const userData = JSON.parse(user);
        this.memberId = userData.userId;
    
        // Optional: Fetch and validate member details if needed
        this.userService.getMemberByUserId(this.memberId).subscribe({
          next: (member) => {
            if (member && member.member_ID) {
              this.memberId = member.member_ID;
              console.log('Member ID:', this.memberId); // Check if this logs the correct ID
            } else {
              console.error('Member ID is undefined in the response');
              this.snackBar.open('Failed to retrieve member information', 'Close', { duration: 5000 });
            }
          },
          error: (error) => {
            console.error('Error fetching member:', error);
            this.snackBar.open('Failed to retrieve member information', 'Close', { duration: 5000 });
          }
        });
      } else {
        this.snackBar.open('User not logged in', 'Close', { duration: 5000 });
        this.router.navigate(['/login']);
      }
    }

    private createOrder(): Promise<OrderViewModel> {
      return new Promise((resolve, reject) => {
        if (this.memberId === undefined) {
          this.snackBar.open('Member ID is not available', 'Close', { duration: 5000 });
          reject('Member ID is not available');
        } else {
          this.orderService.getCartItems().subscribe({
            next: (cartItems) => {
              const order = this.prepareOrderDetails(cartItems);
              this.orderService.createOrder(order).subscribe({
                next: (orderResponse) => {
                  this.snackBar.open('Order Created Successfully', 'Close', { duration: 5000 });
                  resolve(orderResponse);
                },
                error: (error) => {
                  console.error('Error creating order:', error);
                  this.snackBar.open('Failed to create order', 'Close', { duration: 5000 });
                  reject(error);
                }
              });
            },
            error: (error) => {
              console.error('Error fetching cart items:', error);
              this.snackBar.open('Failed to fetch cart items', 'Close', { duration: 5000 });
              reject(error);
            }
          });
        }
      });
    }
    
  private prepareOrderDetails(cartItems: CartItemViewModel[]): OrderViewModel {
    const order: OrderViewModel = {
      order_ID: 0, // ID will be generated by backend
      member_ID: this.memberId,
      order_Date: new Date().toISOString(),
      total_Price: this.calculateTotalPrice(cartItems),
      order_Status_ID: 1, // Ready for Collection
      isCollected: false,
      orderLines: cartItems.map(item => ({
        order_Line_ID: 0, // ID will be generated by backend
        product_ID: item.product_ID,
        product_Name: item.product_Name,
        quantity: item.quantity,
        unit_Price: item.unit_Price
      }))
    };
  
    console.log('Prepared order details:', order);
    return order;
  }

  // Helper method to calculate the total price
  private calculateTotalPrice(cartItems: CartItemViewModel[]): number {
    return cartItems.reduce((total, item) => total + (item.unit_Price * item.quantity), 0);
  }

  private createPayment(order: OrderViewModel) {
    const paymentData: PaymentViewModel = {
      payment_ID: 0,
      amount: order.total_Price,
      payment_Date: new Date().toISOString(),
      order_ID: order.order_ID,
      payment_Type_ID: 1 // Default to 1
    };

    this.paymentService.createPayment(paymentData).subscribe({
      next: (response) => {
        console.log('Payment record created successfully:', response);
        this.router.navigate(['/orders']);
      },
      error: (error) => {
        console.error('Error creating payment record:', error);
      }
    });
  }  
}
downloadDownload PNG downloadDownload JPEG downloadDownload SVG

Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!

Click to optimize width for Twitter