Статьи

Интеграция платежей Braintree в Rails

Braintree-Logo

В предыдущей статье я рассказывал, как создать интернет-магазин с помощью 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> 

IMG_20140817_174536

Шаг 2: инициализировать Braintree

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

Брейнтри Песочница

Braintree предоставляет «песочницу», которая позволяет попробовать Braintree без каких-либо обязательств и проверяет вашу интеграцию перед началом производства. Посетите страницу «Начало работы Braintree» и зарегистрируйте учетную запись «песочницы».

part2_1

После входа в свою учетную запись «песочницы» вы будете перенаправлены на панель мониторинга, которая позволит вам воспользоваться преимуществами платежного шлюза и хранилища. Он также обеспечивает обработку, хранение и извлечение кредитных карт в соответствии с 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). Вкратце, механизм Брейнтри можно описать следующим образом:

  1. Бэкэнд приложения генерирует клиентский токен, используя Ruby SDK для внешнего интерфейса, который инициализирует JavaScript SDK с использованием этого клиентского токена.
  2. Предоставленная Braintree библиотека JavaScript шифрует конфиденциальные данные с помощью открытого ключа и связывается с Braintree до того, как форма будет отправлена ​​на ваш сервер.
  3. Как только данные поступают на серверы Braintree, они дешифруются с использованием закрытого ключа пары ключей, а затем возвращают одноразовый метод оплаты в ваш код клиента. Ваш код передает этот одноразовый номер на ваш сервер.
  4. Ваш код на стороне сервера предоставляет одноразовый метод оплаты 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 посредством одной из следующих интеграций:

  1. Встроенный пользовательский интерфейс, который получает полнофункциональную проверку с опцией PayPal.
  2. Кнопка PayPal .
  3. Токенизация кредитной карты для использования собственного настраиваемого пользовательского интерфейса проверки карты вместо раскрывающегося списка.

Для создания новой формы транзакции мы будем использовать выпадающий интерфейс оформления заказа.

Используйте 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?%> 

part2_2

Теперь перейдите к новой транзакции, чтобы увидеть полный пользовательский интерфейс платежа, который включает в себя форму ввода карты и кнопку 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, чтобы справиться с этим. Использование хранилища позволяет надежно хранить информацию о кредитной карте клиента без необходимости повторного ввода ее клиентами каждый раз, когда они покупают продукт на нашем веб-сайте.

part2_3

Создать клиента

Создание нового клиента в хранилище вернет идентификатор, который используется для доступа к информации этого клиента. Итак, нам нужно добавить 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> 

part2_4

Затем вернитесь к контроллеру 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 уже есть информация об оплате.

part2_5

Хранение нескольких кредитных карт

Протестируйте сценарий оплаты еще раз, и вы увидите, что ранее введенные данные кредитной карты уже есть! Вы можете нажать оплату напрямую или изменить способ оплаты, чтобы ввести новые данные кредитной карты, которые также будут связаны с вашим клиентом Braintree и доступны для будущих транзакций.

part2_6

part2_7

Все еще нужно больше?

Теперь вы знакомы с API Braintree и понимаете, как обращаться с их Client & Server SDK. Приложение может принимать платежи от пользователей с кредитных карт и хранить их в хранилище.

С помощью Braintree можно сделать еще больше, включая предоставление планов подписки для пользователей, которые могут быть обновлены, понижены или отменены. Может быть, я расскажу об этом в следующем посте …