offsite_payments 2.0.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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +70 -0
  4. data/lib/offsite_payments.rb +46 -0
  5. data/lib/offsite_payments/action_view_helper.rb +72 -0
  6. data/lib/offsite_payments/helper.rb +119 -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/direc_pay.rb +339 -0
  15. data/lib/offsite_payments/integrations/directebanking.rb +237 -0
  16. data/lib/offsite_payments/integrations/doku.rb +171 -0
  17. data/lib/offsite_payments/integrations/dotpay.rb +166 -0
  18. data/lib/offsite_payments/integrations/dwolla.rb +160 -0
  19. data/lib/offsite_payments/integrations/e_payment_plans.rb +146 -0
  20. data/lib/offsite_payments/integrations/easy_pay.rb +137 -0
  21. data/lib/offsite_payments/integrations/epay.rb +161 -0
  22. data/lib/offsite_payments/integrations/first_data.rb +133 -0
  23. data/lib/offsite_payments/integrations/gestpay.rb +201 -0
  24. data/lib/offsite_payments/integrations/hi_trust.rb +179 -0
  25. data/lib/offsite_payments/integrations/ipay88.rb +240 -0
  26. data/lib/offsite_payments/integrations/klarna.rb +291 -0
  27. data/lib/offsite_payments/integrations/liqpay.rb +216 -0
  28. data/lib/offsite_payments/integrations/maksuturva.rb +231 -0
  29. data/lib/offsite_payments/integrations/mollie_ideal.rb +213 -0
  30. data/lib/offsite_payments/integrations/moneybookers.rb +199 -0
  31. data/lib/offsite_payments/integrations/nochex.rb +228 -0
  32. data/lib/offsite_payments/integrations/pag_seguro.rb +255 -0
  33. data/lib/offsite_payments/integrations/paxum.rb +114 -0
  34. data/lib/offsite_payments/integrations/pay_fast.rb +269 -0
  35. data/lib/offsite_payments/integrations/paydollar.rb +142 -0
  36. data/lib/offsite_payments/integrations/payflow_link.rb +194 -0
  37. data/lib/offsite_payments/integrations/paypal.rb +362 -0
  38. data/lib/offsite_payments/integrations/paypal_payments_advanced.rb +23 -0
  39. data/lib/offsite_payments/integrations/paysbuy.rb +71 -0
  40. data/lib/offsite_payments/integrations/payu_in.rb +266 -0
  41. data/lib/offsite_payments/integrations/payu_in_paisa.rb +46 -0
  42. data/lib/offsite_payments/integrations/platron.rb +153 -0
  43. data/lib/offsite_payments/integrations/pxpay.rb +271 -0
  44. data/lib/offsite_payments/integrations/quickpay.rb +232 -0
  45. data/lib/offsite_payments/integrations/rbkmoney.rb +110 -0
  46. data/lib/offsite_payments/integrations/robokassa.rb +154 -0
  47. data/lib/offsite_payments/integrations/sage_pay_form.rb +425 -0
  48. data/lib/offsite_payments/integrations/two_checkout.rb +332 -0
  49. data/lib/offsite_payments/integrations/universal.rb +180 -0
  50. data/lib/offsite_payments/integrations/valitor.rb +200 -0
  51. data/lib/offsite_payments/integrations/verkkomaksut.rb +143 -0
  52. data/lib/offsite_payments/integrations/web_pay.rb +186 -0
  53. data/lib/offsite_payments/integrations/webmoney.rb +119 -0
  54. data/lib/offsite_payments/integrations/wirecard_checkout_page.rb +359 -0
  55. data/lib/offsite_payments/integrations/world_pay.rb +273 -0
  56. data/lib/offsite_payments/notification.rb +71 -0
  57. data/lib/offsite_payments/return.rb +37 -0
  58. data/lib/offsite_payments/version.rb +3 -0
  59. metadata +270 -0
