Статьи

Создание подписного веб-сайта с Laravel и Recurly, часть 1

Планирование и обработка платежей просты, но не думайте, что повторяющиеся платежи тривиальны. Все может сложиться очень быстро. Вам необходимо решить, как обрабатывать сбои (сколько неудачных попыток оплаты кто-либо получает?), Детали счетов должны обновляться, а планы обновления и понижения могут создавать всевозможные проблемы. Тогда есть, пожалуй, самая значительная проблема с регулярными платежами — вам нужно хранить данные платежа ваших посетителей в файле. Это вводит вопросы безопасности, соответствия и правовые вопросы.

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

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

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

Настройка приложения

Начните с создания новой папки проекта и выполните следующую команду:

  композитор создание-проект laravel / laravel recurly --prefer-dist 

Это создает новый проект Laravel с использованием Composer, который загружает фреймворк и его зависимости.

Нам также понадобятся некоторые дополнительные библиотеки для дальнейшего использования, поэтому добавьте следующее в раздел require файла composer.json и запустите composer.phar update .

 "machuga/authority": "dev-develop", "machuga/authority-l4" : "dev-master", "recurly/recurly-client": "2.1.*@dev" 

Это загружает библиотеку Authority, которую мы будем использовать для пользовательских ролей и разрешений, и клиентскую библиотеку Recurly.

Затем настройте соединение с базой данных, указав соответствующее имя схемы, имя пользователя, пароль и имя хоста / адрес в app/config/database.php . Laravel работает «из коробки» с рядом баз данных, таких как MySQL, Postgres, SQLite и SQL Server.

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

  php artisan migrate: создайте create_users_table 

Это создает файл миграции в папке app/database/migrations . Файл создан с базовым контуром, но давайте уточним его с помощью нашего определения таблицы:

 public function up() { Schema::create('users', function($table) { $table->increments('id'); $table->string('email')->unique(); $table->string('name'); $table->string('password'); $table->timestamps(); }); } public function down() { Schema::drop('users'); } 

Вернитесь в командную строку и запустите:

  php ремесленник мигрировать 

Загляните в базу данных, и вы обнаружите, что Laravel создал таблицу пользователей для вас.

Настройка полномочий для ролей и разрешений

Чтобы определить тип учетной записи, которую пользователь имеет, и привилегии, которые он ему предоставляет, мы назначим пользователям роль. Мы уже скачали Authority через Composer, чтобы помочь с этим; нам просто нужно выполнить еще несколько шагов, чтобы полностью его настроить.

В app/config/app.php добавьте следующую строку в провайдеры:

 'Authority\AuthorityL4\AuthorityL4ServiceProvider', 

Добавьте следующее к псевдонимам:

 'Authority' => 'Authority\AuthorityL4\Facades\Authority', 

И опубликуйте файл конфигурации Authority:

  Конфигурация PHP Artisan: опубликовать Machuga / Authority-L4 

Мы вернемся к файлу конфигурации позже. Сейчас нам нужно создать несколько дополнительных таблиц базы данных. К счастью, пакет содержит свои собственные миграции для этого. Запустите их с помощью следующей команды:

  php artisan migrate --package = "Machuga / Authority-L4" 

Вы обнаружите, что у вас есть три дополнительные таблицы: permissions , roles и role_user .

Нам также необходимо создать модели для представления ролей и разрешений. Мы пока оставим их простыми. В app/models/Role.php :

 <?php class Role extends Eloquent { } 

И в app / models / Permission.php:

 <?php class Permission extends Eloquent { } 

Теперь нам нужно изменить класс модели User который уже был создан, чтобы связать пользователя с ролями и разрешениями. Добавьте следующие строки в app/models/User.php :

 public function roles() { return $this->belongsToMany('Role'); } public function permissions() { return $this->hasMany('Permission'); } public function hasRole($key) { foreach($this->roles as $role){ if ($role->name === $key) { return true; } } return false; } 

