Создаём сайт, используя Laravel и Recurly. Часть 2

Добрый день, Хабр! После изучения первой части, я начал искать вторую, но её не оказалось, поэтому решил сам написать перевод. Итак, начнем.

Введение

В первой части данной серии мы создали голый костяк сайта с подписками на основе Laravel. Так же, мы создали сайт с ролями и создали распределение ролей для зарегистрированных пользователей. Теперь мы подошли к тому моменту, где мы можем создать и выполнить регистрацию, авторизацию и выход.
В этой части мы будем интегрировать Recurly для настройки наших платных планов членства.

Приступаем к работе


Есть две библиотеки Recurly — клиентская библиотека PHP, которую мы ранее установили с помощью composer (в первой части) и Recurly JS. Recurly JS — это клиентская библиотека для динамической интеграции формы, которая безопасно обрабатывает информацию с карт.
Информация карт размещается на серверах Recurly, а не в нашем веб-приложении, что уменьшает нашу головную боль.

Скачаем библиотеку Recurly JS и скопируем recurly.min.js из папки сборки в папку public/js/libs и добавим его в макете перед закрывающим тегом:
<script src="/js/libs/recurly.min.js"></script>
</body>

Мы также должны использовать CSS-стили, которые используются для отображения формы оплаты. Создайте каталог в директории css/recurly и скопируйте в него директорию themes, а затем мы будем ссылаться на него в соответствующем разделе нашего макета
<link href="/css/recurly/themes/default/recurly.css" rel="stylesheet">

Когда вы первый раз залогинитесь после создания учетной записи с Recurly, вам предложат создать свои планы подписок. В первой части мы создали три разных уровня подписок — бронза, серебро и золото. Вы всегда можете изменить их или добавить новые, но нет никакого вреда в том, что мы сделаем это сейчас.

Создайте план под названием Бронзовый; Будьте внимательны, нужно что бы план кода был установлен “bronze” (в нижнем регистре). Установить цену — я написал £ 4,99 в месяц, но вы можете выбрать любую сумму и / или сроки, которые вам нравится. При необходимости вы можете установить одноразовую плату или бесплатный ознакомительный период.

Повторите процесс еще два раза для настройки серебряного (код: «silver») и золотого (код: «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'
);

Теперь перейдем к всплывающим уведомлениям на левой стороне. Нажмите кнопку настроить. Введите URL адрес вашего приложения включая /recurly, например www.example.com/recurly. Оставьте поля HTTP авторизация Имя пользователя (Username) и HTTP авторизация пароля (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

Конечно, если вы хотите обеспечить своевременное обновление цен, то нужно использовать Recurly API и заполнять эти данные в шаблоне автоматически.
Теперь добавьте следующие строки в 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 обратного вызова(callback). Вместо перенаправления на домашнюю страницу, мы будем перемещать пользователя на страницу оплаты. Замените эти строки:
return Redirect::to('/')->with(
    'success',
    'Welcome to the site, . Auth::user()->name . '!'
);

на эти:
Session::put('register_user', $user);
return Redirect::to('/user/register/payment');

Нам необходимо расширить макет по умолчанию так, что мы сможем вставлять JavaScript код в футер (низ). Добавьте следующую строку после последнего тега сценария:
@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, используя несколько частей информации, в их числе идентификатор пользователя, который мы создаем связывая класс (пользователя) и его ID
  • Эта подпись передается на ряду с другой информацией в данном представлении

Страница оплаты состоит из двух частей, первая часть — простой 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: '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

Здесь происходит волшебство — Recurly строит форму оплаты и выводит его в div c id #recurly-subscribe, которая требует часть информации, мы пришли к виду, который требует генерируемые подписи.
Затем, обратный вызов, который Recurly возвращает после успешной отправки формы, которая определяется в 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(), что бы взглянуть на данные.

Для начала, давайте получим код плана, что бы узнать на что подписался пользователь:
$plan_code = $result->plan->plan_code;

Теперь, нужно найти соответствующую роль, заметьте — мы дали им имена “bronze”, “silver” и “gold”.
$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:
@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 будет создать и заполнять форму для вас, объявляя (POSTing) новую информацию к 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.');
});

И вот оно! пользователи могу обновить свою платежную информацию!
Я не реализовал редактирование учетной записи. Это довольно просто, и пусть это останется упражнением для вас.

Push-уведомления



В дополнение к возможностям для запроса API Recurly, сервис «ping» приложение с уведомлением, когда происходит какое-либо событие.
Эти уведомления не следует путать с мобильного разнообразием, однако — они совершенно разные.
По существу служба отправляет POST-запрос к URI, и посылает XML документ как тело запроса. Вы можете использовать библиотеки Recurly, что бы извлечь необходимую информацию, и действовать соответственно. Эти уведомления подробно описаны в документации Recurly.

Давайте определим нашу функцию обратного вызова, когда было получено уведомление:
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
}


Есть целый ряд других вещей, которые вы могли бы сделать, в зависимости от типа уведомления.
Вы можете использовать уведомления новый, обновленный, отменен, и т.д., чтобы создать историю подписки, отслеживать количество членов, и анализировать отказы. Вы также можете использовать уведомления о транзакции — будь то положительные (платежи) или отрицательные (возвраты) — отслеживать фактический и ожидаемый доход.

На этом оригинальная статья заканчивается. Спасибо всем, кто прочитал мою статью. Местами, я не понимал, что хочет сказать автор, вы поймете это при прочтении. Если вам будет интересен результат, то я выложу на гитхаб то, что получилось у меня (Сейчас, к сожалению не могу выложить — нет времени доделать практику по второй части). Спасибо за уделенное время!
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 4
  • +3
    «Creating a Subscription-Based Website» => «Создаём сайт»

    Местами, я не понимал, что хочет сказать автор, вы поймете это при прочтении.

    Мы поняли, что вы местами не поняли :)
    • 0
      Я взял название первого поста что бы не нарушить цепочку :) Прошу прощения за невнимательность, даже не посмотрел название оригинальной статьи)
    • 0
      Жаль, что у Recurly нет поддержки России.
      • 0
        Почему урлы вы хардкодите в шаблоне?

        <li><a href="/user/account/edit">Edit your account information</a></li>

        разве в ларавел нельзя сделать что-то вида url('action-name')?

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.