WEB DEVELOPER SITE
HTMLCSSJAVASCRIPTSQLPHPBOOTSTRAPJQUERYANGULARXML
 

PayPal Express Checkout with React JS and PHP RESTful


There are several payment options available in the market today. PayPal checkout option is one among them.

Database Design

To build the PayPal checkout ordering system, you have to create the following tables. Social login user table has been present in the previous article.

Products

Product table contains all the product details. You can extend this table based on your needs.
CREATE TABLE products(
pid int AUTO_INCREMENT PRIMARY KEY,
product varchar(200),
product_image varchar(100),
price float,
currency varchar(10)
);

Orders

Order table contains all the user order details with PayPal payment ids.

CREATE TABLE orders(
oid int AUTO_INCREMENT PRIMARY KEY,
pid_fk int,
uid_fk int,
payerID varchar(200),
paymentID varchar(200),
token varchar(200),
created int(11)
);

Home.js

Let continue the provious article. Here the user session storage data is present, storing the data with setState

import React, {Component} from 'react';
import './Home.css';
import {Redirect} from 'react-router-dom';
class Home extends Component {

constructor(props){
    super(props);
    this.state = {
       name:'',
       redirect: false,
   };
}

componentDidMount() {
     let data = JSON.parse(sessionStorage.getItem('userData'));
     console.log(data);
     this.setState({name: data.userData.name})
}

render() {

if(!sessionStorage.getItem('userData') || this.state.redirect){
    return ()
}

return (
Welcome {this.state.name}
); } } export default Home;

Title.js

This component contains project Title and log out functionality. Using the props you can disturbute the data. You can import this component in multiple pages.

import React, { Component } from "react";
import { Redirect } from "react-router-dom";
import "./Title.css";

class Title extends Component {
constructor(props) {
   super(props);
   this.logout = this.logout.bind(this);
   this.state = {
     redirect: false
   };
}

logout() {
   sessionStorage.setItem("userData", "");
   sessionStorage.clear();
   this.setState({ redirect: true });
}

render() {
if (this.state.redirect) {
   return ;
}
return (
   

Welcome {this.props.name}

); } } export default Title;

ProductsList.js

Component for displaying all of the product details. Here the product data is calling from React props constuctor. onClick button this.props.checkOut points to parent function, you will find this in next step.
import React, { Component } from "react";
import "./ProductsList.css";
class ProductsList extends Component {
constructor(props) {
   super(props);
}

render() {
let productList = this.props.productsData.map(function(productData, index) {
return (
   <div className="row list" key={index}>
   <div className="medium-3 columns">
   <img
src={
"https://demos.9lessons.info/PHP-PayPal-ExpressCheckout/img/" +
productData.product_img
}
/>
</div>
   <div className="medium-3 columns">{productData.product}</div>
   <div className="medium-3 columns">$ {productData.price}</div>
   <div className="medium-3 columns">
<button className="button" value={productData.pid}
onClick={this.props.checkout}>
   Order
</button>
</div>
</div>
);
}, this);

return <div>{productList}</div>;
}
}

export default ProductsList;

Importing Components and Data Binding with RESTful APIs

Home.js

Imported Title and ProductList components with PostData URL fetch provider. Function getProducts() fetch the data from products API based on the login user id and token.
import React, { Component } from "react";
import "./Home.css";
import { Redirect } from "react-router-dom";
import { PostData } from "../../services/PostData";
import Title from "../Title/Title";
import ProductsList from "../ProductsList/ProductsList";
class Home extends Component {
constructor(props) {
   super(props);
   this.state = {
      name: "",
      redirect: false,
      products: [],
      pid: ""
   };

this.getProducts = this.getProducts.bind(this);
this.checkout = this.checkout.bind(this);
}

componentDidMount() {
   let data = JSON.parse(sessionStorage.getItem("userData"));
   this.getProducts(data.userData);
   this.setState({ name: data.userData.name });
}

getProducts(userData) {
let postData = { uid: userData.uid, token: userData.token };
   console.log(postData);
   PostData("products", postData).then(result => {
      let responseJson = result;
      this.setState({ products: responseJson.products });
   });
}

checkout(e) {
   let pid = e.target.getAttribute("value");
   sessionStorage.setItem("pid", pid);
   this.setState({ pid: pid });
}

render() {
if (!sessionStorage.getItem("userData") || this.state.redirect) {
   return <Redirect to={"/"} />;
}

if (this.state.pid > 0) {
   return <Redirect to={"/checkout"} />;
}

return (
<div className="row body">
<Title name={this.state.name} />
<ProductsList productsData={this.state.products}
checkout={this.checkout}/>
</div>
);
}
}
export default Home;

