Class: Robokassa::Interface

Inherits:
Object
  • Object
show all
Includes:
ActionDispatch::Routing::UrlFor
Defined in:
lib/robokassa/interface.rb

Constant Summary collapse

@@default_options =
{
  :language => "ru"
}
@@notification_params_map =
{
  'OutSum'         => :amount,
  'InvId'          => :invoice_id,
  'SignatureValue' => :signature,
  'Culture'        => :language
}
@@params_map =
{
  'MrchLogin'      => :login,
  'OutSum'         => :amount,
  'InvId'          => :invoice_id,
  'Desc'           => :description,
  'Email'          => :email,
  'IncCurrLabel'   => :currency,
  'Culture'        => :language,
  'SignatureValue' => :signature
}.invert
@@service_params_map =
{
  'MerchantLogin'  => :login,
  'Language'       => :language,
  'IncCurrLabel'   => :currency,
  'OutSum'         => :amount
}.invert

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Interface

Takes options to access Robokassa API

Example

Robokassa::Interface.new test_mode: true, login: 'demo', password1: '12345', password2: 'qweqwe123'


34
35
36
37
# File 'lib/robokassa/interface.rb', line 34

def initialize(options)
  @options = @@default_options.merge(options.symbolize_keys)
  @cache   = {}
end

Class Method Details

.create_by_notification_key(key) ⇒ Object

This method creates new instance of Interface for specified key (for multi-account support) it calls then Robokassa call ResultURL callback



368
369
370
# File 'lib/robokassa/interface.rb', line 368

def create_by_notification_key(key)
  self.new get_options_by_notification_key(key)
end

.fail(params, controller) ⇒ Object

Fail callback requiest handler It requires Robokassa::Interface.fail_implementation to be inmplemented by user



65
66
67
68
69
70
71
72
73
# File 'lib/robokassa/interface.rb', line 65

def self.fail(params, controller)
  parsed_params = map_params(params, @@notification_params_map)
  fail_implementation(
    parsed_params[:invoice_id],
    parsed_params[:amount],
    parsed_params[:language],
    parsed_params[:custom_options],
    controller)
end

.map_params(params, map) ⇒ Object

Maps gem parameter names, to robokassa names



338
339
340
# File 'lib/robokassa/interface.rb', line 338

def self.map_params(params, map)
  Hash[params.map do|key, value| [(map[key] || map[key.to_sym] || key), value] end]
end

.success(params, controller) ⇒ Object

Handler for success api callback this method calls from RobokassaController It requires Robokassa::Interface.success_implementation to be inmplemented by user



53
54
55
56
57
58
59
60
61
# File 'lib/robokassa/interface.rb', line 53

def self.success(params, controller)
  parsed_params = map_params(params, @@notification_params_map)
  success_implementation(
    parsed_params[:invoice_id],
    parsed_params[:amount],
    parsed_params[:language],
    parsed_params[:custom_options],
    controller)
end

Instance Method Details

#base_urlObject

returns test.robokassa.ru or merchant.roboxchange.com in order to current mode



288
289
290
# File 'lib/robokassa/interface.rb', line 288

def base_url
  test_mode? ? 'http://test.robokassa.ru' : 'https://merchant.roboxchange.com'
end

#currenciesObject



191
192
193
194
195
196
197
198
# File 'lib/robokassa/interface.rb', line 191

def currencies
  @cache[:currencies] ||= Hash[currencies_long.map do |key, value|
    [key, {
      :description => value[:description],
      :currencies => value[:currencies]
    }]
  end]
end

#currencies_longObject



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/robokassa/interface.rb', line 162

