Android Integrating PayPal using PHP, MySQL – Part 1

When you are building an eCommerce app, integrating the payment gateway is one of the most important building blocks of an app. Today we are going to learn how to integrate most popular payment gateway PayPal in our android app.

For an eCommerce app, building android app is not enough. We need to have a server side component too to maintain the inventory, customers, transactions and other important things. So this tutorial covers both the topics by diving the article into two parts. In this part, we are going to learn how to build the PHP, MySQL server and integrating the PayPal REST API. In the next part Android Integrating PayPal using PHP, MySQL – Part 2 we’ll learn how to build the android app and integrate the PayPal gateway.

android paypal integration php mysql

1. Overview
2. Creating PayPal App (Client ID & Secret)
3. PayPal Sandbox Test Account
4. Downloading PayPal PHP Rest API SDK
5. Downloading Slim Framework
6. Downloading & Installing WAMP
7. Creating MySQL Database
8. Creating PHP Project
9. Downloading PayPal Android SDK (Part 2)
10. Creating the Android Project (Part 2)
11. Testing the App (Part 2)
12. Final Thoughts (Part 2)

Below are the screenshots of the final app.

android paypal listing the shopping cart
android paypal payment gateway

1. Overview

PayPal provides multiple payment options such as Single Payment (Receives the payment immediately), Future Payments (Can be used for recurring payments) and Profile Sharing (Obtains information about customer). For our use case Single payment is best suited options as we need to get the payment immediately once user purchases some products.

Below diagram explains the complete app flow from listing the products to completing the checkout.

1. First the android app connects with server and fetches the products json. Products json will be parsed and all the products will be displayed on the android app.

2. User selects the products and make the payment using PayPal payment option.

3. After successful payment, PayPal issues the json that contains the payment id.

4. Android app sends the payment id to our server for verification.

5. Server make a REST API call to PayPal along with payment id to verify the payment.

6. PayPal responds a json in which we have to check for “state”: “approved” (and few other flags) for a successful payment.

2. Creating PayPal App (Client ID & Secret)

In order to make the calls to PayPal API we need to create an app in developer.paypal.com and obtain Client ID & Secret.

1. Log into PayPal’s Developer account. If you are visiting for the first time, register and create a new account.

2. Once logged in, you will be redirected to my apps page where you can create a new app.

3. Give your app name, select developer account and click on create app. Once the app is created, you can notice the Client id & Secret. We’re gonna need these keys in our both server and client apps.

pay pal creating new app
pay pal client id and secret

3. PayPal Sandbox Test Account

Paypal provides a test environment called sandbox to test the app before going live. These test accounts comes with paypal balance credited into it using which you can make test purchases. To get your sandbox account credentials, follow below steps.

1. Go to PayPal’s Developer account and click on Accounts under Sandbox on the left panel.

2. On the right you can see the sandbox test accounts. Select the buyer email and click on Profile.

3. In the popup window, click on Change Password to change the password in case if you are not sure about the password.

4. In the popup window, goto Funding tab to see the test balance.

You have to use these credentials to test the app in sandbox environment.

paypal sanbox developer account
paypal sandbox account change password
paypal sandbox developer account money

4. Downloading PayPal PHP Rest API SDK

It’s always a good practise to use the SDKs provided by the vendors rather than building ourselves. Paypal provides REST API SDK for multiple platforms. As we choose to write the server side code in PHP, download the latest release of PayPal-PHP-SDK.

Here is the direct link for PayPal-PHP-SDK-1.2.0.zip

5. Downloading Slim Framework

PHP Slim library allows you to write simple and efficient REST APIs. This framework we use here to generate json responses. To know more about Slim, read my previous article Building REST API for Android app using PHP, Slim and MySQL about building a perfect REST API when your app needs to talk to a php, mysql server.

Download the latest version of Slim Framework.

6. Downloading & Installing WAMP

Download and Install WAMP from http://www.wampserver.com/en/ and start the program from Start => All Programs. Once started, you should be able to access via http://localhost/ in the browser.

Watch the below video to know how to download and install WAMP.

7. Creating MySQL Database

Overall we are going to create four tables. users (to store the customer information), products (to store the product information like name, price, description), payments (to store the paypal transactions) and sales (to keep the product sales done by customers). This is very simple database design. In real world scenario, the database will be more complex than this.

android paypal shopping cart database design

Open phpmyadmin by going to http://localhost/phpmyadmin and execute below SQL query to create required database and tables. Also I am inserting a user and few sample products for testing.


CREATE DATABASE IF NOT EXISTS `paypal` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
USE `paypal`;


