payment 0.9 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +71 -0
- data/Rakefile +95 -0
- data/lib/payment.rb +6 -55
- data/lib/payment/authorize_net.rb +178 -174
- data/lib/payment/base.rb +43 -20
- data/test/authorize_net/base_test.rb +51 -0
- data/test/test_helper.rb +4 -0
- metadata +53 -32
data/README
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
== Welcome to Payment
|
2
|
+
|
3
|
+
Payment is used to process credit cards and electronic cash through merchant accounts.
|
4
|
+
|
5
|
+
To choose a gateway, require 'payment/gateway_name'. For example, if you want to use Authorize.net, do a require 'payment/authorize_net'. If you would like to use multiple gateways in the same code, simply do a require 'payment' and it will grab each gateway's code for you.
|
6
|
+
|
7
|
+
Payment was inspired by and has borrowed much of its infrastructure from the Business::OnlinePayment Perl module (http://search.cpan.org/~jasonk/Business-OnlinePayment-2.01/OnlinePayment.pm).
|
8
|
+
|
9
|
+
If you would like to loosely check the validity of a credit card without using a merchant account, look at the CreditCard module (http://rubyforge.org/projects/creditcard/).
|
10
|
+
|
11
|
+
== Example
|
12
|
+
|
13
|
+
require 'payment/authorize_net'
|
14
|
+
|
15
|
+
transaction = Payment::AuthorizeNet.new (
|
16
|
+
:login => 'username',
|
17
|
+
:password => 'password',
|
18
|
+
:amount => '49.95',
|
19
|
+
:card_number => '4012888818888',
|
20
|
+
:expiration => '03/10',
|
21
|
+
:first_name => 'John',
|
22
|
+
:last_name => 'Doe'
|
23
|
+
)
|
24
|
+
begin
|
25
|
+
transaction.submit
|
26
|
+
puts "Card processed successfully: #{transaction.authorization}"
|
27
|
+
rescue
|
28
|
+
puts "Card was rejected: #{transaction.error_message}"
|
29
|
+
end
|
30
|
+
|
31
|
+
To set default values, setup a file called .payment.yml in the home directory of the user who will be using this library. An example file would be:
|
32
|
+
|
33
|
+
username: my_account
|
34
|
+
transaction_key: my_trans_key
|
35
|
+
|
36
|
+
That way there is no need to keep sensitive information in your application's code itself. To specify an alternative configuration file, add a :prefs => '/path/to/file.yml' to the initialization parameters.
|
37
|
+
|
38
|
+
== Currently Supported Gateways
|
39
|
+
|
40
|
+
* Authorize.Net (authorize_net) - http://www.authorize.net
|
41
|
+
|
42
|
+
== Upcoming Gateways
|
43
|
+
|
44
|
+
* Moneris - http://www.moneris.com
|
45
|
+
* Skipjack - http://www.skipjack.com
|
46
|
+
|
47
|
+
== Planned Support for the Following Gateways
|
48
|
+
|
49
|
+
* Trust Commerce - http://www.trustcommerce.com
|
50
|
+
* Verisign Payflow Pro - http://www.verisign.com
|
51
|
+
* Cardstream - http://www.cardstream.com
|
52
|
+
* Beanstream - http://www.beanstream.com
|
53
|
+
* Vital - http://www.vitalps.com
|
54
|
+
* Merchant Commerce - http://www.innuity.com
|
55
|
+
* iAuthorizer - http://www.iauthorizer.com
|
56
|
+
* Electronic Clearing House, Inc. - http://www.echo-inc.com
|
57
|
+
* Bank of America eStores - http://www.bankofamerica.com
|
58
|
+
* Network1Financial - http://www.eftsecure.com
|
59
|
+
* eSec - http://www.esecpayments.com.au
|
60
|
+
* Ingenico OCV - http://www.ingenico.com.au
|
61
|
+
* St.George IPG - https://www.ipg.stgeorge.com.au
|
62
|
+
* Jettis - http://www.jettis.com
|
63
|
+
* PaymentsGateway.Net - http://www.paymentsgateway.net
|
64
|
+
* PayConnect - http://www.paymentone.com
|
65
|
+
* Secure Hosting UPG - http://www.securehosting.com
|
66
|
+
|
67
|
+
== About
|
68
|
+
|
69
|
+
Author:: Lucas Carlson (mailto:lucas@rufy.com)
|
70
|
+
Copyright:: Copyright (c) 2005 Lucas Carlson
|
71
|
+
License:: Distributes under the same terms as Ruby
|
data/Rakefile
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require 'rake/contrib/rubyforgepublisher'
|
7
|
+
|
8
|
+
PKG_VERSION = "1.0.1"
|
9
|
+
|
10
|
+
PKG_FILES = FileList[
|
11
|
+
"lib/**/*", "bin/*", "test/**/*", "[A-Z]*", "Rakefile", "doc/**/*"
|
12
|
+
]
|
13
|
+
|
14
|
+
desc "Default Task"
|
15
|
+
task :default => [ :test ]
|
16
|
+
|
17
|
+
# Run the unit tests
|
18
|
+
desc "Run all unit tests"
|
19
|
+
Rake::TestTask.new("test") { |t|
|
20
|
+
t.libs << "lib"
|
21
|
+
t.pattern = 'test/*/*_test.rb'
|
22
|
+
t.verbose = true
|
23
|
+
}
|
24
|
+
|
25
|
+
# Make a console, useful when working on tests
|
26
|
+
desc "Generate a test console"
|
27
|
+
task :console do
|
28
|
+
verbose( false ) { sh "irb -I lib/ -r 'payment'" }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Genereate the RDoc documentation
|
32
|
+
desc "Create documentation"
|
33
|
+
Rake::RDocTask.new("doc") { |rdoc|
|
34
|
+
rdoc.title = "Ruby Merchant Payment - Authorize.Net and others"
|
35
|
+
rdoc.rdoc_dir = 'doc'
|
36
|
+
rdoc.rdoc_files.include('README')
|
37
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
38
|
+
}
|
39
|
+
|
40
|
+
# Genereate the package
|
41
|
+
spec = Gem::Specification.new do |s|
|
42
|
+
|
43
|
+
#### Basic information.
|
44
|
+
|
45
|
+
s.name = 'payment'
|
46
|
+
s.version = PKG_VERSION
|
47
|
+
s.summary = <<-EOF
|
48
|
+
Payment is used to process credit cards and electronic cash through merchant accounts.
|
49
|
+
EOF
|
50
|
+
s.description = <<-EOF
|
51
|
+
These functions tell you whether a credit card number is
|
52
|
+
self-consistent using known algorithms for credit card numbers.
|
53
|
+
All non-integer values are removed from the string before parsing
|
54
|
+
so that you don't have to worry about the format of the string.
|
55
|
+
EOF
|
56
|
+
|
57
|
+
#### Which files are to be included in this gem? Everything! (Except SVN directories.)
|
58
|
+
|
59
|
+
s.files = PKG_FILES
|
60
|
+
|
61
|
+
#### Load-time details: library and application (you will need one or both).
|
62
|
+
|
63
|
+
s.require_path = 'lib'
|
64
|
+
s.autorequire = 'payment'
|
65
|
+
|
66
|
+
#### Documentation and testing.
|
67
|
+
|
68
|
+
s.has_rdoc = true
|
69
|
+
|
70
|
+
#### Author and project details.
|
71
|
+
|
72
|
+
s.author = "Lucas Carlson"
|
73
|
+
s.email = "lucas@rufy.com"
|
74
|
+
s.homepage = "http://payment.rufy.com/"
|
75
|
+
end
|
76
|
+
|
77
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
78
|
+
pkg.need_zip = true
|
79
|
+
pkg.need_tar = true
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "Report code statistics (KLOCs, etc) from the application"
|
83
|
+
task :stats do
|
84
|
+
require 'code_statistics'
|
85
|
+
CodeStatistics.new(
|
86
|
+
["Library", "lib"],
|
87
|
+
["Units", "test"]
|
88
|
+
).to_s
|
89
|
+
end
|
90
|
+
|
91
|
+
desc "Publish new documentation"
|
92
|
+
task :publish do
|
93
|
+
`ssh rufy update-payment-doc`
|
94
|
+
Rake::RubyForgePublisher.new('payment', 'cardmagic').upload
|
95
|
+
end
|
data/lib/payment.rb
CHANGED
@@ -20,62 +20,13 @@
|
|
20
20
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
|
+
#
|
24
|
+
# See Payment documentation.
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# To choose a gateway, require 'payment/gateway_name'. For example, if you want to use Authorize.net, do a require 'payment/authorize_net'. If you would like to use multiple gateways in the same code, simply do a require 'payment' and it will grab each gateway's code for you.
|
29
|
-
#
|
30
|
-
# Payment was inspired by and has borrowed much of its infrastructure from the Business::OnlinePayment Perl module (http://search.cpan.org/~jasonk/Business-OnlinePayment-2.01/OnlinePayment.pm).
|
31
|
-
#
|
32
|
-
# If you would like to loosely check the validity of a credit card without using a merchant account, look at the CreditCard module (http://rubyforge.org/projects/creditcard/).
|
33
|
-
#
|
34
|
-
# == Example
|
35
|
-
#
|
36
|
-
# require 'payment/authorize_net'
|
37
|
-
#
|
38
|
-
# transaction = Payment::AuthorizeNet.new (
|
39
|
-
# :login => 'username',
|
40
|
-
# :password => 'password',
|
41
|
-
# :amount => '49.95',
|
42
|
-
# :card_number => '1234123412341238',
|
43
|
-
# :expiration => '03/05',
|
44
|
-
# :first_name => 'John',
|
45
|
-
# :last_name => 'Doe'
|
46
|
-
# )
|
47
|
-
# transaction.submit
|
48
|
-
#
|
49
|
-
# if transaction.success?
|
50
|
-
# puts "Card processed successfully: #{transaction.authorization}";
|
51
|
-
# else
|
52
|
-
# puts "Card was rejected: #{transaction.error_message}";
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# == Currently Supported Gateways
|
56
|
-
#
|
57
|
-
# * Authorize.Net (authorize_net) - http://www.authorize.net/
|
58
|
-
#
|
59
|
-
# == Planned Support for the Following Gateways
|
60
|
-
#
|
61
|
-
# * Trust Commerce - http://www.trustcommerce.com/
|
62
|
-
# * Verisign Payflow Pro - http://www.verisign.com/
|
63
|
-
# * Moneris - http://www.moneris.com/
|
64
|
-
# * Cardstream - http://www.cardstream.com/
|
65
|
-
# * Beanstream - http://www.beanstream.com/
|
66
|
-
# * Vital - http://www.vitalps.com/
|
67
|
-
# * Merchant Commerce - http://www.innuity.com/
|
68
|
-
# * iAuthorizer - http://www.iauthorizer.com/
|
69
|
-
# * Electronic Clearing House, Inc. - http://www.echo-inc.com/
|
70
|
-
# * Bank of America eStores - http://www.bankofamerica.com/
|
71
|
-
# * Network1Financial - http://www.eftsecure.com/
|
72
|
-
# * eSec - http://www.esecpayments.com.au/
|
73
|
-
# * Ingenico OCV - http://www.ingenico.com.au/
|
74
|
-
# * St.George IPG - https://www.ipg.stgeorge.com.au/
|
75
|
-
# * Jettis - http://www.jettis.com/
|
76
|
-
# * PaymentsGateway.Net - http://www.paymentsgateway.net/
|
77
|
-
# * PayConnect - http://www.paymentone.com/
|
78
|
-
# * Secure Hosting UPG - http://www.securehosting.com/
|
26
|
+
require 'yaml'
|
27
|
+
require 'rexml/document'
|
28
|
+
require 'net/http'
|
29
|
+
require 'net/https'
|
79
30
|
|
80
31
|
require 'payment/base'
|
81
32
|
require 'payment/authorize_net'
|
@@ -2,206 +2,210 @@
|
|
2
2
|
# Copyright:: Copyright (c) 2005 Lucas Carlson
|
3
3
|
# License:: Distributes under the same terms as Ruby
|
4
4
|
|
5
|
-
require 'payment/base'
|
6
5
|
require 'cgi'
|
7
6
|
require 'csv'
|
8
7
|
|
8
|
+
require 'payment/base'
|
9
|
+
|
9
10
|
module Payment
|
10
11
|
|
11
12
|
class AuthorizeNet < Base
|
12
13
|
attr_reader :server_response, :avs_code, :order_number, :md5, :cvv2_response, :cavv_response
|
13
|
-
attr_accessor :first_name, :last_name, :transaction_key
|
14
|
+
attr_accessor :first_name, :last_name, :transaction_key, :transaction_id, :invoice_num, :description
|
14
15
|
|
15
16
|
# version of the gateway's API
|
16
17
|
API_VERSION = '3.1'
|
17
18
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
'trans_id' => 'x_Trans_ID',
|
30
|
-
'auth_code' => 'x_Auth_Code',
|
31
|
-
'cust_id' => 'x_Cust_ID',
|
32
|
-
'customer_ip' => 'x_Customer_IP',
|
33
|
-
'last_name' => 'x_Last_Name',
|
34
|
-
'first_name' => 'x_First_Name',
|
35
|
-
'company' => 'x_Company',
|
36
|
-
'address' => 'x_Address',
|
37
|
-
'city' => 'x_City',
|
38
|
-
'state' => 'x_State',
|
39
|
-
'zip' => 'x_Zip',
|
40
|
-
'country' => 'x_Country',
|
41
|
-
'ship_to_last_name' => 'x_Ship_To_Last_Name',
|
42
|
-
'ship_to_first_name' => 'x_Ship_To_First_Name',
|
43
|
-
'ship_to_address' => 'x_Ship_To_Address',
|
44
|
-
'ship_to_city' => 'x_Ship_To_City',
|
45
|
-
'ship_to_state' => 'x_Ship_To_State',
|
46
|
-
'ship_to_zip' => 'x_Ship_To_Zip',
|
47
|
-
'ship_to_country' => 'x_Ship_To_Country',
|
48
|
-
'phone' => 'x_Phone',
|
49
|
-
'fax' => 'x_Fax',
|
50
|
-
'email' => 'x_Email',
|
51
|
-
'card_number' => 'x_Card_Num',
|
52
|
-
'expiration' => 'x_Exp_Date',
|
53
|
-
'card_code' => 'x_Card_Code',
|
54
|
-
'echeck_type' => 'x_Echeck_Type',
|
55
|
-
'account_name' => 'x_Bank_Acct_Name',
|
56
|
-
'account_number' => 'x_Bank_Acct_Num',
|
57
|
-
'account_type' => 'x_Bank_Acct_Type',
|
58
|
-
'bank_name' => 'x_Bank_Name',
|
59
|
-
'bank_aba_code' => 'x_Bank_ABA_Code',
|
60
|
-
'customer_org' => 'x_Customer_Organization_Type',
|
61
|
-
'customer_ssn' => 'x_Customer_Tax_ID',
|
62
|
-
'drivers_license_num' => 'x_Drivers_License_Num',
|
63
|
-
'drivers_license_state' => 'x_Drivers_License_State',
|
64
|
-
'drivers_license_dob' => 'x_Drivers_License_DOB',
|
65
|
-
'recurring_billing' => 'x_Recurring_Billing',
|
66
|
-
'test_request' => 'x_Test_Request',
|
67
|
-
'adc_delim_data' => 'x_ADC_Delim_Data',
|
68
|
-
'adc_url' => 'x_ADC_URL',
|
69
|
-
'version' => 'x_Version',
|
70
|
-
}
|
71
|
-
|
72
|
-
# map the actions to the merchant's action names
|
73
|
-
ACTIONS = {
|
74
|
-
'normal authorization' => 'AUTH_CAPTURE',
|
75
|
-
'authorization only' => 'AUTH_ONLY',
|
76
|
-
'credit' => 'CREDIT',
|
77
|
-
'post authorization' => 'PRIOR_AUTH_CAPTURE',
|
78
|
-
'void' => 'VOID',
|
79
|
-
}
|
80
|
-
|
19
|
+
# Set some sensible defaults for Authorize.Net
|
20
|
+
# transaction = Payment::AuthorizeNet.new (
|
21
|
+
# :login => 'username',
|
22
|
+
# :password => 'password',
|
23
|
+
# :amount => '49.95',
|
24
|
+
# :card_number => '4012888818888',
|
25
|
+
# :expiration => '03/10',
|
26
|
+
# :first_name => 'John',
|
27
|
+
# :last_name => 'Doe'
|
28
|
+
# )
|
29
|
+
#
|
81
30
|
def initialize(options = {})
|
82
31
|
# set some sensible defaults
|
83
32
|
@url = 'https://secure.authorize.net/gateway/transact.dll'
|
84
|
-
@
|
85
|
-
@
|
33
|
+
@delim_data = 'TRUE'
|
34
|
+
@relay_response = 'FALSE'
|
86
35
|
@version = API_VERSION
|
87
36
|
|
88
37
|
# include all provided data
|
89
38
|
super options
|
90
39
|
end
|
91
40
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
begin
|
100
|
-
response = http.post uri.path, post_data
|
101
|
-
rescue
|
102
|
-
@is_success = false
|
103
|
-
@error_message = "Could not access #{@url}. Error code #{response.code}. #{response.message}"
|
104
|
-
return
|
105
|
-
end
|
106
|
-
|
107
|
-
col = nil
|
108
|
-
CSV::Reader.parse response.body do |row|
|
109
|
-
col ||= row
|
110
|
-
end
|
111
|
-
|
112
|
-
if col
|
113
|
-
@server_response = response.code
|
114
|
-
@result_code = col[0].data
|
115
|
-
@avs_code = col[5].data
|
116
|
-
@order_number = col[6].data
|
117
|
-
@md5 = col[37].data
|
118
|
-
@cvv2_response = col[38].data
|
119
|
-
@cavv_response = col[39].data
|
120
|
-
|
121
|
-
if @result_code == "1" # Approved/Pending/Test
|
122
|
-
@is_success = true
|
123
|
-
@authorization = col[4].data
|
124
|
-
else
|
125
|
-
@is_success = false
|
126
|
-
@error_code = col[2].data
|
127
|
-
@error_message = col[3].data
|
128
|
-
end
|
129
|
-
else
|
130
|
-
@is_success = false
|
131
|
-
@error_message = "Could not interpret the result. #{response.body}"
|
132
|
-
end
|
133
|
-
|
134
|
-
return @is_success
|
41
|
+
# Submit the order to be processed. If it is not fulfilled for any reason
|
42
|
+
# this method will raise an exception.
|
43
|
+
def submit
|
44
|
+
set_post_data
|
45
|
+
get_response @url
|
46
|
+
parse_response
|
135
47
|
end
|
136
48
|
|
137
|
-
|
138
|
-
|
49
|
+
private
|
50
|
+
|
51
|
+
def set_post_data
|
52
|
+
prepare_data
|
139
53
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
54
|
+
post_array = Array.new
|
55
|
+
FIELDS.each do |loc_var, gate_var|
|
56
|
+
if @required.include?(loc_var) && eval("@#{loc_var}").nil?
|
57
|
+
raise PaymentError, "The required variable '#{loc_var}' was left empty"
|
58
|
+
else
|
59
|
+
value = eval "CGI.escape(@#{loc_var}.to_s)"
|
60
|
+
post_array << "#{gate_var}=#{value}" unless value.empty?
|
61
|
+
end
|
62
|
+
end
|
149
63
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
def prepare_data
|
154
|
-
# make sensible changes to data
|
155
|
-
@card_number = @card_number.to_s.gsub(/[^\d]/, "") unless @card_number.nil?
|
156
|
-
|
157
|
-
@test_request = @test_transaction == true ? "TRUE" : "FALSE"
|
64
|
+
@data = post_array.join('&')
|
65
|
+
end
|
158
66
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
67
|
+
def parse_response
|
68
|
+
data = @response.plain.split(',').unshift nil
|
69
|
+
|
70
|
+
@result_code = data[1].to_i
|
71
|
+
@result_reason_code = data[3]
|
72
|
+
@result_reason = data[4]
|
73
|
+
@authorization = data[5]
|
74
|
+
@avs_code = data[6]
|
75
|
+
@transaction_id = @order_number = data[7]
|
76
|
+
@md5 = data[38]
|
77
|
+
@cvv2_response = data[39]
|
78
|
+
@cavv_response = data[40]
|
79
|
+
|
80
|
+
if @result_code == 1 # Approved/Pending/Test
|
81
|
+
return @transaction_id
|
82
|
+
else
|
83
|
+
@error_code = data[3]
|
84
|
+
@error_message = data[4]
|
85
|
+
raise PaymentError, @error_message
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
# make sensible changes to data
|
91
|
+
def prepare_data
|
92
|
+
@card_number = @card_number.to_s.gsub(/[^\d]/, "") unless @card_number.nil?
|
93
|
+
|
94
|
+
@test_request = @test_transaction.to_s.downcase == 'true' ? 'TRUE' : 'FALSE'
|
95
|
+
|
96
|
+
if @recurring_billing.class != String
|
97
|
+
if @recurring_billing == true
|
98
|
+
@recurring_billing = "YES"
|
99
|
+
elsif @recurring_billing == false
|
100
|
+
@recurring_billing = "NO"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
@expiration = @expiration.strftime "%m/%y" rescue nil # in case a date or time is passed
|
105
|
+
|
106
|
+
@method = (@method.nil? || @card_number) ? 'CC' : @method.upcase
|
107
|
+
|
108
|
+
# convert the action
|
109
|
+
if TYPES.include?(@type)
|
110
|
+
@type = TYPES[@type]
|
111
|
+
elsif ! TYPES.has_value?(@type)
|
112
|
+
raise PaymentError, "The type '#{@type}' is not valid"
|
113
|
+
end
|
114
|
+
|
115
|
+
# add some required fields specific to this payment gateway and the provided data
|
116
|
+
@required += %w(method type login test_request delim_data relay_response)
|
117
|
+
|
118
|
+
# If a transaction key is specified, use that instead
|
119
|
+
if @transaction_key.nil?
|
120
|
+
@transaction_key = nil
|
121
|
+
@required += %w(password)
|
122
|
+
else
|
123
|
+
@password = nil
|
124
|
+
@required += %w(transaction_key)
|
125
|
+
end
|
126
|
+
|
127
|
+
unless @method == 'VOID'
|
128
|
+
if @method == 'ECHECK'
|
129
|
+
@required += %w(amount routing_code account_number account_type bank_name account_name account_type)
|
130
|
+
@required += %w(customer_org customer_ssn) unless @customer_org.nil?
|
131
|
+
elsif @method == 'CC'
|
132
|
+
@required += %w(amount)
|
133
|
+
if @type == 'PRIOR_AUTH_CAPTURE'
|
134
|
+
@required += @order_number ? %w(order_number) : %w(card_number expiration)
|
135
|
+
else
|
136
|
+
@required += %w(card_number expiration)
|
137
|
+
end
|
138
|
+
else
|
139
|
+
raise PaymentError, "Can't handle transaction method: #{@method}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
@required.uniq!
|
144
|
+
end
|
145
|
+
|
146
|
+
# map the instance variable names to the gateway's requested variable names
|
147
|
+
FIELDS = {
|
148
|
+
'method' => 'x_Method',
|
149
|
+
'type' => 'x_Type',
|
150
|
+
'login' => 'x_Login',
|
151
|
+
'password' => 'x_Password',
|
152
|
+
'transaction_key' => 'x_Tran_Key',
|
153
|
+
'description' => 'x_Description',
|
154
|
+
'amount' => 'x_Amount',
|
155
|
+
'currency_code' => 'x_Currency_Code',
|
156
|
+
'invoice_num' => 'x_Invoice_Num',
|
157
|
+
'transaction_id' => 'x_Trans_ID',
|
158
|
+
'auth_code' => 'x_Auth_Code',
|
159
|
+
'cust_id' => 'x_Cust_ID',
|
160
|
+
'customer_ip' => 'x_Customer_IP',
|
161
|
+
'last_name' => 'x_Last_Name',
|
162
|
+
'first_name' => 'x_First_Name',
|
163
|
+
'company' => 'x_Company',
|
164
|
+
'address' => 'x_Address',
|
165
|
+
'city' => 'x_City',
|
166
|
+
'state' => 'x_State',
|
167
|
+
'zip' => 'x_Zip',
|
168
|
+
'country' => 'x_Country',
|
169
|
+
'ship_to_last_name' => 'x_Ship_To_Last_Name',
|
170
|
+
'ship_to_first_name' => 'x_Ship_To_First_Name',
|
171
|
+
'ship_to_address' => 'x_Ship_To_Address',
|
172
|
+
'ship_to_city' => 'x_Ship_To_City',
|
173
|
+
'ship_to_state' => 'x_Ship_To_State',
|
174
|
+
'ship_to_zip' => 'x_Ship_To_Zip',
|
175
|
+
'ship_to_country' => 'x_Ship_To_Country',
|
176
|
+
'phone' => 'x_Phone',
|
177
|
+
'fax' => 'x_Fax',
|
178
|
+
'email' => 'x_Email',
|
179
|
+
'card_number' => 'x_Card_Num',
|
180
|
+
'expiration' => 'x_Exp_Date',
|
181
|
+
'card_code' => 'x_Card_Code',
|
182
|
+
'echeck_type' => 'x_Echeck_Type',
|
183
|
+
'account_name' => 'x_Bank_Acct_Name',
|
184
|
+
'account_number' => 'x_Bank_Acct_Num',
|
185
|
+
'account_type' => 'x_Bank_Acct_Type',
|
186
|
+
'bank_name' => 'x_Bank_Name',
|
187
|
+
'bank_aba_code' => 'x_Bank_ABA_Code',
|
188
|
+
'customer_org' => 'x_Customer_Organization_Type',
|
189
|
+
'customer_ssn' => 'x_Customer_Tax_ID',
|
190
|
+
'drivers_license_num' => 'x_Drivers_License_Num',
|
191
|
+
'drivers_license_state' => 'x_Drivers_License_State',
|
192
|
+
'drivers_license_dob' => 'x_Drivers_License_DOB',
|
193
|
+
'recurring_billing' => 'x_Recurring_Billing',
|
194
|
+
'test_request' => 'x_Test_Request',
|
195
|
+
'delim_data' => 'x_Delim_Data',
|
196
|
+
'relay_response' => 'x_Relay_Response',
|
197
|
+
'version' => 'x_Version',
|
198
|
+
}
|
199
|
+
|
200
|
+
# map the types to the merchant's action names
|
201
|
+
TYPES = {
|
202
|
+
'normal authorization' => 'AUTH_CAPTURE',
|
203
|
+
'authorization only' => 'AUTH_ONLY',
|
204
|
+
'credit' => 'CREDIT',
|
205
|
+
'post authorization' => 'PRIOR_AUTH_CAPTURE',
|
206
|
+
'void' => 'VOID',
|
207
|
+
}
|
180
208
|
|
181
|
-
if @transaction_key.nil?
|
182
|
-
@@required.concat %w(password)
|
183
|
-
else
|
184
|
-
@@required.concat %w(transaction_key)
|
185
|
-
end
|
186
|
-
|
187
|
-
unless @type == 'VOID'
|
188
|
-
if @type == 'ECHECK'
|
189
|
-
@@required.concat %w(amount routing_code account_number account_type bank_name account_name account_type)
|
190
|
-
@@required.concat %w(customer_org customer_ssn) unless @customer_org.nil?
|
191
|
-
elsif @type == 'CC'
|
192
|
-
@@required.concat %w(amount)
|
193
|
-
if @action == 'PRIOR_AUTH_CAPTURE'
|
194
|
-
@@required.concat @order_number ? %w(order_number) : %w(card_number expiration)
|
195
|
-
else
|
196
|
-
@@required.concat %w(last_name first_name card_number expiration)
|
197
|
-
end
|
198
|
-
else
|
199
|
-
raise PaymentError, "Can't handle transaction type: #{@type}"
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
@@required.uniq!
|
204
|
-
end
|
205
209
|
end
|
206
210
|
|
207
211
|
end
|
data/lib/payment/base.rb
CHANGED
@@ -1,38 +1,61 @@
|
|
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 'net/http'
|
6
|
-
require 'net/https'
|
7
|
-
|
8
1
|
module Payment
|
9
2
|
|
10
|
-
class PaymentError < StandardError
|
3
|
+
class PaymentError < StandardError#:nodoc:
|
4
|
+
end
|
11
5
|
|
12
6
|
class Base
|
13
|
-
attr_reader :error_message, :authorization, :transaction_type, :result_code
|
7
|
+
attr_reader :response, :error_code, :error_message, :authorization, :transaction_type, :result_code
|
14
8
|
attr_accessor :url
|
15
|
-
attr_accessor :require_avs, :test_transaction
|
16
|
-
attr_accessor :type, :login, :password, :action, :description, :amount, :invoice_number, :customer_id, :name, :address, :city, :state, :zip, :country, :phone, :fax, :email, :card_number, :expiration, :account_number, :routing_code, :bank_name
|
9
|
+
attr_accessor :require_avs, :test_transaction
|
10
|
+
attr_accessor :method, :type, :login, :password, :action, :description, :amount, :invoice_number, :customer_id, :name, :address, :city, :state, :zip, :country, :phone, :fax, :email, :card_number, :expiration, :account_number, :routing_code, :bank_name
|
17
11
|
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
# Set the variables and get default variables from the :prefs file.
|
13
|
+
# This method will be overriden by each gateway to set sensible defaults
|
14
|
+
# for each gateway.
|
15
|
+
#
|
16
|
+
def initialize(options = {}) #:nodoc:
|
21
17
|
# set some sensible defaults
|
22
|
-
@
|
23
|
-
|
18
|
+
@type = 'normal authorization'
|
19
|
+
|
20
|
+
# get defaults from a preference file
|
21
|
+
prefs = File.expand_path(options[:prefs] || "~/.payment.yml")
|
22
|
+
YAML.load(File.open(prefs)).each {|pref, value| instance_variable_set("@#{pref}", value) } if File.exists?(prefs)
|
24
23
|
|
25
24
|
# include all provided data
|
26
|
-
options.each { |
|
25
|
+
options.each { |pref, value| instance_variable_set("@#{pref}", value) }
|
26
|
+
|
27
|
+
@required = Array.new
|
27
28
|
end
|
28
29
|
|
29
|
-
def submit
|
30
|
+
def submit #:nodoc:
|
30
31
|
raise PaymentError, "No gateway specified"
|
31
32
|
end
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
private
|
35
|
+
|
36
|
+
# Make sure that the required fields are not empty
|
37
|
+
def check_required
|
38
|
+
for var in @required
|
39
|
+
raise PaymentError, "The #{var} variable needs to be set" if eval("@#{var}").nil?
|
40
|
+
end
|
35
41
|
end
|
42
|
+
|
43
|
+
# Goes out, posts the data, and sets the @response variable with the information
|
44
|
+
def get_response(url)
|
45
|
+
check_required
|
46
|
+
uri = URI.parse url
|
47
|
+
http = Net::HTTP.new uri.host, uri.port
|
48
|
+
if uri.port == 443
|
49
|
+
http.use_ssl = true
|
50
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
51
|
+
end
|
52
|
+
@response_plain = http.post(uri.path, @data).body
|
53
|
+
@response = @response_plain.include?('<?xml') ? REXML::Document.new(@response_plain) : @response_plain
|
54
|
+
|
55
|
+
@response.instance_variable_set "@response_plain", @response_plain
|
56
|
+
def @response.plain; @response_plain; end
|
57
|
+
end
|
58
|
+
|
36
59
|
end
|
37
60
|
|
38
61
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
=begin
|
2
|
+
Test cards available:
|
3
|
+
370000000000002 - American Express Test Card
|
4
|
+
6011000000000012 - Discover Test Card
|
5
|
+
5424000000000015 - MasterCard Test Card
|
6
|
+
4007000000027 - Visa Test Card
|
7
|
+
4012888818888 - Visa Test Card II
|
8
|
+
3088000000000017 - JCB Test Card (Use expiration date 0905)
|
9
|
+
38000000000006 - Diners Club/Carte Blanche Test (Use expiration date 0905)
|
10
|
+
=end
|
11
|
+
|
12
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
13
|
+
|
14
|
+
class AuthorizeNetTest < Test::Unit::TestCase
|
15
|
+
CARD = '4012888818888'
|
16
|
+
|
17
|
+
# In order to test this code, create a .payment.yml file in the code
|
18
|
+
# home directory of the user that will test this that looks like this:
|
19
|
+
#
|
20
|
+
# username: my_uname
|
21
|
+
# transaction_key: my_key
|
22
|
+
#
|
23
|
+
def setup
|
24
|
+
@transaction = Payment::AuthorizeNet.new(
|
25
|
+
:amount => '49.95',
|
26
|
+
:expiration => '0310',
|
27
|
+
:first_name => 'John',
|
28
|
+
:last_name => 'Doe',
|
29
|
+
:card_number => '4012888818888',
|
30
|
+
:test_transaction => true
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_submit_bad_card
|
35
|
+
@transaction.card_number = ''
|
36
|
+
assert_raise(Payment::PaymentError) { @transaction.submit }
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_submit_good_card
|
40
|
+
@transaction.card_number = CARD
|
41
|
+
assert_nothing_raised { @transaction.submit }
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_authorization
|
45
|
+
@transaction.card_number = CARD
|
46
|
+
@transaction.submit
|
47
|
+
assert_kind_of String, @transaction.authorization
|
48
|
+
assert_nil @transaction.error_message
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,40 +1,61 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.8.3
|
3
|
-
specification_version: 1
|
4
2
|
name: payment
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version:
|
7
|
-
date: 2005-02-04
|
8
|
-
summary: Payment is used to process credit cards and electronic cash through merchant accounts.
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: lucas@rufy.com
|
12
|
-
homepage: http://payment.rubyforge.org
|
13
|
-
rubyforge_project: payment
|
14
|
-
description: "These functions tell you whether a credit card number is self-consistent using known algorithms for credit card numbers. All non-integer values are removed from the string before parsing so that you don't have to worry about the format of the string."
|
15
|
-
autorequire:
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
-
|
22
|
-
- ">"
|
23
|
-
- !ruby/object:Gem::Version
|
24
|
-
version: 0.0.0
|
25
|
-
version:
|
4
|
+
version: 1.0.1
|
26
5
|
platform: ruby
|
27
6
|
authors:
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
7
|
+
- Lucas Carlson
|
8
|
+
autorequire: payment
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-10 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: " These functions tell you whether a credit card number is\n self-consistent using known algorithms for credit card numbers.\n All non-integer values are removed from the string before parsing\n so that you don't have to worry about the format of the string.\n"
|
17
|
+
email: lucas@rufy.com
|
37
18
|
executables: []
|
19
|
+
|
38
20
|
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/payment/authorize_net.rb
|
26
|
+
- lib/payment/base.rb
|
27
|
+
- lib/payment.rb
|
28
|
+
- test/authorize_net/base_test.rb
|
29
|
+
- test/test_helper.rb
|
30
|
+
- Rakefile
|
31
|
+
- README
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://payment.rufy.com/
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options: []
|
38
|
+
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
39
53
|
requirements: []
|
40
|
-
|
54
|
+
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.3.5
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: Payment is used to process credit cards and electronic cash through merchant accounts.
|
60
|
+
test_files: []
|
61
|
+
|