activemerchant 1.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.
- data/CHANGELOG +40 -0
- data/MIT-LICENSE +20 -0
- data/README +93 -0
- data/lib/active_merchant.rb +59 -0
- data/lib/active_merchant/billing/base.rb +52 -0
- data/lib/active_merchant/billing/credit_card.rb +217 -0
- data/lib/active_merchant/billing/gateway.rb +100 -0
- data/lib/active_merchant/billing/gateways.rb +15 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +236 -0
- data/lib/active_merchant/billing/gateways/bogus.rb +92 -0
- data/lib/active_merchant/billing/gateways/eway.rb +235 -0
- data/lib/active_merchant/billing/gateways/linkpoint.rb +445 -0
- data/lib/active_merchant/billing/gateways/moneris.rb +205 -0
- data/lib/active_merchant/billing/gateways/payflow.rb +84 -0
- data/lib/active_merchant/billing/gateways/payflow/f73e89fd.0 +17 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +190 -0
- data/lib/active_merchant/billing/gateways/payflow/payflow_express_response.rb +30 -0
- data/lib/active_merchant/billing/gateways/payflow_express.rb +123 -0
- data/lib/active_merchant/billing/gateways/payflow_express_uk.rb +9 -0
- data/lib/active_merchant/billing/gateways/payflow_uk.rb +17 -0
- data/lib/active_merchant/billing/gateways/paypal.rb +90 -0
- data/lib/active_merchant/billing/gateways/paypal/api_cert_chain.crt +35 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +208 -0
- data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +30 -0
- data/lib/active_merchant/billing/gateways/paypal_express.rb +115 -0
- data/lib/active_merchant/billing/gateways/psigate.rb +265 -0
- data/lib/active_merchant/billing/gateways/trust_commerce.rb +330 -0
- data/lib/active_merchant/billing/gateways/usa_epay.rb +189 -0
- data/lib/active_merchant/billing/integrations.rb +12 -0
- data/lib/active_merchant/billing/integrations/action_view_helper.rb +65 -0
- data/lib/active_merchant/billing/integrations/bogus.rb +17 -0
- data/lib/active_merchant/billing/integrations/bogus/helper.rb +17 -0
- data/lib/active_merchant/billing/integrations/bogus/notification.rb +11 -0
- data/lib/active_merchant/billing/integrations/chronopay.rb +17 -0
- data/lib/active_merchant/billing/integrations/chronopay/helper.rb +81 -0
- data/lib/active_merchant/billing/integrations/chronopay/notification.rb +156 -0
- data/lib/active_merchant/billing/integrations/gestpay.rb +21 -0
- data/lib/active_merchant/billing/integrations/gestpay/common.rb +42 -0
- data/lib/active_merchant/billing/integrations/gestpay/helper.rb +72 -0
- data/lib/active_merchant/billing/integrations/gestpay/notification.rb +83 -0
- data/lib/active_merchant/billing/integrations/helper.rb +79 -0
- data/lib/active_merchant/billing/integrations/nochex.rb +21 -0
- data/lib/active_merchant/billing/integrations/nochex/helper.rb +68 -0
- data/lib/active_merchant/billing/integrations/nochex/notification.rb +101 -0
- data/lib/active_merchant/billing/integrations/notification.rb +52 -0
- data/lib/active_merchant/billing/integrations/paypal.rb +35 -0
- data/lib/active_merchant/billing/integrations/paypal/helper.rb +103 -0
- data/lib/active_merchant/billing/integrations/paypal/notification.rb +187 -0
- data/lib/active_merchant/billing/response.rb +28 -0
- data/lib/active_merchant/lib/country.rb +297 -0
- data/lib/active_merchant/lib/posts_data.rb +21 -0
- data/lib/active_merchant/lib/requires_parameters.rb +17 -0
- data/lib/active_merchant/lib/validateable.rb +76 -0
- data/lib/tasks/cia.rb +90 -0
- metadata +129 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'active_merchant/billing/response'
|
4
|
+
|
5
|
+
module ActiveMerchant #:nodoc:
|
6
|
+
module Billing #:nodoc:
|
7
|
+
# The Gateway class is the base class for all ActiveMerchant gateway
|
8
|
+
# implementations. The list of gateway functions that concrete
|
9
|
+
# gateway classes can and should implement include the following:
|
10
|
+
#
|
11
|
+
# === Core operations supported by most gateways
|
12
|
+
# * purchase(money, creditcard, options = {})
|
13
|
+
# * authorize(money, creditcard, options = {})
|
14
|
+
# * capture(money, authorization, options = {})
|
15
|
+
# * void(identification, options = {})
|
16
|
+
# * credit(money, identification, options = {})
|
17
|
+
class Gateway
|
18
|
+
include PostsData
|
19
|
+
include RequiresParameters
|
20
|
+
|
21
|
+
# The format of the amounts used by the gateway
|
22
|
+
# :dollars => '12.50'
|
23
|
+
# :cents => '1250'
|
24
|
+
class_inheritable_accessor :money_format
|
25
|
+
self.money_format = :dollars
|
26
|
+
|
27
|
+
# Return the matching gateway for the provider
|
28
|
+
# * <tt>bogus</tt>: BogusGateway - Does nothing ( for testing)
|
29
|
+
# * <tt>moneris</tt>: MonerisGateway
|
30
|
+
# * <tt>authorize_net</tt>: AuthorizeNetGateway
|
31
|
+
# * <tt>trust_commerce</tt>: TrustCommerceGateway
|
32
|
+
#
|
33
|
+
# ActiveMerchant::Base.gateway('moneris').new
|
34
|
+
def self.gateway(name)
|
35
|
+
ActiveMerchant::Billing.const_get("#{name.to_s.downcase}_gateway".camelize)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Does this gateway support credit cards of the passed type?
|
39
|
+
def self.supports?(type)
|
40
|
+
supported_cardtypes.include?(type.intern)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get a list of supported credit card types for this gateway
|
44
|
+
def self.supported_cardtypes
|
45
|
+
[]
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :options
|
49
|
+
# Initialize a new gateway
|
50
|
+
#
|
51
|
+
# See the documentation for the gateway you will be using to make sure there
|
52
|
+
# are no other required options
|
53
|
+
def initialize(options = {})
|
54
|
+
@ssl_strict = options[:ssl_strict] || false
|
55
|
+
end
|
56
|
+
|
57
|
+
# Are we running in test mode?
|
58
|
+
def test?
|
59
|
+
Base.gateway_mode == :test
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def name
|
64
|
+
self.class.name.scan(/\:\:(\w+)Gateway/).flatten.first
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_result_from_cc_number(number)
|
68
|
+
return false unless test?
|
69
|
+
|
70
|
+
case number.to_s
|
71
|
+
when '1', 'success'
|
72
|
+
Response.new(true, 'Successful test mode response', {:receiptid => '#0001'}, :test => true, :authorization => '5555')
|
73
|
+
when '2', 'failure'
|
74
|
+
Response.new(false, 'Failed test mode response', {:receiptid => '#0001'}, :test => true)
|
75
|
+
when '3', 'error'
|
76
|
+
raise Error, 'big bad exception'
|
77
|
+
else
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Return a string with the amount in the appropriate format
|
83
|
+
def amount(money)
|
84
|
+
return nil if money.nil?
|
85
|
+
cents = money.respond_to?(:cents) ? money.cents : money
|
86
|
+
|
87
|
+
if money.is_a?(String) or cents.to_i < 0
|
88
|
+
raise ArgumentError, 'money amount must be either a Money object or a positive integer in cents.'
|
89
|
+
end
|
90
|
+
|
91
|
+
case self.money_format
|
92
|
+
when :cents
|
93
|
+
cents.to_s
|
94
|
+
else
|
95
|
+
sprintf("%.2f", cents.to_f/100)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'active_merchant/billing/gateway'
|
2
|
+
require 'active_merchant/billing/gateways/bogus'
|
3
|
+
require 'active_merchant/billing/gateways/psigate'
|
4
|
+
require 'active_merchant/billing/gateways/authorize_net'
|
5
|
+
require 'active_merchant/billing/gateways/moneris'
|
6
|
+
require 'active_merchant/billing/gateways/trust_commerce'
|
7
|
+
require 'active_merchant/billing/gateways/linkpoint'
|
8
|
+
require 'active_merchant/billing/gateways/paypal'
|
9
|
+
require 'active_merchant/billing/gateways/paypal_express'
|
10
|
+
require 'active_merchant/billing/gateways/eway'
|
11
|
+
require 'active_merchant/billing/gateways/usa_epay'
|
12
|
+
require 'active_merchant/billing/gateways/payflow'
|
13
|
+
require 'active_merchant/billing/gateways/payflow_express'
|
14
|
+
require 'active_merchant/billing/gateways/payflow_uk'
|
15
|
+
require 'active_merchant/billing/gateways/payflow_express_uk'
|
@@ -0,0 +1,236 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
|
4
|
+
class AuthorizeNetGateway < Gateway
|
5
|
+
API_VERSION = '3.1'
|
6
|
+
LIVE_URL = "https://secure.authorize.net/gateway/transact.dll"
|
7
|
+
TEST_URL = "https://test.authorize.net/gateway/transact.dll"
|
8
|
+
|
9
|
+
APPROVED, DECLINED, ERROR = 1, 2, 3
|
10
|
+
|
11
|
+
RESPONSE_CODE, RESPONSE_REASON_CODE, RESPONSE_REASON_TEXT = 0, 2, 3
|
12
|
+
AVS_RESULT_CODE, TRANSACTION_ID, CARD_CODE_RESPONSE_CODE = 5, 6, 38
|
13
|
+
|
14
|
+
CARD_CODE_ERRORS = %w( N S )
|
15
|
+
|
16
|
+
CARD_CODE_MESSAGES = {
|
17
|
+
"M" => "Card verification number matched",
|
18
|
+
"N" => "Card verification number didn't match",
|
19
|
+
"P" => "Card verification number was not processed",
|
20
|
+
"S" => "Card verification number should be on card but was not indicated",
|
21
|
+
"U" => "Issuer was not certified for card verification"
|
22
|
+
}
|
23
|
+
|
24
|
+
AVS_ERRORS = %w( A E N R W Z )
|
25
|
+
|
26
|
+
AVS_MESSAGES = {
|
27
|
+
"A" => "Street address matches billing information, zip/postal code does not",
|
28
|
+
"B" => "Address information not provided for address verification check",
|
29
|
+
"E" => "Address verification service error",
|
30
|
+
"G" => "Non-U.S. card-issuing bank",
|
31
|
+
"N" => "Neither street address nor zip/postal match billing information",
|
32
|
+
"P" => "Address verification not applicable for this transaction",
|
33
|
+
"R" => "Payment gateway was unavailable or timed out",
|
34
|
+
"S" => "Address verification service not supported by issuer",
|
35
|
+
"U" => "Address information is unavailable",
|
36
|
+
"W" => "9-digit zip/postal code matches billing information, street address does not",
|
37
|
+
"X" => "Street address and 9-digit zip/postal code matches billing information",
|
38
|
+
"Y" => "Street address and 5-digit zip/postal code matches billing information",
|
39
|
+
"Z" => "5-digit zip/postal code matches billing information, street address does not",
|
40
|
+
}
|
41
|
+
|
42
|
+
# URL
|
43
|
+
attr_reader :url
|
44
|
+
attr_reader :response
|
45
|
+
attr_reader :options
|
46
|
+
|
47
|
+
def initialize(options = {})
|
48
|
+
requires!(options, :login, :password)
|
49
|
+
@options = options
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def authorize(money, creditcard, options = {})
|
54
|
+
post = {}
|
55
|
+
add_invoice(post, options)
|
56
|
+
add_creditcard(post, creditcard)
|
57
|
+
add_address(post, options)
|
58
|
+
add_customer_data(post, options)
|
59
|
+
|
60
|
+
commit('AUTH_ONLY', money, post)
|
61
|
+
end
|
62
|
+
|
63
|
+
def purchase(money, creditcard, options = {})
|
64
|
+
post = {}
|
65
|
+
add_invoice(post, options)
|
66
|
+
add_creditcard(post, creditcard)
|
67
|
+
add_address(post, options)
|
68
|
+
add_customer_data(post, options)
|
69
|
+
|
70
|
+
commit('AUTH_CAPTURE', money, post)
|
71
|
+
end
|
72
|
+
|
73
|
+
def capture(money, authorization, options = {})
|
74
|
+
post = {:trans_id => authorization}
|
75
|
+
add_customer_data(post, options)
|
76
|
+
commit('PRIOR_AUTH_CAPTURE', money, post)
|
77
|
+
end
|
78
|
+
|
79
|
+
def void(authorization, options = {})
|
80
|
+
post = {:trans_id => authorization}
|
81
|
+
commit('VOID', nil, post)
|
82
|
+
end
|
83
|
+
|
84
|
+
# We support visa and master card
|
85
|
+
def self.supported_cardtypes
|
86
|
+
[:visa, :master, :american_express, :discover]
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def expdate(creditcard)
|
92
|
+
year = sprintf("%.4i", creditcard.year)
|
93
|
+
month = sprintf("%.2i", creditcard.month)
|
94
|
+
|
95
|
+
"#{year[-2..-1]}#{month}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def commit(action, money, parameters)
|
99
|
+
parameters[:amount] = amount(money) unless action == 'VOID'
|
100
|
+
|
101
|
+
# Only activate the test_request when the :test option is passed in
|
102
|
+
parameters[:test_request] = @options[:test] ? 'TRUE' : 'FALSE'
|
103
|
+
|
104
|
+
if result = test_result_from_cc_number(parameters[:card_num])
|
105
|
+
return result
|
106
|
+
end
|
107
|
+
|
108
|
+
url = test? ? TEST_URL : LIVE_URL
|
109
|
+
data = ssl_post url, post_data(action, parameters)
|
110
|
+
|
111
|
+
@response = parse(data)
|
112
|
+
success = @response[:response_code] == APPROVED
|
113
|
+
message = message_from(@response)
|
114
|
+
|
115
|
+
# Return the response. The authorization can be taken out of the transaction_id
|
116
|
+
# Test Mode on/off is something we have to parse from the response text.
|
117
|
+
# It usually looks something like this
|
118
|
+
#
|
119
|
+
# (TESTMODE) Successful Sale
|
120
|
+
#
|
121
|
+
|
122
|
+
test_mode = test? || message =~ /TESTMODE/
|
123
|
+
|
124
|
+
Response.new(success, message, @response,
|
125
|
+
:test => test_mode,
|
126
|
+
:authorization => @response[:transaction_id]
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
def parse(body)
|
131
|
+
fields = body[1..-2].split(/\$,\$/)
|
132
|
+
|
133
|
+
results = {
|
134
|
+
:response_code => fields[RESPONSE_CODE].to_i,
|
135
|
+
:response_reason_code => fields[RESPONSE_REASON_CODE],
|
136
|
+
:response_reason_text => fields[RESPONSE_REASON_TEXT],
|
137
|
+
:avs_result_code => fields[AVS_RESULT_CODE],
|
138
|
+
:transaction_id => fields[TRANSACTION_ID],
|
139
|
+
:card_code => fields[CARD_CODE_RESPONSE_CODE]
|
140
|
+
}
|
141
|
+
|
142
|
+
|
143
|
+
results[:card_code_message] = CARD_CODE_MESSAGES[results[:card_code]] if results[:card_code]
|
144
|
+
results[:avs_message] = AVS_MESSAGES[results[:avs_result_code]] if results[:avs_result_code]
|
145
|
+
|
146
|
+
results
|
147
|
+
end
|
148
|
+
|
149
|
+
def post_data(action, parameters = {})
|
150
|
+
post = {}
|
151
|
+
|
152
|
+
post[:version] = API_VERSION
|
153
|
+
post[:login] = @options[:login]
|
154
|
+
post[:tran_key] = @options[:password]
|
155
|
+
post[:relay_response] = "FALSE"
|
156
|
+
post[:type] = action
|
157
|
+
post[:delim_data] = "TRUE"
|
158
|
+
post[:delim_char] = ","
|
159
|
+
post[:encap_char] = "$"
|
160
|
+
|
161
|
+
request = post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
162
|
+
request
|
163
|
+
end
|
164
|
+
|
165
|
+
def add_invoice(post, options)
|
166
|
+
post[:invoice_num] = options[:order_id]
|
167
|
+
post[:description] = options[:description]
|
168
|
+
end
|
169
|
+
|
170
|
+
def add_creditcard(post, creditcard)
|
171
|
+
post[:card_num] = creditcard.number
|
172
|
+
post[:card_code] = creditcard.verification_value if creditcard.verification_value?
|
173
|
+
post[:exp_date] = expdate(creditcard)
|
174
|
+
post[:first_name] = creditcard.first_name
|
175
|
+
post[:last_name] = creditcard.last_name
|
176
|
+
end
|
177
|
+
|
178
|
+
def add_customer_data(post, options)
|
179
|
+
if options.has_key? :email
|
180
|
+
post[:email] = options[:email]
|
181
|
+
post[:email_customer] = false
|
182
|
+
end
|
183
|
+
|
184
|
+
if options.has_key? :customer
|
185
|
+
post[:cust_id] = options[:customer]
|
186
|
+
end
|
187
|
+
|
188
|
+
if options.has_key? :ip
|
189
|
+
post[:customer_ip] = options[:ip]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def add_address(post, options)
|
194
|
+
|
195
|
+
if address = options[:billing_address] || options[:address]
|
196
|
+
post[:address] = address[:address1].to_s
|
197
|
+
post[:company] = address[:company].to_s
|
198
|
+
post[:phone] = address[:phone].to_s
|
199
|
+
post[:zip] = address[:zip].to_s
|
200
|
+
post[:city] = address[:city].to_s
|
201
|
+
post[:country] = address[:country].to_s
|
202
|
+
post[:state] = address[:state].blank? ? 'n/a' : address[:state]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Make a ruby type out of the response string
|
207
|
+
def normalize(field)
|
208
|
+
case field
|
209
|
+
when "true" then true
|
210
|
+
when "false" then false
|
211
|
+
when "" then nil
|
212
|
+
when "null" then nil
|
213
|
+
else field
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def message_from(results)
|
218
|
+
if results[:response_code] == DECLINED
|
219
|
+
return CARD_CODE_MESSAGES[results[:card_code]] if CARD_CODE_ERRORS.include?(results[:card_code])
|
220
|
+
return AVS_MESSAGES[results[:avs_result_code]] if AVS_ERRORS.include?(results[:avs_result_code])
|
221
|
+
end
|
222
|
+
|
223
|
+
return results[:response_reason_text][0..-2] # Forget the punctuation at the end
|
224
|
+
end
|
225
|
+
|
226
|
+
def expdate(creditcard)
|
227
|
+
year = sprintf("%.4i", creditcard.year)
|
228
|
+
month = sprintf("%.2i", creditcard.month)
|
229
|
+
|
230
|
+
"#{month}#{year[-2..-1]}"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
AuthorizedNetGateway = AuthorizeNetGateway
|
235
|
+
end
|
236
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
module Billing #:nodoc:
|
3
|
+
# Bogus Gateway
|
4
|
+
class BogusGateway < Gateway
|
5
|
+
|
6
|
+
def authorize(money, creditcard, options = {})
|
7
|
+
case creditcard.number
|
8
|
+
when '1'
|
9
|
+
Response.new(true, "Bogus Gateway: Forced success", {:authorized_amount => money.to_s}, :test => true, :authorization => '53433' )
|
10
|
+
when '2'
|
11
|
+
Response.new(false, "Bogus Gateway: Forced failure", {:authorized_amount => money.to_s, :error => 'Bogus Gateway: Forced failure' }, :test => true)
|
12
|
+
else
|
13
|
+
raise Error, 'Bogus Gateway: Use CreditCard number 1 for success, 2 for exception and anything else for error'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def purchase(money, creditcard, options = {})
|
18
|
+
case creditcard.number
|
19
|
+
when '1'
|
20
|
+
Response.new(true, "Bogus Gateway: Forced success", {:paid_amount => money.to_s}, :test => true)
|
21
|
+
when '2'
|
22
|
+
Response.new(false, "Bogus Gateway: Forced failure", {:paid_amount => money.to_s, :error => 'Bogus Gateway: Forced failure' },:test => true)
|
23
|
+
else
|
24
|
+
raise Error, 'Bogus Gateway: Use CreditCard number 1 for success, 2 for exception and anything else for error'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def credit(money, ident, options = {})
|
29
|
+
case ident
|
30
|
+
when '1'
|
31
|
+
Response.new(true, "Bogus Gateway: Forced success", {:paid_amount => money.to_s}, :test => true)
|
32
|
+
when '2'
|
33
|
+
Response.new(false, "Bogus Gateway: Forced failure", {:paid_amount => money.to_s, :error => 'Bogus Gateway: Forced failure' },:test => true)
|
34
|
+
else
|
35
|
+
raise Error, 'Bogus Gateway: Use trans_id 1 for success, 2 for exception and anything else for error'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def capture(money, ident, options = {})
|
40
|
+
case ident
|
41
|
+
when '1'
|
42
|
+
raise Error, 'Bogus Gateway: Use authorization number 1 for exception, 2 for error and anything else for success'
|
43
|
+
when '2'
|
44
|
+
Response.new(false, "Bogus Gateway: Forced failure", {:paid_amount => money.to_s, :error => 'Bogus Gateway: Forced failure' }, :test => true)
|
45
|
+
else
|
46
|
+
Response.new(true, "Bogus Gateway: Forced success", {:paid_amount => money.to_s}, :test => true)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def store(creditcard, options = {})
|
51
|
+
case creditcard.number
|
52
|
+
when '1'
|
53
|
+
Response.new(true, "Bogus Gateway: Forced success", {:billingid => '1'}, :test => true, :authorization => '53433' )
|
54
|
+
when '2'
|
55
|
+
Response.new(false, "Bogus Gateway: Forced failure", {:billingid => nil, :error => 'Bogus Gateway: Forced failure' }, :test => true)
|
56
|
+
else
|
57
|
+
raise Error, 'Bogus Gateway: Use CreditCard number 1 for success, 2 for exception and anything else for error'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def unstore(identification, options = {})
|
62
|
+
case identification
|
63
|
+
when '1'
|
64
|
+
Response.new(true, "Bogus Gateway: Forced success", {}, :test => true)
|
65
|
+
when '2'
|
66
|
+
Response.new(false, "Bogus Gateway: Forced failure", {:error => 'Bogus Gateway: Forced failure' },:test => true)
|
67
|
+
else
|
68
|
+
raise Error, 'Bogus Gateway: Use trans_id 1 for success, 2 for exception and anything else for error'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# We support visa and master card
|
73
|
+
def self.supported_cardtypes
|
74
|
+
[:bogus]
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def deal_with_cc(creditcard)
|
80
|
+
case creditcard.number
|
81
|
+
when '1'
|
82
|
+
Response.new(true, "Bogus Gateway: Forced success", {}, :test => true)
|
83
|
+
when '2'
|
84
|
+
Response.new(false, "Bogus Gateway: Forced failure", @response, :test => true)
|
85
|
+
else
|
86
|
+
raise Error, 'Bogus Gateway: Use CreditCard number 1 for success, 2 for exception and anything else for error'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
# Author:: Lucas Carlson (mailto:lucas@rufy.com)
|
2
|
+
# Copyright:: Copyright (c) 2005 Lucas Carlson
|
3
|
+
# License:: Distributes under the same terms as Ruby
|
4
|
+
|
5
|
+
require 'rexml/document'
|
6
|
+
|
7
|
+
module ActiveMerchant #:nodoc:
|
8
|
+
module Billing #:nodoc:
|
9
|
+
# TO USE:
|
10
|
+
# First, make sure you have everything setup correctly and all of your dependencies in place with:
|
11
|
+
#
|
12
|
+
# require 'rubygems'
|
13
|
+
# require 'money'
|
14
|
+
# require 'active_merchant'
|
15
|
+
#
|
16
|
+
# The second line is a require for the 'money' library. Make sure you have it installed with 'gem install money'
|
17
|
+
#
|
18
|
+
# Using the money library, create a money object. Pass the dollar value in cents. In this case, $10 US becomes 1000.
|
19
|
+
#
|
20
|
+
# tendollar = Money.us_dollar(1000)
|
21
|
+
#
|
22
|
+
# Next, create a credit card object using a TC approved test card.
|
23
|
+
#
|
24
|
+
# creditcard = ActiveMerchant::Billing::CreditCard.new({
|
25
|
+
# :number => '4111111111111111',
|
26
|
+
# :month => 8,
|
27
|
+
# :year => 2006,
|
28
|
+
# :first_name => 'Longbob',
|
29
|
+
# :last_name => 'Longsen'
|
30
|
+
# })
|
31
|
+
# options = {
|
32
|
+
# :login => '87654321',
|
33
|
+
# :order_id => '1230123',
|
34
|
+
# :email => 'bob@testbob.com',
|
35
|
+
# :address => { :address1 => '47 Bobway, Bobville, WA, Australia',
|
36
|
+
# :zip => '2000'
|
37
|
+
# }
|
38
|
+
# :description => 'purchased items'
|
39
|
+
# }
|
40
|
+
#
|
41
|
+
# To finish setting up, create the active_merchant object you will be using, with the eWay gateway. If you have a
|
42
|
+
# functional eWay account, replace :login with your account info.
|
43
|
+
#
|
44
|
+
# gateway = ActiveMerchant::Billing::Base.gateway(:eway).new()
|
45
|
+
#
|
46
|
+
# Now we are ready to process our transaction
|
47
|
+
#
|
48
|
+
# response = gateway.purchase(tendollar, creditcard, options)
|
49
|
+
#
|
50
|
+
# Sending a transaction to TrustCommerce with active_merchant returns a Response object, which consistently allows you to:
|
51
|
+
#
|
52
|
+
# 1) Check whether the transaction was successful
|
53
|
+
#
|
54
|
+
# response.success?
|
55
|
+
#
|
56
|
+
# 2) Retrieve any message returned by eWay, either a "transaction was successful" note or an explanation of why the
|
57
|
+
# transaction was rejected.
|
58
|
+
#
|
59
|
+
# response.message
|
60
|
+
#
|
61
|
+
# 3) Retrieve and store the unique transaction ID returned by eWway, for use in referencing the transaction in the future.
|
62
|
+
#
|
63
|
+
# response.authorization
|
64
|
+
#
|
65
|
+
# This should be enough to get you started with eWay and active_merchant. For further information, review the methods
|
66
|
+
# below and the rest of active_merchant's documentation.
|
67
|
+
|
68
|
+
class EwayGateway < Gateway
|
69
|
+
TEST_URL = 'https://www.eway.com.au/gateway/xmltest/testpage.asp'
|
70
|
+
LIVE_URL = 'https://www.eway.com.au/gateway/xmlpayment.asp'
|
71
|
+
|
72
|
+
TEST_CVN_URL = 'https://www.eway.com.au/gateway_cvn/xmltest/testpage.asp'
|
73
|
+
LIVE_CVN_URL = 'https://www.eway.com.au/gateway_cvn/xmlpayment.asp'
|
74
|
+
|
75
|
+
MESSAGES = {
|
76
|
+
"00" => "Transaction was successfully processed",
|
77
|
+
"A8" => "Amount is invalid",
|
78
|
+
"A9" => "Card number is invalid",
|
79
|
+
"AA" => "Account is invalid",
|
80
|
+
"AB" => "Card expiry date is invalid",
|
81
|
+
"01" => "Card verification number didn't match",
|
82
|
+
"05" => "Card verification number didn't match"
|
83
|
+
}
|
84
|
+
|
85
|
+
attr_reader :url
|
86
|
+
attr_reader :response
|
87
|
+
attr_reader :options
|
88
|
+
|
89
|
+
self.money_format = :cents
|
90
|
+
|
91
|
+
def initialize(options = {})
|
92
|
+
requires!(options, :login)
|
93
|
+
@options = options
|
94
|
+
super
|
95
|
+
end
|
96
|
+
|
97
|
+
# ewayCustomerEmail, ewayCustomerAddress, ewayCustomerPostcode
|
98
|
+
def purchase(money, creditcard, options = {})
|
99
|
+
requires!(options, :order_id)
|
100
|
+
|
101
|
+
post = {}
|
102
|
+
add_creditcard(post, creditcard)
|
103
|
+
add_address(post, options)
|
104
|
+
add_customer_data(post, options)
|
105
|
+
add_invoice_data(post, options)
|
106
|
+
# The request fails if all of the fields aren't present
|
107
|
+
add_optional_data(post)
|
108
|
+
|
109
|
+
commit(money, post)
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.supported_cardtypes
|
113
|
+
[:visa, :master]
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
def add_creditcard(post, creditcard)
|
118
|
+
post[:CardNumber] = creditcard.number
|
119
|
+
post[:CardExpiryMonth] = sprintf("%.2i", creditcard.month)
|
120
|
+
post[:CardExpiryYear] = sprintf("%.4i", creditcard.year)[-2..-1]
|
121
|
+
post[:CustomerFirstName] = creditcard.first_name
|
122
|
+
post[:CustomerLastName] = creditcard.last_name
|
123
|
+
post[:CardHoldersName] = creditcard.name
|
124
|
+
|
125
|
+
post[:CVN] = creditcard.verification_value if creditcard.verification_value?
|
126
|
+
end
|
127
|
+
|
128
|
+
def add_address(post, options)
|
129
|
+
if address = options[:billing_address] || options[:address]
|
130
|
+
post[:CustomerAddress] = address[:address1]
|
131
|
+
post[:CustomerPostcode] = address[:zip]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def add_customer_data(post, options)
|
136
|
+
post[:CustomerEmail] = options[:email]
|
137
|
+
end
|
138
|
+
|
139
|
+
def add_invoice_data(post, options)
|
140
|
+
post[:CustomerInvoiceRef] = options[:order_id]
|
141
|
+
post[:CustomerInvoiceDescription] = options[:description]
|
142
|
+
end
|
143
|
+
|
144
|
+
def add_optional_data(post)
|
145
|
+
post[:TrxnNumber] = nil
|
146
|
+
post[:Option1] = nil
|
147
|
+
post[:Option2] = nil
|
148
|
+
post[:Option3] = nil
|
149
|
+
end
|
150
|
+
|
151
|
+
def commit(money, parameters)
|
152
|
+
|
153
|
+
parameters[:TotalAmount] = amount(money)
|
154
|
+
|
155
|
+
if result = test_result_from_cc_number(parameters[:CardNumber])
|
156
|
+
return result
|
157
|
+
end
|
158
|
+
|
159
|
+
data = ssl_post gateway_url(parameters[:CVN], test?), post_data(parameters)
|
160
|
+
|
161
|
+
@response = parse(data)
|
162
|
+
|
163
|
+
success = (response[:ewaytrxnstatus] == "True")
|
164
|
+
message = message_from(response[:ewaytrxnerror])
|
165
|
+
|
166
|
+
Response.new(success, message, @response,
|
167
|
+
:authorization => response[:ewayauthcode]
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Parse eway response xml into a convinient hash
|
172
|
+
def parse(xml)
|
173
|
+
# "<?xml version=\"1.0\"?>".
|
174
|
+
# <ewayResponse>
|
175
|
+
# <ewayTrxnError></ewayTrxnError>
|
176
|
+
# <ewayTrxnStatus>True</ewayTrxnStatus>
|
177
|
+
# <ewayTrxnNumber>10002</ewayTrxnNumber>
|
178
|
+
# <ewayTrxnOption1></ewayTrxnOption1>
|
179
|
+
# <ewayTrxnOption2></ewayTrxnOption2>
|
180
|
+
# <ewayTrxnOption3></ewayTrxnOption3>
|
181
|
+
# <ewayReturnAmount>10</ewayReturnAmount>
|
182
|
+
# <ewayAuthCode>123456</ewayAuthCode>
|
183
|
+
# <ewayTrxnReference>987654321</ewayTrxnReference>
|
184
|
+
# </ewayResponse>
|
185
|
+
|
186
|
+
response = {}
|
187
|
+
|
188
|
+
xml = REXML::Document.new(xml)
|
189
|
+
xml.elements.each('//ewayResponse/*') do |node|
|
190
|
+
|
191
|
+
response[node.name.downcase.to_sym] = normalize(node.text)
|
192
|
+
|
193
|
+
end unless xml.root.nil?
|
194
|
+
|
195
|
+
response
|
196
|
+
end
|
197
|
+
|
198
|
+
def post_data(parameters = {})
|
199
|
+
parameters[:CustomerID] = @options[:login]
|
200
|
+
|
201
|
+
xml = REXML::Document.new
|
202
|
+
root = xml.add_element("ewaygateway")
|
203
|
+
|
204
|
+
parameters.each do |key, value|
|
205
|
+
root.add_element("eway#{key}").text = value
|
206
|
+
end
|
207
|
+
xml.to_s
|
208
|
+
end
|
209
|
+
|
210
|
+
def message_from(message)
|
211
|
+
return '' if message.blank?
|
212
|
+
MESSAGES[message[0,2]] || message
|
213
|
+
end
|
214
|
+
|
215
|
+
# Make a ruby type out of the response string
|
216
|
+
def normalize(field)
|
217
|
+
case field
|
218
|
+
when "true" then true
|
219
|
+
when "false" then false
|
220
|
+
when "" then nil
|
221
|
+
when "null" then nil
|
222
|
+
else field
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def gateway_url(cvn, test)
|
227
|
+
if cvn
|
228
|
+
test ? TEST_CVN_URL : LIVE_CVN_URL
|
229
|
+
else
|
230
|
+
test ? TEST_URL : LIVE_URL
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|