CREATE TABLE IF NOT EXISTS `payments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` int(11) NOT NULL,
  `paypalPaymentId` text NOT NULL,
  `create_time` text NOT NULL,
  `update_time` text NOT NULL,
  `state` varchar(15) NOT NULL,
  `amount` decimal(6,2) NOT NULL,
  `currency` varchar(3) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `userId` (`userId`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;


CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` text NOT NULL,
  `price` decimal(6,2) NOT NULL,
  `description` text NOT NULL,
  `image` text NOT NULL,
  `sku` text NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;


INSERT INTO `products` (`id`, `name`, `price`, `description`, `image`, `sku`, `created_at`) VALUES
(1, 'Google Nexus 6', '690.50', 'Midnight Blue, with 32 GB', 'http://api.androidhive.info/images/nexus5.jpeg', 'sku-2123wers100', '2015-02-04 23:19:42'),
(2, 'Sandisk Cruzer Blade 16 GB Flash Pendrive', '4.50', 'USB 2.0, 16 GB, Black & Red, Read 17.62 MB/sec, Write 4.42 MB/sec', 'http://api.androidhive.info/images/sandisk.jpeg', 'sku-78955545w', '2015-02-10 22:54:28'),
(3, 'Kanvas Katha Backpack', '11.25', '1 Zippered Pocket Outside at Front, Loop Handle, Dual Padded Straps at the Back, 1 Compartment', 'http://api.androidhive.info/images/backpack.jpeg', 'sku-8493948kk4', '2015-02-10 22:55:34'),
(4, 'Prestige Pressure Cooker', '30.00', 'Prestige Induction Starter Pack Deluxe Plus Pressure Cooker 5 L', 'http://api.androidhive.info/images/prestige.jpeg', 'sku-90903034ll', '2015-02-10 22:59:25');


CREATE TABLE IF NOT EXISTS `sales` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `paymentId` int(11) NOT NULL,
  `productId` int(11) NOT NULL,
  `state` varchar(15) NOT NULL,
  `salePrice` decimal(6,2) NOT NULL,
  `quantity` int(4) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `paymentId` (`paymentId`),
  KEY `productId` (`productId`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=17 ;


CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `email` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `id` (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;


INSERT INTO `users` (`id`, `name`, `email`) VALUES
(1, 'Android Hive', 'androidhive@gmail.com');

ALTER TABLE `payments`
  ADD CONSTRAINT `payments_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;

ALTER TABLE `sales`
  ADD CONSTRAINT `sales_ibfk_2` FOREIGN KEY (`productId`) REFERENCES `products` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  ADD CONSTRAINT `sales_ibfk_1` FOREIGN KEY (`paymentId`) REFERENCES `payments` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;

8. Creating PHP Project

Once you are done creating the database, let’s create the PHP project. Below is the project structure that we are going to create now. Carefully place all the folders/files in appropriate location as shown in the diagram. I am using the Netbeans IDE to develop my PHP project.

paypal server php project structure

In the project, the purpose of each file/folder is

include – All the config & helper classes goes into this directory
libs – All the third party libraries (Slim & PayPal) will be placed here
v1 – It is the version directory of our API

index.php – All the API calls will be handled in this file
.htaccess – Contains Apache web server configuration

1. Goto the directory where wamp is installed (generally wamp will be installed at c:\wamp) and open www directory. This is where all the php projects will be placed.

2. Inside www folder, create a folder named PayPalServer. This is the root directory for our project.

3. Now inside PayPalServer, create three folders named include, libs and v1.

4. Paste the Slim Framework and PayPal SDK in libs folder.

5. Create a file named Config.php in the include directory and add the below code. This file contains configuration values like database credentials, paypal client id & secret and default currency. Change the username and password with your mysql credentials.

<?php

/**
 * Database configuration
 */
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '');
define('DB_HOST', 'localhost');
define('DB_NAME', 'paypal');

define('DEFAULT_CURRENCY', 'USD');
define('PAYPAL_CLIENT_ID', 'AdOTNRDUqb6jBLfB1IaVrNHFqLKhWROWCNZiuGrPQBqI0h_Hbf6teycjptu0'); // Paypal client id
define('PAYPAL_SECRET', 'EP5sARCiqusS6XGQG3Y-DpZ5KRL9lagYy8Wg0cvMrnznTUGsen3HMzHqdkXZ'); // Paypal secret

?>

6. Create another file named DBConnect.php in include directory and paste below code. This class takes care of opening database connection.

<?php

/**
 * Handling database connection
 *
 * @author Ravi Tamada
 */
class DbConnect {

    private $conn;

    function __construct() {
        
    }

    /**
     * Establishing database connection
     * @return database connection handler
     */
    function connect() {
        include_once dirname(__FILE__) . '/Config.php';

        // Connecting to mysql database
        $this->conn = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);

