activemerchant 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|