我逐步遵循了本教程: https://appdividend.com/2018/12/05/laravel-stripe-payment-gateway-integration-tutorial-with-example/
但是,当我进行测试时,出现以下错误:
条带\错误\ InvalidRequest 没有这样的付款方式:
一些注意事项:
我确保Stripe处于测试模式,并且我的Stripe API密钥设置正确,并使用了推荐的测试卡:4242 4242 4242 4242 | 04/22 | 222 | 12345
我细读了这篇文章的评论,发现其他人也有一个“相似”的问题-但不是关于付款方式的错误。
自从Laravel 5.8发布以来,Cashier 10被发布了-我看到的是有关“ paymentIntents”的点点滴滴-所以我不确定这是否是导致问题的原因。
有人对我可以解决此错误有任何想法吗?
谢谢!
以下是我使用的各种代码:
路由(web.php)
Route::group(['middleware' => 'auth'], function() {
Route::get('/home', 'HomeController@index')->name('home');
Route::get('/plans', 'PlanController@index')->name('plans.index');
Route::get('/plan/{plan}', 'PlanController@show')->name('plans.show');
Route::post('/subscription', 'SubscriptionController@create')-
>name('subscription.create');
});
计划模型(plan.php)
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Plan extends Model {
protected $fillable = [
'name',
'slug',
'stripe_plan',
'cost',
'description'
];
public function getRouteKeyName() {
return 'slug';
}
}
计划控制器(PlanController.php)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Plan;
class PlanController extends Controller {
public function index() {
$plans = Plan::all();
return view('plans.index', compact('plans'));
}
public function show(Plan $plan, Request $request) {
return view('plans.show', compact('plan'));
}
}
订阅控制器(SubscriptionController.php)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Plan;
class SubscriptionController extends Controller {
public function create(Request $request, Plan $plan) {
$plan = Plan::findOrFail($request->get('plan'));
$request->user()
->newSubscription('main', $plan->stripe_plan)
->create($request->stripeToken);
return redirect()->route('home')->with('success', 'Your plan subscribed successfully');
}
}
显示视图(show.blade.php)
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-12">
<div class="">
<p>You will be charged ${{ number_format($plan->cost, 2) }} for {{ $plan->name }} Plan</p>
</div>
<div class="card">
<form action="{{ route('subscription.create') }}" method="post" id="payment-form">
@csrf
<div class="form-group">
<div class="card-header">
<label for="card-element">
Enter your credit card information
</label>
</div>
<div class="card-body">
<label for="card-element">Credit or debit card</label>
<div id="card-element">
<!-- A Stripe Element will be inserted here. -->
</div>
<!-- Used to display form errors. -->
<div id="card-errors" role="alert"></div>
<input type="hidden" name="plan" value="{{ $plan->id }}" />
</div>
</div>
<div class="card-footer">
<button class="btn btn-dark" type="submit">Submit Payment</button>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
<script src="https://js.stripe.com/v3/"></script>
<script>
// Create a Stripe client.
var stripe = Stripe('{{ env("STRIPE_KEY") }}');
// Create an instance of Elements.
var elements = stripe.elements();
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
base: {
color: '#32325d',
lineHeight: '18px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
// Create an instance of the card Element.
var card = elements.create('card', {style: style});
// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');
// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
stripe.createToken(card).then(function(result) {
if (result.error) {
// Inform the user if there was an error.
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
// Send the token to your server.
stripeTokenHandler(result.token);
}
});
});
// Submit the form with the token ID.
function stripeTokenHandler(token) {
// Insert 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', 'stripeToken');
hiddenInput.setAttribute('value', token.id);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
</script>
@endsection
答案 0 :(得分:6)
解决了Laravel 5.8和Cashier 10.2
PlanController:
public function show(\App\Plan $plan, Request $request)
{
$paymentMethods = $request->user()->paymentMethods();
$intent = $request->user()->createSetupIntent();
return view('plans.show', compact('plan', 'intent'));
}
查看:
<button
id="card-button"
class="btn btn-dark"
type="submit"
data-secret="{{ $intent->client_secret }}"
> Pay </button>
...
<script src="https://js.stripe.com/v3/"></script>
<script>
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
base: {
color: '#32325d',
lineHeight: '18px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
const stripe = Stripe('{{ env("STRIPE_KEY") }}', { locale: 'es' }); // Create a Stripe client.
const elements = stripe.elements(); // Create an instance of Elements.
const cardElement = elements.create('card', { style: style }); // Create an instance of the card Element.
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardElement.mount('#card-element'); // Add an instance of the card Element into the `card-element` <div>.
// Handle real-time validation errors from the card Element.
cardElement.addEventListener('change', function(event) {
var displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
} else {
displayError.textContent = '';
}
});
// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
stripe
.handleCardSetup(clientSecret, cardElement, {
payment_method_data: {
//billing_details: { name: cardHolderName.value }
}
})
.then(function(result) {
console.log(result);
if (result.error) {
// Inform the user if there was an error.
var errorElement = document.getElementById('card-errors');
errorElement.textContent = result.error.message;
} else {
console.log(result);
// Send the token to your server.
stripeTokenHandler(result.setupIntent.payment_method);
}
});
});
// Submit the form with the token ID.
function stripeTokenHandler(paymentMethod) {
// 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', 'paymentMethod');
hiddenInput.setAttribute('value', paymentMethod);
form.appendChild(hiddenInput);
// Submit the form
form.submit();
}
</script>
SubscriptionController
public function create(Request $request, \App\Plan $plan)
{
$plan = \App\Plan::findOrFail($request->get('plan'));
$user = $request->user();
$paymentMethod = $request->paymentMethod;
$user->createOrGetStripeCustomer();
$user->updateDefaultPaymentMethod($paymentMethod);
$user
->newSubscription('main', $plan->stripe_plan)
->trialDays(7)
->create($paymentMethod, [
'email' => $user->email,
]);
return redirect()->route('home')->with('status', 'Your plan subscribed successfully');
}
答案 1 :(得分:3)
将收银员版本降级为9.x。
在Cashier 10.x的create()
方法上,接受paymentMethod
作为第一个参数。
在Cashier 9.x的create()
方法上,接受stripeToken
作为第一个参数。
OR
升级前端JS以与Payment Intents API一起使用。但这将是一个问题,如果您打算使用新的Stripe Checkout(如此处显示https://github.com/laravel/cashier/issues/637)
我的建议是降级收银机版本,直到完全支持。
答案 2 :(得分:3)
使用该教程,您需要在版本10停止使用Stripe Token之前使用Laravel Cashier版本。
对于新项目,我建议您使用Laravel Cashier 10和Stripe Elements,否则当旧API贬值时,您将不得不在不久的将来进行一些认真的重构。
由于Laravel Cashier 10刚刚发布,因此除原始文档外,没有太多其他信息。我刚刚启动并使用了一个项目,如果您决定走这条路,很高兴回答任何问题。
新过程基本上是:
答案 3 :(得分:1)
以防万一,任何人都想知道我如何解决此特定教程的错误:
1)我降级了收银台版本
composer remove laravel/cashier
然后
composer require "laravel/cashier":"~9.0"
2)然后我开始收到另一个错误:
no plan exists with the name (Basic/Professional)
要解决此问题,我在条带中创建了一个新的定期产品而不是一次性产品,并使用此新计划条目更新了计划表
3)然后我再次遇到另一个错误:
no plan id exits
要解决此问题,我使用从步骤2中获得的计划ID更新了计划表strip_plan列条目
它适用于此特定教程,不确定其他版本
答案 4 :(得分:0)
我认为您的问题可能是创建方法。试试这个:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Plan;
class SubscriptionController extends Controller {
public function create(Request $request, Plan $plan) {
$plan = Plan::findOrFail($request->get('plan'));
\Auth::user() //make sure your user is signed in and use the authenticated user
->newSubscription('main', $request->plan) //You just send the name of the subscription in Stripe, not the object
->create($request->stripeToken);
return redirect()->route('home')->with('success', 'Your plan subscribed successfully');
}
我认为您的问题是因为您使用的用户无效和/或因为您发送的是计划对象而不是付款计划的名称。例如,如果您有一个名为Main in Stripe的产品,其定价计划为“计划1”和“计划2”,要订阅经过身份验证的用户,您可以这样做:
\Auth::user
->newSubscription('Main', 'Plan 1')
->create($request->stripeToken);
您的Stripe产品应如下所示:
答案 5 :(得分:0)
也许来晚了,但您不必总是设置付款方式。我能够做到以下
$user = new User();
$user->fill($payload);
$user->createAsStripeCustomer([
'name' => $user->fullname,
]);
$user->updateDefaultPaymentMethod($data->stripeToken);
$user->newSubscription(env('STRIPE_SUBSCRIPTION_NAME'), env('STRIPE_PLAN_ID'))
->create(null, [
'name' => $this->fullname,
'email' => $this->email,
]) // may not be required as we already do this above;
stripeToken 是使用stripe.createPaymentMethod时返回的令牌。需要注意的一件事是,在创建订阅时,我不再需要指定付款方式。同样,在我的情况下,我必须在用户注册期间收集信用卡。我只会在用户验证电子邮件后才开始订阅。
步骤是
我真的不喜欢条纹文档。太多的重大更改让我感到不完整,因为它们不只是一种被记录在案的处理方式
。