There are several payment options available in the market today. PayPal checkout option is one among them. In my previous articles, I have already discussed few payment options - Braintree PayPal using PHP, Payment system and PayPal Express Checkout with PHP and MySQL. These are the most viewed and trending articles till this day. Today, let’s see how the PayPal Express Checkout works with React JS.
Live demo sandbox PayPal credentials for testing username: [email protected] password: 9lessons
Live Demo
Part One: Login with Facebook and Google using ReactJS and RESTful APIs
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(300),
product_image varchar(200),
price float,
currency varchar(10)
);
pid int AUTO_INCREMENT PRIMARY KEY,
product varchar(300),
product_image varchar(200),
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(500),
paymentID varchar(500),
token varchar(500),
created int(11)
);
oid int AUTO_INCREMENT PRIMARY KEY,
pid_fk int,
uid_fk int,
payerID varchar(500),
paymentID varchar(500),
token varchar(500),
created int(11)
);
Home.js
Let continue the provious article. Here the user session storage data is present, storing the data with setState. Using user id and token you can access other informaiton like products and order details.
import React, {Component} from 'react';
export default Home;
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 (<Redirect to={'/'}/>)
}
return (
<div >
Welcome {this.state.name}
</div>
);
}
}
Lets start
You have to create common components for multiple uses. In this project, we are commonly using project title and log out.
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 <Redirect to={"/"} />;
}
return (
<div className="row">
<div className="medium-12 columns">
<ul className="right">
<li>
<a href="/orders">Orders</a>
</li>
<li>
<a href="#" onClick={this.logout}>
Logout
</a>
</li>
</ul>
<h2>Welcome {this.props.name}</h2>
</div>
</div>
);
}
}
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";
export default Home;
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>
);
}
}
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
Create a custom component with Paypal Express Button plugin. You have to configure your PayPal sandbox and production merchent IDs for more inform check PayPal Express Checkout with PHP and MySQL. 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";
For production you can change the let evn value to "production". Use my sandbox token for testing.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;
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";
export default Checkout;#
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>
);
}
}
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";
export default OrdersList;
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>;
}
}
Working with PHP RESTful
This project is the combination of Login with Facebook and Google using ReactJS and PayPal Express Checkout with PHP and MySQL
index.php
Included following POST method calls. Please check Login with Facebook and Google using ReactJS
<?php
/* ### Srinivas Tamada ### */
/* ### https://www.9lessons.info ### */
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"}}';
}
createOrder
Creating order for successful payments. This will call internal CURL actions function paypalCheck.
function createOrder() {
}
$request = \Slim\Slim::getInstance()->request();
$data = json_decode($request->getBody());
$uid=$data->uid;
$token=$data->token;
$pid=$data->pid;
$payerID=$data->payerID;
$paymentToken=$data->paymentToken;
$paymentID=$data->paymentID;
$system_token = apiToken($uid);
if($token == $system_token){
if(paypalCheck($paymentID, $pid, $payerID, $paymentToken, $uid)){
echo '{"status": "true" }';
} else {
echo '{"error":{"text":"No data available"}}';
}
}
else{
echo '{"error":{"text":"No access"}}';
}
paypalCheck internal function
Using CURL methods authenticating PayPal transcation for more understanding check PayPal Express Checkout with PHP and MySQL.
function paypalCheck($paymentID, $pid, $payerID, $paymentToken, $uid){
}
$ch = curl_init();
$clientId = PayPal_CLIENT_ID;
$secret = PayPal_SECRET;
curl_setopt($ch, CURLOPT_URL, PayPal_BASE_URL.'oauth2/token');
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, $clientId . ":" . $secret);
curl_setopt($ch, CURLOPT_POSTFIELDS, "grant_type=client_credentials");
$result = curl_exec($ch);
$accessToken = null;
if (empty($result)){
return false;
}
else {
$json = json_decode($result);
$accessToken = $json->access_token;
$curl = curl_init(PayPal_BASE_URL.'payments/payment/' . $paymentID);
curl_setopt($curl, CURLOPT_POST, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Authorization: Bearer ' . $accessToken,
'Accept: application/json',
'Content-Type: application/xml'
));
$response = curl_exec($curl);
$result = json_decode($response);
$state = $result->state;
$total = $result->transactions[0]->amount->total;
$currency = $result->transactions[0]->amount->currency;
$subtotal = $result->transactions[0]->amount->details->subtotal;
$recipient_name = $result->transactions[0]->item_list->shipping_address->recipient_name;
curl_close($ch);
curl_close($curl);
$product = getProductData($pid);
if($state == 'approved' && $currency == $product->currency && $product->price == $subtotal){
updateOrder($pid, $payerID, $paymentID, $paymentToken, $uid);
return true;
}
else{
return false;
}
}
updateOrder internal function
Insert order details with payment details.
function updateOrder($pid, $payerID, $paymentID, $token,$uid)
}
{
if(paymentCheck($paymentID) < 1 && $uid > 0){
$db = getDB();
$stmt = $db->prepare("INSERT INTO orders(uid_fk, pid_fk, payerID, paymentID, token, created ) VALUES (:uid, :pid,:payerID, :paymentID, :token, :created)");
$stmt->bindParam("paymentID", $paymentID, PDO::PARAM_STR) ;
$stmt->bindParam("payerID", $payerID, PDO::PARAM_STR) ;
$stmt->bindParam("token", $token, PDO::PARAM_STR) ;
$stmt->bindParam("pid", $pid, PDO::PARAM_INT) ;
$stmt->bindParam("uid", $uid, PDO::PARAM_INT) ;
$created = time();
$stmt->bindParam("created", $created, PDO::PARAM_INT) ;
$stmt->execute();
$db=null;
return true;
}
else{
return false;
}
paymentCheck internal function
Using thie method cross checking the payment is preset are not.
function paymentCheck($paymentID)
}
{
$db = getDB();
$stmt = $db->prepare("SELECT * FROM orders WHERE paymentID=:paymentID");
$stmt->bindParam("paymentID", $paymentID, PDO::PARAM_STR) ;
$stmt->execute();
$count = $stmt->rowcount();
$db=null;
return $count;
Run this project React
$ git clone https://github.com/srinivastamada/react-login-paypal.git
$ cd react-login-paypal
$ yarn start
$ cd react-login-paypal
$ yarn start
For project deployment on Google firebase read Create an Angular 2 CLI App with Firebase Social Authentication Facebook, Google, Github and Twitter
Are you using webpack for reactjs ?
ReplyDeleteAmazing tips which improve my knowledge
ReplyDeleteThx for sharing its helpful to me
ReplyDeletenice blog sir
ReplyDeleteGreat Blog Thanks for sharing and keep rocking
ReplyDeleteGreat Blog thanks for sharing
ReplyDeleteGood post sir it's very useful
ReplyDeleteThanks for sharing.
ReplyDeleteNice article got more information about this thanks a lot!
nice
ReplyDeletevery nice thanks for sharing
ReplyDeleteGreat Sir
ReplyDeletehttp://flixweb.in
Really great and useful Post with working code.
ReplyDeleteamazing post, thanks for sharing this article. i am truly motivated by you for blogging
ReplyDeleteThanks Again...
Great Work
ReplyDeletenice blog sir
ReplyDeleteI’m really happy to say it was an interesting post to read. I learned new information from your article, you are doing a great job. Continue
ReplyDeleteAmazing blog,superb description i always get something new here
ReplyDeleteI’m really happy to say it was an interesting post to read. I learned new information from your article, you are doing a great job.
ReplyDeleteNice article. thanks
ReplyDeleteNice article about paypal.Thanks for sharing this type of article.Keep it up. Thanks again.
ReplyDeletesuper job
ReplyDeletegreat article Srinivas Tamada!! this is quite helpful and informative. People who wants to be web developer should excess your articles. keep coming up with more content like these.
ReplyDeletei like your blog sir
ReplyDelete