Function checkOut() changes the this.state.pid value base on the product value attribute. In the render method, if the pid value is present, it is redirecting to /checkout page.

Working with PayPal and Checkout Page

You have to install PayPal checkout component for connecting PayPal API. Install PayPal Express Checkout plugin for React.
$ npm install react-paypal-express-checkout --save

Paypal.js

On payment success, it will call onSuccess with createOther method. This PHP API will validate with payment data like payerID, paymentToken etc.. Valid call changes the redirect state value to true and it redirects to orders page.
import React, { Component } from "react";
import "./PayPal.css";
import PaypalExpressBtn from "react-paypal-express-checkout";
import { PostData } from "../../services/PostData";
import { Redirect } from "react-router-dom";
class PayPal extends Component {
constructor(props) {
   super(props);
   this.state = {
      redirect: false
   };
   this.createOrder = this.createOrder.bind(this);
}

createOrder(payment) {
let postData = {
   uid: this.props.userData.uid,
   token: this.props.userData.token,
   payerID: payment.payerID,
   paymentID: payment.paymentID,
   paymentToken: payment.paymentToken,
   pid: this.props.pid
};

PostData("createOrder", postData).then(result => {
   let responseJson = result;
   if (responseJson.status === "true") {
      this.setState({ redirect: true });
   }
});
}

render() {
if (this.state.redirect) {
   return <Redirect to={"/orders"} />;
}

const onSuccess = payment => {
   this.createOrder(payment);
};

const onCancel = data => {
   console.log("The payment was cancelled!", data);
};

const onError = err => {
console.log("Error!", err);
};

let env = "sandbox"; // Change to "production" for live
let currency = "USD";
let total = this.props.value;

const client = {
sandbox"AQwoZAAHsmA5vBLj_mZffS3NWJjNJODewuV2WakPm-BQilgsawTtnbLvWHNC73idcfiaHBOjaeTDkAS8",
production: "<insert production client id>"
};

return (
<PaypalExpressBtn
env={env}
client={client}
currency={currency}
total={total}
onError={onError}
onSuccess={onSuccess}
onCancel={onCancel}
/>
);
}
}

export default PayPal;
For production you can change the let evn value to "production". Use my sandbox token for testing.

CheckOut.js

Checkout page for displying more details about the selected product. Imported PayPal component and set attributes values like product value, pid and userData.

import React, { Component } from "react";
import "./Checkout.css";
import { Redirect } from "react-router-dom";
import { PostData } from "../../services/PostData";
import Title from "../Title/Title";
import PayPal from "../PayPal/PayPal";
class Checkout extends Component {
constructor(props) {
   super(props);
   this.state = {
      name: "",
      redirect: false,
      pid: "",
      product: [],
      userData: []
};
}

componentDidMount() {
let data = JSON.parse(sessionStorage.getItem("userData"));
this.getProductData(data.userData);
this.setState({ name: data.userData.name });
this.setState({ userData: data.userData });
}

getProductData(userData) {
let pid = sessionStorage.getItem("pid");
let postData = { uid: userData.uid, token: userData.token, pid: pid };

PostData("getProduct", postData).then(result => {
   let responseJson = result;
   this.setState({ product: responseJson.product });
});
}

render() {
if (!sessionStorage.getItem("userData") || this.state.redirect) {
   return <Redirect to={"/"} />;
}

return (
<div className="row body">
<Title name={this.state.name} />
<h4>Checkout</h4>
<div className="row">
<div className="medium-4 columns">Product</div>
<div className="medium-4 columns">Name</div>
<div className="medium-4 columns">Price</div>
</div>
<div className="row">
<div className="medium-4 columns">
<img
src={
"https://demos.9lessons.info/PHP-PayPal-ExpressCheckout/img/" +
this.state.product.product_img
}
/>
</div>
<div className="medium-4 columns">{this.state.product.product}</div>
<div className="medium-4 columns">
{this.state.product.price}
<PayPal
value={this.state.product.price}
pid={this.state.product.pid}
userData={this.state.userData}
/>
</div>
</div>
</div>
);
}
}
export default Checkout;#

Orders.js

Orders page this contains all of your successful orders. Function getOrders fetch orders form RESTful apis, component OrdersList renders based on the state.orders array.
import React, { Component } from "react";
import "./Orders.css";
import { Redirect } from "react-router-dom";
import { PostData } from "../../services/PostData";
import Title from "../Title/Title";
import OrdersList from "../OrdersList/OrdersList";