        // Check for database connection error
        if (mysqli_connect_errno()) {
            echo "Failed to connect to MySQL: " . mysqli_connect_error();
        }

        // returing connection resource
        return $this->conn;
    }

}

?>

7. Create another class named DBHandler.php in include directory. This class contains methods to performs CRUD operations on the database.

getAllProducts() – Retrieves all the products from products table
getProductBySku() – Fetches a product by it’s sku code
storePayment() – Stores paypal payment transaction.
storeSale() – Stores sale of a particular product.

<?php

/**
 * Class to handle all db operations
 * This class will have CRUD methods for database tables
 *
 * @author Ravi Tamada
 */
class DbHandler {

    private $conn;

    function __construct() {

        require_once dirname(__FILE__) . '/DbConnect.php';
        // opening db connection
        $db = new DbConnect();
        $this->conn = $db->connect();
    }

    /**
     * Listing products
     * 
     * */
    public function getAllProducts() {
        $stmt = $this->conn->prepare("SELECT * FROM products");
        $stmt->execute();
        $products = $stmt->get_result();
        $stmt->close();
        return $products;
    }

    /*
     * Fetches a product by its sku
     */
    public function getProductBySku($sku) {
        $stmt = $this->conn->prepare("SELECT * FROM products where sku = ?");
        $stmt->bind_param("s", $sku);

        if ($stmt->execute()) {
            $product = $stmt->get_result()->fetch_assoc();
            $stmt->close();
            return $product;
        } else {
            $stmt->close();
            return NULL;
        }
    }

    /**
     * Stores paypal payment in payments table
     */
    public function storePayment($paypalPaymentId, $userId, $create_time, $update_time, $state, $amount, $currency) {
        $stmt = $this->conn->prepare("INSERT INTO payments(paypalPaymentId, userId, create_time, update_time, state, amount, currency) VALUES(?,?,?,?,?,?,?)") or die(mysql_error());
        $stmt->bind_param("sisssds", $paypalPaymentId, $userId, $create_time, $update_time, $state, $amount, $currency);
        $result = $stmt->execute();
        $stmt->close();

        if ($result) {
            // task row created
            // now assign the task to user
            $payment_id = $this->conn->insert_id;
            return $payment_id;
        } else {
            // task failed to create
            return NULL;
        }
    }

    /**
     * Stores item sale in sales table
     */
    public function storeSale($payment_id, $product_id, $state, $salePrice, $quantity) {
        $stmt = $this->conn->prepare("INSERT INTO sales(paymentId, productId, state, salePrice, quantity) VALUES(?,?,?,?,?)");
        $stmt->bind_param("iisdi", $payment_id, $product_id, $state, $salePrice, $quantity);
        $result = $stmt->execute();
        $stmt->close();

        if ($result) {
            $sale_id = $this->conn->insert_id;
            return $sale_id;
        } else {
            // task failed to create
            return NULL;
        }
    }

}

?>

8. Now create a file named .htaccess inside v1 folder and add below rules.

RewriteEngine On 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteRule ^(.*)$ %{ENV:BASE}index.php [QSA,L]

9. Finally create index.php inside v1 and paste below code. This is most important file where we handle all the REST requests using Slim Framework.

In the below code

echoResponse() – Prints final json response when API call is made

