📜 ⬆️ ⬇️

We create a website using Laravel and Recurly. Part 2

Good afternoon, Habr! After studying the first part , I started looking for the second, but it was not there, so I decided to write the translation myself. So, let's begin.

Introduction

In the first part of this series, we created the bare backbone of the site with subscriptions based on Laravel. We also created a website with roles and created a distribution of roles for registered users. Now we come to the point where we can create and execute registration, authorization and exit.
In this part, we will integrate Recurly to customize our paid membership plans.

Getting Started


There are two Recurly libraries - the PHP client library, which we previously installed using composer (in the first part) and Recurly JS. Recurly JS is a client library for dynamic form integration that safely processes information from maps.
The card information is placed on Recurly servers, not in our web application, which reduces our headache.

Download the Recurly JS library and copy the recurly.min.js from the build folder to the public / js / libs folder and add it in the layout before the closing tag:
<script src="/js/libs/recurly.min.js"></script> </body> 

We also need to use the CSS styles that are used to display the form of payment. Create a directory in the css / recurly directory and copy the themes directory into it, and then we will link to it in the appropriate section of our layout.
 <link href="/css/recurly/themes/default/recurly.css" rel="stylesheet"> 

When you log in for the first time after creating an account with Recurly , you will be prompted to create your subscription plans. In the first part, we created three different subscription levels - bronze, silver and gold. You can always change them or add new ones, but there is no harm in doing it now.
')
Create a plan called Bronze ; Be careful, you need to set the code plan “bronze” (in lower case). Set a price - I wrote £ 4.99 per month, but you can choose any amount and / or time you like. If necessary, you can set a one-time fee or a free trial period.

Repeat the process two more times to tune the silver (code: “silver”) and gold (code: “gold”) plans. I set the minimum at £ 9.99 and £ 14.99 per month, respectively, the maximum.

Now go to the admin panel of Recurly, where there are a few things that we need to configure, as well as find the necessary information to configure your application. Click API access on the left side. Click Enable API Access. Write down your API key, and your subdomain.

Now go to Recurly.js on the left side (under Developer). Click Enable Recurly.js and Transparent Post API. Write down your secret key.

Now create the Recurly configuration file in the app / config / recurly.php folder , replacing the values ​​with the ones you just wrote together with the three-digit code that displays your default currency (for example, USD, GBP, AUD):
 <?php return array( 'api_key' => 'YOUR-API-KEY', 'private_key' => 'YOUR-PRIVATE-KEY', 'subdomain' => 'YOUR-SUBDOMAIN', 'default_currency' => 'XYZ' ); 

We now turn to pop-up notifications on the left side. Click the configure button. Enter the URL of your application including / recurly , for example www.example.com/recurly . Leave the HTTP authorization username (Username) and HTTP password authorization (Password) fields empty for now.

Registration page


Now, let's create a registration page that allows potential customers to choose a plan for themselves. Create the signup.blade.php file with this app app / views / home with the following contents:
 @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> @stop 

Of course, if you want to provide timely price updates, then you need to use the Recurly API and fill this data in the template automatically.
Now add the following lines to 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; } 

Finally, create a route in app / routes.php
 Route::get('/signup', function() { return View::make('home/signup'); }); 


Payment acceptance



So we got to the point of payment, namely the registration page - payment.
First, let's change the callback user / register POST. Instead of redirecting to the home page, we will move the user to the payment page. Replace these lines:
 return Redirect::to('/')->with( 'success', 'Welcome to the site, . Auth::user()->name . '!' ); 

on those:
 Session::put('register_user', $user); return Redirect::to('/user/register/payment'); 

We need to expand the default layout so that we can insert JavaScript code into the footer (bottom). Add the following line after the last script tag:
 @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:

The payment page consists of two parts, the first part is simple HTML:
 @extends('layouts.default') @section('content') <div id="recurly-subscribe"> </div> @stop 

Recurly.js injects its client-generated payment form code into this div.

Next, we add JavaScript to the section that receives the output at the bottom of the layout template:
 @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> @stop 

The magic happens here - Recurly builds a form of payment and displays it in a div c id # recurly-subscribe , which requires some of the information, we have come to a form that requires generated signatures.
Then, the callback that Recurly returns after successfully submitting the form, which is defined in successURL with the following parameters:
 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 initialize Recurly.js with the secret key from the configuration, and use it to retrieve the object represented by the token that Recurly sends as the POST variable (recurly_token). This will be an instance of Recurly_Subscription from which various information can be extracted. I display this with var_dump () to look at the data.

First of all, let's get the plan code to find out what the user has subscribed to:
 $plan_code = $result->plan->plan_code; 

Now, you need to find the appropriate role, notice - we gave them the names “bronze”, “silver” and “gold”.
 $role = Role::where('name', '=', $plan_code)->first(); 

Then get the user from the session (and then remove him from the session):
 $user = Session::get('register_user'); Session::forget('register_user'); 

Next, we provide the role for the appropriate new user:
 $user->roles()->attach($role); 

Then, we must remove the deferred 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(); 

Now, we have completed the registration process by accepting payments, creating subscriptions, and also applying roles for new accounts based on the plan chosen by the user.

Account Management Page


Let's create a simple page from which users can manage their account.
Obviously, this will require the user to log in, so we must apply an authentication filter.
Instead of specifying this filter for each protected page, we can put all the relevant routes into the group, like this:
 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 }); }); 

Calling an account page is simple:
 Route::get('/user/account', function() { return View::make('user/account/index'); }); 

Now the address of the form 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> @stop 

Let's start with the information update billing page. Of course, we don’t store people’s billing information, since on the payment page, Recurly will create and fill out a form for you, announcing (POSTing) new information to Recurly without it sometime near your web application.
 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 initialize the Recurly.js libraries, creating signatures (albeit using different information) and passing through several parameters into the view.
Path app / views / user / account / billing.blade.php
follows pretty much the same pattern on the payment page:
 @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> @stop 

Finally, a very simple callback when the user has filled in payment information:
 Route::post('user/account/billing/confirm', function() { return Redirect::to('/user/account')->with('success', 'Your billing information has been updated.'); }); 

And here it is! Users can update their billing information!
I have not implemented account editing. It's pretty simple, and let it be an exercise for you.

Push notifications



In addition to the capabilities for requesting the Recurly API, a “ping” service is an application with a notification when an event occurs.
These notices should not be confused with mobile variety, however - they are completely different.
Essentially, the service sends a POST request to the URI, and sends the XML document as the request body. You can use Recurly libraries to extract the necessary information and act accordingly. These notifications are described in detail in the Recurly documentation.

Let's define our callback function when the notification was received:
 Route::post('recurly', function(){ $xml = file_get_contents ("php://input"); $notification = new Recurly_PushNotification($xml); switch ($notification->type) { // ... process notification } }); 

You, probably, understood that as soon as someone subscribed and paid for the service, he remains an indefinite subscriber, and it is not clear whether their subscription continues or not. So, the most interesting is the notice of cancellation of the subscription. We can use the notification from Recurly to identify inactive subscriptions and revoke the corresponding roles. For example:
 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 can use new, updated, canceled, etc. notifications to create a subscription history, track the number of members, and analyze rejections. You can also use transaction notifications — whether positive (payments) or negative (returns) —to track actual and expected income.

This is the original article ends. Thanks to everyone who read my article. In some places, I did not understand what the author wants to say, you will understand this when reading. If you are interested in the result, then I will post on the githabe what I got (Now, unfortunately, I cannot lay out - there is no time to finish the practice in the second part). Thank you for your time!

Source: https://habr.com/ru/post/221815/


All Articles