def currencies_long
  return @cache[:currencies_long] if @cache[:currencies_long]
  xml = get_remote_xml(currencies_url)
  if xml.elements['CurrenciesList/Result/Code'].text != '0'
    raise (a=xml.elements['CurrenciesList/Result/Description']) ? a.text : "Unknown error"
  end
  @cache[:currencies_long] = Hash[xml.elements.each('CurrenciesList/Groups/Group'){}.map do|g|
    code = g.attributes['Code']
    description = g.attributes['Description']
    [
      code,
      {
        :code        => code,
        :description => description,
        :currencies  => Hash[g.elements.each('Items/Currency'){}.map do|c|
          label = c.attributes['Label']
          name  = c.attributes['Name']
          [label, {
            :currency             => label,
            :currency_description => name,
            :group                => code,
            :group_description    => description
          }]
        end]
      }
    ]
  end]
end

#currencies_optionsObject



246
247
248
# File 'lib/robokassa/interface.rb', line 246

def currencies_options
  map_params(subhash(@options, %w{login language}), @@service_params_map)
end

#currencies_urlObject



242
243
244
# File 'lib/robokassa/interface.rb', line 242

def currencies_url
  @cache[:get_currencies_url] ||= "#{xml_services_base_url}/GetCurrencies?#{query_string(currencies_options)}"
end

#get_remote_xml(url) ⇒ Object

make request and parse XML from specified url



353
354
355
356
357
358
359
360
361
362
# File 'lib/robokassa/interface.rb', line 353

def get_remote_xml(url)
#   xml_data = Net::HTTP.get_response(URI.parse(url)).body
  begin
    xml_data = URI.parse(url).read
    doc = REXML::Document.new(xml_data)
  rescue REXML::ParseException => e
    sleep 1
    get_remote_xml(url)
  end
end

#init_payment_base_urlObject

returns url to redirect user to payment page



293
294
295
# File 'lib/robokassa/interface.rb', line 293

def init_payment_base_url
  "#{base_url}/Index.aspx"
end

#init_payment_options(invoice_id, amount, description, custom_options = {}, currency = '', language = 'ru', email = '') ⇒ Object

make hash of options for init_payment_url



251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/robokassa/interface.rb', line 251

def init_payment_options(invoice_id, amount, description, custom_options = {}, currency='', language='ru', email='')
  options = {
    :login       => @options[:login],
    :amount      => amount.to_s,
    :invoice_id  => invoice_id,
    :description => description[0, 100],
    :signature   => init_payment_signature(invoice_id, amount, description, custom_options),
    :currency    => currency,
    :email       => email,
    :language    => language
  }.merge(Hash[custom_options.sort.map{|x| ["shp#{x[0]}", x[1]]}])
  map_params(options, @@params_map)
end

#init_payment_signature(invoice_id, amount, description, custom_options = {}) ⇒ Object

calculates md5 from result of :init_payment_signature_string



277
278
279
# File 'lib/robokassa/interface.rb', line 277

def init_payment_signature(invoice_id, amount, description, custom_options={})
  md5 init_payment_signature_string(invoice_id, amount, description, custom_options)
end

#init_payment_signature_string(invoice_id, amount, description, custom_options = {}) ⇒ Object

generates signature string to calculate ‘SignatureValue’ url parameter



282
283
284
285
# File 'lib/robokassa/interface.rb', line 282

def init_payment_signature_string(invoice_id, amount, description, custom_options={})
  custom_options_fmt = custom_options.sort.map{|x|"shp#{x[0]}=#{x[1]}"}.join(":")
  "#{@options[:login]}:#{amount}:#{invoice_id}:#{@options[:password1]}#{unless custom_options_fmt.blank? then ":" + custom_options_fmt else "" end}"
end

#init_payment_url(invoice_id, amount, description, currency = '', language = 'ru', email = '', custom_options = {}) ⇒ Object

Generates url for payment page

Example

