PayPal is one of the most popular ways to sell/buy things online, in this article we take a deep look how it works in Laravel framework.

Related. we’ve also written a similar tutorial: Stripe Payments in Laravel: The Ultimate Guide

In this tutorial we will be creating a demo application for one-time and recurring payments with PayPal:

So, let’s begin!


Preparation and database

First, let’s create a new typical Laravel application

laravel new paypal-demo

Next – usual installation steps:

composer install
cp .env.example .env (and then editing .env with credentials)
php artisan key:generate
php artisan migrate

In this case we will also run php artisan make:auth to have a little better visual theme.

Next, for this tutorial we will be using a package called Laravel-Paypal which has great reputation from the community and is totally recommended, to avoid reinventing the wheel.

composer require srmklive/paypal

Next, add a service provider to $providers array in config/app.php (not needed for Laravel 5.5):

Srmklive\PayPal\Providers\PayPalServiceProvider::class

And publish the config:

php artisan vendor:publish --provider "Srmklive\PayPal\Providers\PayPalServiceProvider"

Now we need to set up our PayPal payment information in .env file

PAYPAL_SANDBOX_API_USERNAME=
PAYPAL_SANDBOX_API_PASSWORD=
PAYPAL_SANDBOX_API_SECRET=

For this you need to go to https://developer.paypal.com/ and login to your dashboard.

Click on the sandbox > accounts in the menu.

Create new business and buyer accounts, so we could “play” in our PayPal’s sandbox.

Click on your business user and go to profile, then open API credentials tab.

Fill in your .env file

PAYPAL_SANDBOX_API_USERNAME=qa-facilitator_api1.quickadminpanel.com
PAYPAL_SANDBOX_API_PASSWORD=JL6YMPSV3JJRPCUT
PAYPAL_SANDBOX_API_SECRET=AFcWxV21C7fd0v3bYYYRCpSSRl31AsqWRQpDCA8YSkVq5GfjhMIjbsw4

Next let’s create a new Invoice model with its migration, so we have a place to store our payment information.

php artisan make:model Invoice --migration

Add fillable fields to Invoice.php model

protected $fillable = ['title', 'price', 'payment_status'];

To check if invoice was paid, let’s add a paid attribute

    public function getPaidAttribute() {
    	if ($this->payment_status == 'Invalid') {
    		return false;
    	}
    	return true;
    }

And our database migration looks like this:

public function up()
 {
     Schema::create('invoices', function (Blueprint $table) {
         $table->increments('id');
         $table->string('title');
         $table->double('price', 2);
         $table->string('payment_status')->nullable();
         $table->string('recurring_id')->nullable();
         $table->timestamps();
    });
 }

Ok, we’re done with the database layer, now let’s move to visual things.


Routes, Views and Redirect to PayPal

Now we can create routes for our application

Route::get('paypal/express-checkout', 'PaypalController@expressCheckout')->name('paypal.express-checkout');
Route::get('paypal/express-checkout-success', 'PaypalController@expressCheckoutSuccess');
Route::post('paypal/notify', 'PaypalController@notify');

For this application we will have two buttons: one will represent a single payment, the second will be a recurring payment. Let’s create them in resources/views/welcome.blade.php:

@extends('layouts.app')

@section('content')
 <div class="container">
  <div class="row">
   <div class="col-md-8 col-md-offset-2">

    @if (Session::has('message'))
     <div class="alert alert-{{ Session::get('code') }}">
      <p>{{ Session::get('message') }}</p>
     </div>
    @endif

    <div class="panel panel-default">
     <div class="panel-heading">Express checkout</div>
     <div class="panel-body">
      Pay $20 via:
      <a href="{{ route('paypal.express-checkout') }}" class='btn-info btn'>PayPal</a>
     </div>
    </div>
   
    <div class="panel panel-default">
     <div class="panel-heading">Recurring payments</div>
     <div class="panel-body">
      Pay $20/month:
      <a href="{{ route('paypal.express-checkout', ['recurring' => true]) }}" class='btn-info btn'>PayPal</a>
     </div>
    </div>

   </div>
  </div>
 </div>
