-
-
Save kryzhovnik/5b73c1c0637e47b01eaa to your computer and use it in GitHub Desktop.
| # config/routes.rb | |
| YandexKassaIntegration::Application.routes.draw do | |
| # ... | |
| scope '/yandex_kassa' do | |
| controller 'yandex_kassa', constraints: { subdomain: 'ssl' } do | |
| post :check | |
| post :aviso | |
| get :success | |
| get :fail | |
| post :fail # исключение: при неуспехе оплаты из кошелька Яндекс.Денег приходит запрос методом POST | |
| end | |
| end | |
| end |
| # app/models/yandex_kassa.rb | |
| module YandexKassa | |
| PARAMS_MAP = { | |
| requestDatetime: :request_datetime, # xs:dateTime Момент формирования запроса в ИС Оператора. | |
| action: :action, # xs:normalizedString, до 16 символов Тип запроса. Значение: «checkOrder» (без кавычек). | |
| md5: :md5, # xs:normalizedString, ровно 32 шестнадцатеричных символа, в верхнем регистре MD5-хэш параметров платежной формы, правила формирования описаны в разделе 4.4 «Правила обработки HTTP-уведомлений Контрагентом». | |
| shopId: :shop_id, # xs:long Идентификатор Контрагента, присваиваемый Оператором. | |
| shopArticleId: :shop_article_id, # xs:long Идентификатор товара, присваиваемый Оператором. | |
| invoiceId: :invoice_id, # xs:long Уникальный номер транзакции в ИС Оператора. | |
| orderNumber: :order_id, # xs:normalizedString, до 64 символов Номер заказа в ИС Контрагента. Передается, только если был указан в платежной форме. | |
| customerNumber: :customer_number, # xs:normalizedString, до 64 символов Идентификатор плательщика (присланный в платежной форме) на стороне Контрагента: номер договора, мобильного телефона и т.п. | |
| orderCreatedDatetime: :order_created_datetime, # xs:dateTime Момент регистрации заказа в ИС Оператора. | |
| orderSumAmount: :order_sum_amount, # CurrencyAmount Стоимость заказа. Может отличаться от суммы платежа, если пользователь платил в валюте, которая отличается от указанной в платежной форме. В этом случае Оператор берет на себя все конвертации. | |
| orderSumCurrencyPaycash: :order_sum_currency_paycash, # CurrencyCode Код валюты для суммы заказа. | |
| orderSumBankPaycash: :order_sum_bank_paycash, # CurrencyBank Код процессингового центра Оператора для суммы заказа. | |
| shopSumAmount: :shop_sum_amount, # CurrencyAmount Сумма к выплате Контрагенту на р/с (стоимость заказа минус комиссия Оператора). | |
| shopSumCurrencyPaycash: :shopSumCurrencyPaycash, # CurrencyCode Код валюты для shopSumAmount. | |
| shopSumBankPaycash: :shop_sum_bank_paycash, # CurrencyBank Код процессингового центра Оператора для shopSumAmount. | |
| paymentPayerCode: :payment_payer_code, # YMAccount Номер счета в ИС Оператора, с которого производится оплата. | |
| paymentType: :payment_type, # xs:normalizedString Способ оплаты заказа. Список значений приведен в таблице 6.6.1. | |
| } | |
| SIGNATURE_PARAMS = [:order_sum_amount, | |
| :order_sum_currency_paycash, :order_sum_bank_paycash, | |
| :shop_id, :invoice_id, :customer_number | |
| ] | |
| class Action | |
| class_attribute :action_name, :shop_id, :password | |
| self.shop_id = Rails.application.secrets.yandex_kassa['shop_id'] | |
| self.password = Rails.application.secrets.yandex_kassa['shop_password'] | |
| attr_reader :params | |
| def initialize(controller_params) | |
| @params = map_params(controller_params) | |
| end | |
| def valid_signature? | |
| values = [action_name] + SIGNATURE_PARAMS.map { |name| params[name] } + [password] | |
| generate_signature(values) == params[:md5] | |
| end | |
| def order | |
| @order ||= Order.find(params[:order_id]) | |
| end | |
| def response | |
| raise NotImplementedError | |
| end | |
| private | |
| def map_params(params) | |
| hashable_array = PARAMS_MAP.map do |param, mapped_param| | |
| [mapped_param, params[param]] | |
| end | |
| HashWithIndifferentAccess[hashable_array] | |
| end | |
| def generate_signature(*params) | |
| Digest::MD5.hexdigest(params.join(';')).upcase | |
| end | |
| end | |
| class CheckOrder < Action | |
| self.action_name = 'checkOrder' | |
| def response | |
| xml = Builder::XmlMarkup.new | |
| xml.instruct! :xml, version: '1.0', encoding: 'UTF-8' | |
| xml.checkOrderResponse(performedDatetime: Time.current.iso8601, | |
| code: code, | |
| invoiceId: params[:invoice_id], | |
| shopId: shop_id | |
| ) | |
| xml.target! | |
| end | |
| private | |
| def code | |
| if valid_signature? | |
| valid_params? ? '0' : '100' | |
| else | |
| '1' | |
| end | |
| end | |
| def valid_params? | |
| if order | |
| order.amount == params[:order_sum_amount].to_i | |
| else | |
| false | |
| end | |
| end | |
| end | |
| class PaymentAviso < Action | |
| self.action_name = 'paymentAviso' | |
| def response | |
| xml = Builder::XmlMarkup.new | |
| xml.instruct! :xml, version: '1.0', encoding: 'UTF-8' | |
| xml.paymentAvisoResponse(performedDatetime: Time.current.iso8601, | |
| code: code, | |
| invoiceId: params[:invoice_id], | |
| shopId: shop_id | |
| ) | |
| xml.target! | |
| end | |
| def payment_type | |
| params[:payment_type] | |
| end | |
| private | |
| def code | |
| valid_signature? ? '0' : '1' | |
| end | |
| end | |
| end |
| # app/controllers/yandex_kassa_controller.rb | |
| class YandexKassaController < ActionController::Base | |
| before_filter :find_order | |
| def check | |
| check_order = YandexKassa::CheckOrder.new(params) | |
| render text: check_order.response | |
| end | |
| def aviso | |
| aviso = YandexKassa::PaymentAviso.new(params) | |
| if aviso.valid_signature? | |
| # Заказ оплачен, платеж поступил на счет Яндекс.Кассы. | |
| # Здесь нужно поместить код исполнения заказа | |
| end | |
| render text: aviso.response | |
| end | |
| def success | |
| # Платеж на сайте Яндекс.Кассы успешно завершен, клиент вернулся на ваш | |
| # сайт по ссылке "Вернуться в магазин". | |
| # В зависимости от выбранного способа оплаты, к этому моменту заказ | |
| # может быть оплачен, а может и нет. Подтверждение оплаты приходит | |
| # в метод `aviso` | |
| redirect_to root_url, notice: I18n.t('messages.payment_completed') | |
| end | |
| def fail | |
| # Платеж на сайте Яндекс.Кассы завершился ошибкой оплаты | |
| redirect_to root_url, notice: I18n.t('messages.payment_failed') | |
| end | |
| private | |
| def find_order | |
| @order = Order.find(params[:orderNumber]) | |
| end | |
| end |
%form{:action => 'https://demomoney.yandex.ru/eshop.xml', :method => 'post', class: 'form-horizontal formpay'}
%input{:name => 'shopId', :type => 'hidden', :value => '101951'}
%input{:name => 'scid', :type => 'hidden', :value => '527643'}
%input{:name => 'CustomerNumber', :size => '64', :type => 'hidden', :value => @active_user_company.id}
.form-group
%input.form-control{placeholder: 'Введите сумму', type: 'text', name: 'sum'}/
.form-group
.row
.col-md-6
.radio
%label
%input#optionsRadios1{:checked => 'true', :name => 'paymentType', :type => 'radio', :value => 'AC'}
%span.titlepay Банковские карты
.payment-big-icons-bankcards
.col-md-6
.radio
%label
%input#optionsRadios1{:checked => '', :name => 'paymentType', :type => 'radio', :value => 'WM'}
%span.titlepay WebMoney (WMR)
.payment-big-icons-wmr
.col-md-6
.radio
%label
%input#optionsRadios1{:checked => '', :name => 'paymentType', :type => 'radio', :value => 'PC'}
%span.titlepay Яндекс.Деньги
.payment-big-icons-yad
.col-md-6
.radio
%label
%input#optionsRadios1{:checked => '', :name => 'paymentType', :type => 'radio', :value => 'AB'}
%span.titlepay Альфа-Клик
.payment-big-icons-alpha
.col-md-6
.radio
%label
%input#optionsRadios1{:checked => '', :name => 'paymentType', :type => 'radio', :value => 'SB'}
%span.titlepay Сбербанк Онлайн
.payment-big-icons-sber
.form-group
.col-md-12.text-right
%button.btn.btn-default{'data-dismiss' => 'modal', :type => 'button'} Отмена
%button.btn.btn-success{:type => 'submit'} Оплатить
А можете вкратце отбъяснить почему Вы реализуете интеграцию яндекс касс таким способом, а не через "active merchant"? И есть ли у Вас более подробный материал по интеграции, а то в интернете по яндекс кассе и rails практически ничего нету.
добавить к модели
require 'builder'
@kryzhovnik спасибо, добрый человек! сэкономил кучи времени разработки
Хорошо.. А что за модель Order? И как инициализировать платёж, т.е. составить url для отправки пользователя на оплату?