#native_company# #native_desc#
#native_cta#

Working with the PayPal API

By Voja Janjic
on March 9, 2015

PayPal recently introduced a new RESTful API that is more convenient and more powerful than the previous version. In this article, I will show you how to integrate your PHP application with the new PayPal API.

PayPal REST API Summary

The new PayPal API uses HTTP verbs (GET, POST, PUT and DELETE) and a RESTful architecture, as well as the OAuth 2.0 authorization standard. The request and response data uses JSON format.

As well as the Classic API, the REST API supports two environments: sandbox (for testing) and live (for production). Their endpoint URLs are:

   • https://api.sandbox.paypal.com

   • https://api.paypal.com

PayPal offers SDKs for a number of programming languages, such as Java, C#, Ruby, Python and PHP. In this tutorial, we will use PayPal REST API PHP SDK, which can be found on GitHub or installed through Composer by adding the following line to the required section in composer.json and then running the composer update command:

"paypal/rest-api-sdk-php" : "0.5.*" 

Please note that this SDK would work only with PHP 5.3 or above, with curl, json, and openssl extensions enabled.

REST API PHP Integration

In this section of the article, we will integrate PayPal with an e-commerce PHP application that will allow a user to purchase products using PayPal, handle shipping, currency and discounts, as well as check the payment status and handle user’s order when the transaction has been confirmed.

If you install the SDK through Composer, it will be autoloaded. If that is not the case, use the sample/bootstrap.php file from the SDK and require it in your PHP application. Also, import the PayPal classes by using the following code:

<?php

use PayPalRestApiContext;
use PayPalAuthOAuthTokenCredential;
use PayPalApiAmount;
use PayPalApiDetails;
use PayPalApiItem;
use PayPalApiItemList;
use PayPalApiPayer;
use PayPalApiPayerInfo;
use PayPalApiPayment;
use PayPalApiRedirectUrls;
use PayPalApiExecutePayment;
use PayPalApiPaymentExecution;
use PayPalApiTransaction; 

Next, we need to create a controller that will connect to the PayPal API and receive its response:

class CartController extends BaseController {

}

As mentioned above, PayPal REST API requires authorization. In order to use it, you will need to register an app on their website, get the credentials (client id and secret) and then use the following credentials in your app:

private $_api_context;

public function __construct()
    {
                 parent::__construct();
		
                // Setup PayPal api context
                $paypal_conf = Config::get('paypal');
                $this->_api_context = new ApiContext(newOAuthTokenCredential($paypal_conf['client_id'], $paypal_conf['secret']));
        $this->_api_context->setConfig($paypal_conf['settings']);
    }

The OAuthTokenCredential and ApiContext classes would handle the authorization, remember the tokens and send them with each request. The context is stored as a controller variable, so it is available in all controller methods. $paypal_conf is just a Laravel configuration file that looks like this:

<?php
return array(
    // Set your PayPal credentials
    'client_id' => 'client_id_here',
    'secret' => 'secret_here',
    

    /**
     * SDK configuration 
     */
    'settings' => array(
        /**
         * Available option 'sandbox' or 'live'
         */
        'mode' => 'live',

        /**
         * Specify the max request time in seconds
         */
        'http.ConnectionTimeOut' => 30,

        /**
         * Whether want to log to a file
         */
        'log.LogEnabled' => true,

        /**
         * Specify the file that want to write on
         */
        'log.FileName' => storage_path() . '/logs/paypal.log',

        /**
         * Available option 'FINE', 'INFO', 'WARN' or 'ERROR'
         *
         * Logging is most verbose in the 'FINE' level and decreases as you
         * proceed towards ERROR
         */
        'log.LogLevel' => 'FINE'
    ),
); 

The PayPal SDK has a few convenient settings, such as switching environments and logging. After authorizing the app, let’s create a controller method that will create a PayPal payment:

