GithubHelp home page GithubHelp logo

laravel / cashier-paddle Goto Github PK

View Code? Open in Web Editor NEW
229.0 22.0 54.0 469 KB

Cashier Paddle provides an expressive, fluent interface to Paddle's subscription billing services.

Home Page: https://laravel.com/docs/cashier-paddle

License: MIT License

PHP 99.03% Blade 0.97%
laravel paddle billing

cashier-paddle's Introduction

Logo Laravel Cashier Paddle

Build Status Total Downloads Latest Stable Version License

Introduction

Laravel Cashier Paddle provides an expressive, fluent interface to Paddle's subscription billing services. It handles almost all of the boilerplate subscription billing code you are dreading writing. In addition to basic subscription management, Cashier can handle swapping subscription, subscription "quantities", subscription pausing, cancellation grace periods and much more.

Official Documentation

Documentation for Cashier Paddle can be found on the Laravel website.

Contributing

Thank you for considering contributing to Cashier Paddle! You can read the contribution guide here.

Code of Conduct

In order to ensure that the Laravel community is welcoming to all, please review and abide by the Code of Conduct.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

License

Laravel Cashier Paddle is open-sourced software licensed under the MIT license.

cashier-paddle's People

Contributors

activ3mined avatar bashgeek avatar bensherred avatar bkirev avatar dasundev avatar driesvints avatar dvhoangfh avatar grahamcampbell avatar helgesverre avatar jbrooksuk avatar jubeki avatar karlomikus avatar kylemilloy avatar larsklopstra avatar lasserafn avatar lvismer avatar majdiyassin20 avatar michavie avatar mmachatschek avatar movicat avatar nunomaduro avatar pataar avatar patrickomeara avatar ralphjsmit avatar stefanzweifel avatar stylecibot avatar taylorotwell avatar themsaid avatar xico2k avatar yoeriboven avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cashier-paddle's Issues

Cashier paddle-button for vue.js

I am creating a tool in laravel nova and it uses vue.js, it would be nice to have a button to use there just like the blade component.

You don't have permission to access this resource

  • Cashier Paddle Version:1.0
    Laravel Version: 7
  • PHP Version: 7.4
  • Database Driver & Version:

Description:

After I have changed account and updated all the security parameter (id,public key etc) I am getting this error:

Laravel/Paddle/Exceptions/PaddleException with message 'You don't have permission to access this resource

has this happened to anyone?, what might trigger it?, I have doubled check all the details and they are alright.

It looks like to the .env details are being cached somewhere.

Passing email on PayLink with custom Billable model

Hey again,

When using something other than User for Billable I can't seem to pass through the customer_email parameter as specified on the Paddle docs.

$payLink = $tenant->newSubscription('default', $starter = 123456)
  ->returnTo(route('home'))
  ->create([
      'customer_email' => $tenant->owner->email,
       'prices' => ["USD:1200"]
  ]);

Price seems to work fine, but using customer_email will not pre-fill the email field. I have tried adding marketing_consent = 1 as it mentions this is required but no difference.

Thanks!

Page Not Found Sorry, the page you were looking for could not be found.

  Hello,

I am trying to create a fake purchase and have a few questions please.

1)When the amount if below 0.75 Euro, I get the error (The price is too low), is this normal?

2)When I use the belowcode:

  $user = User::find(Auth::id());
        $payLink = $user->charge(0.75, 'test onecharge');

        return view('home', ['payLink' => $payLink]);

I see a green button on my front end, click on it but get this message:
image1