<%= link_to “Pay with Robokassa”, interface.init_payment_url(order.id, order.amount, “Order #Robokassa::Interface.orderorder.id”, ”, ‘ru’, order.user.email) %>



81
82
83
84
# File 'lib/robokassa/interface.rb', line 81

def init_payment_url(invoice_id, amount, description, currency='', language='ru', email='', custom_options={})
  url_options = init_payment_options(invoice_id, amount, description, custom_options, currency, language, email)
  "#{init_payment_base_url}?" + url_options.map do |k, v| "#{CGI::escape(k.to_s)}=#{CGI::escape(v.to_s)}" end.join('&')
end

#map_params(params, map) ⇒ Object

:nodoc:



342
343
344
# File 'lib/robokassa/interface.rb', line 342

def map_params(params, map) #:nodoc:
  self.class.map_params params, map
end

#md5(str) ⇒ Object

:nodoc:



327
328
329
# File 'lib/robokassa/interface.rb', line 327

def md5(str) #:nodoc:
  Digest::MD5.hexdigest(str).downcase
end

#notification_urlObject

for testing

Example

i.default_url_options = { :host => ‘127.0.0.1’, :port => 3000 } i.notification_url # => ‘127.0.0.1:3000/robokassa/asfadsf/notify



204
205
206
# File 'lib/robokassa/interface.rb', line 204

def notification_url
  robokassa_notification_url :notification_key => @options[:notification_key]
end

#notify(params, controller) ⇒ Object

This method verificates request params recived from robocassa server



40
41
42
43
44
45
46
47
48
# File 'lib/robokassa/interface.rb', line 40

def notify(params, controller)
  parsed_params = map_params(params, @@notification_params_map)
  notify_implementation(
    parsed_params[:invoice_id],
    parsed_params[:amount],
    parsed_params[:custom_options],
    controller)
  "OK#{parsed_params[:invoice_id]}"
end

#on_fail_urlObject

for testing



214
215
216
# File 'lib/robokassa/interface.rb', line 214

def on_fail_url
  robokassa_on_fail_url
end

#on_success_urlObject

for testing



209
210
211
# File 'lib/robokassa/interface.rb', line 209

def on_success_url
  robokassa_on_success_url
end

#ownerObject



25
26
27
# File 'lib/robokassa/interface.rb', line 25

def owner
  @options[:owner]
end

#parse_response_params(params) ⇒ Object



218
219
220
221
222
223
224
# File 'lib/robokassa/interface.rb', line 218

def parse_response_params(params)
  parsed_params = map_params(params, @@notification_params_map)
  parsed_params[:custom_options] = Hash[args.select do |k,v| o.starts_with?('shp') end.sort.map do|k, v| [k[3, k.size], v] end]
  if response_signature(parsed_params)!=parsed_params[:signature].downcase
    raise "Invalid signature"
  end
end

#payment_methodsObject

:nodoc:



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/robokassa/interface.rb', line 86

def payment_methods # :nodoc:
  return @cache[:payment_methods] if @cache[:payment_methods]
  xml = get_remote_xml(payment_methods_url)
  if xml.elements['PaymentMethodsList/Result/Code'].text != '0'
    raise (a=xml.elements['PaymentMethodsList/Result/Description']) ? a.text : "Unknown error"
  end

  @cache[:payment_methods] ||= Hash[xml.elements.each('PaymentMethodsList/Methods/Method'){}.map do|g|
    [g.attributes['Code'], g.attributes['Description']]
  end]
end

#payment_methods_optionsObject



238
239
240
# File 'lib/robokassa/interface.rb', line 238

def payment_methods_options
  map_params(subhash(@options, %w{login language}), @@service_params_map)
end

#payment_methods_urlObject



234
235
236
# File 'lib/robokassa/interface.rb', line 234

def payment_methods_url
  @cache[:get_currencies_url] ||= "#{xml_services_base_url}/GetPaymentMethods?#{query_string(payment_methods_options)}"
end

#query_string(params) ⇒ Object

:nodoc:



346
347
348
349
350
# File 'lib/robokassa/interface.rb', line 346

def query_string(params) #:nodoc:
  params.map do |name, value|
    "#{CGI::escape(name.to_s)}=#{CGI::escape(value.to_s)}"
  end.join("&")
end

#rates(amount, currency = '') ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
# File 'lib/robokassa/interface.rb', line 130

def rates(amount, currency='')
  cache_key = "rates_#{currency}_#{amount}"
  @cache[cache_key] ||= Hash[rates_long(amount, currency).map do |key, value|
    [key, {
      :description => value[:description],
      :currencies => Hash[(value[:currencies] || []).map do |k, v|
      [k, v]
      end]
    }]
  end]
end

#rates_linear(amount, currency = '') ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/robokassa/interface.rb', line 142

def rates_linear(amount, currency='')
  cache_key = "rates_linear#{currency}_#{amount}"
  @cache[cache_key] ||= begin
                          retval = rates(amount, currency).map do |group|
                            group_name, group = group
                            group[:currencies].map do |currency|
                              currency_name, currency = currency
                              {
                                :name       => currency_name,
                                :desc       => currency[:currency_description],
                                :group_name => group[:name],
                                :group_desc => group[:description],
                                :amount     => currency[:amount]
                              }
                            end
                          end
                          Hash[retval.flatten.map { |v| [v[:name], v] }]
                        end
end

#rates_long(amount, currency = '') ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/robokassa/interface.rb', line 98

def rates_long(amount, currency='')
  cache_key = "rates_long_#{currency}_#{amount}"
  return @cache[cache_key] if @cache[cache_key]
  xml = get_remote_xml(rates_url(amount, currency))
  if xml.elements['RatesList/Result/Code'].text != '0'
    raise (a=xml.elements['RatesList/Result/Description']) ? a.text : "Unknown error"
  end

  @cache[cache_key] = Hash[xml.elements.each('RatesList/Groups/Group'){}.map do|g|
    code = g.attributes['Code']
    description = g.attributes['Description']
    [
      code,
      {
        :code        => code,
        :description => description,
        :currencies  => Hash[g.elements.each('Items/Currency'){}.map do|c|
          label = c.attributes['Label']
          name  = c.attributes['Name']
          [label, {
            :currency             => label,
            :currency_description => name,
            :group                => code,
            :group_description    => description,
            :amount => BigDecimal.new(c.elements['Rate'].attributes['IncSum'])
          }]
        end]
      }
    ]
  end]
end

#rates_options(amount, currency) ⇒ Object



230
231
232
# File 'lib/robokassa/interface.rb', line 230

def rates_options(amount, currency)
  map_params(subhash(@options.merge(:amount=>amount, :currency=>currency), %w{login language amount currency}), @@service_params_map)
end

#rates_url(amount, currency) ⇒ Object



226
227
228
# File 'lib/robokassa/interface.rb', line 226

def rates_url(amount, currency)
  "#{xml_services_base_url}/GetRates?#{query_string(rates_options(amount, currency))}"
end

#response_signature(parsed_params) ⇒ Object

calculates signature to check params from Robokassa



266
267
268
# File 'lib/robokassa/interface.rb', line 266

def response_signature(parsed_params)
  md5 response_signature_string(parsed_params)
end

#response_signature_string(parsed_params) ⇒ Object

build signature string



271
272
273
274
# File 'lib/robokassa/interface.rb', line 271

def response_signature_string(parsed_params)
  custom_options_fmt = custom_options.sort.map{|x|"shp#{x[0]}=x[1]]"}.join(":")
  "#{parsed_params[:amount]}:#{parsed_params[:invoice_id]}:#{@options[:password2]}#{unless custom_options_fmt.blank? then ":" + custom_options_fmt else "" end}"
end

#subhash(hash, keys) ⇒ Object

:nodoc:



331
332
333
334
335
# File 'lib/robokassa/interface.rb', line 331

def subhash(hash, keys) #:nodoc:
  Hash[keys.map do |key|
    [key.to_sym, hash[key.to_sym]]
  end]
end

#test_mode?Boolean

Indicate if calling api in test mode

Returns

true or false

Returns:

  • (Boolean)


21
22
23
# File 'lib/robokassa/interface.rb', line 21

def test_mode?
  @options[:test_mode] || false
end

#xml_services_base_urlObject

returns base url for API access



298
299
300
# File 'lib/robokassa/interface.rb', line 298

def xml_services_base_url
  "#{base_url}/WebService/Service.asmx"
end