public function postCheckout() {
	      // Some code here
	      // Process cart and create and order

	      // Remember order id as a session variable for later use
	      Session::put('order_id', $order->order_id);
				
	      // Create a PayPal payment

	      // Create Payer object
	      $payer = new Payer();

	      // Payment method is via PayPal (we don't do direct credit card processing)
    	      $payer->setPaymentMethod('paypal');
		
	      // Add information about the Payer
              $payer_info = new PayerInfo();
              $payer_info->setEmail($order->billing->address_email);
		
              // Loop through the ordered products		
              $i=0;
              $order_items = array();
              foreach($order->items as $item)
              {
                      // Each product information is sent as an item to PayPal
                      $order_items[$i] = new Item();

                      // Set item name, price, price currency and quantity
                      // Note that the price is the price for a single product unit
                      $order_items[$i]->setName($item->order_item_name)
				       	 ->setCurrency('USD')
				       	 ->setQuantity($item->order_item_quantity)
				       	 ->setPrice($item->order_item_price);
					
			$i++;
		}
			
       		// Check if any discounts are offered
		// If yes, discount the price
		// Discounts are created by creating an order item with negative price value
		if($order->order_discount > 0)
		{
			$order_items[$i] = new Item();
			$order_items[$i]->setName('Discount')
					        ->setCurrency('USD')
					        ->setQuantity(1)
					        ->setPrice('-' . $order->order_discount);	
		}				
				
		// Add items to list
		$item_list = new ItemList();
		$item_list->setItems($order_items);				
		
		
		$amount_details = new Details();
		
		// If shipping is not free, add a fee to the order		
		if($order->order_shipping_total > 0)
		{
	                    $amount_details->setShipping($order->order_shipping_total);	
		}
		
		// Set order subtotal
		// Subtotal is the sum of each product multiplied by quantity and all that minus the discount	
		$amount_details->setSubtotal($order->order_subtotal);
		
		// Set amount currency and total
		// Total is subtotal plus shipping fee		
		$amount = new Amount();
		$amount->setCurrency('USD')
				->setDetails($amount_details)
			        	->setTotal($order->order_total);

		// Create transaction object
		$transaction = new Transaction();
		$transaction->setAmount($amount)
				->setItemList($item_list)
			        ->setDescription('Mystore.com Order  #' . $order->order_id);
		// Set redirect URLs
		// PayPal will post to the return URL if the payment was successful
		// On the other hand, PayPal will post to the cancel URL if the payment was cancelled by the user
		$redirect_urls = new RedirectUrls();
		$redirect_urls->setReturnUrl(URL::route('payment.status'))
			        		->setCancelUrl(URL::to('cart'));
		
		// Create Payment object
		// According to the API documentation, it should hold Payer object, Transaction object,
                // RedirectUrls object and the intent should be set to sale					
		$payment = new Payment();
		$payment->setIntent('Sale')
			        ->setPayer($payer)
			        ->setRedirectUrls($redirect_urls)
			        ->setTransactions(array($transaction));
		
		// Call the PayPal API			
		try {
			$payment->create($this->_api_context);
		} catch (PayPalExceptionPPConnectionException $ex) {
			     if (Config::get('app.debug')) {
			        echo "Exception: " . $ex->getMessage() . PHP_EOL;
			        $err_data = json_decode($ex->getData(), true);
			        exit;
			     } else {
			        die('An error occurred, sorry for the inconvenience.');
                             }
		}
		
		// Get the URL to redirect to
		foreach($payment->getLinks() as $link) {
			if($link->getRel() == 'approval_url') {
				$redirect_url = $link->getHref();
				break;
			}
		}

		// PayPal sends back the payment id
		// Save it to the session
		Session::put('paypal_payment_id', $payment->getId());

		// Do the redirection
		if(isset($redirect_url)) {
			return Redirect::away($redirect_url);
		}

		// Error handling
   		return Redirect::route('checkout')->with('error', 'Unknown error occurred');
                }

If you take a look at the PayPal REST API reference, you will notice that some of the parameters for API calls need to be passed as class objects. These classes are included in the SDK and used in the code above. Also, if the payment is cancelled, the user will be redirected to the shopping cart. If the payment is successful, it will be redirected to the route below. This is where we need to make a few checks to determine whether or not the payment was really made:

public function getPaymentStatus()
{
        // Get the payment ID before session clear
        $payment_id = Session::get('paypal_payment_id');
	
	// Clear the session payment ID
	Session::forget('paypal_payment_id');
	
	// Get the Payer id and token
	$payer_id = Input::get('PayerID');
	$token = Input::get('token');
	
	// If any of the two is empty, payment was not made
	if (empty($payer_id) || empty($token)) 
	{
             return Redirect::to('checkout')->with('error', 'Payment failed');
        }
	
	// Call the API to check if the payment exists
	$payment = Payment::get($payment_id, $this->_api_context);
	
	// PaymentExecution object includes information necessary 
        // to execute a PayPal account payment. 
	// The payer_id is added to the request query parameters
	// when the user is redirected from PayPal back to your site
	$execution = new PaymentExecution();
        $execution->setPayerId(Input::get('PayerID'));
	
	//Execute the payment
	$result = $payment->execute($execution, $this->_api_context);
	
	// If PayPal payment status is approved, the payment is made
        if ($result->getState() == 'approved') 
        {
                // Change Order Status to paid
                $order = Order::find(Session::get('order_id'));
                $order->order_status_id = 2;
			
                if(!$order->save())
                {
                        // @TODO Error handling
                }
			
		
	    	
                // Send confirmation e-mail
                $this->sendConfirmationEmail($order);
			
	    	// Redirect to the order confirmation page
                return Redirect::route('checkout.completed')->with('success', 'Payment success')
                    ->with('order_id', $order->order_id);
}
	
        // Error handling	
        return Redirect::to('checkout')->with('error', 'Payment failed');
}

If the payment was successful, the thank you page will be shown:

public function getCheckoutCompleted()
{
        // Get order id that was saved before connecting to the PayPal
        $order_id = Session::get('order_id');
	
        // If that order id does not exist in the database, show 404 error 	
	if(!$order = Order::find($order_id))
        {
                App::abort(404);
        }

        // Fetch the order from the database
        $order = Order::find($order_id);
	
        // Assign order info to the view	
        $data['order'] = $order;
	
        // Clear cart
        Cart::destroy(Session::getId());
		
        // Show order confirmation page
        return View::make('app.cart.thankyou', $data);
}

Notes

   • This tutorial uses the Laravel framework. Adjust the code according to the framework you use.

   • The REST API tutorial shows only one (however, the most common) possible use of the PayPal API. For other use-cases and advanced options, check the PayPal REST API documentation.

   • Good luck with your online business!