Теперь давайте предварительно заполним базу данных некоторыми данными. Откройте app/database/seeds/DatabaseSeeder.php и вставьте следующее:

 <?php class DatabaseSeeder extends Seeder { public function run() { Eloquent::unguard(); $this->call('UserTableSeeder'); $this->command->info('User table seeded!'); $this->call('RoleTableSeeder'); $this->command->info('Role table seeded!'); } } class UserTableSeeder extends Seeder { public function run() { DB::table('users')->delete(); User::create(array( 'email' => '[email protected]', 'name' => 'Joe Bloggs', 'password' => Hash::make('password') )); } } class RoleTableSeeder extends Seeder { public function run() { DB::table('roles')->delete(); Role::create(array('name' => 'admin')); Role::create(array('name' => 'pending')); Role::create(array('name' => 'member')); Role::create(array('name' => 'bronze')); Role::create(array('name' => 'silver')); Role::create(array('name' => 'gold')); } } 

Затем запустите базу данных, запустив:

  php artisan db: seed 

Создание макета

Теперь мы создадим общий макет страницы. Загрузите Twitter Bootstrap и поместите исходные файлы в public папку — переместите файлы js в public/js/libs .

Создайте файл app/views/layouts/default.blade.php со следующим содержимым:

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Subscription Site Tutorial</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content=""> <meta name="author" content=""> <!-- Le styles --> <link href="/css/bootstrap.css" rel="stylesheet"> <link href="/css/style.css" rel="stylesheet"> </head> <body> <div class="navbar navbar-inverse navbar-fixed-top"> <div class="navbar-inner"> <div class="container"> <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="brand" href="#">Subscription Site Tutorial</a> <div class="nav-collapse collapse"> <ul class="nav"> <li class="active"><a href="#">Home</a></li> <li><a href="#about">About</a></li> <li><a href="#contact">Contact</a></li> </ul> </div><!--/.nav-collapse --> </div> </div> </div> <div class="container"> @if(Session::has('success')) <div class="alert alert-success"> <button type="button" class="close" data-dismiss="alert">&times;</button> {{ Session::get('success') }} </div> @endif @if(Session::has('error')) <div class="alert alert-error"> <button type="button" class="close" data-dismiss="alert">&times;</button> {{ Session::get('error') }} </div> @endif @yield('content') </div> <!-- /container --> <!-- Le javascript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script> <script src="/js/libs/bootstrap.min.js"></script> </body> </html> 

Это довольно простой материал — содержимое нашей страницы выводится в строке с надписью @yield('content') .

Далее создайте файл public/css/style.css :

 body { padding-top: 50px; } p.logged-in { color: white; margin-top: 0.5em; } 

И, наконец, давайте создадим базовую домашнюю страницу для приложения. Создайте файл app/views/home/index.blade.php :

 @extends('layouts.default') @section('content') <h1>Subscription Site Tutorial</h1> @stop 

Объявление @extends говорит Laravel использовать макет по умолчанию, который мы только что создали с содержимым, которое мы @yield в область содержимого с помощью команды @yield обернутой в объявление @section .

Не забудьте соответственно изменить маршрут по умолчанию в app/routes.php :

 Route::get('/', function() { return View::make('home/index'); }); 

Создание механизма входа

У нас есть наши пользователи — и учетная запись, с которой можно начать — так что теперь нам нужна возможность войти в систему. В app/routes.php добавьте маршрут на страницу входа:

 Route::get('/auth/login', function() { return View::make('auth/login'); }); 

Теперь создайте представление app/views/auth/login.blade.php . Расширение .blade.php указывает, что мы собираемся использовать библиотеку шаблонов Blade, которая поставляется с Laravel, которая чище, чем прямой PHP. За кулисами эти файлы шаблонов компилируются в PHP по мере необходимости.

 @extends('layouts.default') @section('content') <h1>Please Log in</h1> {{ Form::open(array('url' => 'auth/login')) }} {{ Form::label('email', 'E-Mail Address') }} {{ Form::text('email') }} {{ Form::label('password', 'Password') }} {{ Form::password('password') }} <div class="form-actions"> {{ Form::submit('Login', array('class' => 'btn btn-primary')) }} </div> {{ Form::close() }} <p>Not a member? <a href="/user/register">Register here</a>.</p> @stop 

