Laravel Cashier 16.x集成Stripe创建订阅时出现Call to a member function stripe() on null错误
错误原因分析
这个错误本质是当前$clinic模型实例未关联有效的Stripe客户对象,导致Cashier创建订阅时无法调用stripe()方法获取Stripe客户实例。大概率是createAsStripeCustomer()执行后,模型的stripe_id字段未持久化到数据库,或是Stripe客户创建过程出现隐性错误。
具体修复步骤
1. 强制保存Stripe客户ID到数据库
Cashier的createAsStripeCustomer()会生成Stripe客户ID,但默认不会自动保存到数据库,必须手动调用save():
if (!$clinic->hasStripeId()) { $clinic->createAsStripeCustomer(); // 新增:将生成的stripe_id写入数据库 $clinic->save(); }
这是核心修复点——如果不保存,当前内存中的$clinic虽有stripe_id,但后续Cashier内部可能依赖数据库中持久化的字段生成Stripe客户实例,导致stripe()返回null。
2. 验证支付方法ID格式
确保前端传入的paymentMethod是Stripe官方要求的支付方法ID(格式为pm_xxxxxx),而非旧版的tok_开头临时token。Cashier 16.x推荐使用支付方法ID创建订阅,临时token可能引发兼容性问题。
3. 移除手动设置Stripe密钥的代码
Cashier会自动读取配置文件中的STRIPE_SECRET,手动调用Stripe::setApiKey(config('cashier.secret'))可能导致密钥冲突,直接删除该行代码即可。
4. 捕获Stripe客户创建时的API错误
在创建Stripe客户时,可能因API权限、参数错误等导致创建失败但未抛出异常,需添加针对性捕获排查问题:
if (!$clinic->hasStripeId()) { try { $clinic->createAsStripeCustomer(); $clinic->save(); } catch (\Stripe\Exception\ApiErrorException $e) { \Log::error('Stripe客户创建失败: ' . $e->getMessage()); return back()->withErrors(['error' => '客户创建失败: ' . $e->getMessage()]); } }
5. 确认订阅计划为价格ID而非产品ID
$request->plan必须是Stripe后台创建的价格ID(格式为price_xxxxxx),而非产品ID。Cashier 16.x订阅仅支持传入价格ID,这是高频错误点。
修复后的完整控制器代码示例
public function subscribe(Request $request) { $request->validate([ 'plan' => 'required|string', 'paymentMethod' => 'required|string', 'hidClinicId' => 'required' ]); $clinic = Clinic::findOrFail($request->hidClinicId); if ($clinic->subscribed('default')) { Alert::warn('Your clinic is already subscribed!'); return redirect()->back(); } try { if (!$clinic->hasStripeId()) { $clinic->createAsStripeCustomer(); $clinic->save(); } $clinic->newSubscription('default', $request->plan) ->create($request->paymentMethod); Alert::success('You have successfully subscribed!'); return redirect() ->route('home') ->with('success', 'Clinic subscription created successfully!'); } catch (\Stripe\Exception\ApiErrorException $e) { \Log::error('Stripe API错误: ', ['message' => $e->getMessage()]); Alert::error('Subscription failed. Please contact support!'); return back()->withErrors(['error' => '订阅失败: ' . $e->getMessage()]); } catch (\Exception $e) { \Log::error('订阅流程错误: ', ['message' => $e->getMessage()]); Alert::error('Subscription failed. Please contact support!'); return back()->withErrors(['error' => '订阅失败: ' . $e->getMessage()]); } }
内容的提问来源于stack exchange,提问作者smzapp