class Orders extends Component {
constructor(props) {
   super(props);
   this.state = {
      name: "",
      redirect: false,
      orders: []
};

this.getOrders = this.getOrders.bind(this);
}

componentDidMount() {
   let data = JSON.parse(sessionStorage.getItem("userData"));
   this.getOrders(data.userData);
   this.setState({ name: data.userData.name });
}

getOrders(userData) {
let postData = { uid: userData.uid, token: userData.token };
PostData("orders", postData).then(result => {
   let responseJson = result;
   this.setState({ orders: responseJson.orders });
});
}

render() {
if (!sessionStorage.getItem("userData") || this.state.redirect) {
   return <Redirect to={"/"} />;
}

return (
<div className="row body">
<Title name={this.state.name} />
<OrdersList ordersData={this.state.orders} />
</div>
);
}
}

export default Orders;

OrderList.js

Component that display the user payment orders.
import React, { Component } from "react";
import "./OrdersList.css";
class OrdersList extends Component {
constructor(props) {
   super(props);
}

render() {
let ordersList = this.props.ordersData.map(function(orderData, index) {
return (
<div clasName="row orderList" key={index}>
<div className="medium-3 columns">{orderData.oid}</div>
<div className="medium-3 columns">{orderData.product}</div>
<div className="medium-3 columns">${orderData.price}</div>
<div className="medium-3 columns">{orderData.created}</div>
</div>
);
}, this);

return <div>{ordersList}</div>;
}
}
export default OrdersList;

Working with PHP RESTful

index.php

Included following POST method calls. Please check Login with Facebook and Google using ReactJS
<?php
/* ### Srinivas Tamada ### */
/* ### https://www.welookups.com ### */
require 'config.php';
require 'Slim/Slim.php';

\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim();

$app->post('/signup','signup'); /* User Signup */
$app->post('/products','products'); /* User Products */
$app->post('/orders','orders'); /* User Orders */
$app->post('/getProduct','getProduct'); /* get proudct */
$app->post('/createOrder','createOrder'); /* Create Order */
$app->run();
......
......
......
......
?>

products

Getting product details for products table. If you want you can extend to add pagination.
function products() {
$request = \Slim\Slim::getInstance()->request();
$data = json_decode($request->getBody());
$uid=$data->uid;
$token=$data->token;
$system_token = apiToken($uid);
if($token == $system_token){
   $db = getDB();
   $sql = "SELECT * FROM products";
   $stmt = $db->prepare($sql);
   $stmt->execute();
   $products = $stmt->fetchALL(PDO::FETCH_OBJ);
   if($products){
      $products = json_encode($products);
      echo '{"products": ' .$products . '}';
   } else {
      echo '{"error":{"text":"No data available"}}';
   }
}
else{
echo '{"error":{"text":"No access"}}';
}
}

orders

User order details based on the post input uid.

function orders() {
$request = \Slim\Slim::getInstance()->request();
$data = json_decode($request->getBody());
$uid=$data->uid;
$token=$data->token;
$system_token = apiToken($uid);
if($token == $system_token){
   $db = getDB();
   $sql = "SELECT * FROM orders O, products P WHERE O.pid_fk = P.pid AND uid_fk=:uid order by O.oid DESC; ";
   $stmt = $db->prepare($sql);
   $stmt->bindParam("uid", $uid,PDO::PARAM_INT);
   $stmt->execute();
   $orders = $stmt->fetchALL(PDO::FETCH_OBJ);
   if($orders){
      $orders = json_encode($orders);
      echo '{"orders": ' .$orders . '}';
   } else {
      echo '{"error":{"text":"No data available"}}';
   }
}
else{
   echo '{"error":{"text":"No access"}}';
}
}

getProduct

Getting individual product details based on product id.


function getProduct() {
$request = \Slim\Slim::getInstance()->request();
$data = json_decode($request->getBody());
$uid=$data->uid;
$token=$data->token;
$pid=$data->pid;
$system_token = apiToken($uid);
if($token == $system_token){
   $db = getDB();
   $sql = "SELECT * FROM products WHERE pid=:pid";
   $stmt = $db->prepare($sql);
   $stmt->bindParam("pid", $pid,PDO::PARAM_STR);
   $stmt->execute();
   $product = $stmt->fetch(PDO::FETCH_OBJ);
   if($product){
      $product = json_encode($product);
      echo '{"product": ' .$product . '}';
   } else {
      echo '{"error":{"text":"No data available"}}';
   }
}
else{
echo '{"error":{"text":"No access"}}';
}
}