Payment Prompt
Sun Jun 30 2024 12:09:26 GMT+0000 (Coordinated Universal Time)
Saved by @iamkatmakhafola
//PaymentsClassDemo L12 //Cancel Component //HTML <h1>Cancelled</h1> <p>Not sure you want to buy?;<br /> we'll preserve your cart until you're ready!</p> //TS import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-cancel', templateUrl: './cancel.component.html', styleUrls: ['./cancel.component.css'] }) export class CancelComponent implements OnInit { constructor() { } ngOnInit(): void { } } //Cart Component //HTML <h2 class="my-5">Items in cart</h2> <div class="container products-container"> <table class="table"> <thead class="table-dark"> <tr> <th scope="col">Subscription</th> <th scope="col">Price</th> <th scope="col">Quantity</th> <th scope="col">Total Cost</th> </tr> </thead> <tbody class="table-body"> <tr *ngFor="let cartSub of subscriptionsInCart"> <td>{{cartSub.subscription.name}}</td> <td>{{cartSub.subscription.price}}</td> <td> <span class="increase" style="color:#89cff0"> <i class="fa-solid fa-circle-left fa-lg" (click)="reduceProdCount(cartSub.subscription)"></i> </span> {{cartSub.quantity}} <span class="decrease" style="color:#89cff0"> <i class="fa-solid fa-circle-right fa-lg" (click)="increaseProdCount(cartSub.subscription)"></i> </span></td> <td>{{cartSub.totalCost}}</td> </tr> </tbody> <tfoot class="table-footer"> <tr> <td></td> <td></td> <td><b>Total:</b></td> <td>{{totalCostOfSubcriptionsInCart}}</td> </tr> </tfoot> </table> <br> <app-payfastcheckout></app-payfastcheckout> </div> //TS import { Component, OnInit } from '@angular/core'; import { SubscriptionCartOrganiserService } from '../services/SubscriptionCartOrganiser.service'; import { CartSubScription } from '../models/CartSubscriptionVM.model'; import { Subscription } from '../models/Subscription.model'; @Component({ selector: 'app-cart', templateUrl: './cart.component.html', styleUrls: ['./cart.component.css'] }) export class CartComponent implements OnInit { subscriptionsInCart : CartSubScription [] = []; totalCostOfSubcriptionsInCart :number = 0; constructor(private cartManager : SubscriptionCartOrganiserService) { this.loadSubscriptions(); cartManager.cartProductsNumberDS.subscribe(num => { this.loadSubscriptions(); }); } ngOnInit(): void { } loadSubscriptions() { this.subscriptionsInCart = this.cartManager.getSubscriptionsInCart(); this.totalCostOfSubcriptionsInCart = this.cartManager.getTotalCostOfSubcriptionsInCart(); } increaseProdCount (sub : Subscription) { for (var idx = 0; idx < this.subscriptionsInCart.length; idx++) { if (this.subscriptionsInCart[idx].subscription.id == sub.id) { this.cartManager.addProdFromCart(this.subscriptionsInCart[idx].subscription); } } } reduceProdCount (sub : Subscription) { for (var idx = 0; idx < this.subscriptionsInCart.length; idx++) { if (this.subscriptionsInCart[idx].subscription.id == sub.id) { this.cartManager.removeProdFromCart(this.subscriptionsInCart[idx].subscription); } } } } //Models //CartSubscriptionVM.model.ts import { Subscription } from "./Subscription.model"; export class CartSubScription { subscription : Subscription; quantity : number = 1; totalCost : number = 0; constructor(subscr : Subscription, quant: number) { this.subscription = subscr; this.quantity = quant; this.totalCost = quant * subscr.price; } increment() { this.quantity +=1 } } //Subscription.model.ts export class Subscription { id : number = 0; name : string = ""; description : string = ""; price : number = 0; } //navigation-bar component //HTML <nav class="navbar navbar-expand-lg navbar-light bg-light"> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"> <a class="nav-link" routerLink="">Home <span class="sr-only">(current)</span></a> </li> </ul> <a class="navbar-brand" routerLink="cart"> <span class="itemCount">{{numCartItems}}</span> <i class="fa-solid fa-cart-shopping"></i> </a> </div> </nav> //TS import { Component, OnInit } from '@angular/core'; import { SubscriptionCartOrganiserService } from '../services/SubscriptionCartOrganiser.service'; @Component({ selector: 'app-navigation-bar', templateUrl: './navigation-bar.component.html', styleUrls: ['./navigation-bar.component.css'] }) export class NavigationBarComponent implements OnInit { numCartItems : number = 0; constructor(private cartManager : SubscriptionCartOrganiserService) { this.numCartItems = cartManager.getNumberOfItemsInCart(); cartManager.cartProductsNumberDS.subscribe(num => { this.numCartItems = num; }); } ngOnInit(): void { } } //payfastcheckout component //HTML <button type="button" class="btn btn-primary m-3" (click)="doOnSitePayment()">Checkout</button> //TS import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Router } from '@angular/router'; import { SubscriptionCartOrganiserService } from '../services/SubscriptionCartOrganiser.service'; import { Md5 } from 'ts-md5'; import { FormBuilder } from '@angular/forms' import { environment } from 'src/environments/environment'; declare function payfast_do_onsite_payment(param1 : any, callback: any): any; @Component({ selector: 'app-payfastcheckout', templateUrl: './payfastcheckout.component.html', styleUrls: ['./payfastcheckout.component.css'] }) export class PayfastcheckoutComponent implements OnInit { constructor(private httpComms : HttpClient, private pageRouter : Router, private cartManager : SubscriptionCartOrganiserService, private formBuilder: FormBuilder) { } ngOnInit(): void { } 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() { let onSiteUserData = new Map<string, string>(); onSiteUserData.set("merchant_id", "10033427") onSiteUserData.set("merchant_key", "mu83ipbgas9p7") onSiteUserData.set('return_url', window.location.origin + '/success') onSiteUserData.set('cancel_url', window.location.origin + '/cancel') onSiteUserData.set("email_address", 'test@user.com'); onSiteUserData.set("amount", this.cartManager.getTotalCostOfSubcriptionsInCart().toString()); onSiteUserData.set("item_name", this.cartManager.getCartOrderName()); onSiteUserData.set('passphrase', 'HelloWorldHello'); let signature = this.getSignature(onSiteUserData); onSiteUserData.set('signature', signature); let formData = new FormData(); onSiteUserData.forEach((val, key) => { formData.append(key, val); }); let response = await fetch(environment.payfastOnsiteEndpoint, { method: 'POST', body: formData, redirect: 'follow' }); let respJson = await response.json(); let uuid = respJson['uuid']; payfast_do_onsite_payment({'uuid': uuid}, (res: any) => { if (res == true) { this.pageRouter.navigate(['/success']) } else { this.pageRouter.navigate(['/cancel']) } }); } doFormPayment() { let onSiteUserData = new Map<string, string>(); onSiteUserData.set("merchant_id", "10033427") onSiteUserData.set("merchant_key", "mu83ipbgas9p7") onSiteUserData.set('return_url', window.location.origin + '/success') onSiteUserData.set('cancel_url', window.location.origin + '/cancel') onSiteUserData.set("email_address", 'test@user.com'); onSiteUserData.set("amount", this.cartManager.getTotalCostOfSubcriptionsInCart().toString()); onSiteUserData.set("item_name", this.cartManager.getCartOrderName()); onSiteUserData.set('passphrase', 'HelloWorldHello'); let signature = this.getSignature(onSiteUserData); onSiteUserData.set('signature', signature); let autoPaymentForm = this.formBuilder.group(onSiteUserData); this.httpComms.post('https://sandbox.payfast.co.za/eng/process', onSiteUserData).subscribe(resp => { console.log(resp); }); } } //productcatalog component //HTML <div class="row mt-5"> <div class="card m-3" style="width: 20rem;" *ngFor="let prod of products"> <div class="card-body"> <h5 class="card-title">{{prod.name}}</h5> <p class="card-text"> {{prod.description}} </p> <button type="button" class="btn btn-primary" (click)="addSubscriptionToCart(prod)"> Add to cart </button> </div> </div> </div> <div aria-live="polite" aria-atomic="true" style="position: relative; min-height: 200px;"> <div class="toast" style="position: absolute; top: 0; right: 0;"> <div class="toast-header"> <img src="..." class="rounded mr-2" alt="..."> <strong class="mr-auto">Cart items</strong> <small>Now</small> <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="toast-body"> Added item to cart. </div> </div> </div> //TS import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { Subscription } from '../models/Subscription.model'; import { FakeSubscriptionDataService } from '../services/FakeSubscriptionData.service'; import { SubscriptionCartOrganiserService } from '../services/SubscriptionCartOrganiser.service'; @Component({ selector: 'app-productcatalog', templateUrl: './productcatalog.component.html', styleUrls: ['./productcatalog.component.css'] }) export class ProductcatalogComponent implements OnInit { products : Subscription [] = []; constructor(private fakeDataProvider : FakeSubscriptionDataService, private cartSubscriptionService : SubscriptionCartOrganiserService) { this.products = fakeDataProvider.getOfferedSubscriptions(); } ngOnInit(): void { } addSubscriptionToCart(product : Subscription) { this.cartSubscriptionService.addProdFromCart(product); } } //Services //FakeSubscriptionData.service.ts import { Injectable } from "@angular/core"; import { Subscription } from "../models/Subscription.model"; @Injectable({ providedIn: 'root' }) export class FakeSubscriptionDataService { subscriptions : Subscription[]; constructor () { this.subscriptions = [ { id: 1, name: "Netflix", description: "At Netflix, we want to entertain the world. Whatever your taste, and no matter where you live, we give you access to best-in-class TV series, documentaries, feature films and mobile games.", price : 100 }, { id: 2, name: "Showmax", description: "Showmax is an internet TV service. What sets Showmax apart is a unique combination of hit African content, first and exclusive international series, movies, the best kids’ shows, and live sport.", price : 500 }, { id: 3, name: "Tencent Video", description: "Tencent Video is China's second-largest video-streaming platform. It includes a variety of categories of online videos. The most popular categories on the platform include Chinese TV shows and China-made animation shows.", price : 800 }, { id: 4, name: "BBC iPlayer", description: "BBC iPlayer is a video on demand service from the BBC. The service is available on a wide range of devices, including mobile phones and tablets, personal computers and smart televisions.", price : 900 }, ]; } getOfferedSubscriptions () { return this.subscriptions; } } //SubscriptionCartOrganiser.service.ts import { Injectable } from "@angular/core"; import { Subject } from "rxjs"; import { CartSubScription } from "../models/CartSubscriptionVM.model"; import { Subscription } from "../models/Subscription.model"; @Injectable({ providedIn: 'root' }) export class SubscriptionCartOrganiserService { static tmpSubscriptionsCartName : string = "ls-cart-subscriptions"; cartProductsNumberDS = new Subject<number>(); cartItemsOrderName : string = "Subs Order @ "; notifyOnNewItemInCart() { this.cartProductsNumberDS.next(this.getNumberOfItemsInCart()); } getLocalStorageSubscriptions(): Subscription[] { let storedSubString = localStorage.getItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName) let cartSubscriptions = []; if (storedSubString) { cartSubscriptions = JSON.parse(storedSubString) } return cartSubscriptions; } getNumberOfItemsInCart() : number { return this.getLocalStorageSubscriptions().length } getSubscriptionsInCart() : CartSubScription[] { let localStorageSubs = this.getLocalStorageSubscriptions(); let cartSubscriptions : CartSubScription[] = []; let subCounts = new Map<Number, Number>(); //temporary storage localStorageSubs.forEach(sub => { if (!subCounts.has(sub.id)) { let count = localStorageSubs.filter(currSub => currSub.id == sub.id).length; subCounts.set(sub.id, count) let cartSub = new CartSubScription(sub, count); cartSubscriptions.push(cartSub); } }); return cartSubscriptions; } getTotalCostOfSubcriptionsInCart() : number { let totalCost = 0; let cartSubs = this.getSubscriptionsInCart(); cartSubs.forEach(cartSub => { totalCost += (cartSub.subscription.price * cartSub.quantity); }); return totalCost; } getCartOrderName() { return this.cartItemsOrderName + Date.now(); } addSubscriptionToCart(product : Subscription) { let storedSubString = localStorage.getItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName) let cartSubscriptions = []; if (storedSubString) { cartSubscriptions = JSON.parse(storedSubString) } cartSubscriptions.push(product); localStorage.setItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName, JSON.stringify(cartSubscriptions)) this.notifyOnNewItemInCart(); } removeProdFromCart(subscr : Subscription) { let storedSubString = localStorage.getItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName) let cartSubscriptions = []; if (storedSubString) { cartSubscriptions = JSON.parse(storedSubString) } for (var idx = 0; idx < cartSubscriptions.length; idx++) { if (cartSubscriptions[idx].id == subscr.id) { cartSubscriptions.splice(idx, 1); break; } } localStorage.setItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName, JSON.stringify(cartSubscriptions)) this.notifyOnNewItemInCart(); } addProdFromCart(subscr : Subscription) { this.addSubscriptionToCart(subscr); this.notifyOnNewItemInCart(); } clearCart () { localStorage.removeItem(SubscriptionCartOrganiserService.tmpSubscriptionsCartName); this.notifyOnNewItemInCart(); } } //Success component //HTML <h1>Success</h1> <p>We received your purchase;<br /> Your items will be sent shortly!</p> //TS import { Component, OnInit } from '@angular/core'; import { SubscriptionCartOrganiserService } from '../services/SubscriptionCartOrganiser.service'; @Component({ selector: 'app-success', templateUrl: './success.component.html', styleUrls: ['./success.component.css'] }) export class SuccessComponent implements OnInit { constructor(private cartManager : SubscriptionCartOrganiserService) { } ngOnInit(): void { this.cartManager.clearCart(); } } //app component //app-routing.module.ts import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CancelComponent } from './cancel/cancel.component'; import { CartComponent } from './cart/cart.component'; import { ProductcatalogComponent } from './productcatalog/productcatalog.component'; import { SuccessComponent } from './success/success.component'; const routes: Routes = [ {path : '', component: ProductcatalogComponent}, {path : 'cart', component: CartComponent}, {path : 'success', component: SuccessComponent}, {path: 'cancel', component: CancelComponent} ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } //app.component.html <app-navigation-bar></app-navigation-bar> <div class="container mt-1"> <router-outlet></router-outlet> </div> //app.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'PaymentsClassDemo'; } //app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { SquarecheckoutComponent } from './squarecheckout/squarecheckout.component'; import { PayfastcheckoutComponent } from './payfastcheckout/payfastcheckout.component'; import { ProductcatalogComponent } from './productcatalog/productcatalog.component'; import { CartComponent } from './cart/cart.component'; import { NavigationBarComponent } from './navigation-bar/navigation-bar.component'; import { HttpClientModule } from '@angular/common/http'; import { SuccessComponent } from './success/success.component'; import { CancelComponent } from './cancel/cancel.component' import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @NgModule({ declarations: [ AppComponent, SquarecheckoutComponent, PayfastcheckoutComponent, ProductcatalogComponent, CartComponent, NavigationBarComponent, SuccessComponent, CancelComponent ], imports: [ BrowserModule, AppRoutingModule, HttpClientModule, FormsModule, ReactiveFormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } //environment //environment.prod.ts export const environment = { production: true, payfastEndpoint: 'https://www.payfast.co.za/en/process', payfastOnsiteEndpoint: 'https://www.payfast.co.za/onsite/process', }; //environment.ts export const environment = { production: false, payfastEndpoint: 'https://sandbox.payfast.co.za/eng/process', payfastOnsiteEndpoint: 'https://sandbox.payfast.co.za/onsite/process' };
Prompt: Consider this application 'PaymentClassDemo' which allows users to buy streaming services through a payfast payment gateway. The application makes use of frontend, meaning it does not have a backend. I want to implement payments on my application but the difference between my application and 'PaymentsClassDemo' is that mine has a backend. With that in mind i'd like process payments using a backend api controller that has endpoints that will be used in the frontend through a service that will allow a component or components to communicate with the backend to successfully make payments.
Comments