@endsection

After creating routes and views we should make a Controller, to handle all of our payment logic.

php artisan make:controller PaypalController

First thing our controller needs is a constructor method, so we can use our laravel-paypal package.

use Srmklive\PayPal\Services\ExpressCheckout;

protected $provider;
public function __construct() {
    $this->provider = new ExpressCheckout();
}

Next we need our expressCheckout method, that will redirect user to PayPal, so he can approve the payment.

public function expressCheckout(Request $request) {
  // check if payment is recurring
  $recurring = $request->input('recurring', false) ? true : false;

  // get new invoice id
  $invoice_id = Invoice::count() + 1;
      
  // Get the cart data
  $cart = $this->getCart($recurring, $invoice_id);

  // create new invoice
  $invoice = new Invoice();
  $invoice->title = $cart['invoice_description'];
  $invoice->price = $cart['total'];
  $invoice->save();

  // send a request to paypal 
  // paypal should respond with an array of data
  // the array should contain a link to paypal's payment system
  $response = $this->provider->setExpressCheckout($cart, $recurring);

  // if there is no link redirect back with error message
  if (!$response['paypal_link']) {
    return redirect('/')->with(['code' => 'danger', 'message' => 'Something went wrong with PayPal']);
    // For the actual error message dump out $response and see what's in there
  }

  // redirect to paypal
  // after payment is done paypal
  // will redirect us back to $this->expressCheckoutSuccess
  return redirect($response['paypal_link']);
}

At this point we should take a look at the $this->getCart() method to find out what information PayPal wants from us in order to make a payment.

private function getCart($recurring, $invoice_id)
    {

        if ($recurring) {
            return [
                // if payment is recurring cart needs only one item
                // with name, price and quantity
                'items' => [
                    [
                        'name' => 'Monthly Subscription ' . config('paypal.invoice_prefix') . ' #' . $invoice_id,
                        'price' => 20,
                        'qty' => 1,
                    ],
                ],

                // return url is the url where PayPal returns after user confirmed the payment
                'return_url' => url('/paypal/express-checkout-success?recurring=1'),
                'subscription_desc' => 'Monthly Subscription ' . config('paypal.invoice_prefix') . ' #' . $invoice_id,
                // every invoice id must be unique, else you'll get an error from paypal
                'invoice_id' => config('paypal.invoice_prefix') . '_' . $invoice_id,
                'invoice_description' => "Order #". $invoice_id ." Invoice",
                'cancel_url' => url('/'),
                // total is calculated by multiplying price with quantity of all cart items and then adding them up
                // in this case total is 20 because price is 20 and quantity is 1
                'total' => 20, // Total price of the cart
            ];
        }

        return [
            // if payment is not recurring cart can have many items
            // with name, price and quantity
            'items' => [
                [
                    'name' => 'Product 1',
                    'price' => 10,
                    'qty' => 1,
                ],
                [
                    'name' => 'Product 2',
                    'price' => 5,
                    'qty' => 2,
                ],
            ],

            // return url is the url where PayPal returns after user confirmed the payment
            'return_url' => url('/paypal/express-checkout-success'),
            // every invoice id must be unique, else you'll get an error from paypal
            'invoice_id' => config('paypal.invoice_prefix') . '_' . $invoice_id,
            'invoice_description' => "Order #" . $invoice_id . " Invoice",
            'cancel_url' => url('/'),
            // total is calculated by multiplying price with quantity of all cart items and then adding them up
            // in this case total is 20 because Product 1 costs 10 (price 10 * quantity 1) and Product 2 costs 10 (price 5 * quantity 2)
            'total' => 20,
        ];
    }

So, first we need to specify the items of the cart. It can be some products that have a name, price and quantity:

