Simplifying PayPal Subscriptions with Laravel: A Practical Guide

Haziq Ali
7 min readJun 4, 2023

--

PayPal subscriptions are a popular choice for businesses to manage recurring payments efficiently. In this article, we will explore how to integrate PayPal subscriptions into a Laravel application. By following the steps outlined here, you’ll be able to create, cancel, pause, resume, and update PayPal subscriptions seamlessly.

Prerequisites:

To implement PayPal subscriptions in your Laravel application, ensure that you have the following prerequisites:

  1. A Laravel project set up and running.
  2. Sufficient knowledge of Laravel’s basic concepts and syntax.
  3. Familiarity with PayPal’s subscription services.
  4. Create a PayPal Business account.
  5. Create subscription plans using the PayPal developer dashboard.
  6. Set PayPal Plan id in the Plans table column.

Step 1: Install Required Packages:

To begin, open your Laravel project in a terminal or command prompt and run the following command:

For Laravel 5.1 to 5.8

composer require srmklive/paypal:~2.0

For Laravel 6, 7, & 8 and onwards

composer require srmklive/paypal:~3.0

This command will install the “srmklive/paypal” package, which provides the necessary functionality to integrate PayPal subscriptions into your Laravel application.

Step 2: Publish Assets

After installation, you can use the following commands to publish the assets:

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

Step 3: Configuration

After publishing the assets, add the following to your .env file.

#PayPal API Mode
# Values: sandbox or live (Default: live)
PAYPAL_MODE=

#PayPal Setting & API Credentials - sandbox
PAYPAL_SANDBOX_CLIENT_ID=
PAYPAL_SANDBOX_CLIENT_SECRET=

#PayPal Setting & API Credentials - live
PAYPAL_LIVE_CLIENT_ID=
PAYPAL_LIVE_CLIENT_SECRET=

Step 4: Configuration File

The configuration file paypal.php is located in the config folder. Following are its contents when published:

return [
'mode' => env('PAYPAL_MODE', 'sandbox'), // Can only be 'sandbox' Or 'live'. If empty or invalid, 'live' will be used.
'sandbox' => [
'client_id' => env('PAYPAL_SANDBOX_CLIENT_ID', ''),
'client_secret' => env('PAYPAL_SANDBOX_CLIENT_SECRET', ''),
'app_id' => 'APP-80W284485P519543T',
],
'live' => [
'client_id' => env('PAYPAL_LIVE_CLIENT_ID', ''),
'client_secret' => env('PAYPAL_LIVE_CLIENT_SECRET', ''),
'app_id' => '',
],

'payment_action' => env('PAYPAL_PAYMENT_ACTION', 'Sale'), // Can only be 'Sale', 'Authorization' or 'Order'
'currency' => env('PAYPAL_CURRENCY', 'USD'),
'notify_url' => env('PAYPAL_NOTIFY_URL', ''), // Change this accordingly for your application.
'locale' => env('PAYPAL_LOCALE', 'en_US'), // force gateway language i.e. it_IT, es_ES, en_US ... (for express checkout only)
'validate_ssl' => env('PAYPAL_VALIDATE_SSL', true), // Validate SSL when creating api client.
];

Step 4: Create PayPal Subscriptions Class

In your Laravel project, create a new PHP file called “PayPalSubscriptions.php” inside the “app/Subscriptions/PayPalSubscriptions” directory.

This class will contain all the necessary code to create, cancel, pause, resume, and update PayPal subscriptions.

Constructor:

Import PayPal class namespace first before using it. Initialize PayPal Client so that we can use all the necessary functions for our app subscriptions to work properly.

<?php

namespace App\Subscriptions\PayPalSubscriptions;

use Exception;
use Illuminate\Support\Facades\Log;
use Srmklive\PayPal\Services\PayPal as PayPalClient;

class PayPalSubscriptions
{
protected $provider;

public function __construct()
{
$this->provider = new PayPalClient;
$this->provider->setApiCredentials(config('paypal'));
}

// Methods for creating, canceling, pausing, resuming, and updating subscriptions
// will be implemented here
}

