В этом уроке мы увидим, как легко создать базовый платный веб-сайт для членов с помощью пакета 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 не имеет ежемесячной платы за подписку, вы платите только тогда, когда вам платят.
Теперь, после получения учетной записи, вам нужно создать Планы для вашего приложения (Ежемесячно, Ежегодно, Серебро, Золото …).
Каждое поле не требует пояснений, поэтому давайте создадим 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
.
Используя BillableTrait
мы получаем доступ к User::setStripeKey(key)
который можно вызывать в любом месте нашего кода, но предпочтительным способом является создание файла services.php
в каталоге config
и возврат массива, подобного следующему:
return [
'stripe' => [
'secret' => 'Your key'
]
];
Когда getStripeKey
пытается загрузить ваш ключ, он будет искать свойство с именем stripeKey
. Если он не найден, он автоматически загрузит ваш services
файл.
Создание наших страниц
Для простоты мы создадим всего несколько страниц:
— Регистрация: где пользователь может зарегистрироваться с помощью плана членства (Basic, Gold).
— Логин: страница входа членов.
— Обновление: обновление с базового до золотого членства.
— Опубликовать: отображать одну страницу сообщения.
Для ускорения процесса мы будем использовать bootsnipp . Вы можете получить окончательный код из репозитория GitHub.
Страница авторизации:
Страница входа имеет основное поле электронной почты и пароль, а страница 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" );
}
Страница регистрации:
На странице регистрации есть поле 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
.
Обновить страницу:
Страница обновления будет 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
, очевидно, в реальном проекте у вас будут некоторые корректировки платы и опция понижения, но она должна работать так же ,
Страница поста:
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, смотрите здесь .