better_offsite_payments 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +76 -0
  4. data/lib/offsite_payments.rb +39 -0
  5. data/lib/offsite_payments/action_view_helper.rb +72 -0
  6. data/lib/offsite_payments/helper.rb +120 -0
  7. data/lib/offsite_payments/integrations.rb +14 -0
  8. data/lib/offsite_payments/integrations/a1agregator.rb +245 -0
  9. data/lib/offsite_payments/integrations/authorize_net_sim.rb +580 -0
  10. data/lib/offsite_payments/integrations/bit_pay.rb +150 -0
  11. data/lib/offsite_payments/integrations/bogus.rb +32 -0
  12. data/lib/offsite_payments/integrations/chronopay.rb +283 -0
  13. data/lib/offsite_payments/integrations/citrus.rb +227 -0
  14. data/lib/offsite_payments/integrations/coinbase.rb +172 -0
  15. data/lib/offsite_payments/integrations/direc_pay.rb +332 -0
  16. data/lib/offsite_payments/integrations/directebanking.rb +237 -0
  17. data/lib/offsite_payments/integrations/doku.rb +171 -0
  18. data/lib/offsite_payments/integrations/dotpay.rb +166 -0
  19. data/lib/offsite_payments/integrations/dwolla.rb +160 -0
  20. data/lib/offsite_payments/integrations/e_payment_plans.rb +146 -0
  21. data/lib/offsite_payments/integrations/easy_pay.rb +137 -0
  22. data/lib/offsite_payments/integrations/epay.rb +161 -0
  23. data/lib/offsite_payments/integrations/first_data.rb +133 -0
  24. data/lib/offsite_payments/integrations/gestpay.rb +205 -0
  25. data/lib/offsite_payments/integrations/hi_trust.rb +179 -0
  26. data/lib/offsite_payments/integrations/ipay88.rb +251 -0
  27. data/lib/offsite_payments/integrations/klarna.rb +275 -0
  28. data/lib/offsite_payments/integrations/liqpay.rb +216 -0
  29. data/lib/offsite_payments/integrations/maksuturva.rb +231 -0
  30. data/lib/offsite_payments/integrations/megakassa.rb +184 -0
  31. data/lib/offsite_payments/integrations/mollie.rb +32 -0
  32. data/lib/offsite_payments/integrations/mollie_ideal.rb +194 -0
  33. data/lib/offsite_payments/integrations/mollie_mistercash.rb +143 -0
  34. data/lib/offsite_payments/integrations/molpay.rb +193 -0
  35. data/lib/offsite_payments/integrations/moneybookers.rb +199 -0
  36. data/lib/offsite_payments/integrations/nochex.rb +228 -0
  37. data/lib/offsite_payments/integrations/pag_seguro.rb +268 -0
  38. data/lib/offsite_payments/integrations/paxum.rb +114 -0
  39. data/lib/offsite_payments/integrations/pay_fast.rb +269 -0
  40. data/lib/offsite_payments/integrations/paydollar.rb +142 -0
  41. data/lib/offsite_payments/integrations/payflow_link.rb +194 -0
  42. data/lib/offsite_payments/integrations/paypal.rb +362 -0
  43. data/lib/offsite_payments/integrations/paypal_payments_advanced.rb +23 -0
  44. data/lib/offsite_payments/integrations/paysbuy.rb +71 -0
  45. data/lib/offsite_payments/integrations/payu_in.rb +276 -0
  46. data/lib/offsite_payments/integrations/payu_in_paisa.rb +46 -0
  47. data/lib/offsite_payments/integrations/platron.rb +153 -0
  48. data/lib/offsite_payments/integrations/pxpay.rb +273 -0
  49. data/lib/offsite_payments/integrations/quickpay.rb +232 -0
  50. data/lib/offsite_payments/integrations/rbkmoney.rb +110 -0
  51. data/lib/offsite_payments/integrations/realex_offsite.rb +317 -0
  52. data/lib/offsite_payments/integrations/robokassa.rb +154 -0
  53. data/lib/offsite_payments/integrations/sage_pay_form.rb +431 -0
  54. data/lib/offsite_payments/integrations/two_checkout.rb +329 -0
  55. data/lib/offsite_payments/integrations/universal.rb +190 -0
  56. data/lib/offsite_payments/integrations/valitor.rb +200 -0
  57. data/lib/offsite_payments/integrations/verkkomaksut.rb +143 -0
  58. data/lib/offsite_payments/integrations/web_pay.rb +186 -0
  59. data/lib/offsite_payments/integrations/webmoney.rb +119 -0
  60. data/lib/offsite_payments/integrations/wirecard_checkout_page.rb +359 -0
  61. data/lib/offsite_payments/integrations/world_pay.rb +280 -0
  62. data/lib/offsite_payments/integrations/yandex_money.rb +175 -0
  63. data/lib/offsite_payments/notification.rb +71 -0
  64. data/lib/offsite_payments/return.rb +37 -0
  65. data/lib/offsite_payments/version.rb +3 -0
  66. metadata +297 -0