'items' => [
  [
    'name' => 'Product 1',
    'price' => 10,
    'qty' => 1,
  ],
  [
    'name' => 'Product 2',
    'price' => 5,
    'qty' => 2,
  ],
],

To get back to our application after user confirms the payment, PayPal needs a return URL:

'return_url' => url('/paypal/express-checkout-success?recurring=1'),

Important: The return URL of recurring payment has a parameter recurring=1, we need this parameter later, to create a recurring payment profile.

Every PayPal transaction needs a unique invoice_id and invoice_description

'invoice_id' => config('paypal.invoice_prefix') . '_' . $invoice_id,
'invoice_description' => "Order #". $invoice_id ." Invoice",

And the last thing our cart needs is a total price of the cart

'total' => 20,

The total is calculated by multiplying each item quantity by it’s price and then adding them up. So in our case it should be (10 * 1) + (5 * 2) = 20.

Note: If cart total is miscalculated, PayPal will redirect you back with an error, and the payment will not be processed.


Processing the transaction

Now that we got the cart figured out we can proceed to the expressCheckoutSuccess() method. This method is executed when user confirms the payment in PayPal and is returned to our application.

public function expressCheckoutSuccess(Request $request) {

        // check if payment is recurring
        $recurring = $request->input('recurring', false) ? true : false;

        $token = $request->get('token');

        $PayerID = $request->get('PayerID');

        // initaly we paypal redirects us back with a token
        // but doesn't provice us any additional data
        // so we use getExpressCheckoutDetails($token)
        // to get the payment details
        $response = $this->provider->getExpressCheckoutDetails($token);

        // if response ACK value is not SUCCESS or SUCCESSWITHWARNING
        // we return back with error
        if (!in_array(strtoupper($response['ACK']), ['SUCCESS', 'SUCCESSWITHWARNING'])) {
            return redirect('/')->with(['code' => 'danger', 'message' => 'Error processing PayPal payment']);
        }

        // invoice id is stored in INVNUM
        // because we set our invoice to be xxxx_id
        // we need to explode the string and get the second element of array
        // witch will be the id of the invoice
        $invoice_id = explode('_', $response['INVNUM'])[1];

        // get cart data
        $cart = $this->getCart($recurring, $invoice_id);

        // check if our payment is recurring
        if ($recurring === true) {
            
            // if recurring then we need to create the subscription
            // you can create monthly or yearly subscriptions
            $response = $this->provider->createMonthlySubscription($response['TOKEN'], $response['AMT'], $cart['subscription_desc']);
            
            $status = 'Invalid';
            // if after creating the subscription paypal responds with activeprofile or pendingprofile
            // we are good to go and we can set the status to Processed, else status stays Invalid
            if (!empty($response['PROFILESTATUS']) && in_array($response['PROFILESTATUS'], ['ActiveProfile', 'PendingProfile'])) {
                $status = 'Processed';
            }

        } else {

            // if payment is not recurring just perform transaction on PayPal
            // and get the payment status
            $payment_status = $this->provider->doExpressCheckoutPayment($cart, $token, $PayerID);
            $status = $payment_status['PAYMENTINFO_0_PAYMENTSTATUS'];

        }

        // find invoice by id
        $invoice = Invoice::find($invoice_id);

        // set invoice status
        $invoice->payment_status = $status;

        // if payment is recurring lets set a recurring id for latter use
        if ($recurring === true) {
            $invoice->recurring_id = $response['PROFILEID'];
        }

        // save the invoice
        $invoice->save();

        // App\Invoice has a paid attribute that returns true or false based on payment status
        // so if paid is false return with error, else return with success message
        if ($invoice->paid) {
            return redirect('/')->with(['code' => 'success', 'message' => 'Order ' . $invoice->id . ' has been paid successfully!']);
        }
        
        return redirect('/')->with(['code' => 'danger', 'message' => 'Error processing PayPal payment for Order ' . $invoice->id . '!']);
    }