$app->get(‘/products’.. – Fetches all the products from products table and prints in json format

$app->post(‘/verifyPayment’.. – Verifies the paypal payment on the server side that was done on mobile app. This server side verification is very important for every paypal transaction to avoid fraudulent payments.



<?php

ini_set('display_errors', 1);
require_once '../include/DBHandler.php';
require '../libs/Slim/Slim.php';

require __DIR__ . '/../libs/PayPal/autoload.php';

use PayPal\Api\Payment;

\Slim\Slim::registerAutoloader();

$app = new \Slim\Slim();

$userId = 1;

/**
 * Echoing json response to client
 * @param String $status_code Http response code
 * @param Int $response Json response
 */
function echoResponse($status_code, $response) {
    $app = \Slim\Slim::getInstance();
    // Http response code
    $app->status($status_code);

    // setting response content type to json
    $app->contentType('application/json');

    echo json_encode($response);
}

function authenticate(\Slim\Route $route) {
    // Implement your user authentication here
    // Check http://www.androidhive.info/2014/01/how-to-create-rest-api-for-android-app-using-php-slim-and-mysql-day-23/
}

/**
 * Lists all products
 * method - GET
 */
$app->get('/products', 'authenticate', function() {
            $response = array();
            $db = new DbHandler();

            // fetching all products
            $result = $db->getAllProducts();

            $response["error"] = false;
            $response["products"] = array();

            // looping through result and preparing products array
            while ($task = $result->fetch_assoc()) {
                $tmp = array();
                $tmp["id"] = $task["id"];
                $tmp["name"] = $task["name"];
                $tmp["price"] = $task["price"];
                $tmp["description"] = $task["description"];
                $tmp["image"] = $task["image"];
                $tmp["sku"] = $task["sku"];
                $tmp["created_at"] = $task["created_at"];
                array_push($response["products"], $tmp);
            }

            echoResponse(200, $response);
        });

/**
 * verifying the mobile payment on the server side
 * method - POST
 * @param paymentId paypal payment id
 * @param paymentClientJson paypal json after the payment
 */
$app->post('/verifyPayment', 'authenticate', function() use ($app) {

            $response["error"] = false;
            $response["message"] = "Payment verified successfully";
            global $userId;


            require_once '../include/Config.php';

            try {
                $paymentId = $app->request()->post('paymentId');
                $payment_client = json_decode($app->request()->post('paymentClientJson'), true);

                $apiContext = new \PayPal\Rest\ApiContext(
                        new \PayPal\Auth\OAuthTokenCredential(
                        PAYPAL_CLIENT_ID, // ClientID
                        PAYPAL_SECRET      // ClientSecret
                        )
                );

                // Gettin payment details by making call to paypal rest api
                $payment = Payment::get($paymentId, $apiContext);

                // Verifying the state approved
                if ($payment->getState() != 'approved') {
                    $response["error"] = true;
                    $response["message"] = "Payment has not been verified. Status is " . $payment->getState();
                    echoResponse(200, $response);
                    return;
                }

                // Amount on client side
                $amount_client = $payment_client["amount"];

                // Currency on client side
                $currency_client = $payment_client["currency_code"];

                // Paypal transactions
                $transaction = $payment->getTransactions()[0];
                // Amount on server side
                $amount_server = $transaction->getAmount()->getTotal();
                // Currency on server side
                $currency_server = $transaction->getAmount()->getCurrency();
                $sale_state = $transaction->getRelatedResources()[0]->getSale()->getState();

                // Storing the payment in payments table
                $db = new DbHandler();
                $payment_id_in_db = $db->storePayment($payment->getId(), $userId, $payment->getCreateTime(), $payment->getUpdateTime(), $payment->getState(), $amount_server, $amount_server);

                // Verifying the amount
                if ($amount_server != $amount_client) {
                    $response["error"] = true;
                    $response["message"] = "Payment amount doesn't matched.";
                    echoResponse(200, $response);
                    return;
                }

                // Verifying the currency
                if ($currency_server != $currency_client) {
                    $response["error"] = true;
                    $response["message"] = "Payment currency doesn't matched.";
                    echoResponse(200, $response);
                    return;
                }

                // Verifying the sale state
                if ($sale_state != 'completed') {
                    $response["error"] = true;
                    $response["message"] = "Sale not completed";
                    echoResponse(200, $response);
                    return;
                }

                // storing the saled items
                insertItemSales($payment_id_in_db, $transaction, $sale_state);

                echoResponse(200, $response);
            } catch (\PayPal\Exception\PayPalConnectionException $exc) {
                if ($exc->getCode() == 404) {
                    $response["error"] = true;
                    $response["message"] = "Payment not found!";
                    echoResponse(404, $response);
                } else {
                    $response["error"] = true;
                    $response["message"] = "Unknown error occurred!" . $exc->getMessage();
                    echoResponse(500, $response);
                }
            } catch (Exception $exc) {
                $response["error"] = true;
                $response["message"] = "Unknown error occurred!" . $exc->getMessage();
                echoResponse(500, $response);
            }
        });

/**
 * method to store the saled items in sales table
 */
function insertItemSales($paymentId, $transaction, $state) {

    $item_list = $transaction->getItemList();

    $db = new DbHandler();

    foreach ($item_list->items as $item) {
        $sku = $item->sku;
        $price = $item->price;
        $quanity = $item->quantity;

        $product = $db->getProductBySku($sku);

        // inserting row into sales table
        $db->storeSale($paymentId, $product["id"], $state, $price, $quanity);
    }
}

$app->run();
?>

Now we have completed the server side part. Below are the final endpoints to which our android app should make requests.

URL endpoints

URLMethodParametersDescription
http://localhost/PayPalServer/v1/productsGETFetches all the products
http://localhost/PayPalServer/v1/verifyPaymentPOSTpaymentId, paymentClientJsonVerifies paypal payment

In the next article Android Integrating PayPal using PHP, MySQL – Part 2 we’re going to build the android app and integrate the PayPal gateway. If you have any queries in this part, please do comment in the comment section below.

Ravi is hardcore Android programmer and Android programming has been his passion since he compiled his first hello-world program. Solving real problems of Android developers through tutorials has always been interesting part for him.