@@ -0,0 +1,280 @@
1
+ require 'ipaddr'
2
+ module OffsitePayments #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module WorldPay
5
+ mattr_accessor :production_url, :test_url
6
+ self.production_url = 'https://secure.worldpay.com/wcc/purchase'
7
+ self.test_url = 'https://secure-test.worldpay.com/wcc/purchase'
8
+
9
+ def self.service_url
10
+ case OffsitePayments.mode
11
+ when :production
12
+ self.production_url
13
+ when :test
14
+ self.test_url
15
+ else
16
+ raise StandardError, "Integration mode set to an invalid value: #{mode}"
17
+ end
18
+ end
19
+
20
+ def self.notification(post, options = {})
21
+ Notification.new(post, options)
22
+ end
23
+
24
+ def self.return(post, options = {})
25
+ Return.new(post, options)
26
+ end
27
+
28
+ class Helper < OffsitePayments::Helper
29
+ mapping :account, 'instId'
30
+ mapping :amount, 'amount'
31
+ mapping :order, 'cartId'
32
+ mapping :currency, 'currency'
33
+
34
+ mapping :customer, :email => 'email',
35
+ :phone => 'tel'
36
+
37
+ mapping :billing_address, :zip => 'postcode',
38
+ :country => 'country'
39
+
40
+ mapping :description, 'desc'
41
+ mapping :notify_url, 'MC_callback'
42
+ mapping :return_url, 'MC_return'
43
+
44
+ # WorldPay supports two different test modes - :always_succeed and :always_fail
45
+ def initialize(order, account, options = {})
46
+ super
47
+
48
+ if OffsitePayments.mode == :test || options[:test]
49
+ test_mode = case options[:test]
50
+ when :always_fail
51
+ 101
52
+ when false
53
+ 0
54
+ else
55
+ 100
56
+ end
57
+ add_field('testMode', test_mode.to_s)
58
+ elsif OffsitePayments.mode == :always_succeed
59
+ add_field('testMode', '100')
60
+ elsif OffsitePayments.mode == :always_fail
61
+ add_field('testMode', '101')
62
+ end
63
+ end
64
+
65
+ # WorldPay only supports a single address field so we
66
+ # have to concat together - lines are separated using &#10;
67
+ def billing_address(params={})
68
+ add_field(mappings[:billing_address][:zip], params[:zip])
69
+ add_field(mappings[:billing_address][:country], lookup_country_code(params[:country]))
70
+
71
+ address = [params[:address1], params[:address2], params[:city], params[:state]].compact
72
+ add_field('address', address.join('&#10;'))
73
+ end
74
+
75
+ # WorldPay only supports a single name field so we have to concat
76
+ def customer(params={})
77
+ add_field(mappings[:customer][:email], params[:email])
78
+ add_field(mappings[:customer][:phone], params[:phone])
79
+ add_field('name', "#{params[:first_name]} #{params[:last_name]}")
80
+ end
81
+
82
+ # Support for a MD5 hash of selected fields to prevent tampering
83
+ # For further information read the tech note at the address below:
84
+ # http://support.worldpay.com/kb/integration_guides/junior/integration/help/tech_notes/sjig_tn_009.html
85
+ def encrypt(secret, fields = [:amount, :currency, :account, :order])
86
+ signature_fields = fields.collect{ |field| mappings[field] }
87
+ add_field('signatureFields', signature_fields.join(':'))
88
+
89
+ field_values = fields.collect{ |field| form_fields[mappings[field]] }
90
+ signature = "#{secret}:#{field_values.join(':')}"
91
+ add_field('signature', Digest::MD5.hexdigest(signature))
92
+ end
93
+
94
+ # Add a time window for which the payment can be completed. Read the link below for how they work
95
+ # http://support.worldpay.com/kb/integration_guides/junior/integration/help/appendicies/sjig_10100.html
96
+ def valid_from(from_time)
97
+ add_field('authValidFrom', from_time.to_i.to_s + '000')
98
+ end
99
+
100
+ def valid_to(to_time)
101
+ add_field('authValidTo', to_time.to_i.to_s + '000')
102
+ end
103
+
104
+ # WorldPay supports the passing of custom parameters prefixed with the following:
105
+ # C_ : These parameters can be used in the response pages hosted on WorldPay's site
106
+ # M_ : These parameters are passed through to the callback script (if enabled)
107
+ # MC_ or CM_ : These parameters are availble both in the response and callback contexts
108
+ def response_params(params={})
109
+ params.each{|k,v| add_field("C_#{k}",v)}
110
+ end
111
+
112
+ def callback_params(params={})
113
+ params.each{|k,v| add_field("M_#{k}",v)}
114
+ end
115
+
116
+ def combined_params(params={})
117
+ params.each{|k,v| add_field("MC_#{k}",v)}
118
+ end
119
+ end
120
+
121
+ class Notification < OffsitePayments::Notification
122
+ def complete?
123
+ status == 'Completed'
124
+ end
125
+
126
+ def account
127
+ params['instId']
128
+ end
129
+
130
+ def item_id
131
+ params['cartId']
132
+ end
133
+
134
+ def transaction_id
135
+ params['transId']
136
+ end
137
+
138
+ # Time this payment was received by the client in UTC time.
139
+ def received_at
140
+ Time.at(params['transTime'].to_i / 1000).utc
141
+ end
142
+
143
+ # Callback password set in the WorldPay CMS
144
+ def security_key
145
+ params['callbackPW']
146
+ end
147
+
148
+ # the money amount we received in X.2 decimal.
149
+ def gross
150
+ params['authAmount']
151
+ end
152
+
153
+ def currency
154
+ params['authCurrency']
155
+ end
156
+
157
+ # Was this a test transaction?
158
+ def test?
159
+ params.key?('testMode') && params['testMode'] != '0'
160
+ end
161
+
162
+ def status
163
+ params['transStatus'] == 'Y' ? 'Completed' : 'Cancelled'
164
+ end
165
+
166
+ def name
167
+ params['name']
168
+ end
169
+
170
+ def address
171
+ params['address']
172
+ end
173
+
174
+ def postcode
175
+ params['postcode']
176
+ end
177
+
178
+ def country
179
+ params['country']
180
+ end
181
+
182
+ def phone_number
183
+ params['tel']
184
+ end
185
+
186
+ def fax_number
187
+ params['fax']
188
+ end
189
+
190
+ def email_address
191
+ params['email']
192
+ end
193
+
194
+ def card_type
195
+ params['cardType']
196
+ end
197
+
198
+ # WorldPay extended fraud checks returned as a 4 character string
199
+ # 1st char: Credit card CVV check
200
+ # 2nd char: Postcode AVS check
201
+ # 3rd char: Address AVS check
202
+ # 4th char: Country comparison check
203
+ # Possible values are:
204
+ # :not_supported - 0
205
+ # :not_checked - 1
206
+ # :matched - 2
207
+ # :not_matched - 4
208
+ # :partial_match - 8
209
+ def cvv_status
210
+ return avs_value_to_symbol(params['AVS'][0].chr)
211
+ end
212
+
213
+ def postcode_status
214
+ return avs_value_to_symbol(params['AVS'][1].chr)
215
+ end
216
+
217
+ def address_status
218
+ return avs_value_to_symbol(params['AVS'][2].chr)
219
+ end
220
+
221
+ def country_status
222
+ return avs_value_to_symbol(params['AVS'][3].chr)
223
+ end
224
+
225
+ def acknowledge(authcode = nil)
226
+ return true
227
+ end
228
+
229
+ # WorldPay supports the passing of custom parameters through to the callback script
230
+ def custom_params
231
+ return @custom_params ||= read_custom_params
232
+ end
233
+
234
+ # Check if the request comes from IP range 195.35.90.0 – 195.35.91.255
235
+ def valid_sender?(ip)
236
+ return true if OffsitePayments.mode == :test
237
+ IPAddr.new("195.35.90.0/23").include?(IPAddr.new(ip))
238
+ end
239
+
240
+ private
241
+
242
+ # Take the posted data and move the relevant data into a hash
243
+ def parse(post)
244
+ @raw = post
245
+ for line in post.split('&')
246
+ key, value = *line.scan( %r{^(\w+)\=(.*)$} ).flatten
247
+ params[key] = value
248
+ end
249
+ end
250
+
251
+ # Read the custom params into a hash
252
+ def read_custom_params
253
+ custom = {}
254
+ params.each do |key, value|
255
+ if /\A(M_|MC_|CM_)/ === key
256
+ custom[key.gsub(/\A(M_|MC_|CM_)/, '').to_sym] = value
257
+ end
258
+ end
259
+ custom
260
+ end
261
+
262
+ # Convert a AVS value to a symbol - see above for more about AVS
263
+ def avs_value_to_symbol(value)
264
+ case value.to_s
265
+ when '8'
266
+ :partial_match
267
+ when '4'
268
+ :no_match
269
+ when '2'
270
+ :matched
271
+ when '1'
272
+ :not_checked
273
+ else
274
+ :not_supported
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,175 @@
1
+ require 'net/http'
2
+
3
+ module OffsitePayments #:nodoc:
4
+ module Integrations #:nodoc:
5
+ module YandexMoney
6
+
7
+ # Start integration with yandex.money here:
8
+ # https://money.yandex.ru/joinups
9
+
10
+ # Shop example:
11
+ # https://github.com/yurijmi/yandex_money_offsite_payments_demo
12
+
13
+ mattr_accessor :production_url, :test_url
14
+
15
+ self.production_url = 'https://money.yandex.ru/eshop.xml'
16
+ self.test_url = 'https://demomoney.yandex.ru/eshop.xml'
17
+
18
+ def self.service_url
19
+ case OffsitePayments.mode
20
+ when :production
21
+ self.production_url
22
+ when :test
23
+ self.test_url
24
+ else
25
+ raise StandardError, "Integration mode set to an invalid value: #{mode}"
26
+ end
27
+ end
28
+
29
+ def self.notification(post)
30
+ Notification.new(post)
31
+ end
32
+
33
+ class Helper < OffsitePayments::Helper
34
+ mapping :account, 'customerNumber'
35
+ mapping :amount, 'sum'
36
+ mapping :order, 'orderNumber'
37
+ mapping :error_url, 'shopFailURL'
38
+ mapping :return_url, 'shopSuccessURL'
39
+ mapping :description, 'orderDetails'
40
+
41
+ mapping :customer, :email => 'cps_email',
42
+ :phone => 'cps_phone'
43
+
44
+ # additional yandex.money parameters
45
+ mapping :scid, 'scid'
46
+ mapping :shopId, 'shopId'
47
+ mapping :shopArticleId, 'shopArticleId'
48
+ end
49
+
50
+ class Notification < OffsitePayments::Notification
51
+ def initialize(post, options = {})
52
+ super
53
+ @response_code = '200'
54
+ end
55
+
56
+ def complete?
57
+ params['_raw_action'] == 'paymentAviso'
58
+ end
59
+
60
+ def item_id
61
+ params['_raw_orderNumber']
62
+ end
63
+
64
+ def transaction_id
65
+ params['_raw_invoiceId']
66
+ end
67
+
68
+ # When was this payment received by the client.
69
+ def received_at
70
+ params['_raw_orderCreatedDatetime']
71
+ end
72
+
73
+ def currency
74
+ params['_raw_orderSumCurrencyPaycash']
75
+ end
76
+
77
+ def payer_email
78
+ params['_raw_cps_email']
79
+ end
80
+
81
+ # the money amount we received in X.2 decimal.
82
+ def gross
83
+ params['_raw_orderSumAmount'].to_f
84
+ end
85
+
86
+ def customer_id
87
+ params['_raw_customerNumber']
88
+ end
89
+
90
+ def set_response(code)
91
+ @response_code = code
92
+ end
93
+
94
+ def get_response()
95
+ @response_code
96
+ end
97
+
98
+ # Was this a test transaction?
99
+ def test?
100
+ false
101
+ end
102
+
103
+ def status
104
+ case params['_raw_action']
105
+ when 'checkOrder'
106
+ 'pending'
107
+ when 'paymentAviso'
108
+ 'completed'
109
+ else 'unknown'
110
+ end
111
+ end
112
+
113
+ def response
114
+ shop_id = params['_raw_shopId']
115
+ method = params['_raw_action']
116
+ dt = Time.now.iso8601
117
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
118
+ "<#{method}Response performedDatetime=\"#{dt}\" code=\"#{@response_code}\"" +
119
+ " invoiceId=\"#{transaction_id}\" shopId=\"#{shop_id}\"/>"
120
+ end
121
+
122
+ # Acknowledge the transaction to YandexMoney. This method has to be called after a new
123
+ # apc arrives. YandexMoney will verify that all the information we received are correct and will return a
124
+ # ok or a fail.
125
+ #
126
+ # Example:
127
+ #
128
+ # def ipn
129
+ # notify = YandexMoneyNotification.new(request.raw_post)
130
+ #
131
+ # if notify.acknowledge(authcode)
132
+ # if notify.complete?
133
+ # ... process order ...
134
+ # end
135
+ # else
136
+ # ... log possible hacking attempt ...
137
+ # end
138
+ # render text: notify.response
139
+ #
140
+
141
+ def acknowledge(authcode = nil)
142
+ string = [params['_raw_action'],
143
+ params['_raw_orderSumAmount'],
144
+ params['_raw_orderSumCurrencyPaycash'],
145
+ params['_raw_orderSumBankPaycash'],
146
+ params['_raw_shopId'],
147
+ params['_raw_invoiceId'],
148
+ params['_raw_customerNumber'],
149
+ authcode
150
+ ].join(';')
151
+
152
+ digest = Digest::MD5.hexdigest(string)
153
+ res = params['_raw_md5'] == digest.upcase
154
+ if res
155
+ @response_code = '0'
156
+ else
157
+ @response_code = '1'
158
+ end
159
+ end
160
+
161
+ private
162
+
163
+ # Take the posted data and move the relevant data into a hash
164
+ def parse(post)
165
+ @raw = post.to_s
166
+ for line in @raw.split('&')
167
+ key, value = *line.scan( %r{^([A-Za-z0-9_.-]+)\=(.*)$} ).flatten
168
+ # to divide raw values from other
169
+ params['_raw_' + key] = CGI.unescape(value.to_s) if key.present?
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end