Paddle.com API integration for Laravel with support for webhooks/events

Overview

Laravel Paddle

Latest Version on Packagist run-tests Quality Score Total Downloads

This package provides an integration with Paddle.com for Laravel. Read the blogpost about the introduction of the package!

Features

Support

We proudly support the community by developing Laravel packages and giving them away for free. Keeping track of issues and pull requests takes time, but we're happy to help! If this package saves you time or if you're relying on it professionally, please consider supporting the maintenance and development.

Installation

Only the master branch and version 2.0 of this package are compatible with Laravel 8.0. If you're still using an older version of Laravel (or PHP < 7.3), please use the chart below to find out which version you should use. Mind that older versions are no longer supported.

Laravel Version Package Version
8.0 2.0
6.0-7.0 1.0

You can install the package via composer:

composer require protonemedia/laravel-paddle

Configuration

Publish the config file:

php artisan vendor:publish --provider="ProtoneMedia\LaravelPaddle\PaddleServiceProvider" --tag=config

Set your Vendor ID and Code and the Public Key settings in your .env file or in the config/paddle.php file. The Public Key is used to verify incoming webhooks from Paddle.

PADDLE_SANDBOX=false
PADDLE_VENDOR_ID=123
PADDLE_VENDOR_AUTH_CODE=456
PADDLE_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----"

Paddle Sandbox

As of version 2.2.0, this package supports the Paddle Sandbox environment. To use this environment, set the sandbox_environment configuration key to true. This will configure the API URLs, as well as the Paddle JavaScript library. If you've published the Blade View while using a previous version of this package, make sure you republish the view:

php artisan vendor:publish --provider="ProtoneMedia\LaravelPaddle\PaddleServiceProvider" --tag=views

Usage

The API calls are available with the Paddle facade. Check out the the documentation to learn all about the Paddle API. You can build your API calls fluently or you could simply pass an array which holds the data. This package has some basic validation rules for the given data and this might result in an InvalidDataException if your data is invalid. Whenever an API call fails it will throw a PaddleApiException.

// Fluent:
$paddleResponse = Paddle::product()
    ->generatePayLink()
    ->productId($paddlePlanId)
    ->customerEmail($team->owner->email)
    ->passthrough(['team_id' => $team->id])
    ->send();

// Array with payload:
$payload = [
    'product_id' => $paddlePlanId,
    'customer_email' => $team->owner->email,
    'passthrough' => ['team_id' => $team->id],
];

$paddleResponse = Paddle::product()
    ->generatePayLink($payload)
    ->send();

return Redirect::to($paddleResponse['url']);

Available API calls

// alerts
Paddle::alert()->getWebhookHistory();

// checkouts
Paddle::checkout()->getOrderDetails();
Paddle::checkout()->getUserHistory();
Paddle::checkout()->getPrices();

// products
Paddle::product()->listCoupons();
Paddle::product()->createCoupon();
Paddle::product()->updateCoupon();
Paddle::product()->deleteCoupon();

Paddle::product()->listProducts();
Paddle::product()->generateLicense();
Paddle::product()->generatePayLink();
Paddle::product()->listTransactions($entity, $id);

// subscriptions
Paddle::subscription()->listPlans();
Paddle::subscription()->createPlan();

Paddle::subscription()->listUsers();
Paddle::subscription()->updateUser();
Paddle::subscription()->previewUpdate();
Paddle::subscription()->cancelUser();

Paddle::subscription()->listModifiers();
Paddle::subscription()->createModifier();
Paddle::subscription()->deleteModifier();

Paddle::subscription()->listPayments();
Paddle::subscription()->reschedulePayment();
Paddle::subscription()->createOneOffCharge($subscriptionId);

Webhooks and Laravel Events

You can configure your webhook URI in the paddle.php config file. Update your webhook settings at Paddle accordingly. By default the URI is paddle/webhook. This means that the webhook calls will be posted to https://your-domain.com/paddle/webhook.

Every webhook will be mapped to an Event and contains the payload of the webhook. For example when the Subscription Created webhook is called, the request is verified and a SubscriptionCreated event will be fired.

