offsite_payments 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +70 -0
- data/lib/offsite_payments.rb +46 -0
- data/lib/offsite_payments/action_view_helper.rb +72 -0
- data/lib/offsite_payments/helper.rb +119 -0
- data/lib/offsite_payments/integrations.rb +14 -0
- data/lib/offsite_payments/integrations/a1agregator.rb +245 -0
- data/lib/offsite_payments/integrations/authorize_net_sim.rb +580 -0
- data/lib/offsite_payments/integrations/bit_pay.rb +150 -0
- data/lib/offsite_payments/integrations/bogus.rb +32 -0
- data/lib/offsite_payments/integrations/chronopay.rb +283 -0
- data/lib/offsite_payments/integrations/citrus.rb +227 -0
- data/lib/offsite_payments/integrations/direc_pay.rb +339 -0
- data/lib/offsite_payments/integrations/directebanking.rb +237 -0
- data/lib/offsite_payments/integrations/doku.rb +171 -0
- data/lib/offsite_payments/integrations/dotpay.rb +166 -0
- data/lib/offsite_payments/integrations/dwolla.rb +160 -0
- data/lib/offsite_payments/integrations/e_payment_plans.rb +146 -0
- data/lib/offsite_payments/integrations/easy_pay.rb +137 -0
- data/lib/offsite_payments/integrations/epay.rb +161 -0
- data/lib/offsite_payments/integrations/first_data.rb +133 -0
- data/lib/offsite_payments/integrations/gestpay.rb +201 -0
- data/lib/offsite_payments/integrations/hi_trust.rb +179 -0
- data/lib/offsite_payments/integrations/ipay88.rb +240 -0
- data/lib/offsite_payments/integrations/klarna.rb +291 -0
- data/lib/offsite_payments/integrations/liqpay.rb +216 -0
- data/lib/offsite_payments/integrations/maksuturva.rb +231 -0
- data/lib/offsite_payments/integrations/mollie_ideal.rb +213 -0
- data/lib/offsite_payments/integrations/moneybookers.rb +199 -0
- data/lib/offsite_payments/integrations/nochex.rb +228 -0
- data/lib/offsite_payments/integrations/pag_seguro.rb +255 -0
- data/lib/offsite_payments/integrations/paxum.rb +114 -0
- data/lib/offsite_payments/integrations/pay_fast.rb +269 -0
- data/lib/offsite_payments/integrations/paydollar.rb +142 -0
- data/lib/offsite_payments/integrations/payflow_link.rb +194 -0
- data/lib/offsite_payments/integrations/paypal.rb +362 -0
- data/lib/offsite_payments/integrations/paypal_payments_advanced.rb +23 -0
- data/lib/offsite_payments/integrations/paysbuy.rb +71 -0
- data/lib/offsite_payments/integrations/payu_in.rb +266 -0
- data/lib/offsite_payments/integrations/payu_in_paisa.rb +46 -0
- data/lib/offsite_payments/integrations/platron.rb +153 -0
- data/lib/offsite_payments/integrations/pxpay.rb +271 -0
- data/lib/offsite_payments/integrations/quickpay.rb +232 -0
- data/lib/offsite_payments/integrations/rbkmoney.rb +110 -0
- data/lib/offsite_payments/integrations/robokassa.rb +154 -0
- data/lib/offsite_payments/integrations/sage_pay_form.rb +425 -0
- data/lib/offsite_payments/integrations/two_checkout.rb +332 -0
- data/lib/offsite_payments/integrations/universal.rb +180 -0
- data/lib/offsite_payments/integrations/valitor.rb +200 -0
- data/lib/offsite_payments/integrations/verkkomaksut.rb +143 -0
- data/lib/offsite_payments/integrations/web_pay.rb +186 -0
- data/lib/offsite_payments/integrations/webmoney.rb +119 -0
- data/lib/offsite_payments/integrations/wirecard_checkout_page.rb +359 -0
- data/lib/offsite_payments/integrations/world_pay.rb +273 -0
- data/lib/offsite_payments/notification.rb +71 -0
- data/lib/offsite_payments/return.rb +37 -0
- data/lib/offsite_payments/version.rb +3 -0
- 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&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
|