Вернулся спустя полгода к проблеме, допилил, работает, юзайте, и да, укажите на проблемы безопасности, если они есть...
Gemfilegem 'yandex-money-sdk'
gem 'devise' #управление юзерами
routesget '/кошелек/пополнить', :to=> 'cabinet/balance#replenishment', :as => :replenishment
post '/пополнение/счета', :to=> 'cabinet/balance#payment_process', :as => :payment_process
get '/платеж/не/прошел', :to=> 'cabinet/balance#payment_fail', :as => :payment_fail
get '/платеж/прошел/:secure_code', :to=> 'cabinet/balance#payment_success', :as => :payment_success
app/helpers/yandex_money_helper.rbrequire 'yandex_money/api'
module YandexMoneyHelper
class YM
# Класс служит для приема денег на Яндекс кошелек от других пользователей
# Разработчик регистрирует свое приложение в Яндекс.Деньгах,
# получает client_id - идентификатор приложения.
# В течение жизненного цикла приложения client_id не изменяется.
CLIENT_ID = Rails.application.secrets.yandex_money_app_id
# Номер кошелька для приема нлатежей
WALLET_ID = Rails.application.secrets.yandex_money_wallet
attr_reader :successful
attr_reader :error
attr_reader :secure_code
def initialize
puts "=::YANDEX MONEY INITIALIZE::="
puts "CLIENT_ID: #{CLIENT_ID}"
puts "WALLET_ID: #{WALLET_ID}"
#Получение идентификатора экземпляра приложения на основе client_id
@instance_id = YandexMoney::ExternalPayment.get_instance_id(CLIENT_ID)
if @instance_id.status == "success"
@instance_id = @instance_id.instance_id
@ext_pay = YandexMoney::ExternalPayment.new @instance_id
@successful = true
else
@successful = false
@error = @instance_id.error
end
puts "instance_id: #{@instance_id}"
puts "ext_pay: #{@ext_pay}"
end
# Пополнение счета:
def recieve_payment(amount, email)
puts "reciving payment..."
@successful = false
@secure_code = rand 10000000..99999999
puts "secure_code: #{@secure_code}"
response = @ext_pay.request_external_payment({
pattern_id: "p2p", #Фиксированное значение «p2p».
to: WALLET_ID, #Номер счета для пополнения
#amount: "1.00" Сумма к списанию с банковской карты (на счет поступит эта сумма минус комиссия).
amount_due: amount, #Сумма к зачислению на счет (с карты будет списана эта сумма плюс комиссия).
message: "Пополнение счета от #{email}" #Комментарий к зачислению, отображается получателю
})
puts "get request_id: #{response}"
if response.status != "success"
@error = response.error
return
end
@request_id = response.request_id
puts "request_id: #{@request_id}"
puts "process_payment..."
res = @ext_pay.process_external_payment({
request_id: @request_id,
ext_auth_success_uri: Rails.application.routes.url_helpers.payment_success_url(:secure_code => @secure_code),
ext_auth_fail_uri: Rails.application.routes.url_helpers.payment_fail_url
})
@successful = true
puts res
res
rescue => e
@successful = false
@error = e.message
puts "error recieving payment #{e.message}"
{:status => "error", :error => e.message}
end
end
end
/app/controllers/cabinet/balance_controller.rbclass Cabinet::BalanceController < Cabinet::CabinetController
#Пополнение счета
def replenishment
@amount = 100
end
def payment_process
@params[:amount] = {:type => :integer, :in => 1..10000.0}
return if !sanitize_params
@amount = params[:amount]
# Инициализация модуля Яндекс Деньги
ym = YM.new
err = "Ошибка работы с модулем оплаты "
if !ym.successful
err << ym.error if Rails.env.development?
flash[:error] = err
redirect_to replenishment_path
return
end
# Прием платежа на кошелек Яндекс Денег
res = ym.recieve_payment(@amount, "#{current_user.id}: #{current_user.email}")
if !ym.successful
err << ym.error if Rails.env.development?
flash[:error] = err
redirect_to replenishment_path
return
end
# Требуется внешняя аутентификация пользователя
if res.status != "ext_auth_required"
err = "Нет запроса на внешнюю авторизацию"
err << "status: #{res.status} error: #{res.error}" if Rails.env.development?
flash[:error] = err
redirect_to replenishment_path
return
end
# Сохраняем проверочный код платежа в сессии
session[:ym_secure_code] = ym.secure_code
session[:ym_amount] = @amount
# Отсылаем методом POST acs_params:
@acs_params = res.acs_params
@acs_uri = res.acs_uri
render :payment_process
end
def payment_fail
msg = 'Платеж не прошел.'
reason = case params[:reason]
when 'no3ds' then 'Указан неверный код CVV.'
when 'not-enough-funds' then 'Недостаточно средств.'
else
e = Exception.new 'Платеж не прошел.'
ExceptionNotifier.notify_exception(e,
:env => request.env,
:data => {:message => "Ошибка при совершении оплаты: #{params[:reason]}"})
params[:reason]
end
flash[:error] = msg << ' ' << reason
redirect_to replenishment_path
end
def payment_success
#Эти 2 строки измените под свои нужды:
@params[:secure_code] = {:type => :integer, :in => 10000000..99999999}
return if !sanitize_params
secure_code = params[:secure_code]
if !session[:ym_secure_code] || !session[:ym_amount]
flash[:error] = "Платеж не прошел. Ошибка сессии"
redirect_to replenishment_path
return
end
amount = session[:ym_amount].to_i
if session[:ym_secure_code].to_i != secure_code
flash[:error] = "Не верный проверочный код платежа. Платеж не прошел"
else
current_user.with_lock do
@inc = Incoming.create(:user_id => current_user.id, :amount => amount)
current_user.amount += amount
current_user.amount = eval(sprintf("%8.2f",current_user.amount))
current_user.save(validate: false)
end
notify "Баланс пополнен",
"Баланс пополнен на сумму #{amount} #{rub}. Текущий баланс: #{current_user.amount} #{rub}"
UserMailer.new_incoming(@inc.id).deliver if current_user.notify_incomings
flash[:success] = "Платеж прошел"
end
redirect_to replenishment_path
ensure
session[:ym_secure_code] = nil
session[:amount] = nil
end
end
app/views/replenishment.html.haml= form_tag(payment_process_path, method: "post") do
= label_tag(:amount, "Сумма без учета комисси банка, #{rub}:")
= number_field_tag :amount, @amount ,in: 1..10000, step: 1
= submit_tag "Пополнить", :class => "button"
app/views/payment_process.html.haml%p Подождите, Вы будете перенаправлены на страницу Яндекс Денег
%form#ext_auth_required{:action=>@acs_uri}
%input{:type=>"hidden", :name=>"cps_context_id", :value=> @acs_params["cps_context_id"]}
%input{:type=>"hidden", :name=>"paymentType", :value=> @acs_params["paymentType"]}
:javascript
document.forms["ext_auth_required"].submit();
secure_code здесь это случайное число, которое рождается во время нового платежа и сохраняется в сессии юзера. Оно прикручено для того. чтобы пользователь не смог перейти по адресу /платеж/прошел и на шару пополнить себе баланс. Очищается из сессии в любом случае при переходе по этому адресу.
Здесь форма ext_auth_required нужна для посылки POST запроса на сервак яндекса (см. api ЯД)