Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trying to get property 'payment_intent' of non-object #958

Closed
jeffreyzant opened this issue Jun 12, 2020 · 14 comments · Fixed by #959
Closed

Trying to get property 'payment_intent' of non-object #958

jeffreyzant opened this issue Jun 12, 2020 · 14 comments · Fixed by #959

Comments

@jeffreyzant
Copy link

jeffreyzant commented Jun 12, 2020

  • Cashier Version: 12.0.0
  • Laravel Version: 6.18.18
  • PHP Version: 7.4.6

Description:

When swapping a subscription without a latest_invoice the swap errors. When creating a subscription with proration_behavior => none there is no first invoice.

  ErrorException  : Trying to get property 'payment_intent' of non-object

  at /vendor/laravel/cashier/src/Subscription.php:602
    598|         $this->items()->whereNotIn('stripe_plan', $items->pluck('plan')->filter())->delete();
    599| 
    600|         $this->unsetRelation('items');
    601| 
  > 602|         if ($stripeSubscription->latest_invoice->payment_intent) {
    603|             (new Payment(
    604|                 $stripeSubscription->latest_invoice->payment_intent
    605|             ))->validate();
    606|         }

  Exception trace:

  1   Illuminate\Foundation\Bootstrap\HandleExceptions::handleError()
      vendor/laravel/cashier/src/Subscription.php:602

  2   Laravel\Cashier\Subscription::swap()
      app/Console/Commands/StripeUpdateCommand.php:75

Steps To Reproduce:

Looks like the solution would be to alter the if statement to:

if ($stripeSubscription->latest_invoice && $stripeSubscription->latest_invoice->payment_intent) {
@driesvints
Copy link
Member

I can't seem to replicate this with the following test (which passes for me):

    public function test_no_prorate_on_subscription_create()
    {
        $user = $this->createCustomer('no_prorate_on_subscription_create');

        $subscription = $user->newSubscription('main', static::$planId)->noProrate()->create('pm_card_visa');

        $this->assertEquals(static::$planId, $subscription->stripe_plan);
    }

We'll need some more info here why the error is triggered. Afaik there's always a latest invoice. Please share the exact code needed to recreate this as well as how you've set up your subscription and prices.

@jeffreyzant
Copy link
Author

jeffreyzant commented Jun 12, 2020

I'm using the following code to create the subscription:

$client->newSubscription('default', $stripePlan)->create(null, [], [
    'collection_method' => 'send_invoice',
    'days_until_due' => 30,
    'backdate_start_date' => Carbon::now()->addDays(90)->subYear()->startOfDay()->unix(),
    'billing_cycle_anchor' => Carbon::now()->addDays(90)->startOfDay()->unix(),
    'proration_behavior' => 'none',
]);

Afterwards i'm trying to swap the subscription like so:

$client->subscription('default')->swap(self::ENTERPRISE_YEARLY);

Both plans are a standard yearly pricing configuration.

Edit: If im correct, your test is not about swapping but creating the initial subscription. There is no invoice in Stripe after creating the newSubscription for a newly created client/user.

@driesvints
Copy link
Member

Ah okay, you said:

When creating a subscription with proration_behavior => none there is no first invoice.

So I tested that and that wasn't the case (there was one). I'll try again with the info from above.

@driesvints
Copy link
Member

I get:

Stripe\Exception\InvalidRequestException: billing_cycle_anchor cannot be later than next natural billing date (1594546977) for plan

So we'll need even more info on how exactly you set up the plans. Please share all the data you used to set them up.

@jeffreyzant
Copy link
Author

Did you use a yearly or monthly stripe plan? For testing im adding the 90 days to the billing_cycle_anchor. When you are using a monthly plan this won't work, because it then should be within the month. You could also set the addDays to 5 or so.

image

@driesvints
Copy link
Member

Think I cracked it: #959

Thanks for reporting!

@jeffreyzant
Copy link
Author

I've tried the code in my situation and it indeed works! Thanks for the quick response and i'll wait for it to be merged! :)

@jeffreyzant
Copy link
Author

jeffreyzant commented Jun 12, 2020

I've just continued my developing and saw another issue with the solution. There is still an error when using:

$client->subscription('default')->swap(self::ENTERPRISE_YEARLY, [
    'proration_behavior' => 'always_invoice',
]);
 Symfony\Component\Debug\Exception\FatalThrowableError  : Argument 1 passed to Laravel\Cashier\Payment::__construct() must be an instance of Stripe\PaymentIntent, null given, called in app/StripeSubscription.php on line 78

  at vendor/laravel/cashier/src/Payment.php:24
    20|      *
    21|      * @param  \Stripe\PaymentIntent  $paymentIntent
    22|      * @return void
    23|      */
  > 24|     public function __construct(StripePaymentIntent $paymentIntent)
    25|     {
    26|         $this->paymentIntent = $paymentIntent;
    27|     }
    28| 

  Exception trace:

  1   Laravel\Cashier\Payment::__construct()
      app/StripeSubscription.php:78

  2   App\StripeSubscription::swap()
      app/Console/Commands/StripeUpdateCommand.php:81

Edit: The app/StripeSubscription.php is a temporary overwrite for testing. It uses your solution from Laravel\Cashier\Subscription

@jeffreyzant
Copy link
Author

I'm guessing the old check isn't wrong and should be combined with the new check. As there is a last invoice when using the always_invoice method. But this one does not have a payment_intent yet.

if ($this->hasIncompletePayment() && $stripeSubscription->latest_invoice->payment_intent) {

@driesvints
Copy link
Member

I think a lot of the problems that are surfacing here is because of Stripe's new "collection_method: send_invoice" method which we don't officially support yet. But it's good that we're tackling these now.

I've sent a support question to Stripe to ask about this particular case because it's weird to me that the invoice after this scenario is in "draft" and that the subscription is "past due".

@driesvints
Copy link
Member

Gonna re-open this until Stripe has gotten back to me.

@driesvints driesvints reopened this Jun 12, 2020
@driesvints
Copy link
Member

Just pasting in here the failing test for this for future reference:

    /** @group NoProrate */
    public function test_always_invoice_after_no_prorate()
    {
        $user = $this->createCustomer('always_invoice_after_no_prorate');

        $subscription = $user->newSubscription('main', static::$planId)->noProrate()->create('pm_card_visa', [], [
            'collection_method' => 'send_invoice',
            'days_until_due' => 30,
            'backdate_start_date' => Carbon::now()->addDays(5)->subYear()->startOfDay()->unix(),
            'billing_cycle_anchor' => Carbon::now()->addDays(5)->startOfDay()->unix(),
        ]);

        $this->assertEquals(static::$planId, $subscription->stripe_plan);
        $this->assertTrue($subscription->active());

        $subscription = $subscription->swapAndInvoice(self::$otherPlanId);

        $this->assertEquals(static::$otherPlanId, $subscription->stripe_plan);
        $this->assertTrue($subscription->active());
    }

@driesvints
Copy link
Member

Stripe confirmed the behavior is a bug. Moved this to "needs more info" until Stripe has gotten back to me with a fix.

@driesvints
Copy link
Member

driesvints commented Jul 27, 2020

Stripe has fixed the above behavior now. I've added a new test to cover this scenario: 3347f93

Basically it boils down that any invoicing is delayed because of the draft invoice from the beginning of the subscription.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants