Call to a member function stripe() on null when cancelling the subscription

Viewed 26

I am using Laravel 8 and Laravel Cashier for my subscriptions

When I tried to cancel the subscription, it returns

Call to a member function stripe() on null

when cancelling the subscription

Even the Auth::check() returns true

Here is my cancelSubscription method

public function cancelSubscription(Request $request){

    $user =  $request->user();

    try{

      //  if(Auth::check()){
            $user->subscription('My-Subscription-Name')->cancel();
      //  }
     

        if($request->ajax()) {
            return response([
                'success' => true,
                'data' => "Successfully subscribed"
            ], 200);
        }

    }catch (\Throwable $ex) {

        Log::critical($ex);

        if($request->ajax()) {
            return response([
                'success' => false,
                'data' => "Server Error"
            ], 422);
        }

    }

}

Here is the code in api.php for the cancel subscription method

Route::post('/cancel-sub',[SubscriptionController::class, 'cancelSubscription'])->middleware('auth:api');

I've also tried this

public function cancelSubscription(Request $request){

    $user =  UserSubscription::where('stripe_id', $request->subId)->first(); //returns subscription id

    try{

      //  if(Auth::check()){
            $user->subscription('My-Subscription-Name')->cancel();
     //   }
     

        if($request->ajax()) {
            return response([
                'success' => true,
                'data' => "Successfully subscribed"
            ], 200);
        }

    }catch (\Throwable $ex) {

        Log::critical($ex);

        if($request->ajax()) {
            return response([
                'success' => false,
                'data' => "Server Error"
            ], 422);
        }

    }

}

I've also tried

$user = $request->user();
$subscriptions = $user->subscriptions()->active()->first();
$subscriptions->cancel();

but it still doesn't work. Any answers will be appreciated.

I also did dd(auth()->user()->subscription('My-Subscription-Name')) and it returns true and here is the result

    Laravel\Cashier\Subscription {#1293
  #guarded: []
  #with: array:1 [
    0 => "items"
  ]
  #casts: array:1 [
    "quantity" => "integer"
  ]
  #dates: array:4 [
    0 => "created_at"
    1 => "ends_at"
    2 => "trial_ends_at"
    3 => "updated_at"
  ]
  #billingCycleAnchor: null
  #connection: "mysql"
  #table: "subscriptions"
  #primaryKey: "id"
  #keyType: "int"
  +incrementing: true
  #withCount: []
  +preventsLazyLoading: false
  #perPage: 15
  +exists: true
  +wasRecentlyCreated: false
  #escapeWhenCastingToString: false
  #attributes: array:12 [
    "id" => 32
    "user_tbl_id" => 4697
    "name" => "My-Subscription-Name"
    "stripe_id" => "sub_xxx"
    "stripe_status" => "active"
    "stripe_price" => "price_xxx"
    "quantity" => 1
    "trial_ends_at" => null
    "ends_at" => null
    "expires_at" => "2022-10-13 20:38:54"
    "created_at" => "2022-09-13 20:38:54"
    "updated_at" => "2022-09-13 20:38:54"
  ]
  #original: array:12 [
    "id" => 32
    "user_tbl_id" => 4697
    "name" => "Choi-Nomi"
    "stripe_id" => "sub_xxx"
    "stripe_status" => "active"
    "stripe_price" => "price_xxx"
    "quantity" => 1
    "trial_ends_at" => null
    "ends_at" => null
    "expires_at" => "2022-10-13 20:38:54"
    "created_at" => "2022-09-13 20:38:54"
    "updated_at" => "2022-09-13 20:38:54"
  ]
  #changes: []
  #classCastCache: []
  #attributeCastCache: []
  #dateFormat: null
  #appends: []
  #dispatchesEvents: []
  #observables: []
  #relations: array:1 [
    "items" => Illuminate\Database\Eloquent\Collection {#1303
      #items: array:1 [
        0 => Laravel\Cashier\SubscriptionItem {#1301
          #guarded: []
          #casts: array:1 [
            "quantity" => "integer"
          ]
          #connection: "mysql"
          #table: "subscription_items"
          #primaryKey: "id"
          #keyType: "int"
          +incrementing: true
          #with: []
          #withCount: []
          +preventsLazyLoading: false
          #perPage: 15
          +exists: true
          +wasRecentlyCreated: false
          #escapeWhenCastingToString: false
          #attributes: array:9 [
            "id" => 33
            "subscription_id" => 32
            "stripe_id" => "si_xxx"
            "stripe_product" => "prod_xxx"
            "stripe_price" => "price_xxx"
            "quantity" => 1
            "user_subscription_id" => null
            "created_at" => "2022-09-13 20:38:54"
            "updated_at" => "2022-09-13 20:38:54"
          ]
          #original: array:9 [
            "id" => 33
            "subscription_id" => 32
            "stripe_id" => "si_xxx"
            "stripe_product" => "prod_xxx"
            "stripe_price" => "price_xxx"
            "quantity" => 1
            "user_subscription_id" => null
            "created_at" => "2022-09-13 20:38:54"
            "updated_at" => "2022-09-13 20:38:54"
          ]
          #changes: []
          #classCastCache: []
          #attributeCastCache: []
          #dates: []
          #dateFormat: null
          #appends: []
          #dispatchesEvents: []
          #observables: []
          #relations: []
          #touches: []
          +timestamps: true
          #hidden: []
          #visible: []
          #fillable: []
          #paymentBehavior: "allow_incomplete"
          #prorationBehavior: "create_prorations"
        }
      ]
      #escapeWhenCastingToString: false
    }
  ]
  #touches: []
  +timestamps: true
  #hidden: []
  #visible: []
  #fillable: []
  #couponId: null
  #promotionCodeId: null
  #allowPromotionCodes: false
  #paymentBehavior: "allow_incomplete"
  #prorationBehavior: "create_prorations"
}
1 Answers

I did have a look at my old project and what I did was add another method on User Model, to also delete the subscription related entries on the database

public function cancelStripeSubscription( $name = 'My Subscription Name') {
    $sub = $this->subscription($name);
    if ( $sub ) {
        DB::table('subscriptions')->where('id',  $sub->id)->delete();
        DB::table('subscription_items')->where('subscription_id',  $sub->id)->delete();
        if (  $sub->stripe_status != 'canceled' )
            $sub->cancelNow();
    }
}

so you can simply call auth()->user()->cancelStripeSubscription();

In you code however, you should first check if they have active subscription or subscribe to specific product by checking subscription status

something like this;

public function cancelSubscription(Request $request){
    
    try{
        
        if ( auth()->user()->subscribed('default') )
        /*
            or if ( $user->subscribedToProduct('prod_premium', 'default')) 
            or if ( $user->subscribedToPrice('price_basic_monthly', 'default'))
            or
            $sub = auth()->user()->subscription('My-Subscription-Name')
            if ( $sub && $sub->stripe_status == 'canceled' )
        */
        {
            auth()->user()->subscription('My-Subscription-Name')->cancel();
        } else {
            return response([ 'error' => true, 'message' => 'You have no active subscription' ], 404);
        }
        .
        .
    } 
    .
    .
}

Furthermore, verify if that subscripton your trying to cancel really is a subscription object and check if user is even have a subscription

public function cancelSubscription(Request $request){
    
    return [
        'request' => $request->all(),
        'user' => auth()->user(),
        'subscription' => auth()->user()->subscription('My-Subscription-Name'),
        'subscriptions' => auth()->user()->subscriptions()
    ]
    .
    .
}
Related