A
A
Anton Misyagin2015-02-27 12:56:11
API
Anton Misyagin, 2015-02-27 12:56:11

How to make a payment using Yandex.Money API on Ruby on Rails?

I use RoR and the gem at the link https://github.com/yandex-money/yandex-money-sdk-ruby
I read the doc, section: Payments from bank cards without authorization
There are three steps. After the third one, I have the status ext_auth_required , in the incoming parameter acs_uri I get a link to which I need to send a post (read here ):

You should open the WebView and send the client with a POST request to the acs_uri address with the acs_params parameters
. Now I need to somehow redirect to acs_uri using the POST method. How can I do this? The redirect follows a GET request, but you need a POST and a transition to the Yandex.Money page. If you try a simple redirect_to - Yandex gives an error: "Oh, something went wrong." In general, the question is what to do next after the described 3 steps? The instruction ends, but the payment has not yet passed.
My code :
require 'yandex_money/api'
require "httparty"

module YandexMoneyHelper
  class Payment
      include HTTParty

      def initialize
      end

      def account_replenishment(amount, email)
        i_id = ym_get_instance_id
        r_id = ym_get_request_id(i_id, amount, email)
        res = ym_process_payment(i_id, r_id)
      end
    protected
      #Функции для приема платежей с банковских карт

      #Получение идентификатора экземпляра приложения на основе 
      # client_id (выдается при регистрации приложения).
      # Получается один раз. Сохраняется в безопасном месте
      def ym_get_instance_id
        api = YandexMoney::Api.new(client_id: Rails.application.secrets.yandex_money_app_id)
        instance_id = api.get_instance_id
      end

      def ym_get_request_id(inst_id, amount, email)
        api = YandexMoney::Api.new(
          client_id: Rails.application.secrets.yandex_money_app_id,
          instance_id: inst_id
        )
        response = api.request_external_payment({
          pattern_id: "p2p", #Фиксированное значение «p2p».
          to: Rails.application.secrets.yandex_money_wallet, #Номер счета для пополнения
          #amount: "1.00" Сумма к списанию с банковской карты (на счет поступит эта сумма минус комиссия).
          amount_due: amount, #Сумма к зачислению на счет (с карты будет списана эта сумма плюс комиссия).
          message: "Пополнение счета от " + email #Комментарий к зачислению, отображается получателю
        })
        if response.status == "success"
          request_id = response.request_id
        else
          request_id = null
        end
      end

      def ym_process_payment(inst_id, req_id)
        api = YandexMoney::Api.new(instance_id: inst_id)
        result = api.process_external_payment({
          request_id: req_id,
          ext_auth_success_uri: "http://example.com/success",
          ext_auth_fail_uri: "http://example.com/fail"
        })
        if result.status == "ext_auth_required"
          #Сюда нужно написать редирект методом POST!
        end
      end		
  end
end

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Anton Misyagin, 2015-02-27
@sunnmas

Returned six months later to the problem, finished it, it works, use it, and yes, point out security problems, if any...

gem 'yandex-money-sdk'
gem 'devise' #управление юзерами

get		'/кошелек/пополнить', :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

require '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

class 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

= 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"

%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 here is a random number that is generated during a new payment and stored in the user's session. It's screwed on for that. so that the user cannot go to the address /payment/passed and replenish his balance on the ball. It is cleared from the session in any case when going to this address.
Here, the ext_auth_required form is needed to send a POST request to the Yandex server (see api poison)

S
Shaks, 2015-02-27
@shaks

Redirect to do through zhs laying. Submitting the form is easy. I did just that (though not with POISON).

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question