In the first part of this series we created the bare bones of a subscription-based website using Laravel. We also set the site up with roles, and granted a pending role to new users. We’re now at the point where we can perform basic registration, login, and logout. In this second part, we’ll integrate Recurly to set up our paid membership plans.
在本系列的第一部分中,我们使用Laravel创建了基于订阅的网站的基础。 我们还为网站设置了角色,并为新用户授予了待定角色。 现在,我们可以执行基本注册,登录和注销。 在第二部分中,我们将整合Recurly以建立我们的付费会员计划。
There are two Recurly libraries – the PHP client library, which we installed via Composer at the beginning of the first part of the series – and Recurly JS. Recurly JS is te client-side library for dynamically integrating forms that securely handle card information. That card information is POSTed to Recurly’s servers, not our web application, making compliance headaches significantly smaller.
Recurly库有两个-PHP客户端库和Recurly JS,我们在本系列的第一部分开始时通过Composer安装了该库。 Recurly JS是客户端库,用于动态集成可安全处理卡信息的表格。 该卡信息将发布到Recurly的服务器上,而不是我们的Web应用程序中,从而使合规性问题大大减少。
Download the Recurly JS library and copy recurly.min.js from the build folder into public/js/libs, and add it to your layout before the closing
tag:下载Recurly JS库并将recurly.min.js从build文件夹复制到public/js/libs ,并在关闭前将其添加到布局中
标签: <script src="/js/libs/recurly.min.js"></script> </body>We’ll also need the CSS styles which are used when displaying the payment form. Create the directory css/recurly and copy the themes directory into it, and then refer to it in the relevant section of your layout:
我们还需要显示付款表单时使用CSS样式。 创建目录css/recurly并将themes目录复制到其中,然后在布局的相关部分中引用它:
<link href="/css/recurly/themes/default/recurly.css" rel="stylesheet">When you first login after creating an account with Recurly, you’re given the opportunity to create your subscription plans. In the first part we created three levels: Bronze, Silver, and Gold. You can always change them or add more later, but there’s no harm in doing them now.
在使用Recurly创建帐户后首次登录时,您将有机会创建订阅计划。 在第一部分中,我们创建了三个级别:青铜,银和金。 您可以随时更改它们或在以后添加更多内容,但是现在执行它们没有害处。
Create a plan called Bronze; ensuring that the plan code is set to “bronze” (all lower-case). Set a price – I’ve set it for £4.99 per month, but you’re free to select any amount and/or timeframe you like. You can optionally set a one-off setup charge or even set a free trial period.
创建一个名为Bronze的计划; 确保计划代码设置为“古铜色”(全部小写)。 设置价格-我将其设置为每月4.99英镑,但您可以自由选择所需的任何金额和/或时间范围。 您可以选择设置一次性设置费用,甚至可以设置免费试用期。
Repeat the process twice more, setting up Silver (plan code: “silver”) and Gold (plan code: “gold”) plans – I’ve set mine at £9.99 and £14.99 per month respectively.
重复两次以上的过程,设置白银(计划代码:“银”)和金(计划代码:“金”)计划–我将我的计划分别设置为每月9.99英镑和14.99英镑。
Now go to the Recurly admin panel where there are a few things we need to set up, as well as find the relevant information for your application’s configuration. Click API access on the left-hand side. Click Enable API access. Take a note of your API key, and your subdomain.
现在,进入“递归管理”面板,我们需要设置一些内容,并找到与您的应用程序配置相关的信息。 点击左侧的API访问。 点击启用API访问。 记下您的API密钥和您的子域。
Now go to Recurly.js on the left hand side (under Developer). Click Enable Recurly.js and Transparent Post API. Take a note of your private key.
现在转到左侧的Recurly.js(在Developer下)。 单击启用Recurly.js和Transparent Post API。 记下您的私钥。
Now, create a Recurly configuration file in app/config/recurly.php, replacing the values with the ones you’ve just noted down along with the three-digit code that represents your chosen default currency (e.g. USD, GBP, AUD):
现在,在app/config/recurly.php创建一个Recurly配置文件,用您刚刚记下的值以及代表您选择的默认货币的三位数代码(例如,USD,GBP,AUD)替换这些值:
<?php return array( 'api_key' => 'YOUR-API-KEY', 'private_key' => 'YOUR-PRIVATE-KEY', 'subdomain' => 'YOUR-SUBDOMAIN', 'default_currency' => 'XYZ' );Now go to Push Notifications on the left hand side and click Configure. Enter the URL to your application with /recurly appended to it, for example: http://www.example.com/recurly. Leave the HTTP Auth Username and HTTP Auth Password fields blank for now.
现在,转到左侧的“推送通知”,然后单击“配置”。 输入带有/ recurly的应用程序URL,例如: http : //www.example.com/recurly 。 现在将“ HTTP身份验证用户名”和“ HTTP身份验证密码”字段留空。
Now let’s create a signup page that allows a potential customer to select their plan. Create app/views/home/signup.blade.php:
现在,让我们创建一个注册页面,让潜在客户选择他们的计划。 创建app/views/home/signup.blade.php :
@extends('layouts.default') @section('content') <h1>Signup</h1> <p>Please select your plan...</p> <div class="row-fluid pricing-table pricing-three-column"> <div class="span4 plan"> <div class="plan-name-bronze"> <h2>Bronze</h2> <span>£4.99 / Month</span> </div> <ul> <li class="plan-feature">Feature #1</li> <li class="plan-feature">Feature #2</li> <li class="plan-feature"><a href="/user/register/bronze" class="btn btn-primary btn-plan-select"><i class="icon-white icon-ok"></i> Select</a></li> </ul> </div> <div class="span4 plan"> <div class="plan-name-silver"> <h2>Silver <span class="badge badge-warning">Popular</span></h2> <span>£9.99 / Month</span> </div> <ul> <li class="plan-feature">Feature #1</li> <li class="plan-feature">Feature #2</li> <li class="plan-feature"><a href="/user/register/silver" class="btn btn-primary btn-plan-select"><i class="icon-white icon-ok"></i> Select</a></li> </ul> </div> <div class="span4 plan"> <div class="plan-name-gold"> <h2>Gold</h2> <span>£4.99 / Month</span> </div> <ul> <li class="plan-feature">Feature #1</li> <li class="plan-feature">Feature #2</li> <li class="plan-feature"><a href="/user/register/gold" class="btn btn-primary btn-plan-select"><i class="icon-white icon-ok"></i> Select</a></li> </ul> </div> </div> @stopOf course, if you wanted to ensure the prices were always up-to-date, you could pull in the plan details via the Recurly API and populate them in the template dynamically.
当然,如果您想确保价格始终是最新的,则可以通过Recurly API提取计划详细信息并将其动态填充到模板中。
Now add the following to css/style.css:
现在将以下内容添加到css/style.css :
.pricing-table .plan { background-color: #f3f3f3; text-align: center; } .plan:hover { background-color: #fff; } .plan { color: #fff; background-color: #5e5f59; padding: 20px; } .plan-name-bronze { background-color: #665D1E; color: #fff; padding: 20px; } .plan-name-silver { background-color: #C0C0C0; color: #fff; padding: 20px; } .plan-name-gold { background-color: #FFD700; color: #fff; padding: 20px; } .pricing-table-bronze { background-color: #f89406; color: #fff; padding: 20px; } .pricing-table .plan .plan-name span { font-size: 20px; } .pricing-table .plan ul { list-style: none; margin: 0; } .pricing-table .plan ul li.plan-feature { border-top: 1px solid #c5c8c0; padding: 15px 10px; } .pricing-three-column { margin: 0 auto; width: 80%; } .pricing-variable-height .plan { display: inline-block; float: none; margin-left: 2%; vertical-align: bottom; zoom:1; *display:inline; } .plan-mouseover .plan-name { background-color: #4e9a06 !important; } .btn-plan-select { font-size: 18px; padding: 8px 25px; }And finally, create the route in app/routes.php:
最后,在app/routes.php创建路由:
Route::get('/signup', function() { return View::make('home/signup'); });Now for the next stage of the registration page – taking payments. First, let’s modify the user/register POST callback; instead of redirecting to the homepage we’ll put the user in the session and redirect to the payment page. Change the following:
现在进入注册页面的下一阶段-付款。 首先,让我们修改user/register POST回调; 而不是重定向到主页,我们将用户置于会话中并重定向到付款页面。 更改以下内容:
return Redirect::to('/')->with( 'success', 'Welcome to the site, . Auth::user()->name . '!' );to:
至:
Session::put('register_user', $user); return Redirect::to('/user/register/payment');We need to expand the default layout so that we can embed the JavaScript code in the footer. Add the following line after the last script tag:
我们需要扩展默认布局,以便可以将JavaScript代码嵌入页脚中。 在最后一个脚本标记之后添加以下行:
@yield('scripts')Now, create a new route:
现在,创建一条新路线:
Route::get('/user/register/payment', function() { Recurly_js::$privateKey = Config::get('recurly.private_key'); $plan = 'bronze'; // todo: get this from vars $user = Session::get('register_user'); $signature = Recurly_js::sign(array( 'account' => array( 'account_code' => 'user_' . $user->id ), 'subscription' => array( 'plan_code' => $plan, 'currency' => Config::get('recurly.default_currency') ) )); return View::make('user/register')->with(array( 'plan' => $plan, 'subdomain' => Config::get('recurly.subdomain'), 'currency' => Config::get('recurly.default_currency'), 'signature' => $signature )); });A few notes on this code:
此代码的一些注意事项:
We need to set the private key on the Recurly_js class, which we’ll take from the configuration file we created earlier.
我们需要在Recurly_js类上设置私钥,我们将从先前创建的配置文件中获取该私钥。
The plan should get carried over from the previous stage in the registration process; I’ve left this implementation piece as a user exercise. 该计划应从注册过程的上一阶段继续进行; 我已将此实施部分留作用户练习。 We need to generate a signature for Recurly.js, using several pieces of information. Amongst them, is an identifier for the user in question, which we create by concatenating the class (user) and the user’s ID. 我们需要使用几条信息为Recurly.js生成签名。 其中,是相关用户的标识符,我们通过将类(用户)和用户ID串联在一起来创建。 This signature is passed along with some other required information to the view. 该签名与一些其他必需信息一起传递到视图。The payment page view comes in two parts. First, the HTML which is is extremely simple:
付款页面视图分为两个部分。 首先,HTML非常简单:
@extends('layouts.default') @section('content') <div id="recurly-subscribe"> </div> @stopRecurly.js will inject its client-side generated payment form into this div.
Recurly.js会将其客户端生成的付款表单注入此div。
Next we add some JavaScript to the view, in a section that will get output at the foot of the layout template:
接下来,在将在布局模板的底部获得输出的部分中,向视图中添加一些JavaScript:
@section('scripts') <script> Recurly.config({ subdomain: '{{ $subdomain }}', currency: '{{ $currency }}' }); Recurly.buildSubscriptionForm({ target: '#recurly-subscribe', // Signature must be generated server-side with a utility // method provided in client libraries. signature: '{{ $signature }}', successURL: '/user/register/confirm', planCode: '{{ $plan }}', distinguishContactFromBillingInfo: true, collectCompany: false, termsOfServiceURL: 'http://www.example.com/terms', acceptPaypal: true, acceptedCards: ['mastercard', 'discover', 'american_express', 'visa'], account: { firstName: 'Joe', lastName: 'User', email: 'test@example.net', phone: '555-555-5555' }, billingInfo: { firstName: 'Joe', lastName: 'User', address1: '123 somestreet', address2: '45', city: 'San Francisco', zip: '94107', state: 'CA', country: 'US', cardNumber: '4111-1111-1111-1111', CVV: '123' } }); </script> @stopThis is where the magic happens – Recurly builds the payment form and injects it into the div with ID #recurly-subscribe, which requires some of the information we’ve passed to the view, along with the server-generated signature.
这就是神奇的地方–递归地构建付款表格并将其注入ID为#recurly-subscribe的div中,这需要我们传递给视图的某些信息以及服务器生成的签名。
Next up, the callback which Recurly POSTs back to upon successful submission of the form which is defined in the successURL parameter above:
接下来,成功提交上面在successURL参数中定义的表单后,递归回发的回调:
Route::post('/user/register/confirm', function() { $recurly_token = Input::get('recurly_token'); Recurly_js::$privateKey = Config::get('recurly.private_key'); $result = Recurly_js::fetch($recurly_token); var_dump($result); });Again, we initialise Recurly.js with the private key from the config, and use it to fetch the object represented by the token that Recurly sends as a POST variable (recurly_token). It’ll be an instance of Recurly_Subscription, from which we can extract various bits of information. I’ve outputted it with var_dump() for you to take a look at.
同样,我们使用配置中的私钥初始化Recurly.js,并使用它来获取由Recurly作为POST变量发送的令牌( recurly_token )表示的对象。 这将是Recurly_Subscription的实例,我们可以从中提取各种信息。 我已经用var_dump()输出了它,供您查看。
Let’s first get the plan code, so we know what subscription plan the user has just signed up for:
首先让我们获取计划代码,这样我们就知道用户刚刚注册了哪种订阅计划:
$plan_code = $result->plan->plan_code;Now find the corresponding role; notice we’ve just given them the same name (e.g. “bronze”, “silver” and “gold”).
现在找到相应的角色; 请注意,我们给它们取了相同的名称(例如“青铜”,“银”和“金”)。
$role = Role::where('name', '=', $plan_code)->first();Then get the user from the session (and then remove it from the session):
然后从会话中获取用户(然后从会话中将其删除):
$user = Session::get('register_user'); Session::forget('register_user');Then we grant the appropriate role to the new user:
然后,我们将适当的角色授予新用户:
$user->roles()->attach($role);We then need to remove the pending role:
然后,我们需要删除挂起的角色:
$role_pending = $role_pending = Role::where('name', '=', 'pending')->first(); DB::table('role_user')->where('user_id', '=', $user->id)->where('role_id', '=', $role_pending->id)->delete();We’ve now enhanced the registration process to take payments, create subscriptions, and apply roles to new user accounts based on the selected plan. In the next part we’ll look at further management of user accounts and subscriptions.
现在,我们已增强了注册过程,以根据选定的计划进行付款,创建订阅并将角色应用于新用户帐户。 在下一部分中,我们将研究用户帐户和订阅的进一步管理。
Let’s create a simple page from which users will be able to manage their account. Obviously it’ll require that the user be logged in, so we’ll need to apply the auth filter. Rather than specify this filter for every protected page, we can put all the relevant routes into a group, like so:
让我们创建一个简单的页面,用户可以从中管理其帐户。 显然,这将要求用户登录,因此我们需要应用auth过滤器。 无需为每个受保护的页面指定此过滤器,我们可以将所有相关的路由归为一组,如下所示:
Route::group(array('before' => 'auth'), function() { Route::get('/user/account', function() { // User must be logged in }); Route::get('user/account/billing', function() { // User must be logged in }); });The account page callback is straightforward:
帐户页面回调很简单:
Route::get('/user/account', function() { return View::make('user/account/index'); });Now address the view app/views/user/account/index.blade.php:
现在处理视图app/views/user/account/index.blade.php :
@extends('layouts.default') @section('content') <h1>Your Account</h1> <ul> <li><a href="/user/account/edit">Edit your account information</a></li> <li><a href="/user/account/plan">Update your subscription plan</a></li> <li><a href="/user/account/billing">Update your Billing information</a></li> </ul> @stopLet’s start with the update billing information page. Of course, we don’t store people’s billing information, so like the payment page, Recurly will create and populate the form for you, POSTing the new information back to Recurly without it ever going near your web application.
让我们从更新帐单信息页面开始。 当然,我们不存储人们的帐单信息,因此像付款页面一样,Recurly会为您创建并填充表单,将新信息回发到Recurly,而不会在您的Web应用程序附近。
Route::get('user/account/billing', function() { Recurly_js::$privateKey = Config::get('recurly.private_key'); $account_code = 'user_' . Auth::user()->id; $signature = Recurly_js::sign( array('account' => array('account_code' => $account_code)) ); return View::make('user/account/billing')->with(array( 'subdomain' => Config::get('recurly.subdomain'), 'currency' => Config::get('recurly.default_currency'), 'account_code' => $account_code, 'signature' => $signature )); });This is very similar to the payment page in that we’re initialising the Recurly.js library, creating a signature (albeit using different information), and passing a few parameters to the view.
这与付款页面非常相似,因为我们正在初始化Recurly.js库,创建签名(尽管使用不同的信息),并将一些参数传递给视图。
The view app/views/user/account/billing.blade.php follows pretty much the same lines as the payment page:
视图app/views/user/account/billing.blade.php与付款页面大致相同:
@extends('layouts.default') @section('content') <div id="recurly-billing"> </div> @stop @section('scripts') <script> Recurly.config({ subdomain: '{{ $subdomain }}', currency: '{{ $currency }}' }); Recurly.buildBillingInfoUpdateForm({ target: '#recurly-billing', successURL: '/user/account/billing/confirm', accountCode: '{{ $account_code }}', signature: '{{ $signature }}' }); </script> @stopFinally, a very simple callback for when a user has submitted the billing info form:
最后,当用户提交帐单信息表单时,一个非常简单的回调:
Route::post('user/account/billing/confirm', function() { return Redirect::to('/user/account')->with('success', 'Your billing information has been updated.'); });And that’s it – users can now update their billing information!
就是这样–用户现在可以更新其帐单信息!
I haven’t implemented the edit account functionality here, but it’s pretty straightforward. I’ll leave it as an exercise for you.
我尚未在此处实现编辑帐户功能,但这非常简单。 我将其作为练习留给您。
In addition to being able to query Recurly’s API, the service can “ping” your application with a notification when one of a number of events occur. These push notifications shouldn’t be confused with the mobile variety, however – they’re completely different. Essentially the service sends a POST request to a URI you specify, and sends an XML document as the request’s body. You can use the Recurly libraries to extract the relevant information, and act accordingly. These push notifications are detailed in the Recurly documentation.
除了能够查询Recurly的API之外,该服务还可以在发生多个事件之一时通过通知“ ping”您的应用程序。 但是,不应将这些推送通知与移动设备类型混淆–它们是完全不同的。 本质上,该服务将POST请求发送到您指定的URI,并发送XML文档作为请求的主体。 您可以使用Recurly库提取相关信息,并采取相应的措施。 这些推送通知在“递归”文档中进行了详细说明。
Let’s define our callback, for when a push notification has been received.
让我们定义回调,用于何时接收到推送通知。
Route::post('recurly', function(){ $xml = file_get_contents ("php://input"); $notification = new Recurly_PushNotification($xml); switch ($notification->type) { // ... process notification } });You’ve probably realised by now that once someone has signed up and paid for the service, they remain a member indefinitely whether their subscription continues or not. So, the notification we’re most interested in for the purposes of this tutorial is the Canceled Subscription Notification. We can use the notification from Recurly to identify inactive subscriptions and revoke the corresponding roles on the account. For example:
您现在可能已经意识到,一旦有人注册并支付了服务费用,无论他们的订阅是否继续,他们都将无限期地成为成员。 因此,对于本教程而言,我们最感兴趣的通知是“取消订阅通知”。 我们可以使用来自Recurly的通知来识别无效的订阅并撤消该帐户上的相应角色。 例如:
switch ($notification->type) { case 'canceled_subscription_notification': // get the account code $account_code = $notification->account->account_code; // extract the user ID (account_code format is user_ID) $user_id = intval(substr($account_code, (strpos($account_code, '_')+1))); // find the user in question $user = User::find($user_id); // get the plan code $plan_code = $notification->subscription->plan->plan_code; // find the corresponding role... $role = Role::where('name', '=', $plan_code)->first(); // ...and revoke it DB::table('role_user')->where('user_id', '=', $user->id)->where('role_id', '=', $role)->delete(); break; // ... process notification }There are a number of other things you could do, depending on the type of notification. You could use subscription-based notifications (new, renewed, canceled, etc.) to create a subscription history, keep track of the number of members, and analyze cancellations. You could also use transactions – whether positive (payments) or negative (refunds) – to keep track of actual and expected revenue.
您还可以执行许多其他操作,具体取决于通知的类型。 您可以使用基于订阅的通知(新的,续订的,取消的等)来创建订阅历史记录,跟踪成员数量并分析取消。 您还可以使用交易(无论是正数(付款)还是负数(退款))来跟踪实际收入和预期收入。
翻译自: https://www.sitepoint.com/creating-subscription-based-website-laravel-recurly-2/
相关资源:Laravel开发-recurly-client-laravel