В первой части этой серии мы создали голые сайты подписного веб-сайта с использованием Laravel. Мы также создали сайт с ролями и предоставили ожидающую роль новым пользователям. Сейчас мы находимся в точке, где мы можем выполнить базовую регистрацию, вход в систему и выход из системы. Во второй части мы интегрируем Recurly для настройки наших платных планов членства.
Начиная
Есть две библиотеки Recurly — клиентская библиотека PHP, которую мы установили через Composer в начале первой части серии — и Recurly JS. Recurly JS — это библиотека на стороне клиента для динамической интеграции форм, которые надежно обрабатывают информацию о карте. Эта информация о карте отправляется на серверы Recurly, а не в наше веб-приложение, что значительно уменьшает проблемы с соблюдением требований
Загрузите библиотеку Recurly JS и скопируйте recurly.min.js из папки build
в public/js/libs
и добавьте ее в свой макет перед закрытием
тег:
<script src="/js/libs/recurly.min.js"></script> </body>
Нам также понадобятся стили CSS, которые используются при отображении формы оплаты. Создайте каталог css/recurly
и скопируйте в него каталог тем, а затем обратитесь к нему в соответствующем разделе макета:
<link href="/css/recurly/themes/default/recurly.css" rel="stylesheet">
Когда вы впервые входите в систему после создания учетной записи в Recurly , вам предоставляется возможность создавать свои планы подписки. В первой части мы создали три уровня: бронзовый, серебряный и золотой. Вы всегда можете изменить их или добавить больше позже, но сейчас делать это не вредно.
Создайте план под названием Бронза; обеспечение того, чтобы код плана был «бронзовым» (все строчные). Установите цену — я установил ее на уровне 4,99 фунтов стерлингов в месяц, но вы можете выбрать любую сумму и / или период, который вам нравится. При желании вы можете установить разовую плату за установку или даже установить бесплатный пробный период.
Повторите процедуру еще два раза, настроив планы Silver (код плана: «silver») и Gold (код плана: «gold») — я установил для себя £ 9,99 и £ 14,99 в месяц соответственно.
Теперь перейдите в панель администратора Recurly, где нужно настроить несколько вещей, а также найти соответствующую информацию для конфигурации вашего приложения. Нажмите API доступ с левой стороны. Нажмите Включить доступ к API. Запишите ваш ключ API и ваш поддомен.
Теперь перейдите к Recurly.js с левой стороны (под Developer). Нажмите Включить Recurly.js и Transparent Post API. Запишите ваш личный ключ.
Теперь создайте файл конфигурации Recurly в app/config/recurly.php
, заменив значения на те, которые вы только что отметили, вместе с трехзначным кодом, который представляет выбранную вами валюту по умолчанию (например, USD, GBP, AUD):
<?php return array( 'api_key' => 'YOUR-API-KEY', 'private_key' => 'YOUR-PRIVATE-KEY', 'subdomain' => 'YOUR-SUBDOMAIN', 'default_currency' => 'XYZ' );
Теперь перейдите к Push-уведомлениям с левой стороны и нажмите Configure. Введите URL-адрес вашего приложения, добавив / recurly к нему, например: http://www.example.com/recurly . Оставьте пока пустыми поля HTTP Auth Username и HTTP Auth Password.
Страница регистрации
Теперь давайте создадим страницу регистрации, которая позволит потенциальному клиенту выбрать свой план. Создайте 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> @stop
Конечно, если вы хотите, чтобы цены всегда были актуальными, вы можете получить детали плана через API Recurly и динамически заполнить их в шаблоне.
Теперь добавьте следующее в 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; }
И, наконец, создайте маршрут в app/routes.php
:
Route::get('/signup', function() { return View::make('home/signup'); });
Прием платежей
Теперь о следующем этапе регистрации страницы — прием платежей. Во-первых, давайте изменим обратный вызов user/register
POST; вместо перенаправления на домашнюю страницу мы поместим пользователя в сеанс и перенаправим на страницу оплаты. Измените следующее:
return Redirect::to('/')->with( 'success', 'Welcome to the site, . Auth::user()->name . '!' );
чтобы:
Session::put('register_user', $user); return Redirect::to('/user/register/payment');
Нам нужно расширить макет по умолчанию, чтобы встраивать код JavaScript в нижний колонтитул. Добавьте следующую строку после последнего тега script:
@yield('scripts')
Теперь создайте новый маршрут:
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 )); });
Несколько замечаний по этому коду:
- Нам нужно установить закрытый ключ
Recurly_js
классаRecurly_js
, который мы возьмем из файла конфигурации, который мы создали ранее. - План должен быть перенесен с предыдущего этапа в процессе регистрации; Я оставил эту часть реализации в качестве пользовательского упражнения.
- Нам нужно сгенерировать подпись для Recurly.js, используя несколько частей информации. Среди них есть идентификатор для рассматриваемого пользователя, который мы создаем путем объединения класса (пользователя) и идентификатора пользователя.
- Эта подпись передается вместе с другой необходимой информацией для просмотра.
Просмотр страницы оплаты состоит из двух частей. Во-первых, HTML, который очень прост:
@extends('layouts.default') @section('content') <div id="recurly-subscribe"> </div> @stop
Recurly.js вставит свою сгенерированную на стороне клиента форму оплаты в этот div.
Затем мы добавляем 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: '[email protected]', 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
Вот где происходит волшебство — Recurly создает платежную форму и внедряет ее в div с идентификатором #recurly-subscribe
, что требует некоторой информации, которую мы передали представлению, вместе с сгенерированной сервером подписью.
Далее, обратный вызов, к которому Recurly POST возвращается после успешной отправки формы, которая определена в параметре 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); });
Опять же, мы инициализируем Recurly.js с закрытым ключом из конфигурации и используем его для извлечения объекта, представленного токеном, который Recurly отправляет как переменную POST ( recurly_token
). Это будет экземпляр Recurly_Subscription
, из которого мы можем извлечь различную информацию. Я var_dump()
его с помощью var_dump()
чтобы вы могли на него взглянуть.
Давайте сначала получим код плана, чтобы мы знали, на какой тарифный план подписан пользователь:
$plan_code = $result->plan->plan_code;
Теперь найдите соответствующую роль; Обратите внимание, что мы только что дали им одно и то же имя (например, «бронза», «серебро» и «золото»).
$role = Role::where('name', '=', $plan_code)->first();
Затем получите пользователя из сеанса (и затем удалите его из сеанса):
$user = Session::get('register_user'); Session::forget('register_user');
Затем мы предоставляем соответствующую роль новому пользователю:
$user->roles()->attach($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();
Теперь мы улучшили процесс регистрации, чтобы принимать платежи, создавать подписки и применять роли к новым учетным записям пользователей на основе выбранного плана. В следующей части мы рассмотрим дальнейшее управление учетными записями пользователей и подписками.
Страницы управления аккаунтом
Давайте создадим простую страницу, с которой пользователи смогут управлять своей учетной записью. Очевидно, что это потребует, чтобы пользователь вошел в систему, поэтому нам нужно применить фильтр аутентификации. Вместо того, чтобы указывать этот фильтр для каждой защищенной страницы, мы можем поместить все соответствующие маршруты в группу, например так:
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 }); });
Обратный вызов на странице аккаунта прост:
Route::get('/user/account', function() { return View::make('user/account/index'); });
Теперь 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> @stop
Давайте начнем со страницы обновления информации об оплате. Конечно, мы не храним информацию о счетах людей, поэтому, как и страница оплаты, Recurly создаст и заполнит форму для вас, отправив новую информацию обратно в Recurly, даже не приближаясь к вашему веб-приложению.
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 )); });
Это очень похоже на страницу оплаты, так как мы инициализируем библиотеку Recurly.js, создаем подпись (хотя и с использованием другой информации) и передаем несколько параметров в представление.
Представление 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> @stop
Наконец, очень простой обратный вызов, когда пользователь отправил форму с информацией для выставления счета:
Route::post('user/account/billing/confirm', function() { return Redirect::to('/user/account')->with('success', 'Your billing information has been updated.'); });
И это все — пользователи теперь могут обновлять свою платежную информацию!
Я не реализовал здесь функцию редактирования учетной записи, но это довольно просто. Я оставлю это как упражнение для вас.
Всплывающее уведомление
Помимо возможности запрашивать API-интерфейс Recurly, служба может «пинговать» ваше приложение с помощью уведомления, когда происходит одно из нескольких событий. Однако эти push-уведомления не следует путать с мобильным разнообразием — они совершенно разные. По сути, служба отправляет запрос POST на указанный вами URI и отправляет XML-документ в качестве тела запроса. Вы можете использовать библиотеки Recurly для извлечения соответствующей информации и действовать соответственно. Эти push-уведомления подробно описаны в документации Recurly.
Давайте определим наш обратный вызов для получения push-уведомления.
Route::post('recurly', function(){ $xml = file_get_contents ("php://input"); $notification = new Recurly_PushNotification($xml); switch ($notification->type) { // ... process notification } });
Вы, вероятно, уже поняли, что, как только кто-то зарегистрировался и оплатил услугу, он остается участником на неопределенный срок, независимо от того, продолжается его подписка или нет. Таким образом, уведомление, которое нас больше всего интересует в данном руководстве, — это уведомление об отмене подписки. Мы можем использовать уведомление от 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 }
Есть несколько других вещей, которые вы можете сделать, в зависимости от типа уведомления. Вы можете использовать уведомления на основе подписки (новые, обновленные, отмененные и т. Д.), Чтобы создавать историю подписок, отслеживать количество участников и анализировать отмены. Вы также можете использовать транзакции — положительные (платежи) или отрицательные (возвраты) — для отслеживания фактического и ожидаемого дохода.