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.
- 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
|