Create Subscriptions Interface:

In the same directory as the “PayPalSubscriptions.php” file, create a new PHP file called “Subscriptions.php”. This file will define an interface for the subscription functionality. It is important if you want to have multiple payment gateways implemented in your app. For example Stripe etc.

You can skip this step if you want to have only PayPal implemented.

Copy and paste the following code into the file:

<?php

namespace App\Subscriptions;

interface Subscriptions
{
public function create(int $plan_id, int $coupon_user_id, string $method, float $amount = 0);
public function cancel(string $subscription_id = null);
public function pause();
public function resume();
}

Implement Subscriptions Interface:

Inside the “PayPalSubscriptions.php” file, update the class declaration to implement the Subscriptions interface:

class PayPalSubscriptions implements Subscriptions
{
// Existing code here (e.g: constructor code)

// Implement the methods defined in the Subscriptions interface
public function create(int $plan_id, int $coupon_user_id, string $method, float $amount = 0)
{
// Implementation goes here
}

public function cancel(string $subscription_id = null)
{
// Implementation goes here
}

public function pause()
{
// Implementation goes here
}

public function resume()
{
// Implementation goes here
}
}

Implement Subscription Methods:

  1. Create Subscription:

https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_create

To create a subscription, you need to provide the following parameters to the create method:

  • $plan_id: The ID of the selected plan
  • $coupon_user_id: The ID of the coupon user (set to 0 if no coupon is used)
  • $method: The payment method used (e.g., 'paypal')
  • $amount: The amount (if applicable)

The method retrieves the plan details, prepares the necessary data, and sends the request to create the subscription. If the subscription creation is successful, it redirects the user to the PayPal approval URL. Otherwise, it redirects them to the home page with an error message.

Make sure to replace the placeholder values with your actual code and customize it according to your specific implementation.

public function create(int $plan_id, int $coupon_user_id, string $method, float $amount = 0)
{
// Set up the shipping address
$address = [
"address_line_1" => "2211 N First Street",
"address_line_2" => "Building 17",
"admin_area_2" => "San Jose",
"admin_area_1" => "CA",
"postal_code" => "95131",
"country_code" => "US",
];

// Retrieve the PayPal plan ID from the selected plan
$paypalPlanId = Plan::where('id', $plan_id)->first()->paypal_plan_id;

// Prepare the data for subscription creation
// example data
$data = [
'plan_id' => $paypalPlanId,
'quantity' => '1',
'shipping_amount' => [
'currency_code' => 'EUR',
'value' => 0.00,
],
'subscriber' => [
'name' => [
'given_name' => auth()->user()->name,
'surname' => '',
],
'email_address' => auth()->user()->email,
'shipping_address' => [
'name' => [
'full_name' => auth()->user()->id . '-' . $coupon_user_id,
],
'address' => $address,
],
],
'application_context' => [
'brand_name' => env('PAYPAL_PRODUCT_ID'),
'locale' => 'en-US',
'shipping_preference' => 'SET_PROVIDED_ADDRESS',
'user_action' => 'SUBSCRIBE_NOW',
'payment_method' => [
'payer_selected' => 'PAYPAL',
'payee_preferred' => 'IMMEDIATE_PAYMENT_REQUIRED',
],
'return_url' => route('payments.paypal.success', [$plan_id]),
'cancel_url' => route('payments.cancel'),
],
];

// Include additional data for subscription with a coupon
if ($coupon_user_id != 0) {
$data['plan'] = [
'billing_cycles' => [
[
'sequence' => 1,
'total_cycles' => 1,
'pricing_scheme' => [
'fixed_price' => [
'value' => $amount, // discounted amount
'currency_code' => 'EUR',
],
],
],
],
];
}

// Send the request to create the subscription
$response = $this->provider->createSubscription($data);

if (isset($response['id']) && $response['id'] != null) {
// Redirect to PayPal approval URL
foreach ($response['links'] as $link) {
if ($link['rel'] == 'approve') {
return redirect()->away($link['href']);
}
}

return redirect()
->route('home')
->with('error', 'Something went wrong.');
} else {
return redirect()
->route('home')
->with('error', $response['message'] ?? 'Something went wrong.');
}
}