Ok, so this method may look big, but it’s quite simple.

1. Get information from PayPal about the payment

$response = $this->provider->getExpressCheckoutDetails($token);

2. Check if payment was successful.

3. Then we check if payment was recurring or not?
Note: This parameter is not added by PayPal. We set this parameter in our cart’s return_url as recurring=1 if the payment was recurring. So if our payment was recurring, we need to create a subscription profile for the user, so he doesn’t need to pay us manually.

$response = $this->provider->createMonthlySubscription($response['TOKEN'], $response['AMT'], $cart['subscription_desc']);

4. Otherwise jus process the payment.

$payment_status = $this->provider->doExpressCheckoutPayment($cart, $token, $PayerID);

5. Lastly we update our invoice and redirect the user to the home page.


At this point you should be able to make your first test payment, yay!

Just go to your applications home page and click one of the buttons we created. You should be redirected to PayPal.

Important: for sandbox, here you should use the buyer account we created earlier.

Hit Agree and continue, and you should be redirected back to your application with a success message.

If you made a recurring payment, you can check your buyer account in PayPal’s sandbox to see if recurring profile was created. Just go to settings > payments > manage pre-approved payments and you should see the active subscription.

Note: you can also login to your business account in PayPal’s sandbox.


Instant Payment Notifications

For the last part of this demo we need to handle PayPal’s IPNs (Instant Payment Notification) that will be sent to our application every payment cycle after subscription.

Add this method to the controller

public function notify(Request $request)
    {

        // add _notify-validate cmd to request,
        // we need that to validate with PayPal that it was realy
        // PayPal who sent the request
        $request->merge(['cmd' => '_notify-validate']);
        $post = $request->all();

        // send the data to PayPal for validation
        $response = (string) $this->provider->verifyIPN($post);

        //if PayPal responds with VERIFIED we are good to go
        if ($response === 'VERIFIED') {

            /**
                This is the part of the code where you can process recurring payments as you like
                in this case we will be checking for recurring_payment that was completed
                if we find that data we create new invoice
            */
            if ($post['txn_type'] == 'recurring_payment' && $post['payment_status'] == 'Completed') {
                $invoice = new Invoice();
                $invoice->title = 'Recurring payment';
                $invoice->price = $post['amount'];
                $invoice->payment_status = 'Completed';
                $invoice->recurring_id = $post['recurring_payment_id'];
                $invoice->save();
            }

            // I leave this code here so you can log IPN data if you want
            // PayPal provides a lot of IPN data that you should save in real world scenarios
            /*                      
                $logFile = 'ipn_log_'.Carbon::now()->format('Ymd_His').'.txt';
                Storage::disk('local')->put($logFile, print_r($post, true));
            */
            
        }  
        
    }

Once PayPal sends a request to our application, we verify the posted data with PayPal and if the response equals “Verified” we can process the information

Only thing left to do is to go to PayPal’s sandbox then login to your business account that we created in the beginning of this tutorial go to Settings > My selling tools.

Go to Instant payment notifications, then enter your application URL and check “Receive IPN messages (Enabled)”.

Save the settings and PayPal will send you recurring payment notifications.

One interesting thing about PayPal is that you can test IPN’s in your developers console but can’t test recurring payments!

So if we want to test them, we need to make some mocks of recurring payments that will hit our notify route.

To do that, just add one more route

Route::get('/test', function() {
	return view('test');
});

Create a new file resources/views/test.blade.php and add some forms for testing.

<!-- Profile Created -->

