active_merchant_card_flex 0.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/.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