Note that in following snippet above:

'shipping_address' => [
'name' => [
'full_name' => auth()->user()->id . '-' . $coupon_user_id,
],

We did that because, PayPal Webhook doesn’t return back metadata, which is important to get back important data, from which we extract useful data like user_id, coupon_id etc. So, I found this method to work properly.

2. Cancelling a PayPal Subscription:

https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_cancel

The cancel method allows you to cancel a subscription in the PayPalSubscriptions system. It takes an optional parameter $subscription_id, which specifies the ID of the subscription to cancel. If the $subscription_id is not provided, the method cancels the subscription associated with the authenticated user.

Here’s the code for the cancel method:

public function cancel(string $subscription_id = null)
{
if (is_null($subscription_id)) {
$subscription = Subscription::where('user_id', auth()->user()->id)->first();
$reason = 'no longer using';
} else {
$subscription = Subscription::where('subscription_id', $subscription_id)->first();
$reason = 'new subscription';
}
$subscriptionId = $subscription->subscription_id;
try {
$response = $this->provider->cancelSubscription($subscriptionId, $reason);
return true;
} catch (Exception $e) {
$error = "Something went wrong." . $e->getMessage();
return false;
}
}

To cancel a subscription, simply call the cancel method. If you provide the $subscription_id parameter, it will cancel the specified subscription. Otherwise, it will cancel the subscription associated with the authenticated user.

If the cancellation is successful, the method will return true. Otherwise, it will return false and an error message will be logged.

Make sure to replace the placeholder values with your actual code and customize it according to your specific implementation.

3. Pausing the Subscription:

https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_suspend

The pause method allows you to pause a subscription in the PayPalSubscriptions system. It temporarily suspends the recurring billing for the subscription. When a subscription is paused, the user will not be charged until it is resumed.

Here’s the code for the pause method:

public function pause()
{
$subscription = Subscription::where('user_id', auth()->user()->id)->first();
$subscriptionId = $subscription->subscription_id;
try {
$response = $this->provider->suspendSubscription($subscriptionId, 'Subscription Paused');
return true;
} catch (Exception $e) {
$error = "Something went wrong." . $e->getMessage();
return false;
}
}

To pause a subscription, simply call the pause method. It retrieves the subscription associated with the authenticated user and suspends it using the PayPal API. If the suspension is successful, the method will return true. Otherwise, it will return false and an error message will be logged.

Make sure to replace the placeholder values with your actual code and customize it according to your specific implementation.

4. Resuming the Subscription:

https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_activate

The resume method allows you to resume a paused subscription in the PayPalSubscriptions system. When a subscription is resumed, the recurring billing is reactivated, and the user will be charged according to the original billing cycle.

Here’s the code for the resume method:

public function resume()
{
$subscription = Subscription::where('user_id', auth()->user()->id)->first();
$subscriptionId = $subscription->subscription_id;
try {
$response = $this->provider->activateSubscription($subscriptionId, 'Reactivating the subscription');
return true;
} catch (Exception $e) {
$error = "Something went wrong." . $e->getMessage();
return false;
}
}

To resume a subscription, simply call the resume method. It retrieves the subscription associated with the authenticated user and activates it using the PayPal API. If the reactivation is successful, the method will return true. Otherwise, it will return false and an error message will be logged.

Conclusion:

Congratulations! You have successfully set up the foundation for implementing PayPal subscriptions in your Laravel application. The provided code gives you a starting point to work with PayPal subscriptions, and you can now proceed to implement the logic for each subscription-related method based on your application’s requirements. Remember to test thoroughly before deploying to a production environment.

You can use this in a running as well as new project. As you progress, explore the PayPal SrmkLive developer documentation and additional resources to enhance your knowledge and customize the implementation further.

For PayPal Webhooks, please see my Handling PayPal Subscription Webhooks in Laravel post.

--

--