<form target="_new" method="post" action="{{ url('/paypal/notify') }}">

 <input type="hidden" name="verify_sign" value="ASsJ54wcfEJZVuwOMU8vBNHZb1TpAf7F4PMLvKL2uni1hb11jdOgdd2V" />
 <input type="hidden" name="period_type" value="Regular" />
 <input type="hidden" name="payer_status" value="verified" />
 <input type="hidden" name="test_ipn" value="1" />
 <input type="hidden" name="tax" value="0.00" />
 <input type="hidden" name="payer_email" value="sandbo_1204199080_biz@angelleye.com" />
 <input type="hidden" name="first_name" value="Drew" />
 <input type="hidden" name="receiver_email" value="sandbo_1215254764_biz@angelleye.com" />
 <input type="hidden" name="payer_id" value="E7BTGVXBFSUAU" />
 <input type="hidden" name="product_type" value="1" />
 <input type="hidden" name="payer_business_name" value="Drew Angell's Test Store" />
 <input type="hidden" name="shipping" value="0.00" />
 <input type="hidden" name="amount_per_cycle" value="30.00" />
 <input type="hidden" name="profile_status" value="Active" />
 <input type="hidden" name="charset" value="windows-1252" />
 <input type="hidden" name="notify_version" value="3.7" />
 <input type="hidden" name="amount" value="30.00" />
 <input type="hidden" name="outstanding_balance" value="0.00" />
 <input type="hidden" name="recurring_payment_id" value="I-VYR2VN3XPVW4" />
 <input type="hidden" name="product_name" value="The HALO Foundation Donation" />
 <input type="hidden" name="ipn_track_id" value="348867a2b7815" />
<input type="submit" value="Send Profile Created"/> </form>

<!-- Payment Made -->

<form target="_new" method="post" action="{{ url('/paypal/notify') }}">

 <input type="hidden" name="mc_gross" value="10.00" />
 <input type="hidden" name="period_type" value=" Regular" />
 <input type="hidden" name="outstanding_balance" value="0.00" />
 <input type="hidden" name="next_payment_date" value="02:00:00 Dec 16, 2013 PST" />
 <input type="hidden" name="protection_eligibility" value="Ineligible" />
 <input type="hidden" name="payment_cycle" value="every 3 Months" />
 <input type="hidden" name="tax" value="0.00" />
 <input type="hidden" name="payer_id" value="3HMDJA96TEQN4" />
 <input type="hidden" name="payment_date" value="05:19:33 Sep 16, 2013 PDT" />
 <input type="hidden" name="payment_status" value="Completed" />
 <input type="hidden" name="product_name" value="platypu subscription" />
 <input type="hidden" name="charset" value="windows-1252" />
 <input type="hidden" name="recurring_payment_id" value="I-R52C41AGNEAP" />
 <input type="hidden" name="first_name" value="test" />
 <input type="hidden" name="mc_fee" value="0.64" />
 <input type="hidden" name="notify_version" value="3.7" />
 <input type="hidden" name="amount_per_cycle" value="10.00" />
 <input type="hidden" name="payer_status" value="unverified" />
 <input type="hidden" name="currency_code" value="USD" />
 <input type="hidden" name="business" value="sandbo_1215254764_biz@angelleye.com" />
 <input type="hidden" name="verify_sign" value="A4QWarlQUU0cupDGeAi-McuvfslGA7lrbrWV735PGPsr3OKdTRFyJtOq" />
 <input type="hidden" name="payer_email" value="test@domain.com" />
 <input type="hidden" name="initial_payment_amount" value="0.00" />
 <input type="hidden" name="profile_status" value="Active" />
 <input type="hidden" name="amount" value="10.00" />
 <input type="hidden" name="txn_id" value="34Y69196BK064583G" />
 <input type="hidden" name="payment_type" value="instant" />
 <input type="hidden" name="last_name" value="test" />
 <input type="hidden" name="receiver_email" value="sandbo_1215254764_biz@angelleye.com" />
 <input type="hidden" name="payment_fee" value="0.64" />
 <input type="hidden" name="receiver_id" value="ATSCG2QMC9KAU" />
 <input type="hidden" name="txn_type" value="recurring_payment" />
 <input type="hidden" name="mc_currency" value="USD" />
 <input type="hidden" name="residence_country" value="US" />
 <input type="hidden" name="test_ipn" value="1" />
 <input type="hidden" name="receipt_id" value="1660-1430-7506-9911" />
 <input type="hidden" name="transaction_subject" value="" />
 <input type="hidden" name="payment_gross" value="10.00" />
 <input type="hidden" name="shipping" value="0.00" />
 <input type="hidden" name="product_type" value="1" />
 <input type="hidden" name="time_created" value="07:54:24 Sep 05, 2013 PDT" />
 <input type="hidden" name="ipn_track_id" value="efd4ee6ea4474" />
