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,273 @@
1
+ require 'rexml/document'
2
+
3
+ module OffsitePayments #:nodoc:
4
+ module Integrations #:nodoc:
5
+ module Pxpay
6
+ def self.token_url
7
+ 'https://sec.paymentexpress.com/pxpay/pxaccess.aspx'
8
+ end
9
+
10
+ def self.notification(post, options={})
11
+ Notification.new(post, options)
12
+ end
13
+
14
+ def self.return(query_string, options={})
15
+ Return.new(query_string, options)
16
+ end
17
+
18
+ class Helper < OffsitePayments::Helper
19
+ include ActiveUtils::PostsData
20
+
21
+ attr_reader :token_parameters, :redirect_parameters
22
+
23
+ def initialize(order, account, options = {})
24
+ @token_parameters = {
25
+ 'PxPayUserId' => account,
26
+ 'PxPayKey' => options[:credential2],
27
+ 'CurrencyInput' => options[:currency],
28
+ 'MerchantReference' => order,
29
+ 'EmailAddress' => options[:customer_email],
30
+ 'TxnData1' => options[:custom1],
31
+ 'TxnData2' => options[:custom2],
32
+ 'TxnData3' => options[:custom3],
33
+ 'AmountInput' => "%.2f" % options[:amount].to_f.round(2),
34
+ 'EnableAddBillCard' => '0',
35
+ 'TxnType' => 'Purchase',
36
+ 'UrlSuccess' => options[:return_url],
37
+ 'UrlFail' => options[:return_url]
38
+ }
39
+ @redirect_parameters = {}
40
+
41
+ super
42
+
43
+ raise ArgumentError, "error - must specify return_url" if token_parameters['UrlSuccess'].blank?
44
+ raise ArgumentError, "error - must specify cancel_return_url" if token_parameters['UrlFail'].blank?
45
+ end
46
+
47
+ def credential_based_url
48
+ raw_response = ssl_post(Pxpay.token_url, generate_request)
49
+ result = parse_response(raw_response)
50
+
51
+ raise ActionViewHelperError, "error - failed to get token - message was #{result[:redirect]}" unless result[:valid] == "1"
52
+
53
+ url = URI.parse(result[:redirect])
54
+
55
+ if url.query
56
+ @redirect_parameters = CGI.parse(url.query)
57
+ url.query = nil
58
+ end
59
+
60
+ url.to_s
61
+ rescue ActiveUtils::ConnectionError
62
+ raise ActionViewHelperError, "A connection error occurred while contacting the payment gateway. Please try again."
63
+ end
64
+
65
+ def form_method
66
+ "GET"
67
+ end
68
+
69
+ def form_fields
70
+ redirect_parameters
71
+ end
72
+
73
+ private
74
+ def generate_request
75
+ xml = REXML::Document.new
76
+ root = xml.add_element('GenerateRequest')
77
+
78
+ token_parameters.each do | k, v |
79
+ next if v.blank?
80
+
81
+ v = v.to_s.slice(0, 50) if k == "MerchantReference"
82
+ root.add_element(k).text = v
83
+ end
84
+
85
+ xml.to_s
86
+ end
87
+
88
+ def parse_response(raw_response)
89
+ xml = REXML::Document.new(raw_response)
90
+ root = REXML::XPath.first(xml, "//Request")
91
+ valid = root.attributes["valid"]
92
+ redirect = root.elements["URI"].try(:text)
93
+ valid, redirect = "0", root.elements["ResponseText"].try(:text) unless redirect
94
+
95
+ # example valid response:
96
+ # <Request valid="1"><URI>https://sec.paymentexpress.com/pxpay/pxpay.aspx?userid=PxpayUser&amp;request=REQUEST_TOKEN</URI></Request>
97
+ # <Request valid='1'><Reco>IP</Reco><ResponseText>Invalid Access Info</ResponseText></Request>
98
+
99
+ # example invalid response:
100
+ # <Request valid="0"><URI>Invalid TxnType</URI></Request>
101
+
102
+ {:valid => valid, :redirect => redirect}
103
+ end
104
+ end
105
+
106
+ class Notification < OffsitePayments::Notification
107
+ include ActiveUtils::PostsData
108
+ include ActiveUtils::RequiresParameters
109
+
110
+ def initialize(query_string, options={})
111
+ # PxPay appends ?result=...&userid=... to whatever return_url was specified, even if that URL ended with a ?query.
112
+ # So switch the first ? if present to a &
113
+ query_string[/\?/] = '&' if query_string[/\?/]
114
+ super
115
+
116
+ @encrypted_params = @params
117
+ @params = {}
118
+
119
+ requires! @encrypted_params, "result"
120
+ requires! @options, :credential1, :credential2
121
+
122
+ decrypt_transaction_result(@encrypted_params["result"])
123
+ end
124
+
125
+ # was the notification a validly formed request?
126
+ def acknowledge(authcode = nil)
127
+ @valid == '1'
128
+ end
129
+
130
+ def status
131
+ return 'Failed' unless success?
132
+ return 'Completed' if complete?
133
+ 'Error'
134
+ end
135
+
136
+ def complete?
137
+ @params['TxnType'] == 'Purchase' && success?
138
+ end
139
+
140
+ def cancelled?
141
+ !success?
142
+ end
143
+
144
+ # for field definitions see
145
+ # http://www.paymentexpress.com/Technical_Resources/Ecommerce_Hosted/PxPay
146
+
147
+ def success?
148
+ @params['Success'] == '1'
149
+ end
150
+
151
+ def gross
152
+ @params['AmountSettlement']
153
+ end
154
+
155
+ def currency
156
+ @params['CurrencySettlement']
157
+ end
158
+
159
+ def account
160
+ @params['userid']
161
+ end
162
+
163
+ def item_id
164
+ @params['MerchantReference']
165
+ end
166
+
167
+ def currency_input
168
+ @params['CurrencyInput']
169
+ end
170
+
171
+ def auth_code
172
+ @params['AuthCode']
173
+ end
174
+
175
+ def card_type
176
+ @params['CardName']
177
+ end
178
+
179
+ def card_holder_name
180
+ @params['CardHolderName']
181
+ end
182
+
183
+ def card_number
184
+ @params['CardNumber']
185
+ end
186
+
187
+ def expiry_date
188
+ @params['DateExpiry']
189
+ end
190
+
191
+ def client_ip
192
+ @params['ClientInfo']
193
+ end
194
+
195
+ def order_id
196
+ item_id
197
+ end
198
+
199
+ def payer_email
200
+ @params['EmailAddress']
201
+ end
202
+
203
+ def transaction_id
204
+ @params['DpsTxnRef']
205
+ end
206
+
207
+ def settlement_date
208
+ @params['DateSettlement']
209
+ end
210
+
211
+ # Indication of the uniqueness of a card number
212
+ def txn_mac
213
+ @params['TxnMac']
214
+ end
215
+
216
+ def message
217
+ @params['ResponseText']
218
+ end
219
+
220
+ def optional_data
221
+ [@params['TxnData1'],@fields['TxnData2'],@fields['TxnData3']]
222
+ end
223
+
224
+ # When was this payment was received by the client.
225
+ def received_at
226
+ settlement_date
227
+ end
228
+
229
+ # Was this a test transaction?
230
+ def test?
231
+ nil
232
+ end
233
+
234
+ private
235
+
236
+ def decrypt_transaction_result(encrypted_result)
237
+ request_xml = REXML::Document.new
238
+ root = request_xml.add_element('ProcessResponse')
239
+
240
+ root.add_element('PxPayUserId').text = @options[:credential1]
241
+ root.add_element('PxPayKey').text = @options[:credential2]
242
+ root.add_element('Response').text = encrypted_result
243
+
244
+ @raw = ssl_post(Pxpay.token_url, request_xml.to_s)
245
+
246
+ response_xml = REXML::Document.new(@raw)
247
+ root = REXML::XPath.first(response_xml)
248
+ @valid = root.attributes["valid"]
249
+ @params = {}
250
+ root.elements.each { |e| @params[e.name] = e.text }
251
+ end
252
+ end
253
+
254
+ class Return < OffsitePayments::Return
255
+ def initialize(query_string, options={})
256
+ @notification = Notification.new(query_string, options)
257
+ end
258
+
259
+ def success?
260
+ @notification && @notification.complete?
261
+ end
262
+
263
+ def cancelled?
264
+ @notification && @notification.cancelled?
265
+ end
266
+
267
+ def message
268
+ @notification.message
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end
@@ -0,0 +1,232 @@
1
+ module OffsitePayments #:nodoc:
2
+ module Integrations #:nodoc:
3
+ module Quickpay
4
+ mattr_accessor :service_url
5
+ self.service_url = 'https://secure.quickpay.dk/form/'
6
+
7
+ def self.notification(post, options = {})
8
+ Notification.new(post)
9
+ end
10
+
11
+ def self.return(post, options = {})
12
+ Return.new(post, options)
13
+ end
14
+
15
+ class Helper < OffsitePayments::Helper
16
+ def initialize(order, account, options = {})
17
+ md5secret options.delete(:credential2)
18
+ super
19
+ add_field('protocol', '7')
20
+ add_field('msgtype', 'authorize')
21
+ add_field('language', 'da')
22
+ add_field('autocapture', 0)
23
+ add_field('testmode', test? ? 1 : 0)
24
+ add_field('ordernumber', format_order_number(order))
25
+ end
26
+
27
+ def md5secret(value)
28
+ @md5secret = value
29
+ end
30
+
31
+ def form_fields
32
+ @fields.merge('md5check' => generate_md5check)
33
+ end
34
+
35
+ def generate_md5string
36
+ MD5_CHECK_FIELDS.map {|key| @fields[key.to_s]} * "" + @md5secret
37
+ end
38
+
39
+ def generate_md5check
40
+ Digest::MD5.hexdigest(generate_md5string)
41
+ end
42
+
43
+ # Limited to 20 digits max
44
+ def format_order_number(number)
45
+ number.to_s.gsub(/[^\w]/, '').rjust(4, "0")[0...20]
46
+ end
47
+
48
+ MD5_CHECK_FIELDS = [
49
+ :protocol,
50
+ :msgtype,
51
+ :merchant,
52
+ :language,
53
+ :ordernumber,
54
+ :amount,
55
+ :currency,
56
+ :continueurl,
57
+ :cancelurl,
58
+ :callbackurl,
59
+ :autocapture,
60
+ :autofee,
61
+ :cardtypelock,
62
+ :description,
63
+ :group,
64
+ :testmode,
65
+ :splitpayment,
66
+ :forcemobile,
67
+ :deadline,
68
+ :cardhash
69
+ ]
70
+
71
+ mapping :protocol, 'protocol'
72
+ mapping :msgtype, 'msgtype'
73
+ mapping :account, 'merchant'
74
+ mapping :language, 'language'
75
+ mapping :amount, 'amount'
76
+ mapping :currency, 'currency'
77
+
78
+ mapping :return_url, 'continueurl'
79
+ mapping :cancel_return_url, 'cancelurl'
80
+ mapping :notify_url, 'callbackurl'
81
+
82
+ mapping :autocapture, 'autocapture'
83
+ mapping :autofee, 'autofee'
84
+ mapping :cardtypelock, 'cardtypelock'
85
+
86
+ mapping :ipaddress, 'ipaddress'
87
+
88
+ mapping :description, 'description'
89
+ mapping :group, 'group'
90
+ mapping :testmode, 'testmode'
91
+
92
+ mapping :splitpayment, 'splitpayment'
93
+ mapping :forcemobile, 'forcemobile'
94
+ mapping :deadline, 'deadline'
95
+ mapping :cardhash, 'cardhash'
96
+
97
+ mapping :customer, ''
98
+ mapping :billing_address, {}
99
+ mapping :tax, ''
100
+ mapping :shipping, ''
101
+ end
102
+
103
+ class Notification < OffsitePayments::Notification
104
+ def complete?
105
+ status == '000'
106
+ end
107
+
108
+ def item_id
109
+ params['ordernumber']
110
+ end
111
+
112
+ def transaction_id
113
+ params['transaction']
114
+ end
115
+
116
+ def received_at
117
+ time = params['time']
118
+ # If time only contains 12 integers then it's pre v5 format
119
+ time = "20#{params['time']}" if /[0-9]{12}/.match(params['time'])
120
+ Time.parse(time)
121
+ end
122
+
123
+ def gross
124
+ "%.2f" % (gross_cents / 100.0)
125
+ end
126
+
127
+ def gross_cents
128
+ params['amount'].to_i
129
+ end
130
+
131
+ def status
132
+ params['qpstat']
133
+ end
134
+
135
+ def currency
136
+ params['currency']
137
+ end
138
+
139
+ # Provide access to raw fields from quickpay
140
+ %w(
141
+ msgtype
142
+ ordernumber
143
+ state
144
+ chstat
145
+ chstatmsg
146
+ qpstat
147
+ qpstatmsg
148
+ merchant
149
+ merchantemail
150
+ cardtype
151
+ cardnumber
152
+ cardhash
153
+ cardexpire
154
+ splitpayment
155
+ fraudprobability
156
+ fraudremarks
157
+ fraudreport
158
+ fee
159
+ ).each do |attr|
160
+ define_method(attr) do
161
+ params[attr]
162
+ end
163
+ end
164
+
165
+ MD5_CHECK_FIELDS = [
166
+ :msgtype,
167
+ :ordernumber,
168
+ :amount,
169
+ :currency,
170
+ :time,
171
+ :state,
172
+ :qpstat,
173
+ :qpstatmsg,
174
+ :chstat,
175
+ :chstatmsg,
176
+ :merchant,
177
+ :merchantemail,
178
+ :transaction,
179
+ :cardtype,
180
+ :cardnumber,
181
+ :cardhash,
182
+ :cardexpire,
183
+ :splitpayment,
184
+ :fraudprobability,
185
+ :fraudremarks,
186
+ :fraudreport,
187
+ :fee
188
+ ]
189
+
190
+ def generate_md5string
191
+ MD5_CHECK_FIELDS.map { |key| params[key.to_s] } * "" + @options[:credential2].to_s
192
+ end
193
+
194
+ def generate_md5check
195
+ Digest::MD5.hexdigest(generate_md5string)
196
+ end
197
+
198
+ # Quickpay doesn't do acknowledgements of callback notifications
199
+ # Instead it uses and MD5 hash of all parameters
200
+ def acknowledge(authcode = nil)
201
+ generate_md5check == params['md5check']
202
+ end
203
+
204
+ # Take the posted data and move the relevant data into a hash
205
+ def parse(post)
206
+ # 30 + 12
207
+ #------------------------------8a827a0e6829
208
+ #Content-Disposition: form-data; name="msgtype"
209
+ #
210
+ #subscribe
211
+ #------------------------------8a827a0e6829
212
+ #Content-Disposition: form-data; name="ordernumber"
213
+ #
214
+ #BILP94406
215
+
216
+ if post =~ /-{20,40}\w{6,24}/
217
+ @raw = post.to_s
218
+ post.split(/-{20,40}\w{6,24}[\n\r]*/m).each do |part|
219
+ part.scan(/([^\n\r]+)[\n\r]+([^\n\r]*)/m) do |header, value|
220
+ if header.match(/name=["'](.*)["']/)
221
+ params[$1] = value.strip
222
+ end
223
+ end
224
+ end
225
+ else
226
+ super
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end