Статьи

Членство в Laravel Cashier

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

Настройка рабочей среды

Нам нужно создать проект Boilreplate, чтобы начать, и мы можем сделать это двумя различными способами:

  • мы можем клонировать репозиторий Github в папку нашего проекта.
  • Предполагая, что у вас установлен composer , (см. Руководство по установке для получения более подробной информации).
    мы запускаем composer create-project laravel/laravel laravel_membership --prefer-dist , это создаст шаблонный проект laravel_membership в нашей папке laravel_membership .

Теперь нам нужно запросить пакет Laravel Cashier "laravel/cashier": "~1.0" для проекта, добавив "laravel/cashier": "~1.0" в наш composer.json в разделе require и запустите composer update для обновления наших зависимостей.

После этого нам нужно сообщить нашему приложению о загрузке класса провайдера услуг Cashier . Мы можем сделать это, добавив "Laravel\Cashier\CashierServiceProvider" в массив providers в файле config/app.php .

Примечание : вы должны запустить composer dump-autoload classMap package для обновления classMap package .

Создание базы данных с использованием миграций

Если вы новичок в Laravel Migrations , обязательно ознакомьтесь с документацией .
Мы собираемся использовать две таблицы:

таблица posts :
INT id
STRING title
LONG_TEXT content
BOOL is_premium

таблица users :
INT id
VARCHAR(60) email
VARCHAR(60) password

Инструмент командной строки Laravel artisan позволяет легко создавать и управлять классами миграции.

 php artisan migrate : make create_posts_table -- create = "posts" php artisan migrate : make create_users_table -- create = "users" 

а затем мы заполняем аргумент функции обратного вызова Schema::create необходимым кодом, который выглядит следующим образом

 Schema :: create ( 'posts' ,   function ( Blueprint  $table ) 
 { $table -> increments ( 'id' ); $table -> string ( 'title' ); $table -> longText ( 'content' ); $table -> boolean ( "is_premium" ); $table -> timestamps (); 
 }); 

 Schema :: create ( 'users' ,   function ( Blueprint  $table ) 
 { $table -> increments ( 'id' ); $table -> string ( 'email' ,   100 )-> unique (); $table -> string ( 'password' ,   60 ); $table -> timestamps (); 
 }); 

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

 php artisan cashier : table users 

Теперь мы готовы перенести нашу базу данных

 php artisan migrate 

если вы откроете таблицу users то увидите, что при переносе пакета будет добавлено несколько полей.
stripe_active если у вас есть активная подписка.
stripe_id пользователя stripe_id на сервере Stripe.
stripe_plan план подписки Stripe.
— последние четыре цифры кредитной карты last_four .
trial_ends_at дата окончания сохраняется, если вы указываете пробный период.
subscription_ends_at дата окончания подписки.

Теперь мы заполним базу данных некоторыми фиктивными данными, чтобы начать; проверьте окончательный результат на GitHub .

Процесс биллинга

Работа с платежами может быть проблемой в шее, и Stripe может помочь вам в этом, они используют tokens вместо номеров карт и т. Д., И таким образом вы можете быть уверены, что ваши клиенты будут в безопасности, оплачивая ваши услуги.

ПРИМЕЧАНИЕ. Проверьте, поддерживается ли Stripe в вашей стране, но вы все равно можете использовать его для тестирования, если нет.

Для начала нам нужно сначала получить аккаунт. Stripe не имеет ежемесячной платы за подписку, вы платите только тогда, когда вам платят.

Теперь, после получения учетной записи, вам нужно создать Планы для вашего приложения (Ежемесячно, Ежегодно, Серебро, Золото …).

new_plan.png

Каждое поле не требует пояснений, поэтому давайте создадим Gold членство, которое будет стоить 40 долларов, и базовое членство за 10 долларов. Они будут выставляться счета каждый месяц.

