payment 0.9 → 1.0.1
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/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
|
+
|