active_merchant_card_flex 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in active_merchant_card_flex.gemspec
4
+ gemspec
data/MIT-LICENSE. ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005-2010 Tobias Luetke
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ desc "Run the unit test suite"
7
+ task :default => 'test:units'
8
+
9
+ namespace :test do
10
+ Rake::TestTask.new(:units) do |t|
11
+ t.pattern = 'test/unit/**/*_test.rb'
12
+ t.ruby_opts << '-rubygems'
13
+ t.libs << 'test'
14
+ t.verbose = true
15
+ end
16
+
17
+ Rake::TestTask.new(:remote) do |t|
18
+ t.pattern = 'test/remote/**/*_test.rb'
19
+ t.ruby_opts << '-rubygems'
20
+ t.libs << 'test'
21
+ t.verbose = true
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "active_merchant_card_flex/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "active_merchant_card_flex"
7
+ s.version = ActiveMerchantCardFlex::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Stephen St. Martin"]
10
+ s.email = ["kuprishuz@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{CardFlex support for ActiveMerchant}
13
+ s.description = %q{Provide support for CardFlex's standard integration and stored profile tokenization integrations.'}
14
+
15
+ s.rubyforge_project = "active_merchant_card_flex"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency 'activemerchant', '> 1.10.0'
23
+ s.add_dependency 'activesupport', '> 2.3.5'
24
+ s.add_dependency 'money'
25
+
26
+ s.add_development_dependency 'actionpack'
27
+ s.add_development_dependency 'mocha'
28
+ end
@@ -0,0 +1,250 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ class CardFlexGateway < Gateway
4
+ cattr_accessor :gateway_url
5
+
6
+ self.display_name = 'CardFlex Inc'
7
+ self.gateway_url = 'https://post.cfinc.com/cgi-bin/process.cgi'
8
+ self.homepage_url = 'http://www.cardflexnow.com/'
9
+ self.default_currency = 'USD'
10
+ self.money_format = :dollars
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
12
+ self.supported_countries = ['US']
13
+
14
+ # Creates a new CardFlexGateway
15
+ #
16
+ # The gateway requires that a valid login and password be passed
17
+ # in the +options+ hash.
18
+ #
19
+ # ==== Options
20
+ #
21
+ # * <tt>:login</tt> -- The CardFlex Account ID (REQUIRED)
22
+ # * <tt>:password</tt> -- The CardFlex Merchant PIN. (REQUIRED)
23
+ # * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
24
+ # Otherwise, perform transactions against the production server.
25
+ def initialize(options = {})
26
+ requires!(options, :login)
27
+ @options = options
28
+ super
29
+ end
30
+
31
+ # Performs an authorization, which reserves the funds on the customer's credit card, but does not
32
+ # charge the card.
33
+ #
34
+ # ==== Parameters
35
+ #
36
+ # * <tt>money</tt> -- The amount to be captured as a FLOAT value in dollars and cents. (REQUIRED)
37
+ # * <tt>creditcard_or_credit_card_id</tt> -- The CreditCard details for the transaction. (REQUIRED)
38
+ # * <tt>options</tt> -- A hash of optional parameters.
39
+ def authorize(money, creditcard_or_credit_card_id, options = {})
40
+ post = {}
41
+ post[:authonly] = 1
42
+
43
+ add_address(post, options)
44
+ add_invoice(post, options)
45
+ add_payment_source(post, creditcard_or_credit_card_id, options)
46
+
47
+ commit(post[:userprofileid] ? :profile_sale : :ns_quicksale_cc, money, post)
48
+ end
49
+
50
+ # Captures the funds from an authorized transaction.
51
+ #
52
+ # ==== Parameters
53
+ #
54
+ # * <tt>money</tt> -- The amount to be captured as a FLOAT value in dollars and cents. (REQUIRED)
55
+ # * <tt>authorization</tt> -- The authorization returned from the previous authorize request. (REQUIRED)
56
+ # * <tt>options</tt> -- A hash of optional parameters
57
+ def capture(money, authorization, options = {})
58
+ post = {}
59
+
60
+ # remove last 4 digits of cc number as they are not required here
61
+ post[:postonly] = authorization[0...-4]
62
+
63
+ commit(post[:userprofileid] ? :profile_sale : :ns_quicksale_cc, money, post)
64
+ end
65
+
66
+ # Credit a transaction.
67
+ #
68
+ # This transaction indicates to the gateway that
69
+ # money should flow from the merchant to the customer.
70
+ #
71
+ # ==== Parameters
72
+ #
73
+ # * <tt>:money</tt> -- The amount to be credited as a FLOAT value in dollars and cents (REQUIRED)
74
+ # * <tt>:creditcard_or_credit_card_id</tt> -- The creditcard or stored creditcard id the refund is being issued to. (REQUIRED)
75
+ # * <tt>options</tt> -- A hash of optional parameters
76
+ def credit(money, creditcard_or_credit_card_id, options = {})
77
+ post = {}
78
+ add_address(post, options)
79
+ add_invoice(post, options)
80
+ add_payment_source(post, creditcard_or_credit_card_id, options)
81
+
82
+ commit(post[:userprofileid] ? :profile_credit : :ns_credit, money, post)
83
+ end
84
+
85
+ # Perform a purchase, which is essentially an authorization and capture in a single operation.
86
+ #
87
+ # ==== Parameters
88
+ #
89
+ # * <tt>money</tt> -- The amount to be purchased as a FLOAT value in dollars and cents. (REQUIRED)
90
+ # * <tt>creditcard_or_credit_card_id</tt> -- The CreditCard details for the transaction or ID of a stored credit card. (REQUIRED)
91
+ # * <tt>options</tt> -- A hash of optional parameters.
92
+ def purchase(money, creditcard_or_credit_card_id, options = {})
93
+ post = {}
94
+ add_address(post, options)
95
+ add_invoice(post, options)
96
+ add_payment_source(post, creditcard_or_credit_card_id, options)
97
+
98
+ commit(post[:userprofileid] ? :profile_sale : :ns_quicksale_cc, money, post)
99
+ end
100
+
101
+ # Stores CreditCard details for later use.
102
+ #
103
+ # ==== Parameters
104
+ #
105
+ # * <tt>creditcard</tt> -- The CreditCard details to store. (REQUIRED)
106
+ # * <tt>options</tt> -- A hash of optional parameters
107
+ def store(creditcard, options = {})
108
+ post = {}
109
+ post[:accttype] = 1
110
+ add_address(post, options)
111
+ add_creditcard(post, creditcard)
112
+
113
+ commit(:profile_add, nil, post)
114
+ end
115
+
116
+ # Removes stored CreditCard details.
117
+ #
118
+ # ==== Parameters
119
+ #
120
+ # * <tt>creditcard_id</tt> -- The ID of the CreditCard details to remove. (REQUIRED)
121
+ # * <tt>options</tt> -- A hash of optional parameters
122
+ def unstore(creditcard_id, options = {})
123
+ commit(:profile_delete, nil, options.merge(:userprofileid => creditcard_id.to_s[0...-4].to_i, :last4digits => creditcard_id.to_s[-4..-1].to_i))
124
+ end
125
+
126
+ # Void a previous transaction
127
+ #
128
+ # ==== Parameters
129
+ #
130
+ # * <tt>authorization</tt> - The authorization returned from the previous authorize request. (REQUIRED)
131
+ # * <tt>options</tt> -- A hash of optional parameters
132
+ def void(authorization, options = {})
133
+ commit(:ns_void, nil, options.merge(:historykeyid => authorization[0...-4], :last4digits => authorization[-4..-1]))
134
+ end
135
+
136
+ private
137
+ # adds a billing or shipping address for the charge
138
+ def add_address(post, options)
139
+ if address = options[:billing_address] || options[:address]
140
+ post[:ci_billaddr1] = address[:address1]
141
+ post[:ci_billaddr2] = address[:address2] if address[:address2]
142
+ post[:ci_billcity] = address[:city]
143
+ post[:ci_billstate] = address[:state]
144
+ post[:ci_billzip] = address[:zip]
145
+ post[:ci_billcountry] = address[:country]
146
+ end
147
+
148
+ if address = options[:shipping_address]
149
+ post[:ci_shipaddr1] = address[:address1]
150
+ post[:ci_shipaddr2] = address[:address2] if address[:address2]
151
+ post[:ci_shipcity] = address[:city]
152
+ post[:ci_shipstate] = address[:state]
153
+ post[:ci_shipzip] = address[:zip]
154
+ post[:ci_shipcountry] = address[:country]
155
+ end
156
+ end
157
+
158
+ # add a new credit card to the transaction
159
+ def add_creditcard(post, creditcard)
160
+ post[:ccname] = "#{creditcard.first_name} #{creditcard.last_name}"
161
+ post[:ccnum] = creditcard.number
162
+ post[:cvv2] = creditcard.verification_value if creditcard.verification_value?
163
+ post[:expmon] = creditcard.month
164
+ post[:expyear] = creditcard.year
165
+ end
166
+
167
+ # add order id to charge
168
+ def add_invoice(post, options)
169
+ post[:merchantordernumber] = options[:order_id] if options.has_key?(:order_id)
170
+ end
171
+
172
+ # determine if we are using a new credit card or stored one
173
+ def add_payment_source(post, credit_card_or_card_id, options)
174
+ if credit_card_or_card_id.is_a?(ActiveMerchant::Billing::CreditCard)
175
+ add_creditcard(post, credit_card_or_card_id)
176
+ else
177
+ post[:userprofileid] = credit_card_or_card_id[0...-4]
178
+ post[:last4digits] = credit_card_or_card_id[-4..-1]
179
+ end
180
+ end
181
+
182
+ def commit(action, money, parameters)
183
+ parameters[:amount] = money unless money.nil?
184
+ response = parse(ssl_post(self.gateway_url, post_data(action, parameters)))
185
+ test_mode = @options[:test] || @options[:login] == "TEST0"
186
+
187
+ Response.new(response[:result] == "1", response[:message], response,
188
+ :avs_result => { :code => response[:avs_result] },
189
+ :authorization => response[:authorization],
190
+ :cvv_result => response[:cvv_result],
191
+ :test => test_mode
192
+ )
193
+ end
194
+
195
+ # parse response body into its components
196
+ def parse(body)
197
+ response = {}
198
+
199
+ # parse reponse body into a hash
200
+ body.gsub!("<html><body><plaintext>", "")
201
+ body.split("\r\n").each do |pair|
202
+ key,val = pair.split("=")
203
+ response[key.underscore.to_sym] = val if key && val
204
+ end
205
+
206
+ # split response from : delimited format
207
+ if response[:result] == "1"
208
+ approval_response = response[:accepted].split(":")
209
+ response[:message] = "Accepted"
210
+ response[:transaction_type] = approval_response[0]
211
+ response[:authorization_code] = approval_response[1]
212
+ response[:reference_number] = approval_response[2]
213
+ response[:batch_number] = approval_response[3]
214
+ response[:transaction_id] = approval_response[4]
215
+ response[:avs_result] = approval_response[5]
216
+ response[:auth_net_message] = approval_response[6]
217
+ response[:cvv_result] = approval_response[7]
218
+ response[:partial_auth] = approval_response[8]
219
+
220
+ # if a stored profile was added use its id for authorization otherwise
221
+ # use the historyid, and append the last4digits of the card number so
222
+ # that it does not have to be passed in, making it more compliant to
223
+ # ActiveMerchant
224
+ if response[:accountnumber]
225
+ response[:authorization] = "#{response[:transaction_type] == 'PROFILEADD' || response[:partial_auth] == 'DUPLICATE' ? response[:userprofileid] : response[:historyid]}#{response[:accountnumber][-4..-1]}"
226
+ end
227
+ else
228
+ decline_response = response[:reason].split(":")
229
+ response[:transaction_result] = decline_response[0]
230
+ response[:decline_code] = decline_response[1]
231
+ response[:message] = decline_response[2]
232
+ end
233
+
234
+ response
235
+ end
236
+
237
+ # format post data for transaction
238
+ def post_data(action, parameters = {})
239
+ post = {}
240
+ post[:action] = action
241
+ post[:usepost] = 1
242
+ post[:acctid] = @options[:test] ? 'TEST0' : @options[:login]
243
+ post[:merchantpin] = @options[:password] if @options[:password] && !@options[:test]
244
+
245
+ request = post.merge(parameters).map {|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
246
+ request
247
+ end
248
+ end
249
+ end
250
+ end
@@ -0,0 +1,2 @@
1
+ require 'active_merchant'
2
+ require 'active_merchant/billing/gateways/card_flex.rb'
@@ -0,0 +1,3 @@
1
+ module ActiveMerchantCardFlex
2
+ VERSION = "0.0.1"
3
+ end
data/test/fixtures.yml ADDED
@@ -0,0 +1,2 @@
1
+ card_flex:
2
+ login: TEST0
@@ -0,0 +1,102 @@
1
+ require 'test_helper'
2
+
3
+ class RemoteCardFlexTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @gateway = CardFlexGateway.new(fixtures(:card_flex))
7
+ @amount = 100
8
+ @credit_card = credit_card('5454545454545454')
9
+ @declined_card = credit_card('4111111111111112')
10
+ @options = {
11
+ :order_id => generate_unique_id,
12
+ :billing_address => address,
13
+ :description => 'Test purchase'
14
+ }
15
+ end
16
+
17
+ def test_sucessful_purchase
18
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
19
+ assert_success response
20
+ assert response.test?
21
+ assert_equal 'Accepted', response.message
22
+ assert response.authorization
23
+ end
24
+
25
+ def test_unsuccessful_purchase
26
+ assert response = @gateway.purchase(@amount, @declined_card, @options)
27
+ assert_failure response
28
+ assert_equal 'Invalid account number', response.message
29
+ end
30
+
31
+ def test_successful_credit
32
+ assert response = @gateway.credit(@amount, @credit_card, @options)
33
+ assert_success response
34
+ assert response.test?
35
+ assert_equal 'Accepted', response.message
36
+ assert response.authorization
37
+ end
38
+
39
+ def test_authorize_and_capture
40
+ amount = @amount
41
+ assert auth = @gateway.authorize(amount, @credit_card, @options)
42
+ assert_success auth
43
+ assert_equal 'Accepted', auth.message
44
+ assert auth.authorization
45
+
46
+ assert capture = @gateway.capture(amount, auth.authorization)
47
+ assert_success capture
48
+ assert_equal 'Accepted', capture.message
49
+ end
50
+
51
+ def test_failed_capture
52
+ authorization = "12345678910"
53
+ assert response = @gateway.capture(@amount, '1234567890')
54
+ assert_failure response
55
+ assert_equal 'ProcessPostTrans... could not load order (123456,)', response.message
56
+
57
+ assert response = @gateway.capture(@amount, '')
58
+ assert_failure response
59
+ assert_equal 'Missing account number', response.message
60
+ end
61
+
62
+ def test_authorize_and_void
63
+ assert auth = @gateway.authorize(@amount, @credit_card, @options)
64
+ assert_success auth
65
+ assert_equal 'Accepted', auth.message
66
+ assert auth.authorization
67
+
68
+ assert void = @gateway.void(auth.authorization)
69
+ assert_success void
70
+ assert_equal 'Accepted', void.message
71
+ end
72
+
73
+ def test_unsuccessful_void
74
+ assert void = @gateway.void('')
75
+ assert_failure void
76
+ assert_equal 'Invalid acct type', void.message
77
+ end
78
+
79
+ def test_store_purchase_unstore
80
+ assert store = @gateway.store(@credit_card)
81
+ assert_success store
82
+ assert_equal 'Accepted', store.message
83
+
84
+ # wait a few seconds before charging a profile
85
+ sleep 10
86
+ assert purchase = @gateway.purchase(@amount, store.authorization, @options)
87
+ assert_success purchase
88
+ assert_equal 'Accepted', purchase.message
89
+
90
+ assert unstore = @gateway.unstore(store.authorization)
91
+ assert_success unstore
92
+ assert_equal 'Accepted', unstore.message
93
+ assert purchase_after_unstore = @gateway.purchase(@amount, store.authorization, @options)
94
+ assert_failure purchase_after_unstore
95
+ assert_equal 'Profile Not Found', purchase_after_unstore.message
96
+ end
97
+
98
+ def test_unsuccessful_unstore
99
+ assert unstore = @gateway.unstore('123456789')
100
+ assert_failure unstore
101
+ end
102
+ end
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+
4
+ begin
5
+ require 'rubygems'
6
+ require 'bundler'
7
+ Bundler.setup
8
+ rescue LoadError => e
9
+ puts "Error loading bundler (#{e.message}): \"gem install bundler\" for bundler support."
10
+ end
11
+
12
+ require 'test/unit'
13
+ require 'money'
14
+ require 'mocha'
15
+ require 'yaml'
16
+ require 'active_merchant'
17
+
18
+ require 'active_support/core_ext/integer/time'
19
+ require 'active_support/core_ext/numeric/time'
20
+
21
+ begin
22
+ require 'active_support/core_ext/time/acts_like'
23
+ rescue LoadError
24
+ end
25
+
26
+ begin
27
+ gem 'actionpack'
28
+ rescue LoadError
29
+ raise StandardError, "The view tests need ActionPack installed as gem to run"
30
+ end
31
+
32
+ require 'action_controller'
33
+ require "action_view/template"
34
+ begin
35
+ require 'active_support/core_ext/module/deprecation'
36
+ require 'action_dispatch/testing/test_process'
37
+ rescue LoadError
38
+ require 'action_controller/test_process'
39
+ end
40
+ require 'active_merchant/billing/integrations/action_view_helper'
41
+ require 'active_merchant_card_flex'
42
+
43
+ ActiveMerchant::Billing::Base.mode = :test
44
+
45
+ if ENV['DEBUG_ACTIVE_MERCHANT'] == 'true'
46
+ require 'logger'
47
+ ActiveMerchant::Billing::Gateway.logger = Logger.new(STDOUT)
48
+ ActiveMerchant::Billing::Gateway.wiredump_device = STDOUT
49
+ end
50
+
51
+ # Test gateways
52
+ class SimpleTestGateway < ActiveMerchant::Billing::Gateway
53
+ end
54
+
55
+ class SubclassGateway < SimpleTestGateway
56
+ end
57
+
58
+
59
+ module ActiveMerchant
60
+ module Assertions
61
+ AssertionClass = RUBY_VERSION > '1.9' ? MiniTest::Assertion : Test::Unit::AssertionFailedError
62
+
63
+ def assert_field(field, value)
64
+ clean_backtrace do
65
+ assert_equal value, @helper.fields[field]
66
+ end
67
+ end
68
+
69
+ # Allows the testing of you to check for negative assertions:
70
+ #
71
+ # # Instead of
72
+ # assert !something_that_is_false
73
+ #
74
+ # # Do this
75
+ # assert_false something_that_should_be_false
76
+ #
77
+ # An optional +msg+ parameter is available to help you debug.
78
+ def assert_false(boolean, message = nil)
79
+ message = build_message message, '<?> is not false or nil.', boolean
80
+
81
+ clean_backtrace do
82
+ assert_block message do
83
+ not boolean
84
+ end
85
+ end
86
+ end
87
+
88
+ # A handy little assertion to check for a successful response:
89
+ #
90
+ # # Instead of
91
+ # assert_success response
92
+ #
93
+ # # DRY that up with
94
+ # assert_success response
95
+ #
96
+ # A message will automatically show the inspection of the response
97
+ # object if things go afoul.
98
+ def assert_success(response)
99
+ clean_backtrace do
100
+ assert response.success?, "Response failed: #{response.inspect}"
101
+ end
102
+ end
103
+
104
+ # The negative of +assert_success+
105
+ def assert_failure(response)
106
+ clean_backtrace do
107
+ assert_false response.success?, "Response expected to fail: #{response.inspect}"
108
+ end
109
+ end
110
+
111
+ def assert_valid(validateable)
112
+ clean_backtrace do
113
+ assert validateable.valid?, "Expected to be valid"
114
+ end
115
+ end
116
+
117
+ def assert_not_valid(validateable)
118
+ clean_backtrace do
119
+ assert_false validateable.valid?, "Expected to not be valid"
120
+ end
121
+ end
122
+
123
+ def assert_deprecation_warning(message, target)
124
+ target.expects(:deprecated).with(message)
125
+ yield
126
+ end
127
+
128
+ private
129
+ def clean_backtrace(&block)
130
+ yield
131
+ rescue AssertionClass => e
132
+ path = File.expand_path(__FILE__)
133
+ raise AssertionClass, e.message, e.backtrace.reject { |line| File.expand_path(line) =~ /#{path}/ }
134
+ end
135
+ end
136
+
137
+ module Fixtures
138
+ HOME_DIR = RUBY_PLATFORM =~ /mswin32/ ? ENV['HOMEPATH'] : ENV['HOME'] unless defined?(HOME_DIR)
139
+ LOCAL_CREDENTIALS = File.join(HOME_DIR.to_s, '.active_merchant/fixtures.yml') unless defined?(LOCAL_CREDENTIALS)
140
+ DEFAULT_CREDENTIALS = File.join(File.dirname(__FILE__), 'fixtures.yml') unless defined?(DEFAULT_CREDENTIALS)
141
+
142
+ private
143
+ def credit_card(number = '4242424242424242', options = {})
144
+ defaults = {
145
+ :number => number,
146
+ :month => 9,
147
+ :year => Time.now.year + 1,
148
+ :first_name => 'Longbob',
149
+ :last_name => 'Longsen',
150
+ :verification_value => '123',
151
+ :type => 'visa'
152
+ }.update(options)
153
+
154
+ Billing::CreditCard.new(defaults)
155
+ end
156
+
157
+ def check(options = {})
158
+ defaults = {
159
+ :name => 'Jim Smith',
160
+ :routing_number => '244183602',
161
+ :account_number => '15378535',
162
+ :account_holder_type => 'personal',
163
+ :account_type => 'checking',
164
+ :number => '1'
165
+ }.update(options)
166
+
167
+ Billing::Check.new(defaults)
168
+ end
169
+
170
+ def address(options = {})
171
+ {
172
+ :name => 'Jim Smith',
173
+ :address1 => '1234 My Street',
174
+ :address2 => 'Apt 1',
175
+ :company => 'Widgets Inc',
176
+ :city => 'Ottawa',
177
+ :state => 'ON',
178
+ :zip => 'K1C2N6',
179
+ :country => 'CA',
180
+ :phone => '(555)555-5555',
181
+ :fax => '(555)555-6666'
182
+ }.update(options)
183
+ end
184
+
185
+ def all_fixtures
186
+ @@fixtures ||= load_fixtures
187
+ end
188
+
189
+ def fixtures(key)
190
+ data = all_fixtures[key] || raise(StandardError, "No fixture data was found for '#{key}'")
191
+
192
+ data.dup
193
+ end
194
+
195
+ def load_fixtures
196
+ file = File.exists?(LOCAL_CREDENTIALS) ? LOCAL_CREDENTIALS : DEFAULT_CREDENTIALS
197
+ yaml_data = YAML.load(File.read(file))
198
+ symbolize_keys(yaml_data)
199
+
200
+ yaml_data
201
+ end
202
+
203
+ def symbolize_keys(hash)
204
+ return unless hash.is_a?(Hash)
205
+
206
+ hash.symbolize_keys!
207
+ hash.each{|k,v| symbolize_keys(v)}
208
+ end
209
+ end
210
+ end
211
+
212
+ Test::Unit::TestCase.class_eval do
213
+ include ActiveMerchant::Billing
214
+ include ActiveMerchant::Assertions
215
+ include ActiveMerchant::Utils
216
+ include ActiveMerchant::Fixtures
217
+ end
@@ -0,0 +1,305 @@
1
+ require 'test_helper'
2
+
3
+ class CardFlexTest < Test::Unit::TestCase
4
+ def setup
5
+ @gateway = CardFlexGateway.new(fixtures(:card_flex))
6
+ @amount = 100
7
+ @credit_card = credit_card('5454545454545454')
8
+ @declined_card = credit_card('4111111111111112')
9
+ @options = {
10
+ :order_id => generate_unique_id,
11
+ :billing_address => address,
12
+ :description => 'Test purchase'
13
+ }
14
+ end
15
+
16
+ def test_successful_purchase
17
+ @gateway.expects(:ssl_post).returns(successful_purchase_response)
18
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
19
+ assert_instance_of Response, response
20
+ assert_success response
21
+ assert_equal '1921788665454', response.authorization
22
+ assert response.test?
23
+ end
24
+
25
+ def test_unsuccessful_purchase
26
+ @gateway.expects(:ssl_post).returns(failed_purchase_response)
27
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
28
+ assert_failure response
29
+ assert response.test?
30
+ end
31
+
32
+ def test_authorization
33
+ @gateway.expects(:ssl_post).returns(successful_authorization_response)
34
+ assert response = @gateway.authorize(@amount, @credit_card, @options)
35
+ assert response.success?
36
+ assert_equal '1921799375454', response.authorization
37
+ assert response.test?
38
+ end
39
+
40
+ def test_capture
41
+ @gateway.expects(:ssl_post).returns(successful_capture_response)
42
+ assert response = @gateway.capture(@amount, '1921799375454', @options)
43
+ assert response.success?
44
+ assert_equal '1921799405454', response.authorization
45
+ assert response.test?
46
+ end
47
+
48
+ def test_credit
49
+ @gateway.expects(:ssl_post).returns(successful_credit_response)
50
+ assert_success @gateway.credit(@amount, @credit_card, @options)
51
+ end
52
+
53
+ def test_void
54
+ @gateway.expects(:ssl_post).returns(successful_void_response)
55
+ assert response = @gateway.void('1921799375454')
56
+ assert response.success?
57
+ assert_equal '1921796055454', response.authorization
58
+ assert response.test?
59
+ end
60
+
61
+ def test_store
62
+ @gateway.expects(:ssl_post).returns(successful_store_response)
63
+ assert response = @gateway.store(@credit_card)
64
+ assert response.success?
65
+ assert_equal '65567155454', response.authorization
66
+ assert response.test?
67
+ end
68
+
69
+ def test_unstore
70
+ @gateway.expects(:ssl_post).returns(successful_unstore_response)
71
+ assert response = @gateway.unstore('65567155454')
72
+ assert response.success?
73
+ assert_equal nil, response.authorization
74
+ assert response.test?
75
+ end
76
+
77
+ def test_successful_avs_check
78
+ @gateway.expects(:ssl_post).returns(successful_purchase_response.gsub('192178866:N::U', '192178866:Y::U'))
79
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
80
+ assert_equal response.avs_result['code'], "Y"
81
+ assert_equal response.avs_result['message'], "Street address and 5-digit postal code match."
82
+ assert_equal response.avs_result['street_match'], "Y"
83
+ assert_equal response.avs_result['postal_match'], "Y"
84
+ end
85
+
86
+ def test_unsuccessful_avs_check_with_bad_street_address
87
+ @gateway.expects(:ssl_post).returns(successful_purchase_response.gsub('192178866:N::U', '192178866:Z::U'))
88
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
89
+ assert_equal response.avs_result['code'], "Z"
90
+ assert_equal response.avs_result['message'], "Street address does not match, but 5-digit postal code matches."
91
+ assert_equal response.avs_result['street_match'], "N"
92
+ assert_equal response.avs_result['postal_match'], "Y"
93
+ end
94
+
95
+ def test_unsuccessful_avs_check_with_bad_zip
96
+ @gateway.expects(:ssl_post).returns(successful_purchase_response.gsub('192178866:N::U', '192178866:A::U'))
97
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
98
+ assert_equal response.avs_result['code'], "A"
99
+ assert_equal response.avs_result['message'], "Street address matches, but 5-digit and 9-digit postal code do not match."
100
+ assert_equal response.avs_result['street_match'], "Y"
101
+ assert_equal response.avs_result['postal_match'], "N"
102
+ end
103
+
104
+ def test_successful_cvv_check
105
+ @gateway.expects(:ssl_post).returns(successful_purchase_response.gsub('192178866:N::U', '192178866:N::M'))
106
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
107
+ assert_equal response.cvv_result['code'], "M"
108
+ assert_equal response.cvv_result['message'], "Match"
109
+ end
110
+
111
+ def test_unsuccessful_cvv_check
112
+ @gateway.expects(:ssl_post).returns(successful_purchase_response.gsub('192178866:N::U', '192178866:N::N'))
113
+ assert response = @gateway.purchase(@amount, @credit_card, @options)
114
+ assert_equal response.cvv_result['code'], "N"
115
+ assert_equal response.cvv_result['message'], "No Match"
116
+ end
117
+
118
+ def test_supported_countries
119
+ assert_equal ['US'], CardFlexGateway.supported_countries
120
+ end
121
+
122
+ def test_supported_card_types
123
+ assert_equal [:visa, :master, :american_express, :discover], CardFlexGateway.supported_cardtypes
124
+ end
125
+
126
+ private
127
+ def successful_purchase_response
128
+ <<-eos.gsub(/^ {6}/, '').gsub("\n", "\r\n")
129
+ <html><body><plaintext>
130
+ Accepted=AVSAUTH:TEST:::192178866:N::U
131
+ historyid=192178866
132
+ orderid=141639490
133
+ Accepted=AVSAUTH:TEST:::192178866:N::U
134
+ ACCOUNTNUMBER=************5454
135
+ authcode=TEST
136
+ AuthNo=AVSAUTH:TEST:::192178866:N::U
137
+ ENTRYMETHOD=KEYED
138
+ historyid=192178866
139
+ MERCHANTORDERNUMBER=d86139833ae6f675c21931d1ca68a674
140
+ orderid=141639490
141
+ PAYTYPE=MasterCard
142
+ recurid=0
143
+ refcode=192178866-TEST
144
+ result=1
145
+ Status=Accepted
146
+ transid=0
147
+ eos
148
+ end
149
+
150
+ def successful_authorization_response
151
+ <<-eos.gsub(/^ {6}/, '').gsub("\n", "\r\n")
152
+ <html><body><plaintext>
153
+ Accepted=AVSAUTH:TEST:::192179937:N::U
154
+ historyid=192179937
155
+ orderid=141640360
156
+ Accepted=AVSAUTH:TEST:::192179937:N::U
157
+ ACCOUNTNUMBER=************5454
158
+ authcode=TEST
159
+ AuthNo=AVSAUTH:TEST:::192179937:N::U
160
+ ENTRYMETHOD=KEYED
161
+ historyid=192179937
162
+ MERCHANTORDERNUMBER=dd9d22bc626a793e039975bdc3f70eda
163
+ orderid=141640360
164
+ PAYTYPE=MasterCard
165
+ recurid=0
166
+ refcode=192179937-TEST
167
+ result=1
168
+ Status=Accepted
169
+ transid=0
170
+ eos
171
+ end
172
+
173
+ def successful_credit_response
174
+ <<-eos.gsub(/^ {6}/, '').gsub("\n", "\r\n")
175
+ <html><body><plaintext>
176
+ Accepted=CREDIT:TEST:::192180043:::
177
+ historyid=192180043
178
+ orderid=141640416
179
+ Accepted=CREDIT:TEST:::192180043:::
180
+ ACCOUNTNUMBER=************5454
181
+ authcode=TEST
182
+ AuthNo=CREDIT:TEST:::192180043:::
183
+ ENTRYMETHOD=KEYED
184
+ historyid=192180043
185
+ MERCHANTORDERNUMBER=1fe19bf710c5800dcef50fa9572b8962
186
+ orderid=141640416
187
+ PAYTYPE=MasterCard
188
+ recurid=0
189
+ refcode=192180043-TEST
190
+ result=1
191
+ Status=Accepted
192
+ transid=0
193
+ eos
194
+ end
195
+
196
+ def successful_void_response
197
+ <<-eos.gsub(/^ {6}/, '').gsub("\n", "\r\n")
198
+ <html><body><plaintext>
199
+ Accepted=VOID:TEST:::192179605:::
200
+ historyid=192179605
201
+ orderid=141640067
202
+ Accepted=VOID:TEST:::192179605:::
203
+ ACCOUNTNUMBER=************5454
204
+ authcode=TEST
205
+ AuthNo=VOID:TEST:::192179605:::
206
+ ENTRYMETHOD=KEYED
207
+ historyid=192179605
208
+ MERCHANTORDERNUMBER=42ecde63168c401ccb02a40171aa0042
209
+ orderid=141640067
210
+ PAYTYPE=MasterCard
211
+ recurid=0
212
+ refcode=192179605-TEST
213
+ result=1
214
+ Status=Accepted
215
+ transid=0
216
+ eos
217
+ end
218
+
219
+ def successful_capture_response
220
+ <<-eos.gsub(/^ {6}/, '').gsub("\n", "\r\n")
221
+ <html><body><plaintext>
222
+ Accepted=AVSPOST:TEST:::192179940:::
223
+ historyid=192179940
224
+ orderid=141640360
225
+ Accepted=AVSPOST:TEST:::192179940:::
226
+ ACCOUNTNUMBER=************5454
227
+ authcode=TEST
228
+ AuthNo=AVSPOST:TEST:::192179940:::
229
+ ENTRYMETHOD=KEYED
230
+ historyid=192179940
231
+ MERCHANTORDERNUMBER=dd9d22bc626a793e039975bdc3f70eda
232
+ orderid=141640360
233
+ PAYTYPE=MasterCard
234
+ recurid=0
235
+ refcode=192179940-TEST
236
+ result=1
237
+ Status=Accepted
238
+ transid=192179937
239
+ eos
240
+ end
241
+
242
+ def successful_store_response
243
+ <<-eos.gsub(/^ {6}/, '').gsub("\n", "\r\n")
244
+ <html><body><plaintext>
245
+ Accepted=AVSAUTH:TEST:::192179530:N::U:DUPLICATE
246
+ historyid=192179530
247
+ orderid=141640000
248
+ Accepted=AVSAUTH:TEST:::192179530:N::U:DUPLICATE
249
+ ACCOUNTNUMBER=************5454
250
+ authcode=TEST
251
+ AuthNo=AVSAUTH:TEST:::192179530:N::U:DUPLICATE
252
+ DUPLICATE=1
253
+ ENTRYMETHOD=KEYED
254
+ historyid=192179530
255
+ orderid=141640000
256
+ PAYTYPE=MasterCard
257
+ recurid=0
258
+ refcode=192179530-TEST
259
+ result=1
260
+ Status=Accepted
261
+ transid=0
262
+ USERPROFILEID=6556715
263
+ eos
264
+ end
265
+
266
+ def successful_unstore_response
267
+ <<-eos.gsub(/^ {6}/, '').gsub("\n", "\r\n")
268
+ <html><body><plaintext>
269
+ Accepted=PROFILEDELETE:Success:::0:::
270
+ historyid=0
271
+ orderid=
272
+ Accepted=PROFILEDELETE:Success:::0:::
273
+ authcode=Success
274
+ AuthNo=PROFILEDELETE:Success:::0:::
275
+ historyid=0
276
+ refcode=
277
+ result=1
278
+ Status=Accepted
279
+ transid=0
280
+ USERPROFILEID=6556715
281
+ eos
282
+ end
283
+
284
+ def failed_purchase_response
285
+ <<-eos.gsub(/^ {6}/, '').gsub("\n", "\r\n")
286
+ <html><body><plaintext>
287
+ Declined=DECLINED:1101610001:Invalid account number:
288
+ historyid=192179887
289
+ orderid=141640315
290
+ ACCOUNTNUMBER=************1112
291
+ Declined=DECLINED:1101610001:Invalid account number:
292
+ ENTRYMETHOD=KEYED
293
+ historyid=192179887
294
+ MERCHANTORDERNUMBER=5b42749552174e683282b5b0ddcd6459
295
+ orderid=141640315
296
+ PAYTYPE=Visa
297
+ rcode=1101610001
298
+ Reason=DECLINED:1101610001:Invalid account number:
299
+ recurid=0
300
+ result=0
301
+ Status=Declined
302
+ transid=0
303
+ eos
304
+ end
305
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_merchant_card_flex
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Stephen St. Martin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-06-16 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activemerchant
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 1.10.0
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">"
34
+ - !ruby/object:Gem::Version
35
+ version: 2.3.5
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: money
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: actionpack
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: mocha
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ type: :development
70
+ version_requirements: *id005
71
+ description: Provide support for CardFlex's standard integration and stored profile tokenization integrations.'
72
+ email:
73
+ - kuprishuz@gmail.com
74
+ executables: []
75
+
76
+ extensions: []
77
+
78
+ extra_rdoc_files: []
79
+
80
+ files:
81
+ - .gitignore
82
+ - Gemfile
83
+ - MIT-LICENSE.
84
+ - README.md
85
+ - Rakefile
86
+ - active_merchant_card_flex.gemspec
87
+ - lib/active_merchant/billing/gateways/card_flex.rb
88
+ - lib/active_merchant_card_flex.rb
89
+ - lib/active_merchant_card_flex/version.rb
90
+ - test/fixtures.yml
91
+ - test/remote/gateways/remote_cardflex_test.rb
92
+ - test/test_helper.rb
93
+ - test/unit/gateways/card_flex_test.rb
94
+ has_rdoc: true
95
+ homepage: ""
96
+ licenses: []
97
+
98
+ post_install_message:
99
+ rdoc_options: []
100
+
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: "0"
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: "0"
115
+ requirements: []
116
+
117
+ rubyforge_project: active_merchant_card_flex
118
+ rubygems_version: 1.6.2
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: CardFlex support for ActiveMerchant
122
+ test_files:
123
+ - test/fixtures.yml
124
+ - test/remote/gateways/remote_cardflex_test.rb
125
+ - test/test_helper.rb
126
+ - test/unit/gateways/card_flex_test.rb