Events:

  • ProtoneMedia\LaravelPaddle\Events\HighRiskTransactionCreated
  • ProtoneMedia\LaravelPaddle\Events\HighRiskTransactionUpdated
  • ProtoneMedia\LaravelPaddle\Events\LockerProcessed
  • ProtoneMedia\LaravelPaddle\Events\NewAudienceMember
  • ProtoneMedia\LaravelPaddle\Events\PaymentDisputeClosed
  • ProtoneMedia\LaravelPaddle\Events\PaymentDisputeCreated
  • ProtoneMedia\LaravelPaddle\Events\PaymentRefunded
  • ProtoneMedia\LaravelPaddle\Events\PaymentSucceeded
  • ProtoneMedia\LaravelPaddle\Events\SubscriptionCancelled
  • ProtoneMedia\LaravelPaddle\Events\SubscriptionCreated
  • ProtoneMedia\LaravelPaddle\Events\SubscriptionPaymentFailed
  • ProtoneMedia\LaravelPaddle\Events\SubscriptionPaymentRefunded
  • ProtoneMedia\LaravelPaddle\Events\SubscriptionPaymentSucceeded
  • ProtoneMedia\LaravelPaddle\Events\SubscriptionUpdated
  • ProtoneMedia\LaravelPaddle\Events\TransferCreated
  • ProtoneMedia\LaravelPaddle\Events\TransferPaid
  • ProtoneMedia\LaravelPaddle\Events\UpdateAudienceMember

Some webhooks, like the Fulfillment Webhook, don't have an alert_name key. Those webhooks will be mapped to a ProtoneMedia\LaravelPaddle\Events\GenericWebhook event.

When you register a listener to handle the event, the payload is easily accessible. You also have access to the original HTTP request.

<?php

namespace App\Listeners;

use ProtoneMedia\LaravelPaddle\Events\SubscriptionCreated;

class CreateSubscriptionModel
{
    public function handle(SubscriptionCreated $event)
    {
        $status = $event->status;

        $nextBillDate = $event->next_bill_date;

        // or

        $webhookData = $event->all();

        //

        $request = $event->getRequest();
    }
}

Blade directive

This directive imports the Paddle JavaScript library and configures it with your Vendor ID.

<body>
    {{-- your app --}}

    @paddle
</body>

Testing

composer test

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Other Laravel packages

  • Laravel Analytics Event Tracking: Laravel package to easily send events to Google Analytics.
  • Laravel Blade On Demand: Laravel package to compile Blade templates in memory.
  • Laravel Cross Eloquent Search: Laravel package to search through multiple Eloquent models.
  • Laravel Eloquent Scope as Select: Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes and constraints by adding them as a subquery.
  • Laravel Eloquent Where Not: This Laravel package allows you to flip/invert an Eloquent scope, or really any query constraint.
  • Laravel FFMpeg: This package provides an integration with FFmpeg for Laravel. The storage of the files is handled by Laravel's Filesystem.
  • Laravel Form Components: Blade components to rapidly build forms with Tailwind CSS Custom Forms and Bootstrap 4. Supports validation, model binding, default values, translations, includes default vendor styling and fully customizable!
  • Laravel Mixins: A collection of Laravel goodies.
  • Laravel Verify New Email: This package adds support for verifying new email addresses: when a user updates its email address, it won't replace the old one until the new one is verified.
  • Laravel WebDAV: WebDAV driver for Laravel's Filesystem.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

