Статьи

Продажа загрузок с помощью Stripe и Laravel

Цифровые товары становятся все более ценным товаром. Независимо от того, являетесь ли вы дизайнером, продающим шаблоны или файлы шрифтов, разработчиком, который взимает плату за пакеты кода, или музыкантом, продающим MP3, продавать цифровые товары в Интернете зачастую гораздо проще, чем физические товары — с гораздо более низкими производственными затратами и без затрат на доставку.

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

Прежде чем вы начнете

Сначала вам нужно создать учетную запись в Stripe . Обратите внимание, что услуга в настоящее время доступна только в США, Великобритании, Ирландии и Канаде. Вы можете проверить блог или подписаться на него в Твиттере, чтобы быть в курсе последних изменений в доступности в других странах.

Настройка учетной записи для тестирования не занимает много времени и не требует каких-либо сложных финансовых анкет или информации. Вам нужно будет записать свой тестовый ключ API и публикуемый ключ для дальнейшего использования.

Настройка

Для этого примера приложения я собираюсь использовать Laravel . Он позаботится о некоторых элементарных вещах, таких как маршрутизация, создание шаблонов и запуск загрузок, но пример не должен быть слишком сложным для адаптации для других платформ (или, вообще, вообще без фреймворка). Обратите внимание, что вы можете клонировать весь пример из Github .

Начните с установки Laravel через Composer :

composer create-project laravel/laravel stripe-downloads --prefer-dist

Включите следующее в раздел require вашего файла composer.json и запустите composer update :

"abodeo/laravel-stripe": "dev-master"

Это обеспечивает простую обертку для Stripe SDK, позволяя использовать ее из Laravel, не беспокоясь о том, require соответствующие файлы.

Опубликуйте файл конфигурации, используя:

php artisan config:publish abodeo/laravel-stripe

Затем введите свой ключ API и публикуемый ключ в app/config/packages/abodeo/laravel-stripe/stripe.php

Наконец добавьте пакет в список поставщиков услуг ( app/config/app.php ):

'Abodeo\LaravelStripe\LaravelStripeServiceProvider',

Настройка базы данных

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

php artisan migrate:make create_downloads_table

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

 Schema::create('downloads', function($table) { $table->increments('id')->unsigned(); $table->string('name', 255); $table->string('filepath', 255); $table->integer('price'); $table->timestamps(); }); 

Обратите внимание, что price является целым числом, потому что внутри мы будем иметь дело только с центами / пенсов. Filepath будет указывать на местоположение файла относительно каталога app/storage .

Соответствующая модель очень проста:

 // app/models/Download.php class Download extends Eloquent { protected $fillable = array('name', 'filepath', 'price'); } 

Наконец, давайте заполним базу данных несколькими примерами загрузок:

 class DownloadsSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $data = array( array( 'name' => 'Sample download 1', 'filepath' => 'downloads/one.zip', 'price' => 500, ), array( 'name' => 'Sample download 2', 'filepath' => 'downloads/two.zip', 'price' => 1000, ), ); foreach ($data as $properties) { $download = new Download($properties); $download->save(); } } } 

Простая домашняя страница

Давайте создадим простую домашнюю страницу, которая позволяет людям выбирать, какую загрузку они хотели бы приобрести:

 Route::get('/', function() { $downloads = Download::get(); return View::make('index', array('downloads' => $downloads)); }); 

И мнение:

 // app/views/index.blade.php <h1>Select your download:</h1> <table class="table table-bordered"> @foreach ($downloads as $download) <tr> <td>{{ $download->name }}</td> <td>&pound;{{ round($download->price/100) }}</td> <td><a href="/buy/{{ $download->id }}" class="btn btn-primary">Buy</a></td> </tr> @endforeach </table> 

Принимая оплату

Stripe предоставляет два метода получения информации о карте. Первая — это простая кнопка, сгенерированная с использованием Javascript, которая запускает форму оплаты во всплывающем окне следующим образом:

образ

Второй и более сложный способ позволяет создать форму самостоятельно. Это метод, который я собираюсь использовать в этом примере. Вот маршрут:

 Route::get('/buy/{id}', function($id) { $download = Download::find($id); return View::make('buy', array('download' => $download)); }); 

И мнение:

 // app/views/buy.blade.php <form action="" method="POST" id="payment-form" role="form"> <div class="payment-errors alert alert-danger" style="display:none;"></div> <input type="hidden" name="did" value="{{ $download->id }}" /> <div class="form-group"> <label> <span>Card Number</span> <input type="text" size="20" data-stripe="number" class="form-control input-lg" /> </label> </div> <div class="form-group"> <label> <span>CVC</span> <input type="text" size="4" data-stripe="cvc" class="form-control input-lg" /> </label> </div> <div class="form-group"> <label> <span>Expires</span> </label> <div class="row"> <div class="col-lg-1 col-md-1 col-sm-2 col-xs-3"> <input type="text" size="2" data-stripe="exp-month" class="input-lg" placeholder="MM" /> </div> <div class="col-lg-1 col-md-1 col-sm-2 col-xs-3"> <input type="text" size="4" data-stripe="exp-year" class="input-lg" placeholder="YYYY" /> </div> </div> </div> <div class="form-group"> <button type="submit" class="btn btn-primary btn-lg">Submit Payment</button> </div> </form> 

