Введение
Laravel Cashier (кассир — прим. пер.) обеспечивает выразительный и гибкий интерфейс для сервисов биллинговых подписок Stripe и Braintree. Он сам создаст практически весь шаблонный код биллинговых подписок, который вы боитесь писать. В дополнение к основному управлению подписками Cashier может работать с купонами, заменой подписок, «величинами» подписок, отменой льготного периода, и даже генерировать PDF-файлы счетов.
Если вы обрабатываете только разовые платежи и не предлагаете людям подписки, вам не стоит использовать Cashier. Используйте SDK Stripe и Braintree напрямую.
Настройка
Stripe
Сначала добавьте пакет Cashier для Stripe в свой файл composer.json и выполните команду shcomposer update
:
- "laravel/cashier": "~7.0"
добавлено в 5.2 () 5.1 () 5.0 ()
- "laravel/cashier": "~6.0" — для Laravel 5.2
- "laravel/cashier": "~5.0" — для Stripe SDK ≈2.0 и версии Stripe APIs от 2015-02-18 и выше
- "laravel/cashier": "~4.0" — для версии Stripe APIs от 2015-02-18 и выше
- "laravel/cashier": "~3.0" — для версии Stripe APIs от 2015-02-16 включительно и выше
Затем зарегистрируйте сервис-провайдер Laravel\Cashier\CashierServiceProvider в вашем файле настроек config/app.
Перед тем, как начать использовать Cashier, надо подготовить нашу БД.
Надо добавить несколько столбцов в таблицу users и создать новую таблицу subscriptions для хранения всех подписок пользователей:
Schema::table('users', function ($table) {
$table->string('stripe_id')->nullable();
$table->string('card_brand')->nullable();
$table->string('card_last_four')->nullable();
$table->timestamp('trial_ends_at')->nullable();
});
Schema::create('subscriptions', function ($table) {
$table->increments('id');
$table->integer('user_id');
$table->string('name');
$table->string('stripe_id');
$table->string('stripe_plan');
$table->integer('quantity');
$table->timestamp('trial_ends_at')->nullable();
$table->timestamp('ends_at')->nullable();
$table->timestamps();
});
После создания миграции выполните команду shmigrate
.
Далее добавьте типаж Billable в определение вашей модели. Этот типаж предоставляет различные методы для выполнения основных задач по оплате, таких как создание подписок, применение купонов, обновление информации о банковской карте:
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
Наконец, надо настроить ключ Stripe в файле настроек services.php. Получить свои ключи Stripe API можно в панели управления Stripe:
'stripe' => [
'model' => App\User::class,
'secret' => env('STRIPE_SECRET'),
],
Далее добавьте типаж Billable и соответствующие преобразователи даты в определение вашей модели:
use Laravel\Cashier\Billable;
use Laravel\Cashier\Contracts\Billable as BillableContract;
class User extends Model implements BillableContract
{
use Billable;
protected $dates = ['trial_ends_at', 'subscription_ends_at'];
}
Добавление столбцов в свойство модели PHP$dates
даёт Eloquent команду вернуть столбцы в виде экземпляров Carbon/DateTime вместо «сырых» строк.
Наконец, внесите ваш Stripe-ключ в конфигурационный файл services.php:
'stripe' => [
'model' => 'User',
'secret' => env('STRIPE_API_SECRET'),
],
Braintree
Преимущества и недостатки Braintree
Для многих операций реализация функций Cashier в Stripe и Braintree одинакова. Оба сервиса предоставляют возможность оплаты подписок банковскими картами, но Braintree также поддерживает оплату через PayPal. Однако, в Braintree нет некоторых возможностей, имеющихся в Stripe. При выборе между ними учитывайте следующее:
- Braintree поддерживает PayPal, а Stripe нет.
- Braintree не поддерживает методы
PHPincrement
иPHPdecrement
для подписок. Это ограничение Braintree, а не Cashier. - Braintree не поддерживает скидки в процентах. Это ограничение Braintree, а не Cashier.
Сначала добавьте пакет Cashier для Braintree в ваш файл composer.json и выполните команду shcomposer update
:
sh"laravel/cashier-braintree": "~2.0"
Далее зарегистрируйте сервис-провайдер Laravel\Cashier\CashierServiceProvider в файле настроек config/app.
Перед использованием Cashier с Braintree вам надо определить скидку для плана оплаты в панели настроек Braintree. Эта скидка будет использоваться для пропорционального пересчёта подписок, которые переходят с годовой на ежемесячную оплату, или наоборот с ежемесячной на годовую. Настраиваемый в панели настроек Braintree размер скидки может быть любым, на ваше усмотрение, а Cashier будет просто изменять размер по умолчанию на заданный при каждом применении купона. Этот купон необходим из-за того, что Braintree изначально не поддерживает пропорциональный пересчёт подписок для каждого выставления счёта.
Перед использованием Cashier нам надо подготовить базу данных. Надо добавить несколько столбцов в таблицу users и создать новую таблицу subscriptions для хранения всех подписок пользователей:
Schema::table('users', function ($table) {
$table->string('braintree_id')->nullable();
$table->string('paypal_email')->nullable();
$table->string('card_brand')->nullable();
$table->string('card_last_four')->nullable();
$table->timestamp('trial_ends_at')->nullable();
});
Schema::create('subscriptions', function ($table) {
$table->increments('id');
$table->integer('user_id');
$table->string('name');
$table->string('braintree_id');
$table->string('braintree_plan');
$table->integer('quantity');
$table->timestamp('trial_ends_at')->nullable();
$table->timestamp('ends_at')->nullable();
$table->timestamps();
});
После создания миграций просто выполните Artisan-команду shmigrate
.
Далее добавьте типаж Billable в определение вашей модели:
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
Далее надо настроить следующие параметры в файле настроек services.php:
'braintree' => [
'model' => App\User::class,
'environment' => env('BRAINTREE_ENV'),
'merchant_id' => env('BRAINTREE_MERCHANT_ID'),
'public_key' => env('BRAINTREE_PUBLIC_KEY'),
'private_key' => env('BRAINTREE_PRIVATE_KEY'),
],
Затем надо добавить следующие вызовы Braintree SDK в метод PHPboot()
вашего сервис провайдера AppServiceProvider:
\Braintree_Configuration::environment(config('services.braintree.environment'));
\Braintree_Configuration::merchantId(config('services.braintree.merchant_id'));
\Braintree_Configuration::publicKey(config('services.braintree.public_key'));
\Braintree_Configuration::privateKey(config('services.braintree.private_key'));
\Braintree_Configuration::environment(env('BRAINTREE_ENV'));
\Braintree_Configuration::merchantId(env('BRAINTREE_MERCHANT_ID'));
\Braintree_Configuration::publicKey(env('BRAINTREE_PUBLIC_KEY'));
\Braintree_Configuration::privateKey(env('BRAINTREE_PRIVATE_KEY'));
добавлено в 5.3 ()
Настройка валюты
Стандартная валюта Cashier — доллар США (USD). Вы можете изменить валюту, вызвав метод PHPCashier::useCurrency()
из метода PHPboot()
одного из ваших сервис-провайдеров. Метод PHPuseCurrency()
принимает два строковых параметра: валюту и символ валюты:
use Laravel\Cashier\Cashier;
Cashier::useCurrency('eur', '€');
Подписки
Создание подписок
добавлено в 5.2 ()
Для создания подписки сначала получите экземпляр оплачиваемой модели, который обычно является экземпляром App\User. Когда вы получили модель, вы можете использовать метод PHPnewSubscription()
для управления подписками модели:
$user = User::find(1);
$user->newSubscription('main', 'monthly')->create($creditCardToken);
Первый аргумент метода PHPnewSubscription()
— название подписки. Если в вашем приложении используется только одна подписка, то вы можете назвать её main или primary. Второй аргумент — конкретный план Stripe/Braintree, на который подписывается пользователь. Это значение должно соответствовать идентификатору плана в Stripe или Braintree.
Метод PHPcreate()
создаст подписку, а также внесёт в вашу базу данных ID заказчика и другую связанную информацию по оплате.
Дополнительная информация о пользователе
Если вы хотите указать дополнительную информацию о пользователе, передайте её вторым аргументом методу PHPcreate()
:
$user->newSubscription('main', 'monthly')->create($creditCardToken, [
'email' => $email,
]);
Подробнее о дополнительных полях, поддерживаемых Stripe и Braintree, читайте в документации по созданию заказчика Stripe и в документации Braintree.
Если надо применить купон при создании подписки, используйте метод PHPwithCoupon()
:
$user->newSubscription('main', 'monthly')
->withCoupon('code')
->create($creditCardToken);
Для создания подписки сначала получите экземпляр оплачиваемой модели, который обычно является экземпляром App\User. Когда вы получили модель, вы можете использовать метод PHPsubscription()
для управления подписками модели:
$user = User::find(1);
$user->subscription('monthly')->create($creditCardToken);
Метод PHPcreate()
автоматически создаст подписку Stripe, а также внесёт в вашу базу данных ID заказчика Stripe и другую связанную информацию по оплате. Если для вашего тарифа настроен пробный период в Stripe, то для записи пользователя автоматически будет задана дата окончания периода.
Если вы хотите использовать пробные периоды, но при этом управлять ими полностью из своего приложения, а не определять их в Stripe, то вам надо задать дату окончания периода вручную:
$user->trial_ends_at = Carbon::now()->addDays(14);
$user->save();
Дополнительная информация о пользователе
Если вы хотите указать дополнительную информацию о пользователе, передайте её вторым аргументом методу PHPcreate()
:
$user->subscription('monthly')->create($creditCardToken, [
'email' => $email, 'description' => 'Our First Customer'
]);
Подробнее о дополнительных полях, поддерживаемых Stripe, читайте в документации по созданию заказчика Stripe.
Если надо применить купон при создании подписки, используйте метод PHPwithCoupon()
:
$user->subscription('monthly')
->withCoupon('code')
->create($creditCardToken);
Проверка статуса подписки
Когда пользователь подписан на ваше приложение, вы можете легко проверить статус его подписки при помощи различных удобных методов. Во-первых, метод PHPsubscribed()
возвращает true, если подписка пользователя активна, даже если в данный момент она на пробном периоде:
if ($user->subscribed('main')) {
//
}
Метод PHPsubscribed
создаёт отличный вариант для посредника маршрута, позволяя вам фильтровать доступ к маршрутам и контроллерам на основе статусов подписок:
public function handle($request, Closure $next)
{
if ($request->user() && ! $request->user()->subscribed('main')) {
// Этот пользователь не оплатил подписку...
return redirect('billing');
}
return $next($request);
}
Вы также можете определить, идёт ли до сих пор пробный период у пользователя, с помощью метода PHPonTrial()
. Этот метод полезен для предупреждения пользователя о том, что он на пробном периоде:
if ($user->subscription('main')->onTrial()) {
//
}
Метод PHPsubscribedToPlan()
используется для определения, подписан ли пользователь на данный тариф, на основе его Stripe/Braintree ID. В этом примере мы определим, подписана ли подписка main пользователя на план monthly:
if ($user->subscribedToPlan('monthly', 'main')) {
//
}
Чтобы определить, был ли пользователь ранее активным подписчиком, но позже отменил подписку, используйте метод PHPcancelled()
:
if ($user->subscription('main')->cancelled()) {
//
}
Вы можете также определить, отменил ли пользователь подписку, но находится все ещё на «льготном периоде», пока подписка полностью не истекла. Например, если пользователь отменяет подписку 5 марта, которая по плану закончится 10 марта, пользователь будет на «льготном периоде» до 10 марта. Обратите внимание на то, что метод PHPsubscribed()
всё ещё возвращает true в это время:
if ($user->subscription('main')->onGracePeriod()) {
//
}
Когда пользователь подписан на ваше приложение, вы можете легко проверить статус его подписки при помощи различных удобных методов. Во-первых, метод PHPsubscribed()
возвращает true, если подписка пользователя активна, даже если в данный момент она на пробном периоде:
if ($user->subscribed()) {
//
}
Метод PHPsubscribed
создаёт отличный вариант для посредника маршрута, позволяя вам фильтровать доступ к маршрутам и контроллерам на основе статусов подписок:
public function handle($request, Closure $next)
{
if ($request->user() && ! $request->user()->subscribed()) {
// Этот пользователь не оплатил подписку...
return redirect('billing');
}
return $next($request);
}
Вы также можете определить, идёт ли до сих пор пробный период у пользователя, с помощью метода PHPonTrial()
. Этот метод полезен для предупреждения пользователя о том, что он на пробном периоде:
if ($user->onTrial()) {
//
}
Метод PHPonPlan()
используется для определения, подписан ли пользователь на данный тариф, на основе его Stripe ID:
if ($user->onPlan('monthly')) {
//
}
Чтобы определить, был ли пользователь ранее активным подписчиком, но позже отменил подписку, используйте метод PHPcancelled()
:
if ($user->cancelled()) {
//
}
Вы можете также определить, отменил ли пользователь подписку, но находится все ещё на «льготном периоде», пока подписка полностью не истекла. Например, если пользователь отменяет подписку 5 марта, которая по плану закончится 10 марта, пользователь будет на «льготном периоде» до 10 марта. Обратите внимание на то, что метод PHPsubscribed()
всё ещё возвращает true в это время.
if ($user->onGracePeriod()) {
//
}
Метод PHPeverSubscribed()
используется для определения, подписывался ли пользователь когда-либо на ваше приложение:
if ($user->everSubscribed()) {
//
}
Смена тарифа
Когда пользователь подписан на ваше приложение, он может захотеть сменить свой тарифный план. Чтобы переключить пользователя на новую подписку, передайте идентификатор тарифа в метод PHPswap()
:
$user = App\User::find(1);
$user->subscription('main')->swap('provider-plan-id');
Если пользователь был на пробном периоде, то пробный период продолжится. Кроме того, если у подписки есть «количество», то оно тоже применится.
Если вы хотите сменить план и отменить все текущие пробные периоды пользователя, используйте метод PHPskipTrial()
:
$user->subscription('main')
->skipTrial()
->swap('provider-plan-id');
Когда пользователь подписан на ваше приложение, он может захотеть сменить свой тарифный план. Чтобы переключить пользователя на новую подписку, используйте метод PHPswap()
. Например, мы легко можем переключить пользователя на подписку premium:
$user = App\User::find(1);
$user->subscription('premium')->swap();
Если пользователь был на пробном периоде, то пробный период продолжится. Кроме того, если у подписки есть «количество», то оно тоже применится. При смене тарифа вы можете использовать метод PHPprorate()
, чтобы указать, что стоимость должна быть пересчитана пропорционально. Вдобавок, вы можете использовать метод PHPswapAndInvoice()
для того, чтобы выставить счёт за смену тарифа немедленно:
$user->subscription('premium')
->prorate()
->swapAndInvoice();
Количество подписки
Количество подписки поддерживается только версией Cashier для Stripe. В Braintree нет возможности, подобной «количеству» в Stripe.
Иногда подписки зависят от «количества». Например, ваше приложение стоит $10 в месяц с одного пользователя учётной записи. Чтобы легко увеличить или уменьшить количество вашей подписки, используйте методы PHPincrementQuantity()
и PHPdecrementQuantity()
:
$user = User::find(1);
$user->subscription('main')->incrementQuantity();
// Добавить 5 к текущему количеству подписок...
$user->subscription('main')->incrementQuantity(5);
$user->subscription('main')->decrementQuantity();
// Вычесть 5 от текущего количества подписок...
$user->subscription('main')->decrementQuantity(5);
Или вы можете задать конкретное количество с помощью метода PHPupdateQuantity()
:
$user->subscription('main')->updateQuantity(10);
Иногда подписки зависят от «количества». Например, ваше приложение стоит $10 в месяц с одного пользователя учётной записи. Чтобы легко увеличить или уменьшить количество вашей подписки, используйте методы PHPincrement()
и PHPdecrement()
:
$user = User::find(1);
$user->subscription()->increment();
// Добавить 5 к текущему количеству подписок...
$user->subscription()->increment(5);
$user->subscription()->decrement();
// Вычесть 5 от текущего количества подписок...
$user->subscription()->decrement(5);
Или вы можете задать конкретное количество с помощью метода PHPupdateQuantity()
:
$user->subscription()->updateQuantity(10);
Более подробная информация о количествах подписок в документации Stripe.
Налог на подписку
С помощью Cashier можно легко изменить значение tax_percent, посылаемое в Stripe. Чтобы указать процент налога, который пользователь платит за подписку, реализуйте метод PHPgetTaxPercent()
в своей модели, и верните числовое значение от 0 до 100 с не более, чем двумя знаками после запятой.
public function getTaxPercent() {
return 20;
}
Метод PHPtaxPercentage()
позволяет вам использовать разные налоговые ставки по-модельно, что будет полезно при наличии пользователей из разных стран.
Метод PHPtaxPercentage()
применяется только к оплате подписок. Если вы используете Cashier для разовых платежей, то в таких случаях вам надо указывать размер налога вручную.
Отмена подписки
Отменить подписку можно методом PHPcancel()
:
$user->subscription('main')->cancel();
При отмене подписки Cashier автоматически задаст столбец ends_at в вашей базе данных. Этот столбец используется, чтобы знать, когда метод PHPsubscribed()
должен начать возвращать false. Например, если клиент отменит подписку 1 марта, но срок подписки по плану до 5 марта, то метод PHPsubscribed()
будет продолжать возвращать true до 5 марта.
Вы можете определить то, что пользователь отменил подписку, но находится на «льготном периоде», при помощи метода PHPonGracePeriod ()
:
if ($user->subscription('main')->onGracePeriod()) {
//
}
добавлено в 5.3 ()
Отменить подписку можно методом PHPcancel()
:
$user->subscription()->cancel();
При отмене подписки Cashier автоматически задаст столбец subscription_ends_at в вашей базе данных. Этот столбец используется, чтобы знать, когда метод PHPsubscribed()
должен начать возвращать false. Например, если клиент отменит подписку 1 марта, но срок подписки по плану до 5 марта, то метод PHPsubscribed()
будет продолжать возвращать true до 5 марта.
Вы можете определить то, что пользователь отменил подписку, но находится на «льготном периоде», при помощи метода PHPonGracePeriod ()
:
if ($user->onGracePeriod()) {
//
}
Возобновление подписки
Если пользователь отменит подписку и затем возобновит её до того, как она полностью истекла, тогда не произойдет моментального расчёта оплаты. Его подписка будет просто повторно активирована, и расчёт оплаты будет происходить по изначальному циклу.
добавлено в 5.3 ()
Пробные подписки
С запросом банковской карты
Если вы хотите предлагать клиентам пробные периоды, но при этом сразу собирать данные о способе оплаты, используйте метод PHPtrialDays()
при создании подписок:
$user = User::find(1);
$user->newSubscription('main', 'monthly')
->trialDays(10)
->create($creditCardToken);
Этот метод задаст дату окончания пробного периода в записи БД о подписке, а также проинструктирует Stripe/Braintree о том, что не нужно начинать считать оплату для клиента до окончания этого периода.
Если подписка клиента не будет отменена до окончания пробного периода, ему будет выставлен счёт, как только истечёт этот период, поэтому не забывайте уведомлять своих пользователей о дате окончания их пробного периода.
Вы можете определить, идёт ли до сих пор пробный период у пользователя, с помощью метода PHPonTrial()
экземпляра пользователя или с помощью метода PHPonTrial()
экземпляра подписки. Эти два примера выполняют одинаковую задачу:
if ($user->onTrial('main')) {
//
}
if ($user->subscription('main')->onTrial()) {
//
}
Без запроса банковской карты
Если вы хотите предлагать клиентам пробные периоды, не собирая данные о способе оплаты, вы можете просто задать значение столбца trial_ends_at в записи пользователя равное дате окончания пробного периода. Это обычно делается во время регистрации пользователя:
$user = User::create([
// Заполнение других свойств пользователя...
'trial_ends_at' => Carbon::now()->addDays(10),
]);
Не забудьте добавить преобразователь дат для trial_ends_at в определении вашей модели.
Cashier относится к пробным периодам такого типа, как к «общим пробным периодам», поскольку они не прикреплены к какой-либо из существующих подписок. Метод PHPonTrial()
на экземпляре User вернёт PHPtrue
, если текущая дата не превышает значение trial_ends_at:
if ($user->onTrial()) {
// Пользователь на пробном периоде...
}
Если вы хотите узнать, что пользователь именно на «общем» пробном периоде и ещё не создал настоящую подписку, используйте метод PHPonGenericTrial()
:
if ($user->onGenericTrial()) {
// Пользователь на "общем" пробном периоде...
}
Когда вы готовы создать для пользователя настоящую подписку, используйте метод PHPnewSubscription()
как обычно:
$user = User::find(1);
$user->newSubscription('main', 'monthly')->create($creditCardToken);
Обработка веб-хуков Stripe
Stripe и Braintree могут уведомлять ваше приложение о различных событиях через веб-хуки. Для обработки веб-хуков Stripe определите маршрут для контроллера веб-хуков Cashier. Этот контроллер будет обрабатывать все входящие веб-хук запросы и отправлять их в нужный метод контроллера:
Route::post(
'stripe/webhook',
'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);
Route::post('stripe/webhook', '\Laravel\Cashier\WebhookController@handleWebhook');
После регистрации маршрута не забудьте настроить URL веб-хука в панели настроек Stripe.
По умолчанию этот контроллер будет автоматически обрабатывать отмену подписок, для которых произошло слишком много неудачных попыток оплаты (это число задаётся в настройках Stripe); но вскоре мы рассмотрим, что можно наследовать этот контроллер для обработки любых необходимых веб-хук событий.
Поскольку веб-хуки Stripe должны идти в обход CSRF-защиты Laravel, не забудьте включить URI в список исключений вашего посредника VerifyCsrfToken или разместить маршрут вне группы посредников web (для версии 5.2 и выше):
protected $except = [
'stripe/*',
];
Определение обработчиков веб-хук событий
Cashier автоматически обрабатывает отмену подписки при неудачной оплате, но если у вас есть дополнительные webhook-события для Stripe, которые вы хотели бы обработать, просто наследуйте контроллер PHPWebhook
. Ваши имена методов должны соответствовать принятому в Cashier соглашению, в частности, методы должны быть снабжены префиксом handle и именем того webhook-события Stripe, которое вы хотите обработать, в стиле «CamelCase». Например, если вы хотите обработать webhook PHPinvoice.payment_succeeded
, вы должны добавить метод PHPhandleInvoicePaymentSucceeded()
в контроллер:
<?php
namespace App\Http\Controllers;
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;
//для версии 5.1 и ранее:
//use Laravel\Cashier\WebhookController as CashierController;
class WebhookController extends CashierController
{
/**
* Обработка веб-хука Stripe.
*
* @param array $payload
* @return Response
*/
public function handleInvoicePaymentSucceeded($payload)
{
// Обработка события
}
}
Неудавшиеся подписки
Что если срок действия банковской карты клиента истёк? Никаких проблем — Cashier включает в себя контроллер PHPWebhook
, который может легко отменить подписку клиента. Как уже было сказано, просто укажите путь к контроллеру:
Route::post(
'stripe/webhook',
'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);
Вот и всё! Неудавшиеся платежи будут перехвачены и обработаны контроллером. Контроллер отменит подписку клиента, если Stripe определит, что подписка не удалась (обычно после трёх неудавшихся платежей).
Обработка веб-хуков Braintree
Stripe и Braintree могут уведомлять ваше приложение о различных событиях через веб-хуки. Для обработки веб-хуков Stripe определите маршрут для контроллера веб-хуков Cashier. Этот контроллер будет обрабатывать все входящие веб-хук запросы и отправлять их в нужный метод контроллера:
Route::post(
'braintree/webhook',
'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);
По умолчанию этот контроллер будет автоматически обрабатывать отмену подписок, для которых произошло слишком много неудачных попыток оплаты (это число задаётся в настройках Braintree); но вскоре мы рассмотрим, что можно наследовать этот контроллер для обработки любых необходимых веб-хук событий.
Поскольку веб-хуки Braintree должны идти в обход CSRF-защиты Laravel, не забудьте включить URI в список исключений вашего посредника VerifyCsrfToken или разместить маршрут вне группы посредников web (для версии 5.2 и выше):
protected $except = [
'braintree/*',
];
После регистрации маршрута не забудьте настроить URI веб-хука в панели настроек Braintree.
Определение обработчиков веб-хук событий
Cashier автоматически обрабатывает отмену подписки при неудачной оплате, но если у вас есть дополнительные webhook-события для Braintree, которые вы хотели бы обработать, просто наследуйте контроллер PHPWebhook
. Ваши имена методов должны соответствовать принятому в Braintree соглашению, в частности, методы должны быть снабжены префиксом handle и именем того webhook-события Braintree, которое вы хотите обработать, в стиле «CamelCase». Например, если вы хотите обработать веб-хук PHPdispute_opened
, вы должны добавить метод PHPhandleDisputeOpened()
в контроллер.
<?php
namespace App\Http\Controllers;
use Braintree\WebhookNotification;
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;
class WebhookController extends CashierController
{
/**
* Обработка веб-хука Braintree.
*
* @param WebhookNotification $webhook
* @return Response
*/
public function handleDisputeOpened(WebhookNotification $notification)
{
// Обработка события
}
}
Неудавшиеся подписки
Что если срок действия банковской карты клиента истёк? Никаких проблем — Cashier включает в себя контроллер PHPWebhook
, который может легко отменить подписку клиента. Как уже было сказано, просто укажите путь к контроллеру:
Route::post(
'braintree/webhook',
'\Laravel\Cashier\Http\Controllers\WebhookController@handleWebhook'
);
Вот и всё! Неудавшиеся платежи будут перехвачены и обработаны контроллером. Контроллер отменит подписку клиента, если Braintree определит, что подписка не удалась (обычно после трёх неудавшихся платежей). Не забудьте настроить URL веб-хука в панели настроек Braintree.
Одиночные платежи
Простой платёж
При использовании Stripe метод PHPcharge()
принимает сумму, которую необходимо оплатить, с наименьшим знаменателем используемой в вашем приложении валюты. А при использовании Braintree вы должны передавать в метод PHPcharge()
полную сумму в долларах.
Если вы хотите сделать «одноразовый» платёж вместо использования банковской карты подписанного пользователя, используйте метод PHPcharge()
для экземпляра оплачиваемой модели.
// Stripe принимает сумму в центах...
$user->charge(100);
// Braintree принимает сумму в долларах...
$user->charge(1);
Метод PHPcharge()
принимает вторым аргументом массив, позволяя вам передавать любые необходимые параметры для создания основного Stripe/Braintree-платежа. О доступных при создании платежей опциях читайте в документации Stripe и Braintree:
$user->charge(100, [
'custom_option' => $value,
]);
Метод PHPcharge()
выбросит исключение при ошибке платёжа. Если платёж пройдёт успешно, метод вернёт полный ответ Stripe/Braintree:
try {
$response = $user->charge(100);
} catch (Exception $e) {
//
}
Платёж со счётом
Иногда бывает необходимо сделать одноразовый платёж и сгенерировать счёт-фактуру для него, чтобы вы могли предоставить клиенту PDF-квитанцию. Именно для этого служит метод PHPinvoiceFor()
. Например, давайте выставим клиенту счёт $5.00 «разовой оплаты» («One Time Fee»):
// Stripe принимает сумму в центах...
$user->invoiceFor('One Time Fee', 500);
// Braintree принимает сумму в долларах...
$user->invoiceFor('One Time Fee', 5);
Счёт будет немедленно оплачен банковской картой пользователя. Метод PHPinvoiceFor()
также принимает третьим аргументом массив, позволяя вам передавать любые параметры для создания платежа Stripe/Braintree:
$user->invoiceFor('One Time Fee', 500, [
'custom-option' => $value,
]);
Метод PHPinvoiceFor()
создаст счёт Stripe, который будет повторять проваленные попытки оплаты. Если вы не хотите повторять проваленные платежи, вам необходимо закрывать их с помощью Stripe API после первого неудачного платежа.
Если вы хотите сделать «одноразовый» платёж вместо использования банковской карты подписанного пользователя, используйте метод PHPcharge()
для экземпляра модели. Метод PHPcharge()
принимает сумму, которую необходимо оплатить, с наименьшим знаменателем используемой в вашем приложении валюты. Например, в этом примере будет списано 100 центов, или $1, с банковской карты пользователя:
$user->charge(100);
Метод PHPcharge()
принимает в качестве второго аргумента массив, позволяя вам передавать любые необходимые параметры для создания основного Stripe-платежа:
$user->charge(100, [
'source' => $token,
'receipt_email' => $user->email,
]);
Метод PHPcharge()
вернёт false, если платёж не пройдёт. Обычно это значит, что платёж был отклонён:
if ( ! $user->charge(100)) {
// Платёж был отклонён...
}
Если платёж прошёл успешно, метод возвратит полный Stripe-ответ.
добавлено в 5.0 ()
Без предоплаты
Если в вашем приложении будет бесплатный пробный период, не требующий предварительного предъявления банковской карты, установите свойство cardUpFront вашей модели в false:
protected $cardUpFront = false;
При создании аккаунта не забудьте установить дату окончания пробного периода в модели:
$user->trial_ends_at = Carbon::now()->addDays(14);
$user->save();
Счета
Вы можете легко получить массив счетов пользователя, используя метод PHPinvoices()
:
$invoices = $user->invoices();
// Включить отложенные счета в результаты...
$invoices = $user->invoicesIncludingPending();
При просмотре счетов клиента вы можете использовать эти вспомогательные методы, чтобы вывести на экран соответствующую информацию о счёте. Например, вам может понадобиться просмотреть каждый счёт в таблице, позволив пользователю легко скачать любой из них:
<table>
@foreach ($invoices as $invoice)
<tr>
<td>{{ $invoice->date()->toFormattedDateString() }}</td>
<td>{{ $invoice->total() }}</td>
<td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td>
</tr>
@endforeach
</table>
<table>
@foreach ($invoices as $invoice)
<tr>
<td>{{ $invoice->dateString() }}</td>
<td>{{ $invoice->dollars() }}</td>
<td><a href="/user/invoice/{{ $invoice->id }}">Download</a></td>
</tr>
@endforeach
</table>
Создание PDF-файлов счетов
Перед созданием PDF-файлов счетов вам необходимо установить PHP-библиотеку dompdf:
shcomposer require dompdf/dompdf
Затем используйте метод PHPdownloadInvoice()
в маршруте или контроллере, чтобы cгенерировать PDF-файл счёта. Этот метод автоматически сгенерирует нужный HTTP-отклик чтобы отправить загрузку в браузер:
use Illuminate\Http\Request;
Route::get('user/invoice/{invoice}', function (Request $request, $invoiceId) {
return $request->user()->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
'product' => 'Your Product',
]);
});