Comments
  • "Undefined index: error" when calling getOrderDetails

    Whenever I call Paddle::checkout()->getOrderDetails I get this error, I think it is related to a check for a "success" field in the Request class, the response of getOrderDetails does not include this field.

    if ($json['success'] ?? null) {
       return $json['response'];
    }
    

    Line 116-118 in protonemedia/laravel-paddle/src/Api/Request.php

    opened by jordysinke 7
  • Undefined index: alert_name

    Undefined index: alert_name

    Hello! Thank you for building this awesome package!

    When I receive a "payment_succeeded" webhook from Paddle, an exception log is created:

    [2020-01-27 22:58:09] production.ERROR: Undefined index: alert_name {"exception":"[object] (ErrorException(code: 0): Undefined index: alert_name at .../vendor/protonemedia/laravel-paddle/src/Events/Event.php:59)
    

    However, everything works excellently. My listener catches the event and does the business logic as expected. I don't understand why this exception is happening while everything works as expected. It feels like Paddle is dispatching 2 requests, instead of 1. But the Alert History from Paddle shows only 1 request, and the payload does contain alert_name.

    Should I submit a PR which checks if "alert_name" exists in the payload, inside the fire method from the Event base class? Like this:

        public static function fire(array $data)
        {
            if (isset($data['alert_name'])) {
                $event = Str::studly($data['alert_name']);
    
                $eventClass = __NAMESPACE__ . '\\' . $event;
    
                event(new $eventClass($data));
            }
        }
    

    Thank you very much!

    opened by sandulat 7
  • [139] The given prices format is not valid.

    [139] The given prices format is not valid.

    I got this error.

    ProtoneMedia\LaravelPaddle\Api\PaddleApiException
    [139] The given prices format is not valid. The prices must have the format of ['currency:amount', 'currency:amount', …]. 
    
    

    Here is my code:

            $products = "1,2,3";
            $user = Auth::user();
            
            $checkOutUrl = Paddle::product()->generatePayLink([
                'title' => '3D Wallpaper',
                'webhook_url' => 'http://05c0-118-179-103-52.ngrok.io/paddle/webhook',
                'prices' => '[USD:19.99]',
                'customer_email' => $user->email,
                'customer_country' => 'BD',
                'passthrough' => ['products' => $products],
            ])->send()['url'];
    

    How to solve this?

    opened by MdAnowarHosen 6
  • getPrices returns

    getPrices returns "Bad Method Call"

    Hi there,

    First, thanks for this amazing package.

    I am trying to get the price for my product with the following code:

    Paddle::checkout()
                     ->getPrices(['product_ids' => '594915', 'customer_ip' => '213.55.224.117'])
                     ->send()
    

    But unfortunately, it returns a "Bad Method Call", like below:

    image

    Can I ask your help to clarify what am I doing wrong?

    Thanks in advance, Bruno

    opened by brunocfalcao 6
  • Laravel 8 Support

    Laravel 8 Support

    Installing this package on a Laravel 8 project throws this:

    image

    The problem is kitetail/zttp requires guzzlehttp/guzzle ^6.0 whereas Laravel 8 requires guzzlehttp/guzzle ^7.0.1.

    It doesn't look like the kitetail/zttp package is maintained anymore.

    https://twitter.com/adamwathan/status/1305644837698449408

    Please consider dropping kitetail/zttp in favor of Laravel's baked in HTTP Client: https://laravel.com/docs/8.x/http-client

    opened by pktharindu 3
  • SSLv3 error on webhook for Paddle

    SSLv3 error on webhook for Paddle

    Hi,

    On a Laravel forge spinned up server I am trying to use the package but I get this error: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure

    This is with the Webhook Simulator tester.

    Any ideas how to get it to work?

    opened by petericebear 3
  • wrong API endpoint for getOrderDetails method

    wrong API endpoint for getOrderDetails method

    When we call getOrderDetails method, it looks like the endpoint called is https://vendors.paddle.com/api/1.0/order, but it should be https://checkout.paddle.com/api/1.0/order instead.

    With the first one, the method always returns a 404 error.

    opened by dimzeta 3
  • Getting error in webhook call.

    Getting error in webhook call.

    Thanks a lot for the amazing package.

    While making a webhook call from the Paddle webhook simulator, I am getting below error.

    error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure

    I configure the server with Laravel Forge.

    Is there any steps I missing regarding the configuration.

    opened by hemratna 3
  • Added return_url and annotation methods

    Added return_url and annotation methods

    I've added return_url validation parameter because need to have redirection after payment. Paddle API supports it. Also Added annotation in GeneratePayLinkRequest class because my PHPStorm likes it :) Cheers!

    opened by sergunik 2
  • SSL issue

    SSL issue

    I'm quite lost regarding where exactly this issue occurs, so please excuse me if it turns out that it has nothing to do with this package.

    When I test my webhook in the Paddle dashboard, I get error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure. First, I thought that Paddle was connecting over SSLv3 but that seems highly unlikely as I've done some searching and it seems this is ancient and not recommended encryption.

    On my nginx server I have (this might be relevant to the issue):

    ssl_protocols TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/nginx/dhparams.pem;
    

    The reason that I'm posting this issue here is that WebhookController has

    $verified = openssl_verify(
      serialize($data),
      base64_decode($encodedSignature),
      openssl_get_publickey(config('paddle.public_key')),
      OPENSSL_ALGO_SHA1
    );
    

    Judging from the response that I get in the Paddle dashboard I still think it's on their end but I wanted to double check if the issue could come from the code above, or that maybe you ran into this issue as well while working on this package.

    I have set my public_key in my ENV file and run config:cache on deployment.

    opened by joostvanhoof 2
  • Package installation problem

    Package installation problem

    I'm using laravel 7 and php 8.0. When I'm trying to install this package, I got this error:

    Problem 1 - Root composer.json requires protonemedia/laravel-paddle ^2.3 -> satisfiable by protonemedia/laravel-paddle[v2.3.0]. - protonemedia/laravel-paddle v2.3.0 requires illuminate/support ^8.67 -> found illuminate/support[v8.67.0, ..., 8.x-dev] but these were not loaded, likely because it conflicts with another require.

    and when I'm trying to install 1.0v of this package, I got this error:

    [InvalidArgumentException] Package protonemedia/laravel-paddle at version ^1 has a PHP requirement incompatible with your PHP version, PHP ext ensions and Composer version: - protonemedia/laravel-paddle 1.2.1 requires php ^7.2 which does not match your installed version 8.0.10

    It would be better If I can install the latest version of this package. Please help me to solve this.

    opened by MdAnowarHosen 1
  • Live mode problem

    Live mode problem

    This paddle package working fine for sandbox. But, if I change to PADDLE_SANDBOX=false and give my exact Paddle credentials for the production server, then it redirect me to this link: https://buy.paddle.com/checkout/error. Why does it happening?

    opened by MdAnowarHosen 0
  • Support .env variables according read.me

    Support .env variables according read.me

    Hello! I'm using v2.4.0 and according to documentation I could set the vendor_id, vendor_auth_code and public_key on .env or in published config file. But I've set in .env and my Paddle::subscription()->listPlans() returned a non permission returned from Paddle ([107] You don't have permission to access this resource). So I investigated and found on the src/Api/Request.php line 101 the code:

    $data = $this->getData() + ['vendor_id' => config('paddle.vendor_id')];
    
    if ($method === static::METHOD_POST) {
        $data['vendor_auth_code'] = config('paddle.vendor_auth_code');
    }
    

    This mean that the vendor_id and vendor_auth_code is coming only from config file and it's not getting from .env, right?

    I solved it adding a null coalescing operator in this part and I would like to check if can I send a PR with this new code?

    $data = $this->getData() + ['vendor_id' => config('paddle.vendor_id')??env('PADDLE_VENDOR_ID')];
    
    if ($method === static::METHOD_POST) {
        $data['vendor_auth_code'] = config('paddle.vendor_auth_code')??env('PADDLE_VENDOR_AUTH_CODE');
    }
    

    I've changed all other config usages too for try to use the .env if config variable is not set.
    Thanks!

    opened by rcarvs 0
  • Support vendors in blade directive and Request

    Support vendors in blade directive and Request

    I mentioned this feature in #28.

    I added support for multi vendors via configuration in Request:

    Usage:

    Paddle::product()
            ->generatePayLink([
                'vendor_id' => '44334',
                'vendor_auth_code' => 'fdfsdfsdf'
            ])
            ->productId('5345')
            ->send();
    

    Also, I added support for multi vendors in blade directive:

    @paddle(123)
    

    Both changes are covered in unit tests. What have left is to add support for verifySignature in WebhookController, but only this method can support overloading public_key from config, not the controller because I think we can't support multi-vendors in that case. Users need to create own controllers because they know where from they will read public_key.

    We can discuss Do we want to change WebhookController or not.

    opened by Djuki 0
  • Multi vendor  support?

    Multi vendor support?

    At the moment, this library can be user for one vendor. What if our app has multiple tenants, and for each one of them we want to allow payments?

    That would be possible if Request would not overwrite the vendor id and vendor_auth_code. and just read them from the config if the values do not exist in the $data.

    If you would support PR for this, I can do that.

    enhancement 
    opened by Djuki 4
Releases(2.5.0)
Owner
Protone Media
We are a Dutch software company that develops apps, websites, and cloud platforms. As we're building projects, we gladly contribute to OSS by sharing our work.
Protone Media
Laravel Pipeline with DB transaction support, events and additional methods

Laravel Enhanced Pipeline Laravel Pipeline with DB transaction support, events and additional methods #StandWithUkraine Installation Install the packa

Michael Rubél 33 Dec 3, 2022
Organizr Plugin Equivilent of TehMuffinMoo/Throttling-Webhooks

Sonarr Throttling Organizr Plugin ❗ Important To add this plugin to Organizr, please add https://github.com/TehMuffinMoo/Organizr-Plugins to the Plugi

null 5 Jun 13, 2022
⚓️ Easily test HTTP webhooks with this handy tool that displays requests instantly.

Webhook.site With Webhook.site, you instantly get a unique, random URL that you can use to test and debug Webhooks and HTTP requests, as well as to cr

Webhook.site 3.7k Jan 9, 2023
A simple HTTP server behaving as proxy between webhooks and Appwrite Functions.

A simple HTTP server behaving as proxy between webhooks and Appwrite Functions, allowing for instance Stripe payments integration into Appwrite.

Matej Bačo 21 Nov 30, 2022
An un-offical API wrapper for logsnag.com to get notifications and track your project events

An un-offical API wrapper for logsnag.com to get notifications and track your project events

David Oti 3 Oct 15, 2022
Laravel Plans is a package for SaaS apps that need management over plans, features, subscriptions, events for plans or limited, countable features.

Laravel Plans Laravel Plans is a package for SaaS apps that need management over plans, features, subscriptions, events for plans or limited, countabl

ángel 2 Oct 2, 2022
A Pocketmine-MP plugin to add King Of The Hill events to your server.

KOTH KOTH is an event popular on HCF and Faction servers. This plugin lets you add this minigame to you server. Support For questions, please join the

ItsMax123 9 Sep 17, 2022
A bundle to handle encoding and decoding of parameters using OpenSSL and Doctrine lifecycle events.

SpecShaper Encrypt Bundle A bundle to handle encoding and decoding of parameters using OpenSSL and Doctrine lifecycle events. Features include: Master

Mark Ogilvie 48 Nov 4, 2022
Generates a static website of metal music events in Leipzig

About EN: This projects generates a static website of metal music events in Leipzig (Ger). DE: Dieses Projekt erstellt einen Webkalender zu diversen M

null 3 Dec 15, 2022
OwnagePE Staff Events Core

An events plugin created for discord.gg/ownage enabling staff members to host small scale events within the respective server.

null 2 Aug 9, 2022
A XOOPS module for handling events, including online registrations.

wgEvents A XOOPS module for handling events, including online registrations. Support If you like the wgEvents module and thanks to the long process fo

XOOPS 2.5.x Modules 6 Dec 15, 2022
A PocketMine-MP plugin that allows you to comfortably register events using a new piece of PHP 8 power — attributes.

AdvancedEvents This is a PocketMine-MP plugin that allows you to comfortably register events using a new piece of PHP 8 power — attributes. Inspired b

JuraSciix 7 Dec 5, 2022
An advanced plugin to manage events when the player enters the server!

⭐ • Better Join Version Status Date 1.0.0 stable-dev 12/10/2022 ?? • General: Plugin Introduction: This is an advanced player input management plugin

HenryDM 3 Nov 2, 2022
Source control integration plugin framework for MantisBT, including support for Github, Gitlab, Bitbucket, Gitweb, Cgit, Subversion, Mercurial and more

Source control integration plugin framework for MantisBT, including support for Github, Gitlab, Bitbucket, Gitweb, Cgit, Subversion, Mercurial and more

MantisBT Community Plugins 175 Sep 3, 2022
Michael Pratt 307 Dec 23, 2022
The Assure Alliance support website. This website is based on Questions2Answers and is a forum for support using Biblical Tools

The Assure Alliance support website. This website is based on Questions2Answers and is a forum for support using Biblical Tools

United Bible Societies Institute for Computer Assisted Publishing 3 Jul 29, 2022
API Integration for UVdesk Community Helpdesk System.

The API bundle allows integration developers to utilize uvdesk's REST api to easily communicate with their community helpdesk system. Installation Thi

UVdesk 12 Oct 3, 2022
Okex API Like the official document interface, Support for arbitrary extension.

It is recommended that you read the official document first Okex docs https://www.okex.com/docs/en Okex Simulation Test API https://www.okex.com/docs/

lin 34 Jan 1, 2023
A simple implementation of the api-problem specification. Includes PSR-15 support.

ApiProblem This library provides a simple and straightforward implementation of the IETF Problem Details for HTTP APIs, RFC 7807. RFC 7807 is a simple

Larry Garfield 236 Dec 21, 2022