Laravel Cashier 新订阅 Stripe 抛出“无法确定请求哪个 URL:”错误

Posted

技术标签:

【中文标题】Laravel Cashier 新订阅 Stripe 抛出“无法确定请求哪个 URL:”错误【英文标题】:Laravel Cashier new subscription with Stripe throws "Could not determine which URL to request:" error 【发布时间】:2020-09-24 19:53:53 【问题描述】:

我正在使用 Laravel 6 和 Cashier 10.7 来处理我平台上的订阅。一次性支付给 Stripe 的工作正常,从一个订阅切换到另一个订阅也是如此,但是当我尝试为没有存储卡的客户启动新订阅时,我收到一个错误

无法确定请求哪个 URL:Stripe\PaymentMethod 实例的 ID 无效:

我的付款表格如下所示;

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-12 col-md-6 mt-5 mb-2">

            @if (session('status'))
                <div class="alert alert-success" role="alert">
                     session('status') 
                </div>
            @endif

            @if ($errors->any())
                <div class="alert alert-danger">
                    <strong>Whoops!</strong> There was a problem.
                    <ul>
                        @foreach ($errors->all() as $error)
                        <li> $error </li>
                        @endforeach
                    </ul>
                </div>
            @endif

            <div class="h2 text-center">
                 __('Please enter your card details') 
            </div>

            <div class="card">
                <div class="card-body">
                    <form method="post" action=" route('startSubscription') " id="payment-form">
                        @csrf

                        <div class="group">
                            <label class="stripe-label">
                                <span class="mr-1 p-2"> __('Cardholder Name') </span>
                                <input id="card-holder-name" class="field" placeholder="Jordan Jones" />
                            </label>
                        </div>

                        <!-- Stripe Elements Placeholder -->
                        <div class="group">
                          <label class="stripe-label">
                            <span class="mr-1 p-2"> __('Card') </span>
                            <div id="card-element" class="field p-3"></div>
                          </label>
                        </div>

                        <!-- Used to display form errors. -->
                        <div id="card-errors" role="alert"></div>
                        <button id="card-button" class="btn btn-lg btn-block btn-success" data-secret=" $intent->client_secret ">
                            <i class="fas fa-credit-card mr-1"></i> __('Add Payment Method') 
                        </button>
                    </form>
                </div>
            </div>

        </div>
    </div>
</div>
@endsection

@section('javascript')

<script>
    const stripe = Stripe(' env("STRIPE_KEY") ');
    const elements = stripe.elements();
    const cardElement = elements.create('card', hidePostalCode: true);

    cardElement.mount('#card-element');

    const cardHolderName = document.getElementById('card-holder-name');
    const cardButton = document.getElementById('card-button');
    const clientSecret = cardButton.dataset.secret;

    cardButton.addEventListener('click', async (e) => 
        const  setupIntent, error  = await stripe.confirmCardSetup(
            clientSecret, 
                payment_method: 
                    card: cardElement,
                    billing_details:  name: cardHolderName.value 
                
            
        );

        if (error) 
            var errorElement = document.getElementById('card-errors');
            errorElement.textContent = error.message;
         else 
          // Submit the form with the token ID.
           function stripePaymentHandler(setupIntent) 
           // Insert the token ID into the form so it gets submitted to the server
           var form = document.getElementById('payment-form');
           var hiddenInput = document.createElement('input');
           hiddenInput.setAttribute('type', 'hidden');
           hiddenInput.setAttribute('name', 'payment_method');
           hiddenInput.setAttribute('value', setupIntent.payment_method);
           form.appendChild(hiddenInput);

           // Submit the form
           form.submit();
          
        
        );

</script>
@endsection

我的控制器是这样的;

public function subscriptionPayment(Request $request) 
  $user = Auth::user();
  $paymentMethod = $request->payment_method;

  try 
    if ($user->hasPaymentMethod()) 
      $user->updateDefaultPaymentMethod($paymentMethod);
     else 
      $user->addPaymentMethod($paymentMethod);
    

$newSubscription = $user->newSubscription('User', 'plan_HGvJkewD0eaY6G')->trialDays(60)->create($paymentMethod, ['email' => $user->email]);

   catch ( IncompletePayment $exception )
    return redirect()->route('payment',
                [$exception->payment->id, 'redirect' => route('home')]
    );
  

  return redirect()->route('home')
    ->with('status', 'Your payment has been processed and your subscription is now active.');
  

Stripe 事件日志显示了这一点;

“对象”: "id": "seti_*******", “对象”:“设置意图”, “应用程序”:空,

"cancellation_reason": null, "client_secret": "seti_*********", “创建”:1591367952, “客户”:空, “描述”:空, “last_setup_error”:空, “现场模式”:假, “授权”:空, “元数据”: , “下一个动作”:空, “on_behalf_of”:空, “付款方式”:空, “付款方式选项”: “卡片”: “request_three_d_secure”:“自动” , “payment_method_types”:[ “卡片” ], “single_use_mandate”:空, “状态”:“requires_payment_method”, “用法”:“off_session”

任何人都可以帮助解决我做错的事情,我就是无法解决。

【问题讨论】:

听起来您将无效的 id 字符串传递给 $user->updateDefaultPaymentMethod($paymentMethod)。它应该采用pm_****(或有时card_****)的形式。可能是您传递整个 PaymentMethod 对象而不仅仅是 id 只是 var_dumped $paymentMethod 为空。 看起来表单可能在使用 paymentMethod 的 Stripe 响应之前提交,但不应该等待命令这样做吗? 尝试硬编码 hiddenInput.setAttribute('value', '1234');进入 js 代码,但 PaymentMethod 上的 var_dump 仍然返回 null。看起来js表单追加不起作用,但我不明白为什么。向表单本身添加一个简单的隐藏字段就可以了。 【参考方案1】:

我将此追踪到标题中的 js 脚本标记中存在“延迟”,这意味着事件侦听器无法正常工作,并且表单正在提交而无需等待来自 Stripe 的调用和响应。 用一个简单的

修复
<script src=" asset('js/app.js') "></script>

2天后?‍♂️

【讨论】:

以上是关于Laravel Cashier 新订阅 Stripe 抛出“无法确定请求哪个 URL:”错误的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 7.0 Cashier - 条纹支付异常

Laravel Cashier SCA 订阅返回不完整的订阅

无需订阅即可使用 Laravel Cashier 一次性付款

使用具有多个订阅的 Laravel Cashier

Laravel Cashier - $stripeToken 来自哪里?

Laravel Cashier - 更新订阅的付款方式