Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Интеграция Яндекс.Кассы с Rails
# 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
@voodee

This comment has been minimized.

Copy link

commented Sep 17, 2015

Хорошо.. А что за модель Order? И как инициализировать платёж, т.е. составить url для отправки пользователя на оплату?

@yuriineb

This comment has been minimized.

Copy link

commented Oct 2, 2015

%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'} Оплатить

@bzvyagintsev

This comment has been minimized.

Copy link

commented Feb 23, 2016

А можете вкратце отбъяснить почему Вы реализуете интеграцию яндекс касс таким способом, а не через "active merchant"? И есть ли у Вас более подробный материал по интеграции, а то в интернете по яндекс кассе и rails практически ничего нету.

@mibamur

This comment has been minimized.

Copy link

commented Sep 3, 2016

добавить к модели
require 'builder'

@Timrael

This comment has been minimized.

Copy link

commented May 18, 2018

@kryzhovnik спасибо, добрый человек! сэкономил кучи времени разработки

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.