Если вы в браузере перейдете к / auth / login, вы должны увидеть простую форму входа.

Чтобы обработать логин, нам нужно построить POST-маршрут. Аутентификация в Laravel очень проста; мы просто делаем это:

 Route::post('/auth/login', function() { $email = Input::get('email'); $password = Input::get('password'); if (Auth::attempt(array('email' => $email, 'password' => $password))) { return Redirect::to('/')->with('success', 'You have been logged in'); } else { return Redirect::to('auth/login')->with('error', 'Login Failed'); } return View::make('auth/login'); }); 

Вся магия происходит в Auth::attempt() ; если вход выполнен успешно, создается сеанс, и заполненный экземпляр объекта User доступен через статический метод Auth::user() .

Метод выхода из системы одинаково прост:

 Route::get('/auth/logout', function() { Auth::logout(); return Redirect::to('/')->with('success', 'You have successfully logged out'); }); из Route::get('/auth/logout', function() { Auth::logout(); return Redirect::to('/')->with('success', 'You have successfully logged out'); }); 

Базовая регистрация

Наша последняя задача в этой части — настроить базовый процесс регистрации. Создайте маршрут регистрации в app/routes.php :

 Route::get('/user/register', function() { return View::make('user/register/index'); }); 

Теперь создайте представление app/views/user/register/index.blade.php :

 @extends('layouts.default') @section('content') {{ Form::open(array('url' => 'user/register')) }} {{ Form::label('email', 'E-Mail Address') }} {{ Form::text('email') }} {{ $errors->first('email') }} {{ Form::label('name', 'Your name') }} {{ Form::text('name') }} {{ $errors->first('name') }} {{ Form::label('password', 'Password') }} {{ Form::password('password') }} {{ $errors->first('password') }} {{ Form::label('password_confirmation', 'Repeat') }} {{ Form::password('password_confirmation') }} <div class="form-actions"> {{ Form::submit('Register', array('class' => 'btn btn-primary')) }} </div> {{ Form::close() }} @stop 

Здесь следует отметить пару вещей:

  • Объект $errors автоматически передается представлению, независимо от того, есть ли какие-либо ошибки в представлении. Если с полем все в порядке, оно просто ничего не напечатает.
  • _confirmation к имени поля, _confirmation проверить два поля вместе; т.е. подтвердите, что пользователь повторно ввел выбранный пароль правильно.

Теперь давайте реализуем действие POST:

 Route::post('/user/register', function() { $validator = Validator::make( Input::all(), array( 'name' => array('required', 'min:5'), 'email' => array('required', 'email', 'unique:users'), 'password' => array('required', 'confirmed') ) ); if ($validator->passes()) { $user = new User(); $user->name = Input::get('name'); $user->email = Input::get('email'); $user->password = Hash::make(Input::get('password')); $user->save(); $role_pending = Role::where('name', '=', 'pending')->first(); $user->roles()->attach($role_pending); Auth::login($user); return Redirect::to('/')->with( 'success', 'Welcome to the site, . Auth::user()->name . '!' ); } else { return Redirect::to('user/register')->with( 'error', 'Please correct the following errors:' )->withErrors($validator); } }); 

Это все довольно простые вещи — мы создаем валидатор, передавая ему POST-переменные с помощью Input::all() и набора правил валидации. Если проверка пройдена, мы создаем нового пользователя, назначаем его ожидающей роли, регистрируем его, а затем перенаправляем на первую страницу.

Если проверка не пройдена, мы перенаправляем обратно в форму, создаем флэш-сообщение об ошибке и передаем сообщения об ошибках из валидатора — они затем будут доступны в представлении в переменной $errors .

Вывод

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