I thought this code could be used to create 1 time charge from the back end(without firs inputting the product name in the paddle admin panel.

Any idea why this is happening please?

Thank you.

Issue with models other than User

Hi,

Thanks for creating this package! Hoping to test this out on my next app.

I've discovered a couple small issues so far.

  1. When creating the migration files it seems to still use the stripe_id etc instead of the one's made automatically by this package.

  2. If using another Model for Billable, when I try to go create a payLink it will return the following error (seems like it's trying to check the auth user?) This could be related to a multi-tenancy package I'm using but seems to work fine if using the normal User model.

Thanks!

Code:
$tenant->newSubscription('default', '123456')->returnTo(route('home'))->create();

Error:
Call to undefined method App\Tenant::getAuthIdentifier()

View is not found in production (I know, this should not be used in production).

  • Cashier Paddle Version: ^1.0@beta (v1.0.0-beta2)
  • Laravel Version: 7.25.0
  • PHP Version: ^7.2.5
  • Database Driver & Version: mysql

Description:

I know this shouldn't be used in production, still, I really have no other option than giving it a try. In my country, paddle seems to be the best and only solution.

Steps To Reproduce:

well, my view (blade, by the way) is indeed loaded in the localhost (homestead), but not in the production server.

Support Frontend via paylinks

Support FrontEnd payments using laravel cachier-paddle as backend

I tried to send paylinks to nuxtjs application via json using the below code. I was able to make order but there is no webhook request coming back to store subscription information to the laravel backend. I noticed that customer email was not shown in the checkout as when i do x-paddle-button which recognize customer email.

  $payLinkMonthly = $user->newSubscription('default', 'xxxxxx')
            ->returnTo('http://localhost:8000/pro/return')
            ->create();


        return response()->json([
            'payLinkMonthly' => $payLinkMonthly,
        ]);

Unable to find requested product

        "php": "^7.2.5",
        "fideloper/proxy": "^4.2",
        "fruitcake/laravel-cors": "^1.0",
        "guzzlehttp/guzzle": "^6.3",
        "laravel/cashier-paddle": "^1.0@beta",
        "laravel/framework": "^7.0",
        "laravel/tinker": "^2.0",
        "laravel/ui": "^2.0"

Description:

I cannot get the test working form the doc

Steps To Reproduce:

I added this in my blade header:

  @paddleJS
</head>

This in my home.blade.php

                <x-paddle-button :url="$payLink" class="w-8 h-4">
                    Subscribe
                </x-paddle-button>

My home controller:

    public function index()
    {
        $user = User::find(1);

$payLink = $user->newSubscription('default', 'premium')
    ->returnTo(route('home'))
    ->create();
    }

I get this:

Laravel\Paddle\Exceptions\PaddleException
Unable to find requested product

So I thought first that I had to create manually the product which I did in my paddle account and called it "premium".

I have also set the API details in the .ENV . I checked my key details 3 times and they are correct.

PADDLE_VENDOR_ID=
PADDLE_VENDOR_AUTH_CODE=
PADDLE_PUBLIC_KEY=""

Any idea why this does not give me any output please?

Thank you.

Cancel subscription immediately works incorrectly?

  • Cashier Paddle Version: 1.2.3
  • Laravel Version: 7.16.1
  • PHP Version: 7.2
  • Database Driver & Version: MySQL 8

Description:

Not sure if it's a bug or not.

When I use cancelNow() method it should cancel the subscription immediately. What happens:

    /**
     * Cancel the subscription immediately.
     *
     * @return $this
     */
    public function cancelNow()
    {
        $payload = $this->billable->paddleOptions([
            'subscription_id' => $this->paddle_id,
        ]);

        // Here Laravel sends the request to Paddle
        Cashier::post('/subscription/users_cancel', $payload);

       // Here Laravel sets ends_at to the current date, immediately, and the subscription should be considered cancelled.
        $this->forceFill([
            'paddle_status' => self::STATUS_DELETED,
            'ends_at' => Carbon::now(),
        ])->save();

        $this->paddleInfo = null;

        return $this;
    }

However, then Paddle makes a webhook, notifying that the subscription has been cancelled. It also sends cancellation_effective_date which is used, as I get it, for grace period. Even though the subscription is cancelled right away (on the Paddle side), Paddle still sends the effective date.

And here how Cashier handles the webhook:

        $subscription->ends_at = $subscription->onTrial()
            ? $subscription->trial_ends_at
            : Carbon::createFromFormat('Y-m-d', $payload['cancellation_effective_date'], 'UTC')->startOfDay();

The subscription is not on trial => setup ends_at to the cancellation_effective_date.

For example, In the case of monthly subscriptions, the ends_at will be equal the current date + 30 days. And the subscription will be considered valid.

Then I don't get what's the difference between cancel and cancelNow, and why in the current logic it happens that end_at being rewritten twice: once to the current date, the second time to the effective date.

New method to end a subscription for testing purposes

Hello,

I was trying to test this:

if ($user->subscription('default')->ended();) {
    //
}

I was unable to create a fake "end" to the subscription.
I tried to change the paid_at date, updated_at, created_at in both the subscriptions and receipts tables but no luck.

Would it be possible to have a function where the tester can fake an end to a subscription please(not a cancellation but actual end)?

Thank you

Webhook error after creating subscription

  • Cashier Paddle Version: 1.2
  • Laravel Version: 8.0
  • PHP Version: 7.4
  • Database Driver & Version: mySQL

Description:

After subscribing to a subscription the received answer triggers the following error:

Trying to access array offset on value of type int {"exception":"[object] (ErrorException(code: 0): Trying to access array offset on value of type int at /home/forge/project/vendor/laravel/cashier-paddle/src/Http/Controllers/WebhookController.php:246)

Thats the following function, which seems to be correct:

    protected function findOrCreateCustomer(string $passthrough)
    {
        $passthrough = json_decode($passthrough, true);

        return Customer::firstOrCreate([
            'billable_id' => $passthrough['billable_id'],
            'billable_type' => $passthrough['billable_type'],
        ])->billable;
    }

Eager loading not possible

  • Cashier Paddle Version: 1.2.0
  • Laravel Version: 7.26.1
  • PHP Version: 7.4.10
  • Database Driver & Version: MySQL

Description:

I'm accessing the user's subscription multiple times on my settings page. This results in a separate query for every access.

According to Laravel Debugbar I'm making the same subscriptions request nine times.

This is the culprit:

$user->subscription('default')

How to eager load this query? Using $with on the User actually adds a query.

Part of this could also me not understanding something about relationships, but there's a case to be made that there shouldn't be a database query every time you use the subscription() function.

Steps To Reproduce:

Run $user->subscription() multiple times.

Unable to get trialEndsAt date without payment up front after trial expired

  • Cashier Paddle Version: 1.2.2
  • Laravel Version: 8.10.0
  • PHP Version: 7.4.7
  • Database Driver & Version: MySQL 5.7.29

Description:

After a trial has expired (without payment up front) the trialEndsAt method tries to get the trial_ends_at date from the subscription instead of the customer.

Steps To Reproduce:

Create a user, then use the following to simulate a trial without payment up front that is expired:

$user->createAsCustomer([
    'trial_ends_at' => now()->subDays(10)
]);

When calling $user->trialEndsAt() the following exception is thrown: https://flareapp.io/share/J7oEp0Vm#F73

How to check if the user is one trial or subscribed to any plan

I have 3 different subscriptions : Basic,pro,unlimited.

I am planning to use policies to check if a user is subscribed to a determined plan or to some plan at all or if he/she is on trial, is there a function or a way to check if a user is on trial of any plan or subscribed to any plan?

Thanks for this wonderful packages @driesvints , I am almost done with the implementation in my SaaS.

$user->subscribed('default') returns true no matter what.

"require": {
        "php": "^7.3",
        "laravel/cashier-paddle": "^1.2",
        "laravel/framework": "^8.6.0",
    },

Description:

I have cancelled 2 subscriptions from the only user I have on the system as you can see here from the Paddle callback:
subscription

Based on the documentation, I was expecting the result to return 'false':
https://laravel.com/docs/7.x/cashier-paddle#checking-subscription-status

Steps To Reproduce:

    public function index()
    {
        $user = User::where('id',Auth::id())->first();  // Same as: firstWhere('id',Auth::id())
        if ($user->subscribed('default')) {
            logger('true');
        }else{
            logger('false');
        }
    }

I always get "true" in the logger even if the subscription status shows in the database "deleted"

Any idea why please?

Thank you.

Can we use it in production?

Hello!,

Thanks for this superb package, I cannot wait to install it on my Laravel 7 app.
Is it safe to use in production? If not, when do you think it will be safe enough for a launch?

Thank you so much,

Expose payment method

Subscription methods like cardBrand are only available when the payment method is credit card, it makes sense to check payment method before accessing it, currently I can get the payment method via $subscription->paddleInfo()['payment_information']['payment_method'] but it's not documented, maybe it's better to expose it as $subscription->paymentMethod() too.

Column 'billable_id' cannot be null when testing from the Paddle developer tools

  • Cashier Paddle Version: 1.0
  • Laravel Version: 7.24
  • PHP Version: 7.3.5
  • Database Driver & Version: MYSql latest

When sending a test subscription created webhook from Paddle DevTools, here: https://vendors.paddle.com/webhook-alert-test
I'm getting the following error returned back:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'billable_id' cannot be null (SQL: insert into `customers` (`billable_id`, `billable_type`, `updated_at`, `created_at`) values (?, ?, 2020-09-26 18:46:38, 2020-09-26 18:46:38))

Steps To Reproduce:

  1. Send a test subscription_created webhook alert here: https://vendors.paddle.com/webhook-alert-test

`handleSubscriptionUpdated` references an undocumented field

Description:

// Quantity...
if (isset($payload['quantity'])) {
$subscription->quantity = $payload['quantity'];
}

According to the Paddle documentation, quantity does not exist on the subscription updated event, instead there are new_quantity and old_quantity.

Steps To Reproduce:

โš ๏ธ I was just checking against the documentation / the webhook alert test. I'm not actually running Cashier, just using it as a reference for implementation in a different language.

Get next_bill_date for a subscription

Normally for any subscription user should know their next payment date . It will be good idea if there is a way to update subscription table to next_bill_date. There is data coming from paddle "next_bill_date" when order is completed but not sure if there a webhook to update this information or if there is a way cachier-paddle can pull information from time to time and update all subscription table with new data such as status, next bill date etc.

SubscriptionBuilder missing custom pricing payloads i.e Overriding Checkout Prices

Paddle supports Overriding Checkout Prices this helps to customise recurring charges and avoids relying on paddle product data for prices,passing in prices to override the initial checkout price or recurring_prices if you wish to override a subscriptionโ€™s subsequent recurring payment amount

Read more here
https://developer.paddle.com/guides/how-tos/pricing/override-checkout-prices#overriding-checkout-prices

1 Month subscription save the ends_at date - 3 months subscription ends_at missing

  • Cashier Paddle Version: 1.2.0
  • Laravel Version: 8.6.0
  • PHP Version: 7.3
  • Database Driver & Version: libmysql - 5.6.43

Description:

When a subscription is created in Paddle with a billing interval of 1 month, the ends_at column is filled with the right date.
When a subscription is created in Paddle with a billing interval of 3 months, the ends_at column shows as NULL.

Paddle example of a 3 months subscription.

Steps To Reproduce:

Create in Paddle:
A 1-month SUBSCRIPTION billing interval.
A 3-months SUBSCRIPTION billing interval.

When a subscription is created in Paddle with a billing interval of 1 month, the ends_at column is filled with the right date.
When a subscription is created in Paddle with a billing interval of 3 months, the ends_at column shows as NULL.

Improving contributing guide

The contributing guide is not clear in what is needed to setup the project.

It seems like you need to add your Paddle API keys, but some more information would be appreciated.

Currently can't get tests to green on my machine because I'm missing variables and the correct setup.

Unable to get related subscription from a receipt

  • Cashier Paddle Version: 1.0@beta
  • Laravel Version: 7.23.2
  • PHP Version: 7.4
  • Database Driver & Version: MySQL

Description:

This code $receipt->subscription returns null but the record is existed in the subscriptions table.

It seems the relationship definition between the two models is not correct.
I can help to create a PR for this.

Possible bug with Safari 13.1.1

  • Cashier Paddle Version: Latest Dev Build

Description:

When trying to click the "Subscribe" pop-up after creating a payLink in Safari, the browser will hang and never load the pop-up. The same issue occurs when switching to inline checkout.

When attempting the same steps on Firefox or Chrome the pop-up will load correctly. I have cleared cookies/cache and extensions as well incase Safari was conflicting, but seeing the same issue.

@techguydev was also experiencing issues getting the pop-up to appear via a Vue component too yesterday so I wonder if this could well be a Safari issue.

@driesvints are you able to recreate on Safari?

Thanks!

Publish blade file for button component

Couldn't see in docs if this is available, but would be good to allow users to publish the x-paddle-button component so we can override the default green styling better.

Method Illuminate\Database\Eloquent\Collection::subscription does not exist.

Hello,

        "php": "^7.4",
        "laravel/cashier-paddle": "^1.2.0",
        "laravel/framework": "^7.28.3",

When following the doc here:
https://laravel.com/docs/7.x/cashier-paddle#past-and-upcoming-payments

By doing this:

        $user = User::where('id',Auth::id())->get();
        $nextPayment = $user->subscription('default');
        return view('backend.customer.orders.view', ['nextpayment', $nextPayment]);

I am getting:

BadMethodCallException
Method Illuminate\Database\Eloquent\Collection::subscription does not exist.

Any idea why please?

Thank you.

How to get plan name and amount

Is there any way to bring subscription plan name and amount along with subscription model ? now I can see only plan id. other way is to maintain another table in backend to hold paddle plan id and other plan details manually.

Paypal: expiry_date and last_four_digits not available

  • Cashier Paddle Version: 1.4.1
  • Laravel Version: 8.28.0
  • PHP Version: 8.0.0
  • Database Driver & Version: MySQL 8

Description:

Using the new Spark. After connecting to Paypal (German account) I receive a 500. It originates in Subscription.php:
Undefined array key "expiry_date" (and afterwards the same for last_four_digits).

paddleInfo returns:

array (
  'subscription_id' => 625...,
  'plan_id' => 6451...,
  'user_id' => 204...,
  'user_email' => '[email protected]',
  'marketing_consent' => false,
  'update_url' => 'https://checkout.paddle.com/subscription/update?user=204...&subscription=625...',
  'cancel_url' => 'https://checkout.paddle.com/subscription/cancel?user=204...&subscription=625...',
  'state' => 'active',
  'signup_date' => '2021-02-16 16:52:15',
  'last_payment' => 
  array (
    'amount' => 0,
    'currency' => 'USD',
    'date' => '2021-02-16',
  ),
  'linked_subscriptions' => 
  array (
  ),
  'payment_information' => 
  array (
    'payment_method' => 'paypal',
  ),
  'next_payment' => 
  array (
    'amount' => 0,
    'currency' => 'USD',
    'date' => '2021-03-16',
  ),
)  

There is no array key ['payment_information']['expiry_date'].

Just FYI I applied a 100% coupon.

Steps To Reproduce:

Connect to PayPal.

Arr::get() solves the problem - I'm just curious why this seems to be only me? Many others should run into this too.

How to check user generic trial

  • Cashier Paddle Version: 1.1.0
  • Laravel Version: 7.27.0
  • PHP Version: 7.4.3

Description:

In my setup, I'm using Paddle with trial period without asking for payment method upfront.
I'm trying to create middleware that would check if user is on trial period or having active subscription.
However, it seems that $user->subscription('default') or $user->subscribed('default') are not consider users on trial period.

From what I can see, if I'm using generic trial, subscription is not created and the method will always return false.

Steps To Reproduce:

Create middleware for checking subscription status.

class CheckSubscriptionStatus
{
    public function handle($request, Closure $next)
    {
        if ($request->user() && ! $request->user()->onGenericTrial()) {
            return response()->json(['status' => 'TRIAL_EXPIRED']);
        }

        if ($request->user() && ! $request->user()->subscribed('default')) {
            return response()->json(['status' => 'SUBSCRIPTION_EXPIRED']);
        }

        return $next($request);
    }
}

In this case, newly registered users who is on a 7-day trial, will keep getting SUBSCRIPTION_EXPIRED response while it should be allowed to access the app.

When the trial expires, the user will receive TRIAL_EXPIRED, as expected.

1 Month subscription save the ends_at date - 3 months subscription ends_at missing

save

        "php": "^7.3",
        "ext-dom": "*",
        "laravel/cashier-paddle": "^1.2",
        "laravel/framework": "^8.6.0",

I have created in Paddle 2 subscriptions.
1 set with a 1-month billing interval.
1 set with a 3 months billing interval.

When the transactions happen, the webhook call back from Paddle is sent correctly to the Paddle Laravel callback URL on my website. The "next_bill_date" in the json response shows with the first subscription a 1-month billing interval while with the other subscription a 3 months billing interval, all good.

The problem happens in the database table called "subscriptions".

On a 1 month subscription, the "ends_at" column is filled in with the correct 1-month billing interval.
On a 3 months subscription, the "ends_at" column stays as null.

I have repeated the process again and again with no luck.

Thank you.

Error Trait 'Laravel\Cashier\Billable' not found

  • Cashier Paddle Version: v.1.0.0-beta
  • Laravel Version: 7.13.0
  • PHP Version: 7.4
  • Database Driver & Version: Mysql

Description:

After following the steps in the docs I get this error:

Trait 'Laravel\Cashier\Billable' not found

Steps To Reproduce:

$user = App\User::find(1);
$payLink = $user->newSubscription('default', 'premium')
->returnTo(route('home'))
->create();

Coupon Code is not applied when using "withCoupon()"?

  • Cashier Paddle Version: 1.0.0-beta
  • Laravel Version: 7.13.0
  • PHP Version: 7.4.5
  • Database Driver & Version: MySQL 5.7

Description:

I'm trying to apply a coupon to a pay link by using the SubscriptionBuilder and the withCoupon()-method.

In my app, the checkout process looks like this:

  1. User selects a planfrom a list of plans and adds an optional coupon code and submits the form
  2. Controller builds PayLink and redirects user to Paddle (I'm not using the inline Checkout right now)
  3. User completes checkout on Paddle and is redirected back to the app

However, the coupon code doesn't seem to be applied. The price is not reduced and the user still has the option to add the coupon again by clicking "Add Coupon". (The coupon is valid as it's being applied when doing on paddle.com)

While debugging I've seen, that the coupon is available during the buildPayload-method call. The value is also still present before calling Cashier::post() here

I have the problem both with "Checkout" and "Product" Coupons.

Steps To Reproduce:

Build a paylink by using withCoupon().

$payLink = $user->newSubscription('default', $paddlePlanId)
    ->returnTo(route('home'))
    ->withCoupon('NAME-OF-COUPON')
    ->create();

return redirect($payLink);

allowQuantity set to False not working

### Versions
        "php": "^7.4",
        "laravel/cashier-paddle": "^1.0.0-beta.2",
        "laravel/framework": "^7.24.0",

Description:

Unable to add a custom parameter called 'allowQuantity' => 'false' with the chargeProduct() method.
When set to false, the quantity in the popup is still visible.

Steps To Reproduce:

       $payLink = $user->chargeProduct('111111', [
       'allowQuantity' => 'false',
]);

        return view('view', ['payLink' => $payLink]);

To test if custom parameters are working I tried:

       $payLink = $user->chargeProduct('111111', [
       'quantity' => 1,
]);

And the above worked.
I followed the Paddle parameters from here:

Thank you.

ps: obviously the 11111 has been replaced but I was using a working product integer from my Paddle admin panel

Invalid signature from webhook

  • Cashier Paddle Version: 1.0@beta
  • Laravel Version: 7.15
  • PHP Version: 7.4
  • Database Driver & Version: Mysql 8.0

Description:

I am getting invalid signature error from webhook. After debug just noticed :

By removing q from field list it works.

  $fields = $request->except([self::SIGNATURE_KEY, 'q']);

Steps To Reproduce:

  • Just create any Laravel form
  • Submmit
  • On controller store() method place dd($request->all()))
  • See extra field at bottom q

Simple Charge The checkout id must be a valid checkout id.

  • Cashier Paddle Version: 1.0@beta
  • Laravel Version: 7.0
  • PHP Version: 7.2.5

Hi, I am using cashier-paddle for one of my laravel 7 projects. I am trying to integrate a paddle simple charge. I followed the official documentation and generated the payLink. But after putting the Cashier's provided paddle-button Blade component it doesn't work. It shows Page Not Found and in the developer network tab, I am getting this error The checkout id must be a valid checkout id. I will be thankful if anyone can help me to solve the issue. Thanks in advance.

Subsription model scopes bug

  • Cashier Paddle Version: v1.4.3
  • Laravel Version: 8
  • PHP Version: 7.4
  • Database Driver & Version: MySQL 8

Description:

/**
     * Determine if the subscription is active.
     *
     * @return bool
     */
    public function active()
    {
        return (is_null($this->ends_at) || $this->onGracePeriod() || $this->onPausedGracePeriod()) &&
            (! Cashier::$deactivatePastDue || $this->paddle_status !== self::STATUS_PAST_DUE) &&
            $this->paddle_status !== self::STATUS_PAUSED;
    }

    /**
     * Filter query by active.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return void
     */
    public function scopeActive($query)
    {
        $query->where(function ($query) {
            $query->whereNull('ends_at')
                ->orWhere(function ($query) {
                    $query->onGracePeriod();
                })
                ->orWhere(function ($query) {
                    $query->onPausedGracePeriod();
                });
        })->where('paddle_status', '!=', self::STATUS_PAUSED);

        if (Cashier::$deactivatePastDue) {
            $query->where('paddle_status', '!=', self::STATUS_PAST_DUE);
        }
       // missed return statement here <----
    }

You missed return statement in all scopes in Subscription model (maybe other models, didn't check)

Example, Laravel scope

/**
     * Scope a query to only include active users.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }

Also you override scope "scopeActive" by "active" function in the same model, is it expected?

Thanks,
Konstantin
Massive Kinetic

Cancel paused subscriptions

  • Cashier Paddle Version: 1.4.0
  • Laravel Version: 8.20.1
  • PHP Version: 7.4 & 8.0
  • Database Driver & Version: Mysql 5.7

Description:

First of all I am not really sure if this is a bug report or feature request.
Paddle itself supports cancelling pause subscriptions. Cashier unfortunately tries to access the next payment date which throws an exception since nextPayment() returns null.

Steps To Reproduce:

  1. Subscribe to a plan
  2. Pause the subscription
  3. Cancel the subscription

Stacktrace

[2020-12-29 11:01:23] local.ERROR: Call to a member function date() on null {"userId":1,"exception":"[object] (Error(code: 0): Call to a member function date() on null at /Users/samuelnitsche/Sites/siteguard/vendor/laravel/cashier-paddle/src/Subscription.php:616)
[stacktrace]
#0 /Users/samuelnitsche/Sites/siteguard/routes/web.php(65): Laravel\\Paddle\\Subscription->cancel()
#1 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Routing/Route.php(230): Illuminate\\Routing\\RouteFileRegistrar->{closure}()
#2 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Routing/Route.php(200): Illuminate\\Routing\\Route->runCallable()
#3 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Routing/Router.php(692): Illuminate\\Routing\\Route->run()
#4 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#5 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Auth/Middleware/EnsureEmailIsVerified.php(30): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#6 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Auth\\Middleware\\EnsureEmailIsVerified->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#7 /Users/samuelnitsche/Sites/siteguard/vendor/pragmarx/google2fa-laravel/src/Middleware.php(15): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#8 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): PragmaRX\\Google2FALaravel\\Middleware->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#9 /Users/samuelnitsche/Sites/siteguard/app/Http/Middleware/SetActiveTeam.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#10 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): App\\Http\\Middleware\\SetActiveTeam->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#11 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(41): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#12 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#13 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(44): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#14 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Auth\\Middleware\\Authenticate->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#15 /Users/samuelnitsche/Sites/siteguard/app/Http/Middleware/SetLanguage.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#16 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): App\\Http\\Middleware\\SetLanguage->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#17 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#18 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#19 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#20 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#21 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#22 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(63): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest(Object(Illuminate\\Http\\Request), Object(Illuminate\\Session\\Store), Object(Closure))
#23 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Session\\Middleware\\StartSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#24 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#25 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#26 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#27 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#28 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#29 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Routing/Router.php(694): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#30 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Routing/Router.php(669): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#31 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Routing/Router.php(635): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#32 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Routing/Router.php(624): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#33 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(166): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#34 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(128): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#35 /Users/samuelnitsche/Sites/siteguard/vendor/barryvdh/laravel-debugbar/src/Middleware/InjectDebugbar.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#36 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Barryvdh\\Debugbar\\Middleware\\InjectDebugbar->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#37 /Users/samuelnitsche/Sites/siteguard/vendor/fideloper/proxy/src/TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#38 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fideloper\\Proxy\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#39 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#40 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#41 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#42 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#43 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#44 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#45 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php(87): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#46 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#47 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#48 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(141): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#49 /Users/samuelnitsche/Sites/siteguard/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(110): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#50 /Users/samuelnitsche/Sites/siteguard/public/index.php(52): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#51 /Users/samuelnitsche/.composer/vendor/laravel/valet/server.php(214): require('/Users/samuelni...')
#52 {main}
"}

Problem with WebHook Custom handlers

Hello,

        "php": "^7.4",
        "laravel/cashier-paddle": "^1.0.0-beta.2",
        "laravel/framework": "^7.24.0",

Based on the documentation:
https://laravel.com/docs/7.x/cashier-paddle#defining-webhook-event-handlers

I have created a controller at \App\Http\Controllers\WebhookController and added this code in it:

<?php

namespace App\Http\Controllers;

use Laravel\Paddle\Http\Controllers\WebhookController as CashierController;

class WebhookController extends CashierController
{
    /**
     * Handle payment succeeded.
     *
     * @param  array  $payload
     * @return void
     */
    public function handlePaymentSucceeded(array $payload)
    {
        if (Receipt::where('order_id', $payload['order_id'])->count()) {
            return;
        }

        $this->findOrCreateCustomer($payload['passthrough'])->receipts()->create([
            'checkout_id' => $payload['checkout_id'],
            'order_id' => $payload['order_id'],
            'amount' => $payload['sale_gross'],
            'tax' => $payload['payment_tax'],
            'currency' => $payload['currency'],
            'quantity' => (int) $payload['quantity'],
            'receipt_url' => $payload['receipt_url'],
            'paid_at' => Carbon::createFromFormat('Y-m-d H:i:s', $payload['event_time'], 'UTC'),
        ]);
    }
}

I was hoping to be able to use the handlePaymentSucceeded to see if I could recreate a receipt(like the original method from CashierController as I need later on to add a bit more code to it).

I have also added this route in the web.php:

Route::post('paddle/webhook', '\App\Http\Controllers\WebhookController@handleWebhook');

When passing a purchase via:

           $payLink = $user->chargeProduct($product->product_id, [
               'quantity_variable' => 0,
           ]);

Which works perfectly as I receive both a Paypal receipt and Paddle receipt, I receive this error in telescope:

BadMethodCallException
Method App\Http\Controllers\WebhookController::handleWebhook does not exist.

If I remove this controller App\Http\Controllers\WebhookController, the receipt is created correctly in the database(demonstrating that everything works), but the issue only happens when I use the "extends CashierController".

Any idea if there is something missing in your doc there please? Perhaps the constructor needs to be copied aswell?

Thank you,

Method "isValidSignature" actually is "isInvalidSignature"

Hi,

functionality is right, but VerifyWebhookSignature@isValidSignature actually checks if the signature is invalid. I would propose to leave the method name, but return return openssl_verify(...) === 1 instead of return openssl_verify(...) !== 1 and change if ($this->isValidSignature($fields, $signature)) { to if (!$this->isValidSignature($fields, $signature)) {.

protected function isValidSignature(array $fields, $signature)

PR?

Sebastian

"development.ERROR: You don't have permission to access this resource"

  • Cashier Paddle Version: 1.3.0
  • Laravel Version: 8.13.0
  • PHP Version: 7.4.9
  • Database Driver & Version:
    MariaDB 10.3

Description:

Upgraded from version 1.2.0 to 1.3.0 and suddenly started to get the error:
"development.ERROR: You don't have permission to access this resource"

Steps To Reproduce:

    $user = User::find(1);
    $subscription = $user->subscription('default');
    $nextPayment = $subscription->nextPayment();

Data in my test DB:

REPLACE INTO `subscriptions` (`id`, `billable_id`, `billable_type`, `name`, `paddle_id`, `paddle_status`, `paddle_plan`, `quantity`, `trial_ends_at`, `paused_from`, `ends_at`, `created_at`, `updated_at`) VALUES
(63, 124, 'App\\User', 'default', 16225936, 'active', 622049, 1, NULL, NULL, NULL, '2020-12-09 18:54:47', '2020-12-09 18:54:47');
REPLACE INTO `receipts` (`id`, `billable_id`, `billable_type`, `paddle_subscription_id`, `checkout_id`, `order_id`, `amount`, `tax`, `currency`, `quantity`, `receipt_url`, `paid_at`, `created_at`, `updated_at`) VALUES
(64, 124, 'App\\User', 16225936, '3-5b645075b08383e-ce6d40efa7', '20124987-16225987', '11', '0.32', 'EUR', 1, 'https://my.paddle.com/receipt/7/XXXXXXXXXXXXXXX', '2020-12-09 18:53:01', '2020-12-09 18:53:02', '2020-12-09 18:53:02');

Stacktrace

[stacktrace]
#0 C:\\laragon\\www\\mysite\\vendor\\laravel\\cashier-paddle\\src\\Cashier.php(118): Laravel\\Paddle\\Cashier::makeApiCall('post', 'https://vendors...', Array)
#1 C:\\laragon\\www\\mysite\\vendor\\laravel\\cashier-paddle\\src\\Subscription.php(709): Laravel\\Paddle\\Cashier::post('/subscription/u...', Array)
#2 C:\\laragon\\www\\mysite\\vendor\\laravel\\cashier-paddle\\src\\Subscription.php(637): Laravel\\Paddle\\Subscription->paddleInfo()
#3 C:\\laragon\\www\\mysite\\app\\Http\\Controllers\\User\\TextController.php(51): Laravel\\Paddle\\Subscription->nextPayment()
#4 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Controller.php(54): App\\Http\\Controllers\\User\\TextController->checkTtsMembership()
#5 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction('checkMembers...', Array)
#6 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php(255): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(App\\Http\\Controllers\\User\\TextController), 'checkMembers...')
#7 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php(197): Illuminate\\Routing\\Route->runController()
#8 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(691): Illuminate\\Routing\\Route->run()
#9 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(128): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#10 C:\\laragon\\www\\mysite\\vendor\\spatie\\laravel-permission\\src\\Middlewares\\RoleMiddleware.php(25): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#11 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Spatie\\Permission\\Middlewares\\RoleMiddleware->handle(Object(Illuminate\\Http\\Request), Object(Closure), 'customer')
#12 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\SubstituteBindings.php(41): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#13 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#14 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken.php(77): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#15 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#16 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\View\\Middleware\\ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#17 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#18 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#19 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php(63): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest(Object(Illuminate\\Http\\Request), Object(Illuminate\\Session\\Store), Object(Closure))
#20 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Session\\Middleware\\StartSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#21 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#22 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#23 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Cookie\\Middleware\\EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#24 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#25 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#26 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(693): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#27 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(668): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#28 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(634): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#29 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(623): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#30 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(166): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#31 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(128): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#32 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#33 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#34 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#35 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#36 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#37 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#38 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance.php(87): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#39 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#40 C:\\laragon\\www\\mysite\\vendor\\fruitcake\\laravel-cors\\src\\HandleCors.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#41 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Fruitcake\\Cors\\HandleCors->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#42 C:\\laragon\\www\\mysite\\vendor\\fideloper\\proxy\\src\\TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#43 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Fideloper\\Proxy\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#44 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#45 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(141): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#46 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(110): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#47 C:\\laragon\\www\\mysite\\public\\index.php(60): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#48 {main}
"} 
[2020-12-09 19:17:50] development.ERROR: You don't have permission to access this resource {"userId":124,"exception":"[object] (Laravel\\Paddle\\Exceptions\\PaddleException(code: 107): You don't have permission to access this resource at C:\\laragon\\www\\mysite\\vendor\\laravel\\cashier-paddle\\src\\Cashier.php:136)
[stacktrace]
#0 C:\\laragon\\www\\mysite\\vendor\\laravel\\cashier-paddle\\src\\Cashier.php(118): Laravel\\Paddle\\Cashier::makeApiCall('post', 'https://vendors...', Array)
#1 C:\\laragon\\www\\mysite\\vendor\\laravel\\cashier-paddle\\src\\Subscription.php(709): Laravel\\Paddle\\Cashier::post('/subscription/u...', Array)
#2 C:\\laragon\\www\\mysite\\vendor\\laravel\\cashier-paddle\\src\\Subscription.php(637): Laravel\\Paddle\\Subscription->paddleInfo()
#3 C:\\laragon\\www\\mysite\\app\\Http\\Controllers\\User\\TextController.php(51): Laravel\\Paddle\\Subscription->nextPayment()
#4 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Controller.php(54): App\\Http\\Controllers\\User\\TextController->checkMembership()
#5 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction('checkMembers...', Array)
#6 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php(255): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(App\\Http\\Controllers\\User\\TextController), 'checkMembers...')
#7 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php(197): Illuminate\\Routing\\Route->runController()
#8 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(691): Illuminate\\Routing\\Route->run()
#9 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(128): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#10 C:\\laragon\\www\\mysite\\vendor\\spatie\\laravel-permission\\src\\Middlewares\\RoleMiddleware.php(25): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#11 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Spatie\\Permission\\Middlewares\\RoleMiddleware->handle(Object(Illuminate\\Http\\Request), Object(Closure), 'customer')
#12 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Middleware\\SubstituteBindings.php(41): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#13 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#14 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken.php(77): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#15 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#16 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\View\\Middleware\\ShareErrorsFromSession.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#17 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\View\\Middleware\\ShareErrorsFromSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#18 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#19 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php(63): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest(Object(Illuminate\\Http\\Request), Object(Illuminate\\Session\\Store), Object(Closure))
#20 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Session\\Middleware\\StartSession->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#21 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#22 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#23 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Cookie\\Middleware\\EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#24 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#25 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#26 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(693): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#27 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(668): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#28 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(634): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#29 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(623): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#30 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(166): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#31 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(128): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#32 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#33 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#34 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#35 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#36 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#37 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#38 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance.php(87): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#39 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#40 C:\\laragon\\www\\mysite\\vendor\\fruitcake\\laravel-cors\\src\\HandleCors.php(37): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#41 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Fruitcake\\Cors\\HandleCors->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#42 C:\\laragon\\www\\mysite\\vendor\\fideloper\\proxy\\src\\TrustProxies.php(57): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#43 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(167): Fideloper\\Proxy\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#44 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(103): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#45 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(141): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#46 C:\\laragon\\www\\mysite\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(110): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#47 C:\\laragon\\www\\mysite\\public\\index.php(60): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#48 {main}
"} 

I also ran the commands:

php artisan cache:clear
php artisan view:clear
php artisan config:clear
php artisan config:cache
php artisan route:cache

Getting SubscriptionPaymentFailed not found on log

  • Cashier Paddle Version: v1.2.0
  • Laravel Version:7.16.1
  • PHP Version: 7.2
  • Database Driver & Version: mysql

Description:

I keep getting " Class 'Laravel\Paddle\Http\Controllers\SubscriptionPaymentFailed' not found " error on my logs, it looks like paddle is making a post request to my system and not finding that to do next.

Enable opt-in Subscription model publishing

Every system where one user can have multiple subscriptions (for example owns multiple organizations with different subscription plans) would need to have some additional internal subscription information (maybe a foreign key).

I think it would be beneficial to allow publishing the Subscription model so that some methods and fillable fields may be overriden.

If this is already possible, please explain it since the docs don't hint at Subscription customization other than the migration publishing.

Also yes, this could be solved with creating another table which has a 1:1 foreign key to the subscription and contains additional subscription info. However, this can be too much overhead for an additional 1-3 columns.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.