@@ -0,0 +1,271 @@
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 ActiveMerchant::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
+ end
62
+
63
+ def form_method
64
+ "GET"
65
+ end
66
+
67
+ def form_fields
68
+ redirect_parameters
69
+ end
70
+
71
+ private
72
+ def generate_request
73
+ xml = REXML::Document.new
74
+ root = xml.add_element('GenerateRequest')
75
+
76
+ token_parameters.each do | k, v |
77
+ next if v.blank?
78
+
79
+ v = v.to_s.slice(0, 50) if k == "MerchantReference"
80
+ root.add_element(k).text = v
81
+ end
82
+
83
+ xml.to_s
84
+ end
85
+
86
+ def parse_response(raw_response)
87
+ xml = REXML::Document.new(raw_response)
88
+ root = REXML::XPath.first(xml, "//Request")
89
+ valid = root.attributes["valid"]
90
+ redirect = root.elements["URI"].try(:text)
91
+ valid, redirect = "0", root.elements["ResponseText"].try(:text) unless redirect
92
+
93
+ # example valid response:
94
+ # <Request valid="1"><URI>https://sec.paymentexpress.com/pxpay/pxpay.aspx?userid=PxpayUser&amp;request=REQUEST_TOKEN</URI></Request>
95
+ # <Request valid='1'><Reco>IP</Reco><ResponseText>Invalid Access Info</ResponseText></Request>
96
+
97
+ # example invalid response:
98
+ # <Request valid="0"><URI>Invalid TxnType</URI></Request>
99
+
100
+ {:valid => valid, :redirect => redirect}
101
+ end
102
+ end
103
+
104
+ class Notification < OffsitePayments::Notification
105
+ include ActiveMerchant::PostsData
106
+ include ActiveMerchant::RequiresParameters
107
+
108
+ def initialize(query_string, options={})
109
+ # PxPay appends ?result=...&userid=... to whatever return_url was specified, even if that URL ended with a ?query.
110
+ # So switch the first ? if present to a &
111
+ query_string[/\?/] = '&' if query_string[/\?/]
112
+ super
113
+
114
+ @encrypted_params = @params
115
+ @params = {}
116
+
117
+ requires! @encrypted_params, "result"
118
+ requires! @options, :credential1, :credential2
119
+
120
+ decrypt_transaction_result(@encrypted_params["result"])
121
+ end
122
+
123
+ # was the notification a validly formed request?
124
+ def acknowledge(authcode = nil)
125
+ @valid == '1'
126
+ end
127
+
128
+ def status
129
+ return 'Failed' unless success?
130
+ return 'Completed' if complete?
131
+ 'Error'
132
+ end
133
+
134
+ def complete?
135
+ @params['TxnType'] == 'Purchase' && success?
136
+ end
137
+
138
+ def cancelled?
139
+ !success?
140
+ end
141
+
142
+ # for field definitions see
143
+ # http://www.paymentexpress.com/Technical_Resources/Ecommerce_Hosted/PxPay
144
+
145
+ def success?
146
+ @params['Success'] == '1'
147
+ end
148
+
149
+ def gross
150
+ @params['AmountSettlement']
151
+ end
152
+
153
+ def currency
154
+ @params['CurrencySettlement']
155
+ end
156
+
157
+ def account
158
+ @params['userid']
159
+ end
160
+
161
+ def item_id
162
+ @params['MerchantReference']
163
+ end
164
+
165
+ def currency_input
166
+ @params['CurrencyInput']
167
+ end
168
+
169
+ def auth_code
170
+ @params['AuthCode']
171
+ end
172
+
173
+ def card_type
174
+ @params['CardName']
175
+ end
176
+
177
+ def card_holder_name
178
+ @params['CardHolderName']
179
+ end
180
+
181
+ def card_number
182
+ @params['CardNumber']
183
+ end
184
+
185
+ def expiry_date
186
+ @params['DateExpiry']
187
+ end
188
+
189
+ def client_ip
190
+ @params['ClientInfo']
191
+ end
192
+
193
+ def order_id
194
+ item_id
195
+ end
196
+
197
+ def payer_email
198
+ @params['EmailAddress']
199
+ end
200
+
201
+ def transaction_id
202
+ @params['DpsTxnRef']
203
+ end
204
+
205
+ def settlement_date
206
+ @params['DateSettlement']
207
+ end
208
+
209
+ # Indication of the uniqueness of a card number
210
+ def txn_mac
211
+ @params['TxnMac']
212
+ end
213
+
214
+ def message
215
+ @params['ResponseText']
216
+ end
217
+
218
+ def optional_data
219
+ [@params['TxnData1'],@fields['TxnData2'],@fields['TxnData3']]
220
+ end
221
+
222
+ # When was this payment was received by the client.
223
+ def received_at
224
+ settlement_date
225
+ end
226
+
227
+ # Was this a test transaction?
228
+ def test?
229
+ nil
230
+ end
231
+
232
+ private
233
+
234
+ def decrypt_transaction_result(encrypted_result)
235
+ request_xml = REXML::Document.new
236
+ root = request_xml.add_element('ProcessResponse')
237
+
238
+ root.add_element('PxPayUserId').text = @options[:credential1]
239
+ root.add_element('PxPayKey').text = @options[:credential2]
240
+ root.add_element('Response').text = encrypted_result
241
+
242
+ @raw = ssl_post(Pxpay.token_url, request_xml.to_s)
243
+
244
+ response_xml = REXML::Document.new(@raw)
245
+ root = REXML::XPath.first(response_xml)
246
+ @valid = root.attributes["valid"]
247
+ @params = {}
248
+ root.elements.each { |e| @params[e.name] = e.text }
249
+ end
250
+ end
251
+
252
+ class Return < OffsitePayments::Return
253
+ def initialize(query_string, options={})
254
+ @notification = Notification.new(query_string, options)
255
+ end
256
+
257
+ def success?
258
+ @notification && @notification.complete?
259
+ end
260
+
261
+ def cancelled?
262
+ @notification && @notification.cancelled?
263
+ end
264
+
265
+ def message
266
+ @notification.message
267
+ end
268
+ end
269
+ end
270
+ end
271
+ 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