Мы уже добавили необходимые столбцы в нашу таблицу users , теперь нам нужно сообщить Laravel Cashier , что мы будем использовать класс User качестве нашего класса биллинга.

 use   Laravel \Cashier\BillableInterface ; 
 use   Laravel \Cashier\BillableTrait ; 

 class   User   extends   Eloquent   implements   BillableInterface   { 

     use   BillableTrait ; 
     protected  $dates =   [ 'trial_ends_at' ,   'subscription_ends_at' ]; 

Примечание: мы используем BillableTrait и для его использования требуется PHP 5.4 или выше.

Теперь нам нужно установить наш ключ доступа Stripe API, который вы можете получить из Your account > Account settings > API Keys и скопировать свой Test Secret Key .

Settings API Keys

Используя BillableTrait мы получаем доступ к User::setStripeKey(key) который можно вызывать в любом месте нашего кода, но предпочтительным способом является создание файла services.php в каталоге config и возврат массива, подобного следующему:

 return   [ 
     'stripe'   =>   [ 
             'secret'      =>   'Your key' 
         ] 
 ]; 

Когда getStripeKey пытается загрузить ваш ключ, он будет искать свойство с именем stripeKey . Если он не найден, он автоматически загрузит ваш services файл.

Создание наших страниц

Для простоты мы создадим всего несколько страниц:
— Регистрация: где пользователь может зарегистрироваться с помощью плана членства (Basic, Gold).
— Логин: страница входа членов.
— Обновление: обновление с базового до золотого членства.
— Опубликовать: отображать одну страницу сообщения.

Для ускорения процесса мы будем использовать bootsnipp . Вы можете получить окончательный код из репозитория GitHub.

Страница авторизации:

login page

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

 public   function  index (){ 
     return   View :: make ( 'login' ); 
 } 
 public   function  store (){ 
     if (   Auth :: attempt (   Input :: only (   [ 'email' ,   'password' ]   ),   true )){ 
         return   Redirect :: to ( '/' ); 
     } 
     else { 
         return   Redirect :: back ()-> withInput ()-> with (   'message' ,   'Email or password incorrect'   ); 
     } 
 } 
 public   function  destroy (){ 
     Auth :: logout (); 
     return   Redirect :: route ( "login" ); 
 } 

Страница регистрации:

signup page

На странице регистрации есть поле Subscription plan используемое для назначения пользователю плана.
У нас также есть номер Credit card number , Expiration date , CVC .

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

Давайте посмотрим, как выглядит интерфейсный код:

 <script   type = "text/javascript"   src = "https://js.stripe.com/v2/" ></script> 
 <script> 
     Stripe . setPublishableKey ( 'Your public key' ); jQuery ( function ( $ )   { $ ( '#subscription-form' ). submit ( function ( event )   { 
             var  $form =  $ ( this ); $form . find ( 'button' ). prop ( 'disabled' ,   true ); 
             Stripe . card . createToken ( $form ,  stripeResponseHandler ); 

             return   false ; 
         }); 
     }); 

     var  stripeResponseHandler =   function ( status ,  response )   { 
         var  $form =  $ ( '#subscription-form' ); 

         if   ( response . error )   { $form . find ( '.payment-errors' ). text ( response . error . message ); $form . find ( 'button' ). prop ( 'disabled' ,   false ); 
         }   else   { 
             var  token =  response . id ; $form . append ( $ ( '<input type="hidden" name="stripeToken" />' ). val ( token )); $form . get ( 0 ). submit (); 
         } 
     }; 
 </script> 

Сначала мы включаем файл JavaScript API, затем устанавливаем наш открытый ключ, который мы взяли из настроек панели Stripe.

Затем мы присоединяем функцию обратного вызова к нашей форме отправки (убедитесь, что ваш идентификатор формы совпадает с идентификатором, используемым в обработчике событий), чтобы предотвратить двойную отправку, мы отключаем нашу кнопку отправки.
Stripe.card.createToken принимает два аргумента, первый — это объект JSON, имеющий некоторые обязательные и дополнительные значения.

Обязательные значения:

  • number : number карты в виде строки без разделителей.
  • exp_month : двухзначное число, обозначающее месяц истечения срока действия карты.
  • exp_year : двух или четырехзначное число, обозначающее год истечения срока действия карты.

Необязательные значения:

  • cvc : код безопасности карты в виде строки, номер cvc не обязателен, но рекомендуется для предотвращения мошенничества.
  • name : имя владельца карты.
  • address_line1 : адресная строка для выставления счетов 1.
  • address_line2 : адресная строка для выставления счетов 2.
  • address_city : город для выставления счетов.
  • address_state : состояние адреса выставления счета.
  • address_zip : почтовый индекс в виде строки.
  • address_country : страна для выставления счетов.
    Вы можете заметить, что мы передаем объект формы вместо объекта JSON, вы можете выбрать захват значений вручную или использовать атрибут data-stripe html5 на своих входах, и Stripe будет использовать некоторые вспомогательные методы для автоматического получения этих значений для вас. , Пример:
      <input   data-stripe = "number"   type = "text" > 

Второй аргумент, передаваемый методу Stripe.card.createToken является функцией обратного вызова для обработки ответа.

В случае сбоя stripeResponseHandler попытается найти элемент с классом payment_errors чтобы отобразить некоторые описательные ошибки для пользователя.
В случае успеха скрытый ввод stripeToken будет добавлен в форму и будет доступен после отправки.

Дополнительные опции

  • Trial periods : как мы указывали ранее, при создании нового плана у вас есть выбор указать пробный период для пользователей, чтобы протестировать ваш продукт, и с них не будет взиматься плата до истечения указанного периода.
  • Coupons : вы создаете купоны через меню панели инструментов, где вы можете указать фиксированную сумму или в процентах, с некоторыми другими полезными опциями.

Теперь давайте перейдем к нашему SignupController чтобы посмотреть, как мы будем это SignupController .

 public   function  store (){ $user =   new   User ; $user -> email =   Input :: get (   'email'   ); $user -> username =   Input :: get (   'username'   ); $user -> password =   Hash :: make (   Input :: get (   'password'   )   ); $user -> save (); $user -> subscription ( Input :: get (   'subscription'   ))-> create (   Input :: get (   'stripeToken'   )   ); 

     return   'you are now registred' ; 
 } 

Мы пропустим процесс проверки, чтобы все было просто.

После создания нового User и его сохранения у нас теперь есть возможность подписать пользователя на новый тарифный план. Метод subscription принимает в качестве аргумента уже зарегистрированный план, который может быть PlanInterface или String и возвращает StripeGateway .
Метод create принимает токен в качестве параметра; мы передаем новое скрытое входное значение с именем stripeToken .

Обновить страницу:

upgrade page

Страница обновления будет UpgradeController в UpgradeController который выглядит следующим образом:

 public   function  store (){ 
     if (   ! Auth :: check ()   ) 
         return   Redirect :: route ( "login" ); 

     Auth :: user ()-> subscription ( 'gold' )-> swap (); 

     return   'You are now a GOLD member' ; 
 } 

Сначала мы проверяем, вошел ли пользователь в систему, затем создаем новую subscription с новым планом и вызываем метод swap , очевидно, в реальном проекте у вас будут некоторые корректировки платы и опция понижения, но она должна работать так же ,

Страница поста:

Post page

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

 public   function  show (  $id ){ $post =   Post :: find (  $id ); 

     if (  $post -> is_premium &&   Auth :: user ()-> stripe_plan !=   'gold'   ) 
         return   View :: make ( 'error' ,   [   'message'   =>   'Only GOLD members can read this post, <a href="/upgrade">upgrade</a> your membership to get access'   ]   ); 

     return   View :: make ( 'post' ,   [   'post'   =>  $post ]   ); 
 } //show 

Конечно, в нашем файле routes.php нам нужно добавить фильтр routes.php чтобы предотвратить доступ к странице неаутентифицированным пользователям.
Наш файл маршрутов будет выглядеть так:

 Route :: get ( '/' ,   function () 
 { $posts =   Post :: all (); 

     return   View :: make ( 'index' ,   [   'posts'   =>  $posts ]); 
 })-> before ( 'auth' ); 


 Route :: get ( '/post/{id}' ,   [   'as'   =>   'post' ,   'uses'   =>   'PostsController@show'   ])-> before ( 'auth' ); 
 Route :: resource ( 'login' ,   'LoginController' ,   [   'only'   =>   [   'index' ,   'store' ,   'destroy'   ]   ]); 
 Route :: resource ( 'signup' ,   'SignupController' ,   [   'only'   =>   [   'index' ,   'store'   ]   ]); 
 Route :: resource ( 'upgrade' ,   'UpgradeController' ,   [   'only'   =>   [   'index' ,   'store'   ]   ]); 

Другие полезные методы

  • withCoupon : мы говорили ранее, что у нас есть возможность создавать купоны на скидку, в нашем примере мы можем сделать это так:
 $user -> subscription ( Input :: get (   'subscription'   ))-> withCoupon ( 'coupon code' )-> create (   Input :: get (   'stripeToken'   )   ); 
  • cancel : вы можете легко отменить подписку, используя этот метод, но вы должны проверить, является ли пользователь onGracePeriod чтобы убедиться, что вы не заблокируете его немедленно:
 User :: find ( 1 )-> onGracePeriod (); 
  • onPlan : посмотреть, есть ли у пользователя определенный план.
  • onTrial : посмотреть, находится ли пользователь еще на испытательном сроке.
  • canceled : если пользователь отменил свою подписку.
  • getLastFourCardDigits : получить последние четыре цифры пользовательской карты.
  • getSubscriptionEndDate : получить дату окончания подписки.
  • getTrialEndDate : получить дату окончания пробной версии.
  • invoices : получить список пользовательских счетов.
  • findInvoice : найти счет по идентификатору.
  • downloadInvoice : создать загружаемый счет по идентификатору.

Вывод

В этом руководстве мы рассмотрели, как Laravel Cashier может упростить процесс выставления счетов и упростить управление вашими клиентами.

Мы, конечно, не все здесь описали, но для вас стоит начать копаться в исходном коде, чтобы узнать, что еще можно сделать. Если вы хотите посмотреть демоверсию этого приложения на Nitrous, смотрите здесь .