Имею огромное желание рассказать о том, как просто работать с QIWI Shop, используя Ruby on Rails.
Для чего нужен QIWI Shop? Например, у Вас есть свой онлайн-магазин и Вам необходимо принимать платежи от пользователей. Qiwi достаточно распространен в мире. Он не требует наличия персонального аттеста для вывода средств, как это, например, требуют в WebMoney. Поэтому QIWI достаточно привлекателен для интеграрации в онлайн-магазины.
Плюсы:
— Платежный сервис Qiwi представлен в 8 странах: России, Казахстане, Молдове, Румынии, Беларуси, США, Бразилии, Иордании.
— Тысячи терминалов по всей стране (Россия), что облегчает пополнение кошельков без дополнительных процентов.
— Человеку не надо быть знакомым с интернетом, чтобы за минуту оплатить покупку или пополнить баланс из другого города – достаточно дойти до терминала в ближайшем магазине.
— Отсутствие процентов между переводами с одного кошелька на другой.
— Дополнительный уровень защиты с помощью одноразовых СМС-паролей.
— Бесплатное информирование об операциях с Qiwi кошельком.
Я разрабатываю на Ruby On Rails, поэтому примеры, которые буду приводить, будут на языке Ruby.
Qiwi предлагает несколько способов интеграции:
REST-протокол
HTTP-протокол
Форма выставления счета
SOAP-протокол (устаревший)
Несколько слов о каждом способе:
Форма — позволяет сгенерировать HTML-код формы, которую можно разместить на сайте Вашего интернет-магазина, не прибегая к программированию. После чего статус счета можно отслеживать в личном кабинете магазина, либо получая уведомления по электронной почте.
HTTP-протокол — позволяет создавать счета, используя обычные HTTP-запросы. Представляет собой простой запрос методом GET, который формируется на сайте интернет магазина исходя из перечня выбранных пользователем товаров. Легок в реализации на любом языке программирования сайтов. Статус счёта так же можно отслеживать в личном кабинете, либо получая уведомления по электронной почте.
REST-протокол — наиболее полный протокол. Предоставляет всю доступную функциональность для интернет-магазинов. Поддерживает реализацию REST сервера на стороне магазина, поддерживающего автоматизированную логику проведения заказов. Реализацию можно выполнить, например, используя Java, C#, PHP, Ruby и другие.
Рассмотрим REST, который предоставляет множество функций и позволяет комфортно работать с магазином Qiwi.
Функции, которые рассмотрим:
— Создание счета.
— Опрос статуса счета.
Нет смысла рассматривать все методы, т.к. по подобию их легко можно реализовать. Как говорит документация самого Qiwi, сложность реализации REST — высокая, но ничего подобного, все выглядит достаточно просто и лаконично. Рассмотрим пример.
Для начала работы нужно получить свои APP_ID и APP_PASSWORD и SHOP_ID в личном кабинете Qiwi Shop, а для этого нужно пройти регистрацию в Qiwi Shop и дождаться одобрения со стороны Qiwi.
Для авторизации, нужно зашифровать данные магазина в base64 следующим образом:
user_creds = Base64.encode64(Codename::Application::APP_ID + ‘:’ + Codename::Application::APP_PASSWORD)
Codename::Application::APP_ID - глобальная константа из application.rb
В дальнейшем, эти данные будут переданы в заголовке для авторизации. После шифрования необходимо сформировать headers для авторизации в системе:
headers = { :accept => :json, :authorization => 'Basic ' + user_creds, :content_type => 'application/x-www-form-urlencoded; charset=utf-8' }
Так же необходим id счета, который является обычной уникальной строкой, по которой можно будет идентифицировать платеж для дальнейших операций (проверить оплату по счету, отметить счет и т.д.). Сформируем:
bill_id = current_user.email.split("@").first + '-' + SecureRandom.hex(5)
В моем случае формирование bills_id происходит следующим образом.
Берется первая часть email пользователя и добавляется к ней через дефис уникальная строка. Получается на выходе: ‘snowacat-03a765e046’. Формируем ссылку для создания счета:
url = "https://w.qiwi.com/api/v2/prv/" + Codename::Application::SHOP_ID + "/bills/" + bill_id
Время жизни платежа (время в течении которого можно оплатить платеж):
life_time = Time.now.utc + Codename::Application::PAYMENTS_DAYS.day
Важно: время жизни должно быть в формате iso8601.
В нашем случае время платежа неделя:
Codename::Application::PAYMENTS_DAYS.day = 7
Остальные необходимые параметры:
phone_number = '+79181234567' amount = 50
Формируем тело запроса:
data = { user: 'tel:' + phone_number, amount: amount, ccy: 'RUB', comment: 'User email: ' + current_user.email, lifetime: life_time.iso8601, pay_source: 'qw', prv_name: 'Super Mortal Kombat Shop' }
Выполняем транзакцию с помощью гема Rest Client:
begin result = RestClient.put url, data, headers rescue => e payments_logger.info('Creating bill failed! Email: ' + current_user.email + ' Error: ' + e.message ) flash[:error] = 'Выставить счет не вышло. Ошибка: ' + e.message redirect_to action: :pay return end
На этом все. Все основные действия разобраны. Теперь рассмотрим дополнительное и необходимое для понимая полного метода создания счета.
Модель Payment:
class Payment < ActiveRecord::Base STATUSES = [DEFAULT = 0, PAID = 1] belongs_to :user validates :amount, :numericality => { greater_than: 0 } end
Структура таблицы:
class CreatePayments < ActiveRecord::Migration def change create_table :payments do |t| t.integer :user_id, :null => false t.foreign_key :users t.float :amount, :default => 0.0, :null => false t.string :bill_id, :limit => 256, :null => false t.string :phone_number, :limit => 256, :null => false t.integer :status, :limit => 1, :default => 0, :null => false t.timestamps null: false end end end
Полный код метода, в котором отменяется неоплаченный счет, если у пользователя есть таковой.
def create_bill require 'rest-client' require 'base64' require 'securerandom' user_creds = Base64.encode64(Codename::Application::APP_ID + ':' + Codename::Application::APP_PASSWORD) headers = { :accept => :json, :authorization => 'Basic ' + user_creds, :content_type => 'application/x-www-form-urlencoded; charset=utf-8' } # Delete old bill, if current user have it bill = current_user.payments.where(status: Payment::DEFAULT).first if bill # Cancel bill url = "https://w.qiwi.com/api/v2/prv/" + Codename::Application::SHOP_ID + "/bills/" + bill.bill_id data = { status: 'rejected' } begin RestClient.patch url, data, headers bill.delete rescue => e payments_logger.info('Cancelling bill failed! Email: ' + current_user.email + ' Error: ' + e.message ) flash[:error] = 'У вас есть неоплаченный счет. Отменить его не вышло. Причина: ' + e.message redirect_to action: :pay return end end bill_id = current_user.email.split("@").first + '-' + SecureRandom.hex(5) payment = current_user.payments.new( amount: params[:amount], bill_id: bill_id, phone_number: params[:user_phone] ) # Validate data if payment.invalid? flash[:error] = 'Невалидные данные' redirect_to action: :pay return end url = "https://w.qiwi.com/api/v2/prv/" + Codename::Application::SHOP_ID + "/bills/" + payment.bill_id life_time = Time.now.utc + Codename::Application::PAYMENTS_DAYS.day data = { user: 'tel:' + payment.phone_number, amount: payment.amount, ccy: 'RUB', comment: 'User email: ' + current_user.email, lifetime: life_time.iso8601, pay_source: 'qw', prv_name: ' Super Mortal Kombat Shop' } # Start transaction begin result = RestClient.put url, data, headers rescue => e payments_logger.info('Creating bill failed! Email: ' + current_user.email + ' Error: ' + e.message ) flash[:error] = 'Выставить счет не вышло. Ошибка: ' + e.message payment.delete redirect_to action: :pay return end result = JSON.parse(result) if result["response"]["result_code"] == 0 payment.save flash[:notice] = 'Счет выставлен' redirect_to action: :pay else payment.delete flash[:error] = 'Выставить счет не вышло. Статус ошибки: ' + result["response"]["result_code"] redirect_to action: :pay end end
Пояснения:
Для отмены счета используется сгенерированный ранее bill_id и patch запрос.
После успешного выставления счета, необходимо проверять оплату. Для этого создадим модель payment_grabber.rb с методом self.get_payments, который будет вызываться по планировщику (например, с помощью планировщика rufus-sheduler).
def self.get_payments ... ... end
Получаем из БД все неоплаченные счета и проверяем их:
# Get all payments with default statuses bills = Payment.where(status: Payment::DEFAULT) bills.each do |bill| url = "https://w.qiwi.com/api/v2/prv/" + Codename::Application::SHOP_ID + "/bills/" + bill.bill_id begin bill_status = RestClient.get url, headers rescue => e grabber_logger.info('Check bill failed! Error: ' + e.message) next end bill_status = JSON.parse(bill_status) if bill_status["response"]["bill"]["status"] == 'paid' bill.update(status: Payment::PAID) user = User.find_by_id(bill.user_id) new_amount = user.money_real + bill.amount user.update_attribute(:money_real, new_amount) elsif bill_status["response"]["bill"]["status"] == 'waiting' next elsif bill_status["response"]["bill"]["status"] == 'expired' || bill_status["response"]["bill"]["status"] == 'unpaid' || bill_status["response"]["bill"]["status"] == 'rejected' bill.delete else next end
Код планировщика:
scheduler.every Codename::Application::GRABBER_INTERVAL do PaymentGrabber.get_payments end
Переменные, которые хранятся в application.rb
SHOP_ID = '111111' APP_ID = '00000000' APP_PASSWORD = 'XXXXXXXXXXXX' # Count days for payments PAYMENTS_DAYS = 7 # Grabber interval in minutes. How othen sheduler will check payments GRABBER_INTERVAL = '3m'
Эта статья поможет Rails разработчикам интегрировать Qiwi Shop в интернет магазины. На момент написания статьи примеров готового кода на Ruby On Rails в сети не было (не найдено), а в готовых решениях Qiwi — только список CMS.
Спасибо за внимание, на этом все.
ссылка на оригинал статьи http://habrahabr.ru/post/265391/
Добавить комментарий