<input type="submit" value="Send Payment Made"/> </form>

<!-- Payment Skipped -->

<form target="_new" method="post" action="{{ url('/paypal/notify') }}">

 <input type="hidden" name="payment_cycle" value="Monthly" />
 <input type="hidden" name="txn_type" value="recurring_payment_skipped" />
 <input type="hidden" name="last_name" value="Smith" />
 <input type="hidden" name="next_payment_date" value="03:00:00 Sep 21, 2013 PDT" />
 <input type="hidden" name="residence_country" value="US" />
 <input type="hidden" name="initial_payment_amount" value="0.00" />
 <input type="hidden" name="currency_code" value="USD" />
 <input type="hidden" name="time_created" value="19:42:33 Jan 11, 2013 PST" />
 <input type="hidden" name="verify_sign" value="AcyQRlWufyrh0B6-n5swEgNB9oNJAkMm65cAu2bQLTevdnT2JnuIyDQO" />
 <input type="hidden" name="period_type" value=" Regular" />
 <input type="hidden" name="payer_status" value="unverified" />
 <input type="hidden" name="test_ipn" value="1" />
 <input type="hidden" name="tax" value="0.00" />
 <input type="hidden" name="payer_email" value="tester@hey.com" />
 <input type="hidden" name="first_name" value="working" />
 <input type="hidden" name="receiver_email" value="sandbo_1215254764_biz@angelleye.com" />
 <input type="hidden" name="payer_id" value="4ATNY663RDKJA" />
 <input type="hidden" name="product_type" value="1" />
 <input type="hidden" name="shipping" value="0.00" />
 <input type="hidden" name="amount_per_cycle" value="10.00" />
 <input type="hidden" name="profile_status" value="Active" />
 <input type="hidden" name="charset" value="windows-1252" />
 <input type="hidden" name="notify_version" value="3.7" />
 <input type="hidden" name="amount" value="10.00" />
 <input type="hidden" name="outstanding_balance" value="60.00" />
 <input type="hidden" name="recurring_payment_id" value="I-LH2MJXG27TR6" />
 <input type="hidden" name="product_name" value="Angell EYE Web Hosting" />
 <input type="hidden" name="ipn_track_id" value="e3a52d6772d28" />
<input type="submit" value="Send Payment Skipped"/> </form>

<!-- Payment Failed -->

