yurijmi/better_offsite_payments

View on GitHub
lib/offsite_payments/integrations/yandex_money.rb

Summary

Maintainability
A
0 mins
Test Coverage
require 'net/http'

module OffsitePayments #:nodoc:
  module Integrations #:nodoc:
    module YandexMoney

      # Start integration with yandex.money here:
      # https://money.yandex.ru/joinups

      # Shop example:
      # https://github.com/yurijmi/yandex_money_offsite_payments_demo

      mattr_accessor :production_url, :test_url

      self.production_url = 'https://money.yandex.ru/eshop.xml'
      self.test_url       = 'https://demomoney.yandex.ru/eshop.xml'

      def self.service_url
        case OffsitePayments.mode
          when :production
            self.production_url
          when :test
            self.test_url
          else
            raise StandardError, "Integration mode set to an invalid value: #{mode}"
        end
      end

      def self.notification(post)
        Notification.new(post)
      end

      class Helper < OffsitePayments::Helper
        mapping :account,     'customerNumber'
        mapping :amount,      'sum'
        mapping :order,       'orderNumber'
        mapping :error_url,   'shopFailURL'
        mapping :return_url,  'shopSuccessURL'
        mapping :description, 'orderDetails'

        mapping :customer, :email      => 'cps_email',
                           :phone      => 'cps_phone'

        # additional yandex.money parameters
        mapping :scid,          'scid'
        mapping :shopId,        'shopId'
        mapping :shopArticleId, 'shopArticleId'
      end

      class Notification < OffsitePayments::Notification
        def initialize(post, options = {})
          super
          @response_code = '200'
        end

        def complete?
          params['_raw_action'] == 'paymentAviso'
        end

        def item_id
          params['_raw_orderNumber']
        end

        def transaction_id
          params['_raw_invoiceId']
        end

        # When was this payment received by the client.
        def received_at
          params['_raw_orderCreatedDatetime']
        end

        def currency
          params['_raw_orderSumCurrencyPaycash']
        end

        def payer_email
          params['_raw_cps_email']
        end

        # the money amount we received in X.2 decimal.
        def gross
          params['_raw_orderSumAmount'].to_f
        end

        def customer_id
          params['_raw_customerNumber']
        end

        def set_response(code)
          @response_code = code
        end

        def get_response()
          @response_code
        end

        # Was this a test transaction?
        def test?
          false
        end

        def status
          case params['_raw_action']
            when 'checkOrder'
              'pending'
            when 'paymentAviso'
              'completed'
            else 'unknown'
          end
        end

        def response
          shop_id = params['_raw_shopId']
          method = params['_raw_action']
          dt = Time.now.iso8601
          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
              "<#{method}Response performedDatetime=\"#{dt}\" code=\"#{@response_code}\"" +
              " invoiceId=\"#{transaction_id}\" shopId=\"#{shop_id}\"/>"
        end

        # Acknowledge the transaction to YandexMoney. This method has to be called after a new
        # apc arrives. YandexMoney will verify that all the information we received are correct and will return a
        # ok or a fail.
        #
        # Example:
        #
        #   def ipn
        #     notify = YandexMoneyNotification.new(request.raw_post)
        #
        #     if notify.acknowledge(authcode)
        #       if notify.complete?
        #         ... process order ...
        #       end
        #     else
        #       ... log possible hacking attempt ...
        #     end
        #     render text: notify.response
        #

        def acknowledge(authcode = nil)
          string = [params['_raw_action'],
                    params['_raw_orderSumAmount'],
                    params['_raw_orderSumCurrencyPaycash'],
                    params['_raw_orderSumBankPaycash'],
                    params['_raw_shopId'],
                    params['_raw_invoiceId'],
                    params['_raw_customerNumber'],
                    authcode
          ].join(';')

          digest = Digest::MD5.hexdigest(string)
          res = params['_raw_md5'] == digest.upcase
          if res
            @response_code = '0'
          else
            @response_code = '1'
          end
        end

        private

        # Take the posted data and move the relevant data into a hash
        def parse(post)
          @raw = post.to_s
          for line in @raw.split('&')
            key, value = *line.scan( %r{^([A-Za-z0-9_.-]+)\=(.*)$} ).flatten
            # to divide raw values from other
            params['_raw_' + key] = CGI.unescape(value.to_s) if key.present?
          end
        end
      end
    end
  end
end