Регистрация, страница ценообразования, оплата и предоставление доступа — это четыре составные части процесса оформления подписки. Чтобы предоставить доступ, нам нужен (1) зарегистрированный пользователь, который (2) выбрал свою цену на странице ценообразования и (3) установил свою платежную информацию. В этой статье мы покажем примеры на Ruby on Rails, но принципы применимы к любому веб-фреймворку полного стека.
Регистрация
SaaS-приложениям необходим определенный способ аутентификации (проверки того, что пользователи являются теми, за кого себя выдают) и авторизации доступа (проверки того, что пользователям должен быть разрешен доступ к услугам, к которым они допущены). В нашем примере авторизация будет определяться наличием или отсутствием у пользователя активной подписки.
Регистрация — это процесс, когда человек становится новым пользователем приложения SaaS, что позволяет ему пройти аутентификацию, когда он возвращается позже. Вы видели этот процесс миллион раз: нажмите «присоединиться» или «зарегистрироваться» или «зарегистрироваться» > введите электронную почту или имя пользователя и пароль, возможно, подтверждение пароля > возможно, проверьте свою электронную почту, чтобы убедиться, что это вы.
В конце процесса регистрации новая запись о пользователе сохраняется где-то в базе данных. В некоторых SaaS-приложениях вы увидите концепцию команды или организации, где несколько пользователей будут иметь общий доступ к одним и тем же услугам. В конечном итоге нам нужно будет решить, хотим ли мы выставлять счета на уровне пользователя или на уровне команды. В этой статье мы не будем рассматривать детали тарификации по местам, но просто знайте, что подписка на места очень похожа на подписку с фиксированной ценой, за исключением того, что в ней передается количество для линейного элемента, представляющего количество мест.
Клиент Stripe представляет клиента вашего бизнеса. Он позволяет вам создавать повторяющиеся счета и отслеживать платежи, принадлежащие одному и тому же клиенту. Если мы планируем выставлять счета на уровне пользователей, мы привяжем Stripe Customer к пользователю, если мы планируем выставлять счета на уровне команды, мы привяжем Stripe Customer к команде.
Некоторые платежные потоки Stripe автоматически создадут для нас клиента Stripe. Использование ссылок на оплату или встраиваемой таблицы цен приведет к созданию нового клиента Stripe. Если мы не указываем ID клиента в Stripe Checkout в режиме subscription
, он также создаст клиента Stripe при подписке пользователя.
Создание клиента
Нам предстоит сделать выбор: должны ли мы сами создать объект Stripe Customer или позволить Stripe сделать это в рамках существующего потока платежей?
С одной стороны, если мы создадим клиента Stripe заранее, мы сможем гарантировать, что все будущие действия будут связаны с конкретным клиентом. С другой стороны, удобно использовать ссылки на платежи (без кода) или встраиваемую таблицу цен (очень низкокодовый компонент). Если мы используем Payment Links или Pricing table, мы можем включить ссылку на объект User с client_reference_id
. Stripe Checkout также поддерживает передачу client_reference_id
.
Примечание: Для наибольшей гибкости и взаимодействия с большинством продуктов Stripe, позвольте Stripe создать объект Customer для вас и используйте client_reference_id
для отслеживания того, какой пользователь подписался.
Если вы хотите больше контроля, не хотите использовать встраиваемую таблицу цен или ссылки на платежи и хотите получить более продвинутые возможности тестирования, используйте API для создания нового клиента Stripe при регистрации нового пользователя. Это единственный путь, если вы хотите использовать тестовые часы — о них рассказывается в разделе бонусов ниже!
Часто задаваемые вопросы о SaaS onboarding
Если я использую встраиваемую таблицу цен, как мне узнать, кто из пользователей подписался?
Веб-компонент таблицы цен принимает свойство client-reference-id
, в котором вы можете передать идентификатор объекта User, or or Team:
<script async src="https://js.stripe.com/v3/pricing-table.js"></script>
<stripe-pricing-table
pricing-table-id="prctbl_1LLsmTCZ6qsJgndJRFzV9Jia"
publishable-key="pk_test_vAZ3gh1LcuM7fW4rKNvqafgB00DR9RKOjN"
client-reference-id="<%= current_user.id %>">
</stripe-pricing-table>
Уведомления webhook о результирующей сессии Checkout Session будут содержать глобальный ID в свойстве client_reference_id
в полезной нагрузке, так что вы сможете определить, с каким пользователем связана данная сессия Checkout Session.
Должен ли я создавать клиента Stripe до или после фиксации пользователя в базе данных, если я использую API, а не позволяю Stripe создавать клиента за меня?
Мы рекомендуем создавать клиента Stripe после того, как пользователь полностью зафиксирован в базе данных, потому что нет простого способа отменить вызов API для создания клиента. Существует несколько причин, по которым запись пользователя может не быть успешно записана в базу данных: например, сбой ограничения уникальности, когда пользователь с одним и тем же адресом электронной почты пытался зарегистрироваться дважды.
Какую информацию я должен передать Stripe при создании объекта Customer?
Вам не нужно передавать какую-либо информацию. Однако я рекомендую создавать полных клиентов с максимально возможным контекстом. Это облегчит вам жизнь в дальнейшем, когда вам нужно будет обрабатывать запросы в службу поддержки о возврате средств, спорах и других административных задачах. Я также рекомендую хранить ID пользователя из вашей базы данных в метаданных клиента Stripe.
params = {
name: user.name,
email: user.email,
phone: user.phone,
address: {
line1: user.address_line1,
city: user.address_city,
state: user.address_state,
postal_code: user.address_postal_code,
country: user.address_country,
},
shipping: {
name: user.name,
phone: user.phone,
address: {
line1: user.address_line1,
city: user.address_city,
state: user.address_state,
postal_code: user.address_postal_code,
country: user.address_country,
},
},
metadata: {
user_id: user.id,
},
}
customer = Stripe::Customer.create(params)
Какую информацию о Stripe Customer, если таковая имеется, я должен хранить в своей базе данных? Нужен ли каждому пользователю объект Customer? Каждой команде? Существует ли идеальная связь 1:1 между пользователем в моей базе данных и клиентом на Stripe?
Опять же, технически вам не нужно ничего хранить в базе данных. Однако я рекомендую хранить в базе данных хотя бы строковый ID клиента Stripe (который выглядит как cus_abc123
). Вы можете хранить его непосредственно в таблице users
или teams
, или создать отдельную таблицу для хранения идентификаторов клиентов Stripe и их связи с PaymentMethods и Subscriptions. Ведение отдельной таблицы дает немного больше гибкости в дальнейшем, если вы решите перейти от поддержки отдельных пользователей к командам. Это также позволяет разделить проблемы. Посмотрите на схему базы данных pay-rails с открытым исходным кодом или миграции cashier-stripe для вдохновения при моделировании вашей базы данных. Если вы хотите получить больше предложений о том, как смоделировать вашу базу данных, чтобы она хорошо работала с SaaS, дайте мне знать об этом в Twitter: @cjav_dev.
Поскольку вызов API занимает некоторое время, должен ли я отправить запрос API для создания клиента Stripe в фоновом режиме с помощью задания?
Превращение гостей в платящих пользователей — процесс, требующий много времени, поэтому одна из лучших практик — сделать этот процесс как можно более быстрым. Если у вас уже есть система для асинхронной обработки заданий, то я рекомендую создавать клиента и обновлять пользователя в фоновом режиме, который планируется сразу после фиксации пользователя в базе данных.
Если в вашем приложении еще нет фоновых заданий, то достаточно быстро создать клиента Stripe синхронно и вернуться к этому позже по мере масштабирования.
class CreateStripeCustomerJob < ApplicationJob
queue_as :default
def perform(user)
params = {
email: user.email,
# ...
metadata: {
user_id: user.id,
}
}
customer = Stripe::Customer.create(params)
user.update!(stripe_customer_id: customer.id)
end
end
class User < ApplicationRecord
after_commit :ensure_stripe_customer
def ensure_stripe_customer
return if stripe_customer_id.present?
CreateStripeCustomerJob.perform_later(self)
end
end
Что делать, если у меня уже есть много пользователей из успешной бета-программы, запущенной до начала платежей?
Есть несколько вариантов обратного заполнения существующих пользователей и создания для них объектов Stripe Customer. Вы можете запустить одноразовую миграцию или обновить ваш поток onboarding так, чтобы он лениво создавал Stripe клиентов, если таковых не существует, каждый раз, когда существующий пользователь возвращается. Я предпочитаю запустить миграцию и думаю, что это более простой из двух вариантов.
(Бонус!) Тестовые часы
В качестве супер-пупер бонуса я хотел поделиться шаблоном, который я использую в последнее время в разработке, чтобы облегчить создание и тестирование будущих платежей.
Тестовые часы — это относительно новая функция тестового режима, которая позволяет вам имитировать путешествие во времени, чтобы проверить, что ваше приложение правильно обрабатывает все события webhook, которые могут в конечном итоге произойти (например, сбой платежа или переход на более высокий уровень тарифного плана).
Тестовые часы должны быть связаны с клиентом при создании объекта Customer. Это означает, что мы можем использовать тестовые часы только при явном создании клиента. Обратите внимание, что тестовые часы не совместимы с таблицей цен или ссылками платежей, поскольку пока нет возможности использовать существующих клиентов с этими интеграциями. Вы все еще можете протестировать логику веб-крючков, но вам нужно будет вручную создать клиента и подписку с помощью API, а затем продвинуть часы для тестирования различных сценариев веб-крючков.
Если мы находимся в стадии разработки, создайте тестовые часы и прикрепите их к только что созданному клиенту.
params = {
email: user.email,
# ...
metadata: {
user_id: user.id,
}
}
if Rails.env.development?
test_clock = Stripe::TestHelpers::TestClock.create(
frozen_time: Time.now.to_i,
)
params[:test_clock] = test_clock.id
end
customer = Stripe::Customer.create(params)
Позже, когда мы создадим подписку для этого клиента, мы можем зайти в Stripe Dashboard и перевести часы на будущую дату, что приведет к отправке нескольких событий webhook в ваш обработчик webhook (то, что мы обсудим позже в этой серии во время инициализации).
Ознакомьтесь с этим эпизодом о тестовых часах, чтобы узнать больше. Следите за следующей статьей, чтобы узнать больше о создании страниц ценообразования.
Об авторе
CJ Avilla (@cjav_dev) — представитель разработчиков в Stripe, разработчик Ruby on Rails и YouTuber. Он любит изучать и преподавать новые языки программирования и веб-фреймворки. Когда он не за компьютером, он проводит время с семьей или катается на велосипеде 🚲.