<form target="_new" method="post" action="{{ url('/paypal/notify') }}">

 <input type="hidden" name="payment_cycle" value="every 4 Weeks" />
 <input type="hidden" name="txn_type" value="recurring_payment_failed" />
 <input type="hidden" name="last_name" value="Tester" />
 <input type="hidden" name="next_payment_date" value="03:00:00 Oct 03, 2013 PDT" />
 <input type="hidden" name="residence_country" value="US" />
 <input type="hidden" name="initial_payment_amount" value="0" />
 <input type="hidden" name="currency_code" value="JPY" />
 <input type="hidden" name="time_created" value="05:14:37 Aug 01, 2012 PDT" />
 <input type="hidden" name="verify_sign" value="AOTn5qT2D05NGLBeQowuGwhI5kTFAIPV01VWay1FayueRmXhAYd2KLZp" />
 <input type="hidden" name="period_type" value=" Regular" />
 <input type="hidden" name="payer_status" value="unverified" />
 <input type="hidden" name="test_ipn" value="1" />
 <input type="hidden" name="tax" value="0" />
 <input type="hidden" name="payer_email" value="prachi@signyit.com" />
 <input type="hidden" name="first_name" value="Ecaf" />
 <input type="hidden" name="receiver_email" value="sandbo_1215254764_biz@angelleye.com" />
 <input type="hidden" name="payer_id" value="VCLJR9E79V4KJ" />
 <input type="hidden" name="product_type" value="1" />
 <input type="hidden" name="shipping" value="0" />
 <input type="hidden" name="amount_per_cycle" value="1" />
 <input type="hidden" name="profile_status" value="Active" />
 <input type="hidden" name="charset" value="windows-1252" />
 <input type="hidden" name="notify_version" value="3.7" />
 <input type="hidden" name="amount" value="1" />
 <input type="hidden" name="outstanding_balance" value="1" />
 <input type="hidden" name="recurring_payment_id" value="I-P90BX92X15DR" />
 <input type="hidden" name="product_name" value="Welcome to the world of shopping where you get everything" />
 <input type="hidden" name="ipn_track_id" value="ab99ea6823e24" />
<input type="submit" value="Send Payment Failed"/> </form>

<!-- Profile Suspended -->

<form target="_new" method="post" action="{{ url('/paypal/notify') }}">

 <input type="hidden" name="payment_cycle" value="Monthly" />
 <input type="hidden" name="txn_type" value="recurring_payment_suspended_due_to_max_failed_payment" />
 <input type="hidden" name="last_name" value="Lang" />
 <input type="hidden" name="next_payment_date" value="N/A" />
 <input type="hidden" name="residence_country" value="US" />
 <input type="hidden" name="initial_payment_amount" value="4.90" />
 <input type="hidden" name="currency_code" value="USD" />
 <input type="hidden" name="time_created" value="13:45:44 Nov 04, 2010 PDT" />
 <input type="hidden" name="verify_sign" value="A65EYvoNuupMDbNU-2RPi609XJ7LAQ8CzxOV03bR4.O-nKSYG9LjBf10" />
 <input type="hidden" name="period_type" value=" Regular" />
 <input type="hidden" name="payer_status" value="unverified" />
 <input type="hidden" name="test_ipn" value="1" />
 <input type="hidden" name="tax" value="0.00" />
 <input type="hidden" name="payer_email" value="corey@angelleye.com" />
 <input type="hidden" name="first_name" value="Corey" />
 <input type="hidden" name="receiver_email" value="sandbo_1215254764_biz@angelleye.com" />
 <input type="hidden" name="payer_id" value="HKHX3D32P9DXG" />
 <input type="hidden" name="product_type" value="1" />
 <input type="hidden" name="shipping" value="0.00" />
 <input type="hidden" name="amount_per_cycle" value="29.95" />
 <input type="hidden" name="profile_status" value="Suspended" />
 <input type="hidden" name="charset" value="windows-1252" />
 <input type="hidden" name="notify_version" value="3.7" />
 <input type="hidden" name="amount" value="29.95" />
 <input type="hidden" name="outstanding_balance" value="149.75" />
 <input type="hidden" name="recurring_payment_id" value="I-Y0E6UC684RS4" />
 <input type="hidden" name="product_name" value="Achieve Formulas 30 day supply, monthly." />
 <input type="hidden" name="ipn_track_id" value="95c39c8a4b39d" />
<input type="submit" value="Send Profile Suspended"/> </form>

<!-- Profile Canceled -->

