上一节我们简单讲述了Laravel Cashier的安装配置,这一节我们将使用Laravel Cashier来实现一个常见的功能——付费会员。比如QQ、微博、优酷等应用都有这一功能,并且该功能已然成为许多网站收入的重要来源,可见其地位之重要,而在Laravel中我们可以借助Cashier通过Stripe轻松实现该功能,正如我们前面提到的,Laravel Cashier为我们封装了支付功能,所以我们不需要处理如何支付,也不需要关心任何与支付相关的细节,而只需要关心具体的业务逻辑。
正所谓兵马未动,粮草先行,在正式编写业务逻辑代码之前,我们先要到Stripe个人中心创建订购计划。导航到https://dashboard.stripe.com/test/plans页面创建两个付费会员级别:Silver(银牌会员)和Gold(金牌会员):
注意页面左上角的TEST,我们目前是在Stripe的测试环境进行操作。
创建好订购计划后,我们在routes.php
中为业务逻辑定义好相关路由:
//用户个人主页 Route::get('profile','UserController@profile'); //用户会员级别 Route::get('service','UserController@service'); //付费会员页面 Route::get('subscription','UserController@subscription'); //处理付费逻辑 Route::post('subscribe','UserController@subscribe'); //升级到更高级别 Route::get('upgrade','UserController@upgrade');
接下来自然而然就是到控制器UserController
中编写业务逻辑代码,由于之前我们已经讲过如何实现登录认证,这里我们使用GitHub进行登录认证。认证完成后跳转到/profile
页面,然后我们的业务逻辑由此开始。
首先在profile页面会直接跳转到查看会员级别页面:
public function profile(Request $request) { $user = $request->user(); return redirect('service'); }
然后在service页面我们编写控制器代码如下:
public function service(Request $request){ $user = $request->user(); if (!$user->subscribed()) { return redirect('subscription'); } if($user->onPlan('silver')){ $service = ['type'=>1,'name'=>'银牌会员']; }else{ $service = ['type'=>2,'name'=>'金牌会员']; } return view('user.service',['service'=>$service]); }
首先,我们从请求中取出认证用户实例,然后调用该实例上的subscribed
方法判断用户是否是付费会员,如果不是的话跳转到付费页面,否则通过onPlan
方法判断用户是银牌会员还是金牌会员,并渲染对应视图resources/views/user/service.blade.php
:
你已经是{{$service['name']}}<br> @if ($service['type'] == 1) <a href="/upgrade">升级为金牌会员</a> @endif
升级的事情我们先按下不表,先来看看购买付费会员的实现逻辑。我们在UserController
中编写subscription
对应的代码如下:
public function subscription(Request $request) { $user = $request->user(); if($user->subscribed()){ return redirect('service'); } return view('user.subscription'); }
该页面逻辑很简单,先判断是否已经是付费会员,是的话跳转到会员级别页面,否则才会渲染购买付费页面,接下来我们定义其对应视图文件resources/views/user/subscription.blade.php
如下:
<form action="/subscribe" method="post" id="subscription-form"> <span class="payment-errors"></span> {!! csrf_field() !!} <div> 付费计划: <select name="plan"> <option value="silver">银牌会员</option> <option value="gold">金牌会员</option> </select> </div> <div> 信用卡号: <input type="text" name="number" id="number"> </div> <div> 过期时间: 月份:<input type="text" name="exp_month" id="exp_month"> 年份:<input type="text" name="exp_year" id="exp_year"> </div> <button type="submit">订购服务</button> </form> <script type="text/javascript" src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript" src="https://js.stripe.com/v2/"></script> <script> Stripe.setPublishableKey('{{config("services.stripe.key")}}'); jQuery(function($) { $('#subscription-form').submit(function(event) { var form = $(this); form.find('button').prop('disabled', true); Stripe.card.createToken({ number: $('#number').val(), exp_month: $('#exp_month').val(), exp_year: $('#exp_year').val() }, stripeResponseHandler); return false; }); }); var stripeResponseHandler = function(status, response) { var form = $('#subscription-form'); if (response.error) { form.find('.payment-errors').text(response.error.message); form.find('button').prop('disabled', false); } else { var token = response.id; form.append($('<input type="hidden" name="stripeToken" />').val(token)); form.get(0).submit(); } }; </script>
该视图文件相对比较复杂,在该视图中我们要选择购买的会员级别,填写信用卡号,以及信用卡对应的过期时间。会员级别对应的值即为我们在Stripe中创建订购计划时对应的计划ID,至于信用卡号和过期时间是用来生成stripeToken
的,这个stripeToken
就是我们在调用create
创建订购计划时要传入的值。Stripe还贴心的为我们提供了一系列可用的信用卡测试账号:
卡号 | 类型 |
---|---|
4242424242424242 |
Visa |
4012888888881881 |
Visa |
4000056655665556 |
Visa (debit) |
5555555555554444 |
MasterCard |
5200828282828210 |
MasterCard (debit) |
5105105105105100 |
MasterCard (prepaid) |
378282246310005 |
American Express |
371449635398431 |
American Express |
6011111111111117 |
Discover |
6011000990139424 |
Discover |
30569309025904 |
Diners Club |
38520000023237 |
Diners Club |
3530111333300000 |
JCB |
3566002020360505 |
JCB |
至于过期时间嘛,只要大于当前时间就好了。可能在别处你还会看到需要输入CVC,这是生成stripeToken
的可选参数,既然是可选,这里我们就不费那个事了。
这个页面在浏览器中看上去是这样的:
在点击“订购服务”之前,我们先来编写subscribe
对应的代码:
public function subscribe(Request $request) { $plan = $request->input('plan'); $creditCardToken = $request->input('stripeToken'); $user = $request->user(); $user->subscription($plan)->create($creditCardToken); if($user->save()){ return redirect('service'); }else{ return back()->withInput(); } }
可以看到我们调用:
$user->subscription($plan)->create($creditCardToken);
完成付费操作实现订购。
回到subscription
视图页面点击“订购服务”,成功后页面跳转到之前的service
页面,页面显示如下:
最后我们来处理升级逻辑,编写upgrade
对应业务逻辑代码如下:
public function upgrade(Request $request){ $user = $request->user(); if (!$user->subscribed()) { return redirect('subscription'); } if($user->onPlan('gold')){ exit('您已经是金牌会员了'); } try{ $user->subscription('gold')->swap(); }catch(Exception $ex){ exit('升级失败!'); } return redirect('service'); }
通过
$user->subscription('gold')->swap();
即可实现升级操作。
回到service
页面点击“升级到金牌会员”,成功后页面跳转回service
页面并显示如下信息:
你已经是金牌会员
好了,至此我们已经完成了付费会员功能的一般逻辑。后续我们将讨论除信用卡外如何支持更多支付方式,比如银联、支付宝、微信支付等。
走完上面的所有流程后,你会发现该实现逻辑和另外一个应用场景非常类似,就是分期付款,我们可以将商品/服务总价格+利息分成若干期,然后算出每月应还款额,然后在Stripe中创建对应分期付款计划。剩下的业务逻辑实现和付费会员并无二致,将会员级别换成商品规格或对应服务级别,剩下的支付&升级参考其实现即可,这里不再赘述。
对于有时候我们需要对分期付款的物品进行一次性付清,比如剩余总额为100美元,可以通过调用如下方法实现:
$user->charge(100);
该方法有返回值,支付成功返回true
,否则返回false
。
支付完成后,如果想要获取发票信息,Laravel Cashier也对此提供了支持。
首先通过调用用户实例上的invoices
方法获取该用户所有有效发票信息:
$invoices = $user()->invoices();
然后在视图中将获取到的$invoices
循环显示出来:
<table> @foreach ($invoices as $invoice) <tr> <td>{{ $invoice->dateString() }}</td> <td>{{ $invoice->dollars() }}</td> <td><a href="/order/invoice/{{ $invoice->id }}">下载发票</a></td> </tr> @endforeach </table>
我们将这段代码放到上述resources/views/user/service.blade.php
之后:
你已经是{{$service['name']}}<br> @if ($service['type'] == 1) <a href="/upgrade">升级为金牌会员</a> @endif <div> 发票信息: <table> @foreach ($invoices as $invoice) <tr> <td>{{ $invoice->dateString() }}</td> <td>{{ $invoice->dollars() }}</td> <td><a href="/user/invoice/{{ $invoice->id }}">下载发票</a></td> </tr> @endforeach </table> </div>
这样在我们付费成功后,页面显示如下:
最后我们在路由中对点击“下载发票”进行处理:
Route::get('user/invoice/{invoice}', function ($invoiceId) {
return Auth::user()->downloadInvoice($invoiceId, [
'vendor' => 'Laravel Academy',
'product' => 'Gold Member',
]);
});
该代码会将对应发票生成PDF文档并下载。我们点击上述的“下载发票按钮”,下载的PDF发票截图如下: