В предыдущей статье я рассказывал, как создать интернет-магазин с помощью Rails с нуля, и объяснял, как создать корзину для покупок с помощью Redis. Эта статья продолжает этот путь, добавляя, как принимать платежи, используя Braintree. Braintree помогает онлайн-компаниям обрабатывать платежи по кредитным картам, предоставляя торговый счет, платежный шлюз, регулярные счета и хранение кредитных карт.
Исходный код этой статьи находится в том же репозитории, что и упомянутая статья, под веткой Part2 .
Шаг 1: подготовить заявку
Купить Присоединиться Модель
Нам еще предстоит создать какие-либо отношения между моделями User
& Movie
, которые должны представлять купленные фильмы для каждого пользователя или покупателей для каждого фильма. Давайте объявим отношение « many-to-many
используя has_many :through
чтобы сделать ассоциацию косвенно, через модель соединения. Мы назовем это Purchase
.
Сгенерируйте модель « Purchase
с двумя foreign_keys
:
rails g model purchase movie_id:integer buyer_id:integer
Добавьте index
объединяющий внешние ключи для миграции:
class CreatePurchases < ActiveRecord::Migration def change create_table :purchases do |t| t.integer :movie_id t.integer :buyer_id t.timestamps end add_index :purchases, [:movie_id, :buyer_id], unique: true end end
Затем перенесите вашу базу данных:
rake db:migrate
Наконец, добавьте ассоциации к моделям:
# Movie Model has_many :purchases has_many :buyers, through: :purchases # User Model has_many :purchases, foreign_key: :buyer_id has_many :movies, through: :purchases # Purchase Model belongs_to :movie belongs_to :buyer, class_name: 'User'
Вы можете использовать опции
:foreign_key
и:foreign_key
для предоставления имени модели и внешнего ключа, если имя другой модели не может быть получено из имени ассоциации.
Функциональность покупки
Нам необходимо реализовать несколько основных методов для представления процесса покупки. Внутри User
добавьте cart_total_price
, которая возвращает общую стоимость фильмов в корзине:
def cart_total_price total_price = 0 get_cart_movies.each { |movie| total_price+= movie.price } total_price end def get_cart_movies cart_ids = $redis.smembers "cart#{id}" Movie.find(cart_ids) end
Еще один новый метод, purchase_cart_movies!
, зацикливается на тележке с фильмами, покупает ее, затем очищает корзину:
def purchase_cart_movies! get_cart_movies.each { |movie| purchase(movie) } $redis.del "cart#{id}" end def purchase(movie) movies << movie unless purchase?(movie) end def purchase?(movie) movies.include?(movie) end
Дразнящие купленные фильмы
Предположим, что после покупки любого фильма у пользователя будет доступ к его просмотру онлайн. Мы можем посмеяться над этим сценарием, добавив видео YouTube для каждого фильма:
rails g migraiton add_video_url_to_movies
Затем rake db:migrate
базу данных и добавьте несколько видео в каждый фильм. Я обновил файл CSV несколькими URL-адресами YouTube, чтобы вы могли использовать его и заново заполнить базу данных.
Представление show
необходимо изменить для пользователей, которые приобрели фильм.
<%if signed_in?%> <%if current_user.purchase? @movie %> <div class="flex-video"> <iframe width="100%" height="" src="<%= @movie.video_url %>" frameborder="0" allowfullscreen></iframe> </div> <%else%> <%=link_to "", class: "button", data: {target: @cart_action, addUrl: add_to_cart_path(@movie), removeUrl: remove_from_cart_path(@movie)} do%> <i class="fi-shopping-cart"></i> <span><%=@cart_action%></span> Cart <%end%> <%end%> <%end%>
Кроме того, ценник фильма должен быть изменен на «Куплено».
<p class="label movie-label radius mb1"> <%= (current_user && current_user.purchase?(@movie)) ? "Purchased" : "$#{@movie.price}" %> </p>
Шаг 2: инициализировать Braintree
Braintree предоставляет компаниям возможность принимать платежи в Интернете или в своем мобильном приложении. Это полнофункциональная платежная платформа, которая заменяет традиционную модель получения платежного шлюза и торгового счета от разных провайдеров.
Брейнтри Песочница
Braintree предоставляет «песочницу», которая позволяет попробовать Braintree без каких-либо обязательств и проверяет вашу интеграцию перед началом производства. Посетите страницу «Начало работы Braintree» и зарегистрируйте учетную запись «песочницы».
После входа в свою учетную запись «песочницы» вы будете перенаправлены на панель мониторинга, которая позволит вам воспользоваться преимуществами платежного шлюза и хранилища. Он также обеспечивает обработку, хранение и извлечение кредитных карт в соответствии с PCI.
Ключи и настройки песочницы
Внутри панели инструментов песочницы или внутри ключей API в настройках учетной записи пользователя вы найдете ключи и конфигурации приложения, которые добавлены в инициализаторы приложения. В производственной среде вы замените эти ключи на производственные ключи учетной записи Braintree.
Теперь вернемся к нашему каталогу приложений и добавим необходимые гемы в Gemfile
:
gem 'braintree', '~> 2.33.1' gem 'figaro', '~> 0.7.0'
Figaro — простой гем конфигурации приложений Rails, использующий ENV и один файл YAML, чтобы упростить безопасную настройку приложений Rails, поощряя соглашение, которое не допускает конфигурацию в Git.
Создайте новый инициализатор /config/initializers/braintree.rb и добавьте основные ключи конфигурации Braintree в качестве переменных среды, чтобы надежно защитить секретные ключи от контроля версий.
Braintree::Configuration.environment = :sandbox Braintree::Configuration.logger = Logger.new('log/braintree.log') Braintree::Configuration.merchant_id = ENV['BRAINTREE_MERCHANT_ID'] Braintree::Configuration.public_key = ENV['BRAINTREE_PUBLIC_KEY'] Braintree::Configuration.private_key = ENV['BRAINTREE_PRIVATE_KEY']
Мы используем драгоценность figaro
чтобы легко справляться с этими переменными среды. В каталоге вашего приложения запустите:
$ rails generate figaro:install
Это создаст закомментированный файл config / application.yml и добавит его в ваш файл .gitignore
.
Вернитесь в свою учетную запись Braintree Sandbox, в разделе « Код конфигурации » выберите Ruby
качестве языка программирования. Скопируйте ключи конфигурации на стороне сервера, добавьте их в application.yml, и все готово!
# Braintree configuration keys BRAINTREE_MERCHANT_ID: 'use_your_merchant_id' BRAINTREE_PUBLIC_KEY: 'use_your_public_key' BRAINTREE_PRIVATE_KEY: 'use_your_private_key'
Шаг 3: Обработка транзакции
Как работает Брейнтри
Braintree предлагает дополнительные клиентские и серверные SDK. Они представляют собой решение для шифрования на стороне клиента, которое сочетает в себе лучшее из традиционного подхода Braintree Server-to-Server (S2S) и инновационное решение Transparent Redirect (TR). Вкратце, механизм Брейнтри можно описать следующим образом:
- Бэкэнд приложения генерирует клиентский токен, используя Ruby SDK для внешнего интерфейса, который инициализирует JavaScript SDK с использованием этого клиентского токена.
- Предоставленная Braintree библиотека JavaScript шифрует конфиденциальные данные с помощью открытого ключа и связывается с Braintree до того, как форма будет отправлена на ваш сервер.
- Как только данные поступают на серверы Braintree, они дешифруются с использованием закрытого ключа пары ключей, а затем возвращают одноразовый метод оплаты в ваш код клиента. Ваш код передает этот одноразовый номер на ваш сервер.
- Ваш код на стороне сервера предоставляет одноразовый метод оплаты Ruby SDK для выполнения операций Braintree.
Контроллер создания транзакций
Создайте контроллер «транзакций», который будет отвечать за обработку операций Braintree:
$ rails g controller transactions new
Сконфигурируйте файл route.rb, чтобы ограничить транзакции new
, create
только действия:
resources :transactions, only: [:new, :create]
Очевидно, мы должны вызвать authenticate_user!
прежде чем получить доступ к этим действиям контроллера, убедитесь, что current_user
уже добавил некоторые элементы в корзину.
class TransactionsController < ApplicationController before_action :authenticate_user! before_action :check_cart! # Other Code private def check_cart! if current_user.get_cart_movies.blank? redirect_to root_url, alert: "Please add some items to your cart before processing your transaction!" end end end
Создать клиентский токен
Внутри new
действия сгенерируйте клиентский токен и передайте его инициализатору Braintree JS SDK. Существует множество способов передачи переменных из приложения Rails в JavaScript. Одним из них является использование камня Gon . Gon позволяет нам устанавливать переменные в наших контроллерах и затем обращаться к ним из JavaScript. Давайте установим Gon
обычным способом, добавив его в Gemfile
и запустив Gemfile
bundle install
.
gem 'gon', '~> 5.1.2'
Затем обновите файл макета application.html.erb , добавив include_gon
внутри раздела head
.
<%= include_gon %> <%= stylesheet_link_tag "application" %> <%= javascript_include_tag "vendor/modernizr" %> <%= csrf_meta_tags %>
Вернитесь к нашим transactions#new
action и установите переменные для объекта gon
. Сгенерируйте клиентский токен и установите его в client_token
в переменной gon.
def new gon.client_token = generate_client_token end private def generate_client_token Braintree::ClientToken.generate end
Настройка Braintree JavaSript SDK
Примечание. Мы используем Javascript в качестве нашего клиента. Braintree также поддерживает другие клиенты, такие как Android и iOS.
Чтобы настроить JavaScript SDK, просто включите его в макет приложения:
<script src="https://js.braintreegateway.com/v2/braintree.js"></script>
Braintree Client SDK предлагает полный пользовательский интерфейс для удобной интеграции в ваше приложение. Это позволяет вам принимать платежи по кредитным картам и через PayPal посредством одной из следующих интеграций:
- Встроенный пользовательский интерфейс, который получает полнофункциональную проверку с опцией PayPal.
- Кнопка PayPal .
- Токенизация кредитной карты для использования собственного настраиваемого пользовательского интерфейса проверки карты вместо раскрывающегося списка.
Для создания новой формы транзакции мы будем использовать выпадающий интерфейс оформления заказа.
Используйте Drop-In UI
Чтобы использовать пользовательский интерфейс оплаты в выпадающем меню, настройте форму, в которой пользовательский интерфейс будет добавлять метод оплаты nonce:
<div class="form-container radius-box glassy-bg small-10 small-centered medium-8 large-6 columns"> <h2 class="mbs">New Transaction</h2> <%= form_tag transactions_path do%> <p>Please enter your payment details:</p> <div id="dropin"></div> <%=submit_tag "Pay #{current_user.cart_total_price}$", class: "button mt1" %> <%end%> </div>
Затем, в client_token
, инициализируйте Javascript SDK Braintree, передайте ему client_token
сгенерированный нами с помощью gon
, и укажите dropin
интеграцию для добавления пользовательского интерфейса оплаты в форму.
$ -> unless typeof gon is 'undefined' braintree.setup(gon.client_token, 'dropin', { container: 'dropin' });
Мы должны показать кнопку извлечения внутри представления carts / show.html.erb, которая перенаправляет пользователей в новую форму транзакции.
<%=link_to "Checkout", new_transaction_path, class: "button" unless current_user.get_cart_movies.blank?%>
Теперь перейдите к новой транзакции, чтобы увидеть полный пользовательский интерфейс платежа, который включает в себя форму ввода карты и кнопку PayPal с интерфейсом Braintree Drop-In.
Когда пользователь заполняет форму оплаты и отправляет ее, наш клиентский код создает одноразовый метод оплаты для использования на нашем сервере. Конфиденциальные незашифрованные данные не включаются в форму, отправляемую на наш сервер, и, следовательно, никогда не будут проходить через нашу систему. Другие поля, не относящиеся к оплате, будут нам доступны. Это означает, что мы можем выполнять пользовательские проверки, не подвергая себя угрозам безопасности или дополнительным требованиям соответствия PCI, связанным с передачей незашифрованных данных кредитных карт через нашу среду.
Создать транзакцию
Минимальные обязательные атрибуты для отправки транзакции: сумма транзакции и способ оплаты одноразовый. Помните, что одноразовый номер предоставляется клиентским кодом JavaScript SDK.
class TransactionsController < ApplicationController before_action :authenticate_user! before_action :check_cart def new gon.client_token = generate_client_token end def create @result = Braintree::Transaction.sale( amount: current_user.cart_total_price, payment_method_nonce: params[:payment_method_nonce]) if @result.success? current_user.purchase_cart_movies! redirect_to root_url, notice: "Congraulations! Your transaction has been successfully!" else flash[:alert] = "Something went wrong while processing your transaction. Please try again!" gon.client_token = generate_client_token render :new end end private def generate_client_token Braintree::ClientToken.generate end def check_cart if current_user.get_cart_movies.blank? redirect_to root_url, alert: "Please add some items to your cart before processing your transaction!" end end end
Результат транзакции может быть успешным или неудачным. Когда @result.success?
возвращает true
, это означает, что ваша транзакция была принята и сохранена как авторизованная операция:
@result.transaction.status #=> "authorized"
В противном случае он вернет false
если валидация откажет транзакции в авторизации. Объект результата будет содержать ошибки проверки, указывающие, какие параметры были недопустимыми, поэтому вы можете отобразить их в своем представлении:
@result.errors
Кроме того, результат указывает, была ли сделка создана с использованием учетной записи PayPal или кредитной карты:
@result.transaction.payment_instrument_type == PaymentInstrumentType::CreditCard #=> true
Итак, давайте проверим нашу функциональность. Добавьте несколько фильмов в корзину, перейдите на страницу « Моя корзина» и нажмите « Оформить заказ», чтобы перейти к форме транзакции. Введите случайную информацию о кредитной карте, используя один из этих номеров кредитных карт, и отправьте информацию о платеже, нажав кнопку « Оплатить» .
Если все в порядке, выбранные фильмы будут успешно куплены, и у вас будет доступ к их просмотру в Интернете.
Теперь, когда вы заходите в свою учетную запись песочницы и нажимаете на ссылку Транзакции в разделе Расширенный поиск , вы можете искать и просматривать все транзакции, созданные вашим приложением. Здесь мы можем увидеть нашу последнюю транзакцию с авторизованным статусом.
Варианты обработки
Вы можете заметить, что пользовательский интерфейс Braintree Drop-In не отображает некоторые поля, такие как CVV и AVS. Проверка CVV рекомендуется в качестве предварительного способа предотвращения мошенничества. Чтобы добавить их, вы должны включить и настроить их правила для своей учетной записи в песочнице.
Перейдите в раскрывающемся меню «Настройки» в верхнем меню и нажмите « Обработка» . Эти параметры находятся в разделе Базовая защита от мошенничества, и вы можете включить и настроить их правила проверки. После завершения и сохранения конфигурации эти поля появятся в пользовательском интерфейсе, и их проверки будут работать так, как вы их настроили.
Шаг 4: работа с клиентами
Хранилище Брейнтри
Что если мы хотим связать кредитные карты пользователей с их учетными записями, чтобы им не приходилось добавлять данные карты каждый раз, когда они хотят оформить заказ ?. Braintree предлагает технологию Vault, чтобы справиться с этим. Использование хранилища позволяет надежно хранить информацию о кредитной карте клиента без необходимости повторного ввода ее клиентами каждый раз, когда они покупают продукт на нашем веб-сайте.
Создать клиента
Создание нового клиента в хранилище вернет идентификатор, который используется для доступа к информации этого клиента. Итак, нам нужно добавить braintree_customer_id
в модель User
чтобы сохранить этот идентификатор клиента:
$ rails g migration add_braintree_customer_id_to_users $ rake db:migrate
Кроме того, внутри модели User
напишите метод, который проверяет, есть ли у пользователя информация об оплате или нет:
def has_payment_info? braintree_customer_id end
Затем создайте частичную форму для сведений о клиенте и условно отобразите ее в new
форме транзакции:
# views/transactions/new.html.erb <%= form_tag transactions_path do%> <%= render 'customer_form' unless current_user.has_payment_info? %> <p>Please enter your payment details:</p> <div id="dropin"></div> <%=submit_tag "Pay #{current_user.cart_total_price}$", class: "button mt1" %> <%end%> # views/transactions/_customer_form.html.erb` <p>Please enter your personal info:</p> <div class="mb1"> <%= text_field_tag :first_name, "",placeholder: "First Name", class: "radius" %> </div> <div class="mb1"> <%= text_field_tag :last_name, "",placeholder: "Last Name", class: "radius" %> </div> <div class="mb1"> <%= text_field_tag :company, "",placeholder: "Company", class: "radius" %> </div> <div class="mb1"> <%= text_field_tag :phone, "",placeholder: "Phone", class: "radius" %> </div>
Затем вернитесь к контроллеру transactions
и измените закрытый метод generate_client_token
чтобы идентифицировать существующего клиента, добавив customer_id
.
private def generate_client_token if current_user.has_payment_info? Braintree::ClientToken.generate(customer_id: current_user.braintree_customer_id) else Braintree::ClientToken.generate end end
Обновите действие create
чтобы сохранить нового клиента в хранилище, если current_user
не имеет платежной информации:
def create unless current_user.has_payment_info? @result = Braintree::Transaction.sale( amount: current_user.cart_total_price, payment_method_nonce: params[:payment_method_nonce], customer: { first_name: params[:first_name], last_name: params[:last_name], company: params[:company], email: current_user.email, phone: params[:phone] }, options: { store_in_vault: true }) else @result = Braintree::Transaction.sale( amount: current_user.cart_total_price, payment_method_nonce: params[:payment_method_nonce]) end if @result.success? current_user.update(braintree_customer_id: @result.transaction.customer_details.id) unless current_user.has_payment_info? current_user.purchase_cart_movies! redirect_to root_url, notice: "Congraulations! Your transaction has been successfully!" else flash[:alert] = "Something went wrong while processing your transaction. Please try again!" gon.client_token = generate_client_token render :new end end
Обратите внимание: если SDK Braintree Client успешно использует одноразовый метод оплаты, этот одноразовый номер затем будет связан с идентифицированным клиентом. Для этого мы оставляем сделку продажи как есть, если в current_user
уже есть информация об оплате.
Хранение нескольких кредитных карт
Протестируйте сценарий оплаты еще раз, и вы увидите, что ранее введенные данные кредитной карты уже есть! Вы можете нажать оплату напрямую или изменить способ оплаты, чтобы ввести новые данные кредитной карты, которые также будут связаны с вашим клиентом Braintree и доступны для будущих транзакций.
Все еще нужно больше?
Теперь вы знакомы с API Braintree и понимаете, как обращаться с их Client & Server SDK. Приложение может принимать платежи от пользователей с кредитных карт и хранить их в хранилище.
С помощью Braintree можно сделать еще больше, включая предоставление планов подписки для пользователей, которые могут быть обновлены, понижены или отменены. Может быть, я расскажу об этом в следующем посте …