<form target="_new" method="post" action="{{ url('/paypal/notify') }}">

 <input type="hidden" name="payment_cycle" value="Monthly" />
 <input type="hidden" name="txn_type" value="recurring_payment_profile_cancel" />
 <input type="hidden" name="last_name" value="Testerson" />
 <input type="hidden" name="next_payment_date" value="N/A" />
 <input type="hidden" name="residence_country" value="US" />
 <input type="hidden" name="initial_payment_amount" value="69.90" />
 <input type="hidden" name="rp_invoice_id" value="4603" />
 <input type="hidden" name="currency_code" value="USD" />
 <input type="hidden" name="time_created" value="09:40:52 Feb 11, 2013 PST" />
 <input type="hidden" name="verify_sign" value="AGiC06LknLf7LnPNSt03A0q0ajKiAZt35jsIvkcPn5dU7GtRl-ITAf5Q" />
 <input type="hidden" name="period_type" value=" Regular" />
 <input type="hidden" name="payer_status" value="verified" />
 <input type="hidden" name="tax" value="0.00" />
 <input type="hidden" name="payer_email" value="payer@email.com" />
 <input type="hidden" name="first_name" value="Tester" />
 <input type="hidden" name="receiver_email" value="sandbox@domain.com" />
 <input type="hidden" name="payer_id" value="Q28888N" />
 <input type="hidden" name="product_type" value="1" />
 <input type="hidden" name="shipping" value="0.00" />
 <input type="hidden" name="amount_per_cycle" value="1.95" />
 <input type="hidden" name="profile_status" value="Cancelled" />
 <input type="hidden" name="charset" value="windows-1252" />
 <input type="hidden" name="notify_version" value="3.7" />

 <input type="hidden" name="outstanding_balance" value="0.00" />
 <input type="hidden" name="recurring_payment_id" value="I-553Y5PRWJ29F" />
 <input type="hidden" name="product_name" value="USBSwiper Monthly Subscription" />
 <input type="hidden" name="ipn_track_id" value="5ecdc90112398" />
<input type="submit" value="Send Profile Canceled"/> </form>

<!-- Recurring Payment Expired -->

<form target="_new" method="post" action="{{ url('/paypal/notify') }}">

 <input type="hidden" name="payment_cycle" value="Monthly" />
 <input type="hidden" name="txn_type" value="recurring_payment_expired" />
 <input type="hidden" name="last_name" value="Testerson" />
 <input type="hidden" name="next_payment_date" value="N/A" />
 <input type="hidden" name="residence_country" value="US" />
 <input type="hidden" name="initial_payment_amount" value="0.00"/>
 <input type="hidden" name="rp_invoice_id" value="1580"/>
 <input type="hidden" name="currency_code" value="USD"/>
 <input type="hidden" name="time_created" value="09:42:46 Jan 12, 2011 PST"/>
 <input type="hidden" name="verify_sign" value="AbBIww12EQnvrHwYmd1wb98zYz53APIJHOa.GTV4C9Ef0HVE1FWBtxMP"/>
 <input type="hidden" name="period_type" value=" Regular"/>
 <input type="hidden" name="payer_status" value="unverified"/>
 <input type="hidden" name="tax" value="0.00"/>
 <input type="hidden" name="first_name" value="Tester" />
 <input type="hidden" name="receiver_email" value="payments@domain.com" />
 <input type="hidden" name="payer_id" value="R7J55555MN" />
 <input type="hidden" name="product_type" value="1" />
 <input type="hidden" name="shipping" value="0.00" />
 <input type="hidden" name="amount_per_cycle" value="1.00" />
 <input type="hidden" name="profile_status" value="Cancelled" />
 <input type="hidden" name="charset" value="windows-1252" />
 <input type="hidden" name="notify_version" value="3.0" />
 <input type="hidden" name="amount" value="1.00" />
 <input type="hidden" name="outstanding_balance" value="0.00" />
 <input type="hidden" name="recurring_payment_id" value="I-M0555555RY" />
 <input type="hidden" name="product_name" value="USBSwiper Rental Program" />
<input type="submit" value="Send Recurring Payment Expired"/> </form>

Now just go to your applications /test route and there will be all possible recurring payment calls available for your testing purposes.


So, that’s it! That was an overview of how you can use PayPal inside of Laravel. Was it helpful?

Read more: