Имевшие дело с сервисом электронных платежей Stripe знают, что он отлично заточен под разработчиков. Его документация написана людьми для людей; есть хороший тестовый режим — полная копия реального, и для перехода на live-режим нужно только заменить ключи, не трогая API и не получая никаких сюрпризов; админка тестового режима — тоже полная копия боевого. В общем, Stripe — это хорошо, и я хочу посвятить эту статью базовым вопросам интеграции сервиса в e-Commerce-проект, объяснив процессы на конкретных и абстрактных примерах. Надеюсь, что мой опыт поможет всем, кто хочет попробовать Stripe на своём проекте.
Однако перед тем, как использовать Stripe, задайте вопрос: «А где находится бизнес, который мы будем обслуживать?». Например, если бизнес российский, Stripe для нас бесполезен: принимать платежи можно из любой страны, но бизнес владельца аккаунта на Stripe должен быть юридически зарегистрирован в одной из доступных стран. Иначе создать и авторизовать аккаунт невозможно. Список стран можно посмотреть здесь. Если вы хотите выводить деньги на другие счета, например, поставщикам (как это делать, я расскажу ниже), то юридически поставщики тоже должны находиться в странах, с которыми работает Stripe. Бизнес клиента, с которым работала я, зарегистрирован в Америке, что позволяло проводить платежи через Stripe.
Также нужно быть готовым, что Stripe не поддерживает xml-протокол 3-D-secure, который требует от клиента вводить код подтверждения, полученный в SMS-сообщении. Stripe просто пытается провести платеж без этой опции, и если банк принимает платежи без 3-D Secure — хорошо, если нет — всё закончится отказом, и с этой карты платить не получится.
Проведение платежа
Чтобы перевести деньги с карточки клиента на наш Stripe-аккаунт, нам нужно сделать следующее. С помощью скрипта Stripe.js получим на фронтенде Stripe token. Дальше мы будем использовать token с серверной стороны, чтобы провести сам платёж.
Подключаем Stripe.js и указываем публичный ключ:
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script type="text/javascript">
Stripe.setPublishableKey('your_public_key');
</script>
Делаем обычную HTML-форму, на input указываем атрибуты data-stripe для работы скрипта. Нам будет нужен номер карты клиента, год и месяц валидности карты и CVC. Имя и фамилию владельца Stripe не требует.
<form action="" method="POST" id="payment-form">
<span class="payment-errors"></span>
<label>Card Number</label>
<input type="text" size="20" data-stripe="number">
<label>Expiration (MM/YY)</label>
<input type="text" size="2" data-stripe="exp_month">
<input type="text" size="2" data-stripe="exp_year">
<label>CVC</label>
<input type="text" size="4" data-stripe="cvc">
<input type="submit" class="submit" value="Submit">
</form>
Теперь получим token:
$(function() {
var $form = $('#payment-form');
$form.submit(function(event) {
// Отключим кнопку, чтобы предотвратить повторные клики
$form.find('.submit').prop('disabled', true);
// Запрашиваем token у Stripe
Stripe.card.createToken($form, stripeResponseHandler);
// Запретим форме submit
return false;
});
});
function stripeResponseHandler(status, response) {
// Получим форму:
var $form = $('#payment-form');
if (response.error) { // Problem!
// Показываем ошибки в форме:
$form.find('.payment-errors').text(response.error.message);
$form.find('.submit').prop('disabled', false); // Разрешим submit
} else { // Token был создан
// Получаем token id:
var token = response.id;
// Вставим token в форму, чтобы при submit он пришел на сервер:
$form.append($('<input type="hidden" name="stripeToken">').val(token));
// Сабмитим форму:
$form.get(0).submit();
}
};
На всякий случай: этот шаг описан в официальной документации.
Теперь мы можем списать деньги с клиента через сервер. Примеры кода на PHP.
// Устанавливаем секретный ключ
\Stripe\Stripe::setApiKey("your_secret_key");
// Забираем token из формы
$token = $_POST['stripeToken'];
// Создаём оплату
try {
$charge = \Stripe\Charge::create(array(
"amount" => 1000, // сумма в центах
"currency" => "usd",
"source" => $token,
"description" => "Example charge"
));
} catch(\Stripe\Error\Card $e) {
// Платёж не прошёл
}
Это всё, что нужно сделать, чтобы перевести деньги с карты клиента на ваш Stripe-счёт.
Автоматические переводы денег вашим поставщикам
Теперь рассмотрим перевод на рабочем примере. Представим, что вы пишете платформу, которая продаёт редкие книги из маленьких издательств по всему миру. Вам нужно переводить деньги вашим поставщикам-издательствам, чтобы они выслали книгу клиенту, и брать себе комиссию 10$ c каждой продажи. Вы не хотите париться с ежемесячными отчётами и выплатами, вы хотите просто переводить деньги каждый раз, когда платит клиент. Stripe это позволяет.
Как и прежде, обязательное условие для настройки автоматических переводов — это нахождение поставщика в одной из стран, поддерживаемых Stripe.
У Stripe есть замечательная штука Managed Acсounts. С помощью этой опции мы как бы создаем Stripe-аккаунт для нашего поставщика, но берём на себя все заботы по управлению аккаунтом, так что самому издательству не нужно будет регистрироваться в Stripe.
Сначала получим информацию о банковском счёте вашего издательства с помощью уже знакомого нам скрипта Stripe.js. Как и в случае списания денег с карты клиента, для операций над банковским счётом нам тоже нужен Stripe token.
Stripe.bankAccount.createToken({
country: $('.country').val(), // 2-хсимвольный код страны (US)
currency: $('.currency').val(), // 3-хсимвольный код валюты (USD)
routing_number: $('.routing-number').val(), // идентификационый номер банка
account_number: $('.account-number').val(), // номер банковского счёта
account_holder_name: $('.name').val(), // имя владельца бизнеса (в нашем примере — издательства)
account_holder_type: $('.account-holder-type').val() // тип аккаунта — идивидуальный предприниматель или компания (individual, company)
}, stripeResponseHandler);
Это тоже описано в документации.
Ремарка. Имейте в виду, что для каждой страны банковские данные (routing_number, account_number ) заполняются по-разному. Например, для европейских стран нужно получать IBAN-номер. Он кладётся в поле account_number, а routing_number не отпраляется вообще. Также для некоторых стран внутренние номера счетов склеиваются в одну строку и записываются в поля. Например, чтобы получить корректный идентификационный номер банка routing_number для Канады, надо склеить transit number и institution number (transit number + institution number). Если transit number: 02345, а institution number: 987, то routing_number будет ‘02345987’. Account number варьируется в зависимости от банка. А для Германии нужен будет только IBAN номер, он заполняется в поле routing_number. Например, IBAN: DE89370400440532013000 (22 символа). Как заполнять эти поля для остальных стран, можно посмотреть тут.
Итак, теперь у нас есть token банковского счёта, куда мы можем выводить деньги поставщикам. Давайте создадим Managed Account. Пусть наше издательство находится в Америке, является компанией, а не ИП, и мы платим ему в американских долларах.
\Stripe\Stripe::setApiKey("your_secret_key");
$account = Account::create([
"country" => 'US',
"managed" => true,
]);
if (isset($account->id)) {
try {
$account->external_accounts->create(
["external_account" => $token] // наш token банковского счета
);
} catch (InvalidRequest $error) {
// произошла ошибка создания
}
}
Казалось бы, теперь у нас есть Managed Account, и можно переводить деньги, но нет: аккаунт нужно верифицировать. Для этого нужно предоставить Stripe определённую юридическую информацию о компании. Какая именно информация нужна и в каких странах, описано здесь.
Итак, для издательства в Америке нам нужно предоставить:
Название | Описание |
---|---|
legal_entity.address.city | Город, в котором расположена компания |
legal_entity.address.line1 | Адрес компании |
legal_entity.address.postal_code | Почтовый индекс |
legal_entity.address.state | Штат |
legal_entity.business_name | Название компании |
legal_entity.business_tax_id | Налоговый идентификационный номер |
legal_entity.dob.day | День рождения владельца компании |
legal_entity.dob.month | Месяц рождения владельца компании |
legal_entity.dob.year | Год рождения владельца компании |
legal_entity.first_name | Имя владельца компании |
legal_entity.last_name | Фамилия владельца компании |
legal_entity.ssn_last_4 | Четыре последние цифры номера социального страхования владельца компании |
legal_entity.type | individual/company |
tos_acceptance.date | Дата принятия условий использования Stripe |
tos_acceptance.ip | IP-адрес, с которого происходило принятие условий использования Stripe |
Условия использования Stripe здесь. Человек, от чьего имени будет создаваться Managed Account, должен их принять.
Также Stripe может потребовать дополнительную информацию. Для Америки это:
Название | Описание |
---|---|
legal_entity.personal_id_number | Личный идентификационный номер |
legal_entity.verification.document | Скан документа, подтверждающего личность |
Собираем необходимую информацию и редактируем аккаунт.
\Stripe\Stripe::setApiKey("your_secret_key");
$account = Account::retrieve($accountId);
$account->legal_entity->address->city = 'New-York';
$account->legal_entity->address->state = 'New-York';
$account->legal_entity->address->postal_code = '00501';
$account->legal_entity->address->line1 = 'Some address';
$account->legal_entity->business_name = 'US TEST';
$account->legal_entity->business_tax_id = '00000001';
$account->legal_entity->dob->day = 1;
$account->legal_entity->dob->month = 1;
$account->legal_entity->dob->year = 1980;
$account->legal_entity->first_name = 'Bob';
$account->legal_entity->last_name = 'Smith';
$account->legal_entity->type = 'company';
$account->legal_entity->ssn_last_4 = '0000';
$account->tos_acceptance->date = 1466074123; // timestamp
$account->tos_acceptance->ip = 123.123.123.123;
try {
$account->save();
} catch (InvalidRequest $error) {
// ошибка во время сохранения
}
Теперь команда Stripe всё проверит, и в админке мы увидим статус Verified.
https://dashboard.stripe.com/test/applications/users/overview
Но этого нам не будет достаточно. Также команда Stripe может указать ошибки в данных или потребовать дополнительной информации, например personal_id_number.
Когда команда проверит данные, аккаунт будет обновлён. На это событие можно настроить webhook.
Необходимые поля будут описаны в объекте аккаунта:
$account->verification->fields_needed
Также Stripe может выставить дедлайн для предоставления данных. Если дата есть, она будет в свойстве $account->verification->due_by.
Для тестирования верификации Stripe предоставляет хорошую тестовую среду. С помощью переводов с определённых тестовых карт мы можем симулировать разные сценарии поведения верификации аккуантов. Примеры таких сценариев:
- данные не заполнены, и мы вообще не можем совершать переводы;
- сработал лимит на размер платежа. Это происходит, если Stripe считает, что перевод слишком большой, и предоставленной информации ему недостаточно. В этом случае он отключает Managed Account;
- отключение аккаунта с требованием ввести данные к определенной дате;
- загрузка скана документа, подтверждающего личность владельца аккаунта;
- принятие и отклонение этого скана.
Как конкретно симулировать эти случаи, описано здесь.
Обработать все ситуации придётся в любом случае. И по моему опыту, лучше сразу предоставить Stripe максимум информации, чтобы избежать сюрпризов с отключением аккаунта.
Когда все окей, и Stripe верифицировал ваш Managed Account, нужно включить переводы (transfers) с помощью API или отключить автоматические — это одно и то же.
https://dashboard.stripe.com/account/transfers
Итак, у нас есть верифициронный аккаунт, переводы включены, и теперь мы можем делать переводы денег напрямую поставщику.
Предположим, у нас есть книга. Поставщик хочет за неё 50$, мы хотим 10$ долларов комиссии себе, плюс нам надо заложить в цену комиссию Stripe на перевод. Сейчас Stripe берёт за каждый перевод 2,9% + 30?. Мы решили, что оплатим комиссию из своей части. Тогда пользователю надо заплатить за книгу 60$. Из своей части мы отдадим 2,04$ комиссии Stripe.
Получаем token с помощью Stripe.js и проводим платёж со стороны сервера.
$charge = Charge::create([
"amount" => 6000, // в центах
"currency" => 'USD',
"source" => $token,
"application_fee" => 1000,
"destination" => $managedAccountId
]);
Свойство application_fee позволяет указать, какую сумму от перевода оставить на нашем счету. Комиссия Stripe будет списываться в любом случае только с нашего счёта, даже если мы сделаем полный перевод поставщику.
На банковский счёт поставщика деньги сразу не придут, они выводятся раз в семь дней. Т.е. мы переводим деньги на Stripe-аккаунт нашего поставщика, и по истечении семи дней они переводятся аккаунту на привязанный банковский счёт.
Дополнительные фичи
Кроме того, Stripe позволяет сохранять клиентов, добавлять произвольные метаданные при создании платежа, чтобы было проще ориентироваться в проведённых платежах, задавать description при платеже для его более информативного описания, и многое другое. Обо всём этом можно посмотреть в документации к API платежей.
Желаю вам удачи в интеграции Stripe! Я буду рада вашим комментариям, вопросам и уточнениям, которые помогут дополнить статью.
Полезные ссылки:
Страны, которые поддерживают Stripe
Custom HTML form для получения token
Managed Acсounts
Получение token для банковского аккаунта
Необходимая банковская информация по странам
Необходимая юридическая информация для Managed Accounts по странам
Условия использования Stripe
Тестирование верификации аккуанта
Webhooks
Stripe Pricing
Stripe API Reference
Комментарии (15)
Miraage
25.07.2016 17:28+2Stripe хорош, его API восхитителен, чудесная интеграция в Laravel Cashier, однако стоило бы упомянуть, что его основная ниша — США.
Если собираетесь использовать его в сервисах, который поддерживает клиентов со всего мира — гиблый вариант.
qazqazasda
25.07.2016 18:00API есть почти что у всех. Плохое или хорошее — но почти у всех у всех.
Гораздо интереснее — а доходят ли деньги, а каков процент и пр. и пр.
Практически все до единой платежной системы, когда выростают, начинают зарабатывать деньги доп. способами —
задерживают чужие деньги у себя, терять каждый 100 000-ый платеж (если не пожалуешься, то деньги и не дойдут), ограничивать (лимиты вводить) на размеры и количества платежей от одного плательщика (что вынуждает заводить доп. аккаунты)…
taulatin_one
25.07.2016 18:02Отлично, вот только берут они, как водится, нагло — 2.9% + $0.30
Когда же эта вакханалия в PayPal стиле закончится то?zharikovpro
25.07.2016 18:41> Когда же эта вакханалия в PayPal стиле закончится то?
Когда ***валюты (основанные на блокчейне) станут основным средством платежа по всему миру, возможно. Не скоро, в общем. Текущим компаниям нет никакого резона просто так снижать тарифы.
Tramvai
25.07.2016 21:12Было бы интересно почитать сравнение с Braintree. Все они берут одинаковые деньги на сколько я помню.
И бонус в видеWhen you integrate directly, your first $50K in standard card processing volume comes fee-free. This applies to all card and digital wallet transactions not through PayPal or Coinbase.
И позиционируют себя как для девелоперов (внятная документация)
klev
25.07.2016 22:07+1Работаю со Stripe:
*. Сайт самого Stripe выглядит очень по современному, всё чисто и аккуратно.
*. Процент зависит от карточки клиента, страны клиента, валюты оплаты, валюты банка приёма и других нюансов; 2.9% — это в среднем, а в реальности часто больше 3%
*. Связь только по электронной почте и IRC, никаких телефонов, форумов или других современных систем поддержки.
*. Ответы в письмах довольно поверхностные и дублируют информацию доступную на сайте. Лично я их обслуживанием недоволен.Skull
26.07.2016 12:32Там еще есть 1.75% + $0.3 для domestic cards и это подходит многим бизнесам
*. Связь только по электронной почте и IRC, никаких телефонов, форумов или других современных систем поддержки.
*. Ответы в письмах довольно поверхностные и дублируют информацию доступную на сайте. Лично я их обслуживанием недоволен.
На самом деле все есть. Нашему иностранному менеджеру дали и телефон и скайп техподдержки. А ответы на мои вопросы в письмах были просто отличные. Они догадались какую схему charge для managed accounts я использую, скинули пример кода который я использую и скинули пример который мне нужно использовать в этом случае. Разжевали вобщем даже больше чем нужно было.
Eklykti
25.07.2016 23:08телефонов, форумов… современных систем поддержки
Телефоны, по которым на том конце может сидеть индус на аутсорсе, слабо понимающий, что от него вообще хотят?
Форумы, на которых будет толпа анонимусов с патчами бармина, а реальных сотрудников поддержки полтора человека, тупо не успевающих фильтровать толпу анонимусов?
Лучше уж по мылу.
klev
26.07.2016 00:50Основная идея форума, в поиске ответов на проблемы, с которыми уже сталкивались до меня, экономит время и предоставляет возможность лучше понять ситуацию.
Система тикетов предоставляет возможность сгруппировать в одном месте все проблемы и их решения, при этом новые люди получают доступ ко всем предыдущим вопросам и их статусам.
Телефон даёт возможность получить за 5 минут разговора ответы на те вопросы, которые по переписки могли бы занять несколько дней.
Емайл хорош для стандартных ситуаций и имеет свои достоинства и недостатки.
Если идёт разговор о нескольких заказах в неделю или в месяц, то одного емайла для поддержки вполне хватит, но для сайта с количеством заказов больше нескольких сотен в месяц, только одного емайла отделу бухгалтерии может быть недостаточно, так как часто бывают сложные ситуации, отмены, поправки, и их проще и быстрее было бы решить по телефону, особенно в первые разы, когда непонятно что и как делать.
По крайней мере для нас, это оказалось критически важно, и из за этого переходим на другую систему.
Понятное дело, что если телефон есть, но с той стороны никто не хочет помочь, то это тоже самое, что его нет.
la0
26.07.2016 09:28+2Я работал с ними.
Осень слабый антифрод и отсутивие 3d-secure / vbv(нет и не планируется).
Собственно, у меня всё. Если сервис не умеет критисные вещи, какая разница какое у него API.
Arslanbekov
Если кто не знает, для студентов Stripe не облагает процентом первую $1.000
Нужен только github student pack