Острые глаза среди вас заметят, что на входах, связанных с картами, отсутствует один важный атрибут — name . Без этого никакие данные не могут быть переданы на наш сервер при отправке формы. Как это может быть?

На самом деле, это намеренно. Мы не хотим, чтобы данные кредитной карты отправлялись в наше приложение.

Мы собираемся перехватить отправку этой формы с помощью Javascript. Информация о карте будет извлечена из формы — обратите внимание на атрибуты data-stripe — и надежно отправлена ​​на серверы Stripe вместе с нашим публикуемым ключом. Затем Stripe отвечает за проверку этих деталей и, если все в порядке, верните нам специальный жетон. Как только мы получили этот токен, мы можем ввести его в форму и отправить результаты как обычно — минус данные карты.

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

Мы начнем с инициализации Javascript Stripe:

 Stripe.setPublishableKey('@stripeKey'); 

В приведенном выше коде @stripeKey представляет собой специальное расширение Blade, которое выводит публикуемый ключ. Если вы не используете библиотеку шаблонов лезвий, вы можете вместо этого сделать что-то вроде этого:

 Stripe.setPublishableKey('<?php print Config::get('stripe.publishableKey') ?>'); 

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

 $('#payment-form').submit(function(e) { var $form = $(this); $form.find('.payment-errors').hide(); $form.find('button').prop('disabled', true); Stripe.createToken($form, stripeResponseHandler); return false; }); 

Функция для обработки ответа от Stripe:

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

Обратите внимание, как токен вводится в форму путем динамического создания нового скрытого элемента формы с именем stripeToken . Это единственные данные, связанные с платежами, которые отправляются.

Если вы изучите ответ от сервера Stripe, вы заметите, что в дополнение к токену — свойству id — есть объект словаря card . Очевидно, что это не содержит номер карты или CVC, но он включает в себя тип — Visa, Mastercard и т. Д. — и последние 4 цифры номера карты. Это полезно для создания счетов или квитанций; довольно часто включать эту информацию, чтобы указать клиенту, какую карту он использовал для оплаты.

Давайте создадим обработчик формы POST:

  Route::post('/buy/{id}', function($id) { // Set the API key Stripe::setApiKey(Config::get('laravel-stripe::stripe.api_key')); $download = Download::find($id); // Get the credit card details submitted by the form $token = Input::get('stripeToken'); // Charge the card try { $charge = Stripe_Charge::create(array( "amount" => $download->price, "currency" => "gbp", "card" => $token, "description" => 'Order: ' . $download->name) ); // If we get this far, we've charged the user successfully Session::put('purchased_download_id', $download->id); return Redirect::to('confirmed'); } catch(Stripe_CardError $e) { // Payment failed return Redirect::to('buy/'.$id)->with('message', 'Your payment has failed.'); } } 

Давайте пройдем через это.

Сначала мы устанавливаем ключ API для Stripe. Когда мы обрабатывали форму на стороне клиента, у нас было все в порядке с доступным для публикации ключом, потому что токен бесполезен без закрытого ключа API.

Затем мы получаем загрузку по ее идентификатору, чтобы мы могли получить имя (используется в ссылке на транзакцию) и его цену (в центах / пенсов и т. Д.).

Значение $ token — это то, что мы получили от Stripe в Javascript выше, который мы затем вставили в форму.

Далее мы используем Stripe_Charge::create для фактической зарядки карты. Настройка $ валюты не требуется, но по умолчанию она будет равна доллару США, даже если вы создали учетную запись за пределами США.

Если оплата прошла успешно, мы помещаем идентификатор купленного товара в сеанс. Мы будем использовать это для контроля доступа в данный момент.

Если платеж не проходит — обычно потому, что банк отклонил транзакцию — Stripe_CardError исключение Stripe_CardError .

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

Маршрут подтверждения прост:

 Route::get('/confirmed', function() { $download = Download::find(Session::get('purchased_download_id')); return View::make('confirmed', array('download' => $download)); }); 

Представление, которое позволяет покупателю загрузить свой файл, нажав кнопку загрузки:

 // app/views/confirmed.blade.php <h1>Your Order has been Confirmed</h1> <p>You can now download your file using the button below:</p> <p><a href="/download/{{ $download->id }}" class="btn btn-lg btn-primary">Download</a></p> 

Доставка файла

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

 Route::get('/download/{id}', function($id) { $download = Download::find($id); if ((Session::has('purchased_download_id') && (Session::get('purchased_download_id') == $id))) { Session::forget('purchased_download_id'); return Response::download(storage_path().'/'.$download->filepath); } else { App::abort(401, 'Access denied'); } }); 

Эту ссылку можно использовать только один раз; на практике вы, вероятно, захотите сделать возможным скачивание файла позже, но я оставлю вас, чтобы придумать способы сделать это.

Резюме

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