Skip to content

Instantly share code, notes, and snippets.

@ilibel
Forked from kryzhovnik/routes.rb
Created March 27, 2017 16:30
Show Gist options
  • Select an option

  • Save ilibel/cd3b9a08317d511a3fa0940e24431a62 to your computer and use it in GitHub Desktop.

Select an option

Save ilibel/cd3b9a08317d511a3fa0940e24431a62 to your computer and use it in GitHub Desktop.

Revisions

  1. @kryzhovnik kryzhovnik created this gist May 22, 2015.
    14 changes: 14 additions & 0 deletions routes.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,14 @@
    # 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
    128 changes: 128 additions & 0 deletions yandex_kassa.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,128 @@
    # 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
    41 changes: 41 additions & 0 deletions yandex_kassa_controller.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,41 @@
    # 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