ActiveMerchant-FatZebra 1.0.0
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 +4 -0
- data/ActiveMerchant-FatZebra.gemspec +24 -0
- data/Gemfile +4 -0
- data/README.md +62 -0
- data/Rakefile +37 -0
- data/lib/ActiveMerchant-FatZebra.rb +2 -0
- data/lib/active_merchant/billing/gateways/fat_zebra.rb +153 -0
- data/test/comm_stub.rb +39 -0
- data/test/fixtures.yml +3 -0
- data/test/remote/gateways/remote_fat_zebra_test.rb +76 -0
- data/test/test_helper.rb +252 -0
- data/test/unit/gateways/fat_zebra_test.rb +172 -0
- metadata +113 -0
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "ActiveMerchant-FatZebra"
|
6
|
+
s.version = "1.0.0"
|
7
|
+
s.authors = ["Matthew Savage"]
|
8
|
+
s.email = ["matthew.savage@fatzebra.com.au"]
|
9
|
+
s.homepage = "https://www.fatzebra.com.au"
|
10
|
+
s.summary = %q{Fat Zebra support for Active Merchant, as a gem}
|
11
|
+
s.description = %q{Fat Zebra support for Active Merchant - temporary gem until support is combined into active_merchant core.}
|
12
|
+
|
13
|
+
s.rubyforge_project = "ActiveMerchant-FatZebra"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_runtime_dependency "activemerchant"
|
21
|
+
s.add_development_dependency "rake"
|
22
|
+
s.add_development_dependency('mocha', '~> 0.11.3')
|
23
|
+
s.add_development_dependency('rails', '>= 2.3.11')
|
24
|
+
end
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
FatZebra ActiveMerchant integration
|
2
|
+
===================================
|
3
|
+
|
4
|
+
The quick 30-second rundown
|
5
|
+
---------------------------
|
6
|
+
|
7
|
+
1. Install the gem in your Gemfile
|
8
|
+
|
9
|
+
gem "active_merchant_fat_zebra"
|
10
|
+
|
11
|
+
Then `bundle install`
|
12
|
+
|
13
|
+
2. Setup the gateway in your app:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
|
17
|
+
options = {
|
18
|
+
:login => "test",
|
19
|
+
:token => "test_token"
|
20
|
+
}
|
21
|
+
gateway = ActiveMerchant::Billing::FatZebraGateway.new(options)
|
22
|
+
|
23
|
+
# If you are testing, uncomment the following line
|
24
|
+
# ActiveMerchant::Billing::Base.gateway_mode = :test
|
25
|
+
```
|
26
|
+
|
27
|
+
3. Setup the transaction data and then... transact!
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
cc = ActiveMerchant::Billing::CreditCard.new(:first_name => "Joe",
|
31
|
+
:last_name => "Smith",
|
32
|
+
:number => "4444333322221111",
|
33
|
+
:month => 12,
|
34
|
+
:year => 2013,
|
35
|
+
:verification_value => 123)
|
36
|
+
|
37
|
+
amount = 10000 # 100.00 in cents
|
38
|
+
|
39
|
+
result = gateway.purchase(amount, cc, { :order_id => "ORD18239", :ip => request.ip }) # If you are using rails you would use request.remote_ip
|
40
|
+
```
|
41
|
+
|
42
|
+
4. Handle the response - the data you get back is:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
result.success? # True or False depending on if the txn was successful
|
46
|
+
result.message # e.g. "Approved" or "Declined"
|
47
|
+
result.id # The transaction ID
|
48
|
+
result.test? # Indicates if this was a test transaction
|
49
|
+
result.params # Parameters passed by the gateway (e.g. may be additional info like fraud review score.)
|
50
|
+
result.params["result"]["id"] # The transaction ID
|
51
|
+
result.params["result"]["token"] # The card token (when storing a card)
|
52
|
+
```
|
53
|
+
|
54
|
+
|
55
|
+
Refunds
|
56
|
+
-------
|
57
|
+
```ruby
|
58
|
+
# Refunding the amount of $100.00, for the original transaction ID of "AB12887Z"
|
59
|
+
response = gateway.refund(10000, "AB12887Z", "REFUND1")
|
60
|
+
|
61
|
+
response.success? # true or false
|
62
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
$:.unshift File.expand_path('../lib', __FILE__)
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bundler'
|
7
|
+
Bundler.setup
|
8
|
+
rescue LoadError => e
|
9
|
+
puts "Error loading bundler (#{e.message}): \"gem install bundler\" for bundler support."
|
10
|
+
require 'rubygems'
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'rake'
|
14
|
+
require 'rake/testtask'
|
15
|
+
|
16
|
+
desc "Run the unit test suite"
|
17
|
+
task :default => 'test:all'
|
18
|
+
|
19
|
+
namespace :test do
|
20
|
+
|
21
|
+
Rake::TestTask.new(:units) do |t|
|
22
|
+
t.pattern = 'test/unit/**/*_test.rb'
|
23
|
+
t.ruby_opts << '-rubygems'
|
24
|
+
t.libs << 'test'
|
25
|
+
t.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
Rake::TestTask.new(:remote) do |t|
|
29
|
+
t.pattern = 'test/remote/**/*_test.rb'
|
30
|
+
t.ruby_opts << '-rubygems'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Test all"
|
36
|
+
task :all => [:units, :remote]
|
37
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module ActiveMerchant #:nodoc:
|
4
|
+
module Billing #:nodoc:
|
5
|
+
class FatZebraGateway < Gateway
|
6
|
+
LIVE_URL = "https://gateway.fatzebra.com.au/v1.0"
|
7
|
+
SANDBOX_URL = "https://gateway.sandbox.fatzebra.com.au/v1.0"
|
8
|
+
|
9
|
+
self.supported_countries = ['AU']
|
10
|
+
self.default_currency = 'AUD'
|
11
|
+
self.money_format = :cents
|
12
|
+
self.supported_cardtypes = [:visa, :master, :american_express, :jcb]
|
13
|
+
|
14
|
+
self.homepage_url = 'https://www.fatzebra.com.au/'
|
15
|
+
self.display_name = 'Fat Zebra'
|
16
|
+
|
17
|
+
# Setup a new instance of the gateway.
|
18
|
+
#
|
19
|
+
# The options hash should include :username and :token
|
20
|
+
# You can find your username and token at https://dashboard.fatzebra.com.au
|
21
|
+
# Under the Your Account section
|
22
|
+
def initialize(options = {})
|
23
|
+
requires!(options, :username)
|
24
|
+
requires!(options, :token)
|
25
|
+
@username = options[:username]
|
26
|
+
@token = options[:token]
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
# To create a purchase on a credit card use:
|
31
|
+
#
|
32
|
+
# purchase(money, creditcard , { ... })
|
33
|
+
#
|
34
|
+
# To charge a tokenized card
|
35
|
+
#
|
36
|
+
# purchase(money, {:token => "abzy87u", :cvv => "123"}, { ... }})
|
37
|
+
def purchase(money, creditcard, options = {})
|
38
|
+
post = {}
|
39
|
+
|
40
|
+
add_amount(post, money, options)
|
41
|
+
add_creditcard(post, creditcard, options)
|
42
|
+
post[:reference] = options[:order_id]
|
43
|
+
post[:customer_ip] = options[:ip]
|
44
|
+
|
45
|
+
commit(:post, 'purchases', post)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Refund a transaction
|
49
|
+
#
|
50
|
+
# amount - Integer - the amount to refund
|
51
|
+
# txn_id - String - the original transaction to be refunded
|
52
|
+
# reference - String - your transaction reference
|
53
|
+
def refund(money, txn_id, reference)
|
54
|
+
post = {}
|
55
|
+
|
56
|
+
post[:amount] = money
|
57
|
+
post[:transaction_id] = txn_id
|
58
|
+
post[:reference] = reference
|
59
|
+
|
60
|
+
commit(:post, "refunds", post)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Tokenize a credit card
|
64
|
+
def store(creditcard)
|
65
|
+
post = {}
|
66
|
+
add_creditcard(post, creditcard)
|
67
|
+
|
68
|
+
commit(:post, "credit_cards", post)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
# Add the money details to the request
|
73
|
+
def add_amount(post, money, options)
|
74
|
+
post[:amount] = money
|
75
|
+
end
|
76
|
+
|
77
|
+
# Add the credit card details to the request
|
78
|
+
def add_creditcard(post, creditcard, options = {})
|
79
|
+
if creditcard.respond_to?(:number)
|
80
|
+
post[:card_number] = creditcard.number
|
81
|
+
post[:card_expiry] = "#{creditcard.month}/#{creditcard.year}"
|
82
|
+
post[:cvv] = creditcard.verification_value if creditcard.verification_value?
|
83
|
+
post[:card_holder] = creditcard.name if creditcard.name
|
84
|
+
else
|
85
|
+
post[:card_token] = creditcard[:token]
|
86
|
+
post[:cvv] = creditcard[:cvv]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Post the data to the gateway
|
91
|
+
def commit(method, uri, parameters=nil)
|
92
|
+
raw_response = response = nil
|
93
|
+
success = false
|
94
|
+
begin
|
95
|
+
raw_response = ssl_request(method, get_url(uri), parameters.to_json, headers)
|
96
|
+
response = parse(raw_response)
|
97
|
+
success = response["successful"] && (response["response"]["successful"] || response["response"]["token"])
|
98
|
+
rescue ResponseError => e
|
99
|
+
if e.response.code == "401"
|
100
|
+
return Response.new(false, "Invalid Login")
|
101
|
+
end
|
102
|
+
|
103
|
+
raw_response = e.response.body
|
104
|
+
response = parse(raw_response)
|
105
|
+
rescue JSON::ParserError
|
106
|
+
response = json_error(raw_response)
|
107
|
+
end
|
108
|
+
|
109
|
+
message = response["response"]["message"]
|
110
|
+
unless response["successful"]
|
111
|
+
# There is an error, so we will show that instead
|
112
|
+
message = response["errors"].empty? ? "Unknown Error" : response["errors"].join(", ")
|
113
|
+
end
|
114
|
+
|
115
|
+
Response.new(success,
|
116
|
+
message,
|
117
|
+
response,
|
118
|
+
:test => response.has_key?("test") ? response["test"] : false,
|
119
|
+
:authorization => response["response"]["authorization"],
|
120
|
+
:id => response["response"]["id"])
|
121
|
+
end
|
122
|
+
|
123
|
+
# Parse the returned JSON, if parse errors are raised then return a detailed error.
|
124
|
+
def parse(response)
|
125
|
+
begin
|
126
|
+
JSON.parse(response)
|
127
|
+
rescue JSON::ParserError
|
128
|
+
msg = 'Invalid JSON response received from Fat Zebra. Please contact support@fatzebra.com.au if you continue to receive this message.'
|
129
|
+
msg += " (The raw response returned by the API was #{response.inspect})"
|
130
|
+
{
|
131
|
+
"successful" => false,
|
132
|
+
"response" => {},
|
133
|
+
"errors" => [msg]
|
134
|
+
}
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Build the URL based on the AM mode and the URI
|
139
|
+
def get_url(uri)
|
140
|
+
base = test? ? SANDBOX_URL : LIVE_URL
|
141
|
+
base + "/" + uri
|
142
|
+
end
|
143
|
+
|
144
|
+
# Builds the auth and U-A headers for the request
|
145
|
+
def headers
|
146
|
+
{
|
147
|
+
"Authorization" => "Basic " + Base64.encode64(@username.to_s + ":" + @token.to_s).strip,
|
148
|
+
"User-Agent" => "Fat Zebra v1.0/ActiveMerchant #{ActiveMerchant::VERSION}"
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/test/comm_stub.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module CommStub
|
2
|
+
class Stub
|
3
|
+
def initialize(gateway, action)
|
4
|
+
@gateway = gateway
|
5
|
+
@action = action
|
6
|
+
@complete = false
|
7
|
+
end
|
8
|
+
|
9
|
+
def check_request(&block)
|
10
|
+
@check = block
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def respond_with(*responses)
|
15
|
+
@complete = true
|
16
|
+
check = @check
|
17
|
+
(class << @gateway; self; end).send(:define_method, :ssl_post) do |*args|
|
18
|
+
check.call(*args) if check
|
19
|
+
(responses.size == 1 ? responses.last : responses.shift)
|
20
|
+
end
|
21
|
+
@action.call
|
22
|
+
end
|
23
|
+
|
24
|
+
def complete?
|
25
|
+
@complete
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def stub_comms(gateway=@gateway, &action)
|
30
|
+
if @last_comm_stub
|
31
|
+
assert @last_comm_stub.complete?, "Tried to stub communications when there's a stub already in progress."
|
32
|
+
end
|
33
|
+
@last_comm_stub = Stub.new(gateway, action)
|
34
|
+
end
|
35
|
+
|
36
|
+
def teardown
|
37
|
+
assert(@last_comm_stub.complete?) if @last_comm_stub
|
38
|
+
end
|
39
|
+
end
|
data/test/fixtures.yml
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RemoteFatZebraTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@gateway = FatZebraGateway.new(fixtures(:fat_zebra))
|
8
|
+
|
9
|
+
@amount = 100
|
10
|
+
@credit_card = credit_card('5123456789012346')
|
11
|
+
@declined_card = credit_card('4557012345678902')
|
12
|
+
|
13
|
+
@options = {
|
14
|
+
:order_id => rand(100000).to_s,
|
15
|
+
:ip => "123.1.2.3"
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_successful_purchase
|
20
|
+
assert response = @gateway.purchase(@amount, @credit_card, @options)
|
21
|
+
assert_success response
|
22
|
+
assert_equal 'Approved', response.message
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_unsuccessful_purchase
|
26
|
+
assert response = @gateway.purchase(@amount, @declined_card, @options)
|
27
|
+
assert_failure response
|
28
|
+
assert_equal 'Declined', response.message
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_invalid_data
|
32
|
+
@options.delete(:ip)
|
33
|
+
assert response = @gateway.purchase(@amount, @declined_card, @options)
|
34
|
+
assert_failure response
|
35
|
+
assert_equal "Customer ip can't be blank", response.message
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_refund
|
39
|
+
purchase = @gateway.purchase(@amount, @credit_card, @options)
|
40
|
+
|
41
|
+
assert response = @gateway.refund(@amount, purchase.params["response"]["id"], rand(1000000).to_s)
|
42
|
+
assert_success response
|
43
|
+
assert_match /Approved/, response.message
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_invalid_refund
|
47
|
+
purchase = @gateway.purchase(@amount, @credit_card, @options)
|
48
|
+
|
49
|
+
assert response = @gateway.refund(@amount, nil, rand(1000000).to_s)
|
50
|
+
assert_failure response
|
51
|
+
assert_match /Original transaction is required/, response.message
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_store
|
55
|
+
assert card = @gateway.store(@credit_card)
|
56
|
+
|
57
|
+
assert_success card
|
58
|
+
assert_false card.params["response"]["token"].nil?
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_purchase_with_token
|
62
|
+
assert card = @gateway.store(@credit_card)
|
63
|
+
assert purchase = @gateway.purchase(@amount, {:cvv => 123, :token => card.params["response"]["token"]}, @options)
|
64
|
+
assert_success purchase
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_invalid_login
|
68
|
+
gateway = FatZebraGateway.new(
|
69
|
+
:username => 'invalid',
|
70
|
+
:token => 'wrongtoken'
|
71
|
+
)
|
72
|
+
assert response = gateway.purchase(@amount, @credit_card, @options)
|
73
|
+
assert_failure response
|
74
|
+
assert_equal 'Invalid Login', response.message
|
75
|
+
end
|
76
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,252 @@
|
|
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
|
+
require 'active_merchant/billing/gateways/fat_zebra'
|
18
|
+
require 'comm_stub'
|
19
|
+
|
20
|
+
require 'active_support/core_ext/integer/time'
|
21
|
+
require 'active_support/core_ext/numeric/time'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
begin
|
26
|
+
require 'active_support/core_ext/time/acts_like'
|
27
|
+
rescue LoadError
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
gem 'actionpack'
|
32
|
+
rescue LoadError
|
33
|
+
raise StandardError, "The view tests need ActionPack installed as gem to run"
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'action_controller'
|
37
|
+
require "action_view/template"
|
38
|
+
begin
|
39
|
+
require 'active_support/core_ext/module/deprecation'
|
40
|
+
require 'action_dispatch/testing/test_process'
|
41
|
+
rescue LoadError
|
42
|
+
require 'action_controller/test_process'
|
43
|
+
end
|
44
|
+
require 'active_merchant/billing/integrations/action_view_helper'
|
45
|
+
|
46
|
+
ActiveMerchant::Billing::Base.mode = :test
|
47
|
+
|
48
|
+
if ENV['DEBUG_ACTIVE_MERCHANT'] == 'true'
|
49
|
+
require 'logger'
|
50
|
+
ActiveMerchant::Billing::Gateway.logger = Logger.new(STDOUT)
|
51
|
+
ActiveMerchant::Billing::Gateway.wiredump_device = STDOUT
|
52
|
+
end
|
53
|
+
|
54
|
+
# Test gateways
|
55
|
+
class SimpleTestGateway < ActiveMerchant::Billing::Gateway
|
56
|
+
end
|
57
|
+
|
58
|
+
class SubclassGateway < SimpleTestGateway
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
module ActiveMerchant
|
63
|
+
module Assertions
|
64
|
+
AssertionClass = RUBY_VERSION > '1.9' ? MiniTest::Assertion : Test::Unit::AssertionFailedError
|
65
|
+
|
66
|
+
def assert_field(field, value)
|
67
|
+
clean_backtrace do
|
68
|
+
assert_equal value, @helper.fields[field]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Allows the testing of you to check for negative assertions:
|
73
|
+
#
|
74
|
+
# # Instead of
|
75
|
+
# assert !something_that_is_false
|
76
|
+
#
|
77
|
+
# # Do this
|
78
|
+
# assert_false something_that_should_be_false
|
79
|
+
#
|
80
|
+
# An optional +msg+ parameter is available to help you debug.
|
81
|
+
def assert_false(boolean, message = nil)
|
82
|
+
message = build_message message, '<?> is not false or nil.', boolean
|
83
|
+
|
84
|
+
clean_backtrace do
|
85
|
+
assert_block message do
|
86
|
+
not boolean
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# A handy little assertion to check for a successful response:
|
92
|
+
#
|
93
|
+
# # Instead of
|
94
|
+
# assert_success response
|
95
|
+
#
|
96
|
+
# # DRY that up with
|
97
|
+
# assert_success response
|
98
|
+
#
|
99
|
+
# A message will automatically show the inspection of the response
|
100
|
+
# object if things go afoul.
|
101
|
+
def assert_success(response)
|
102
|
+
clean_backtrace do
|
103
|
+
assert response.success?, "Response failed: #{response.inspect}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# The negative of +assert_success+
|
108
|
+
def assert_failure(response)
|
109
|
+
clean_backtrace do
|
110
|
+
assert_false response.success?, "Response expected to fail: #{response.inspect}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def assert_valid(validateable)
|
115
|
+
clean_backtrace do
|
116
|
+
assert validateable.valid?, "Expected to be valid"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def assert_not_valid(validateable)
|
121
|
+
clean_backtrace do
|
122
|
+
assert_false validateable.valid?, "Expected to not be valid"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def assert_deprecation_warning(message, target)
|
127
|
+
target.expects(:deprecated).with(message)
|
128
|
+
yield
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
def clean_backtrace(&block)
|
133
|
+
yield
|
134
|
+
rescue AssertionClass => e
|
135
|
+
path = File.expand_path(__FILE__)
|
136
|
+
raise AssertionClass, e.message, e.backtrace.reject { |line| File.expand_path(line) =~ /#{path}/ }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
module Fixtures
|
141
|
+
HOME_DIR = RUBY_PLATFORM =~ /mswin32/ ? ENV['HOMEPATH'] : ENV['HOME'] unless defined?(HOME_DIR)
|
142
|
+
LOCAL_CREDENTIALS = File.join(HOME_DIR.to_s, '.active_merchant/fixtures.yml') unless defined?(LOCAL_CREDENTIALS)
|
143
|
+
DEFAULT_CREDENTIALS = File.join(File.dirname(__FILE__), 'fixtures.yml') unless defined?(DEFAULT_CREDENTIALS)
|
144
|
+
|
145
|
+
private
|
146
|
+
def credit_card(number = '4242424242424242', options = {})
|
147
|
+
defaults = {
|
148
|
+
:number => number,
|
149
|
+
:month => 9,
|
150
|
+
:year => Time.now.year + 1,
|
151
|
+
:first_name => 'Longbob',
|
152
|
+
:last_name => 'Longsen',
|
153
|
+
:verification_value => '123',
|
154
|
+
:brand => 'visa'
|
155
|
+
}.update(options)
|
156
|
+
|
157
|
+
Billing::CreditCard.new(defaults)
|
158
|
+
end
|
159
|
+
|
160
|
+
def check(options = {})
|
161
|
+
defaults = {
|
162
|
+
:name => 'Jim Smith',
|
163
|
+
:routing_number => '244183602',
|
164
|
+
:account_number => '15378535',
|
165
|
+
:account_holder_type => 'personal',
|
166
|
+
:account_type => 'checking',
|
167
|
+
:number => '1'
|
168
|
+
}.update(options)
|
169
|
+
|
170
|
+
Billing::Check.new(defaults)
|
171
|
+
end
|
172
|
+
|
173
|
+
def address(options = {})
|
174
|
+
{
|
175
|
+
:name => 'Jim Smith',
|
176
|
+
:address1 => '1234 My Street',
|
177
|
+
:address2 => 'Apt 1',
|
178
|
+
:company => 'Widgets Inc',
|
179
|
+
:city => 'Ottawa',
|
180
|
+
:state => 'ON',
|
181
|
+
:zip => 'K1C2N6',
|
182
|
+
:country => 'CA',
|
183
|
+
:phone => '(555)555-5555',
|
184
|
+
:fax => '(555)555-6666'
|
185
|
+
}.update(options)
|
186
|
+
end
|
187
|
+
|
188
|
+
def all_fixtures
|
189
|
+
@@fixtures ||= load_fixtures
|
190
|
+
end
|
191
|
+
|
192
|
+
def fixtures(key)
|
193
|
+
data = all_fixtures[key] || raise(StandardError, "No fixture data was found for '#{key}'")
|
194
|
+
|
195
|
+
data.dup
|
196
|
+
end
|
197
|
+
|
198
|
+
def load_fixtures
|
199
|
+
[DEFAULT_CREDENTIALS, LOCAL_CREDENTIALS].inject({}) do |credentials, file_name|
|
200
|
+
if File.exists?(file_name)
|
201
|
+
yaml_data = YAML.load(File.read(file_name))
|
202
|
+
credentials.merge!(symbolize_keys(yaml_data))
|
203
|
+
end
|
204
|
+
credentials
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def symbolize_keys(hash)
|
209
|
+
return unless hash.is_a?(Hash)
|
210
|
+
|
211
|
+
hash.symbolize_keys!
|
212
|
+
hash.each{|k,v| symbolize_keys(v)}
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
Test::Unit::TestCase.class_eval do
|
218
|
+
include ActiveMerchant::Billing
|
219
|
+
include ActiveMerchant::Assertions
|
220
|
+
include ActiveMerchant::Utils
|
221
|
+
include ActiveMerchant::Fixtures
|
222
|
+
end
|
223
|
+
|
224
|
+
module ActionViewHelperTestHelper
|
225
|
+
|
226
|
+
def self.included(base)
|
227
|
+
base.send(:include, ActiveMerchant::Billing::Integrations::ActionViewHelper)
|
228
|
+
base.send(:include, ActionView::Helpers::FormHelper)
|
229
|
+
base.send(:include, ActionView::Helpers::FormTagHelper)
|
230
|
+
base.send(:include, ActionView::Helpers::UrlHelper)
|
231
|
+
base.send(:include, ActionView::Helpers::TagHelper)
|
232
|
+
base.send(:include, ActionView::Helpers::CaptureHelper)
|
233
|
+
base.send(:include, ActionView::Helpers::TextHelper)
|
234
|
+
base.send(:attr_accessor, :output_buffer)
|
235
|
+
end
|
236
|
+
|
237
|
+
def setup
|
238
|
+
@controller = Class.new do
|
239
|
+
attr_reader :url_for_options
|
240
|
+
def url_for(options, *parameters_for_method_reference)
|
241
|
+
@url_for_options = options
|
242
|
+
end
|
243
|
+
end
|
244
|
+
@controller = @controller.new
|
245
|
+
@output_buffer = ''
|
246
|
+
end
|
247
|
+
|
248
|
+
protected
|
249
|
+
def protect_against_forgery?
|
250
|
+
false
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FatZebraTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@gateway = FatZebraGateway.new(
|
6
|
+
:username => 'TEST',
|
7
|
+
:token => 'TEST'
|
8
|
+
)
|
9
|
+
|
10
|
+
@credit_card = credit_card
|
11
|
+
@amount = 100
|
12
|
+
|
13
|
+
@options = {
|
14
|
+
:order_id => rand(10000),
|
15
|
+
:billing_address => address,
|
16
|
+
:description => 'Store Purchase'
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_successful_purchase
|
21
|
+
@gateway.expects(:ssl_request).returns(successful_purchase_response)
|
22
|
+
|
23
|
+
assert response = @gateway.purchase(@amount, @credit_card, @options)
|
24
|
+
assert_success response
|
25
|
+
|
26
|
+
# Replace with authorization number from the successful response
|
27
|
+
assert_equal '55355', response.authorization
|
28
|
+
assert response.test?
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_unsuccessful_request
|
32
|
+
@gateway.expects(:ssl_request).returns(failed_purchase_response)
|
33
|
+
|
34
|
+
assert response = @gateway.purchase(@amount, @credit_card, @options)
|
35
|
+
assert_failure response
|
36
|
+
assert response.test?
|
37
|
+
assert_match /Invalid Card Number/, response.message
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_declined_purchase
|
41
|
+
@gateway.expects(:ssl_request).returns(declined_purchase_response)
|
42
|
+
|
43
|
+
assert response = @gateway.purchase(@amount, @credit_card, @options)
|
44
|
+
assert_failure response
|
45
|
+
assert response.test?
|
46
|
+
assert_match /Card Declined/, response.message
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_parse_error
|
50
|
+
@gateway.expects(:ssl_request).returns("{") # Some invalid JSON
|
51
|
+
assert response = @gateway.purchase(@amount, @credit_card, @options)
|
52
|
+
assert_failure response
|
53
|
+
assert_match /Invalid JSON response/, response.message
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_request_error
|
57
|
+
@gateway.expects(:ssl_request).returns(missing_data_response)
|
58
|
+
|
59
|
+
assert response = @gateway.purchase(@amount, @credit_card, @options)
|
60
|
+
assert_failure response
|
61
|
+
assert_match /Card Number is required/, response.message
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_successful_tokenization
|
65
|
+
@gateway.expects(:ssl_request).returns(successful_tokenize_response)
|
66
|
+
|
67
|
+
assert response = @gateway.store(@credit_card)
|
68
|
+
assert_success response
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_unsuccessful_tokenization
|
72
|
+
@gateway.expects(:ssl_request).returns(failed_tokenize_response)
|
73
|
+
|
74
|
+
assert response = @gateway.store(@credit_card)
|
75
|
+
assert_failure response
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Place raw successful response from gateway here
|
81
|
+
def successful_purchase_response
|
82
|
+
{
|
83
|
+
:successful => true,
|
84
|
+
:response => {
|
85
|
+
:authorization => "55355",
|
86
|
+
:id => "001-P-12345AA",
|
87
|
+
:card_number => "XXXXXXXXXXXX1111",
|
88
|
+
:card_holder => "John Smith",
|
89
|
+
:card_expiry => "10/2011",
|
90
|
+
:card_token => "a1bhj98j",
|
91
|
+
:amount => 349,
|
92
|
+
:successful => true,
|
93
|
+
:reference => "ABC123",
|
94
|
+
:message => "Approved",
|
95
|
+
},
|
96
|
+
:test => true,
|
97
|
+
:errors => []
|
98
|
+
}.to_json
|
99
|
+
end
|
100
|
+
|
101
|
+
def declined_purchase_response
|
102
|
+
{
|
103
|
+
:successful => true,
|
104
|
+
:response => {
|
105
|
+
:authorization_id => nil,
|
106
|
+
:id => nil,
|
107
|
+
:card_number => "XXXXXXXXXXXX1111",
|
108
|
+
:card_holder => "John Smith",
|
109
|
+
:card_expiry => "10/2011",
|
110
|
+
:amount => 100,
|
111
|
+
:authorized => false,
|
112
|
+
:reference => "ABC123",
|
113
|
+
:message => "Card Declined - check with issuer",
|
114
|
+
},
|
115
|
+
:test => true,
|
116
|
+
:errors => []
|
117
|
+
}.to_json
|
118
|
+
end
|
119
|
+
|
120
|
+
def successful_tokenize_response
|
121
|
+
{
|
122
|
+
:successful => true,
|
123
|
+
:response => {
|
124
|
+
:token => "e1q7dbj2",
|
125
|
+
:card_holder => "Bob Smith",
|
126
|
+
:card_number => "XXXXXXXXXXXX2346",
|
127
|
+
:card_expiry => "2013-05-31T23:59:59+10:00",
|
128
|
+
:authorized => true,
|
129
|
+
:transaction_count => 0
|
130
|
+
},
|
131
|
+
:errors => [],
|
132
|
+
:test => true
|
133
|
+
}.to_json
|
134
|
+
end
|
135
|
+
|
136
|
+
def failed_tokenize_response
|
137
|
+
{
|
138
|
+
:successful => false,
|
139
|
+
:response => {
|
140
|
+
:token => nil,
|
141
|
+
:card_holder => "Bob ",
|
142
|
+
:card_number => "512345XXXXXX2346",
|
143
|
+
:card_expiry => nil,
|
144
|
+
:authorized => false,
|
145
|
+
:transaction_count => 10
|
146
|
+
},
|
147
|
+
:errors => [
|
148
|
+
"Expiry date can't be blank"
|
149
|
+
],
|
150
|
+
:test => false
|
151
|
+
}.to_json
|
152
|
+
end
|
153
|
+
|
154
|
+
# Place raw failed response from gateway here
|
155
|
+
def failed_purchase_response
|
156
|
+
{
|
157
|
+
:successful => false,
|
158
|
+
:response => {},
|
159
|
+
:test => true,
|
160
|
+
:errors => ["Invalid Card Number"]
|
161
|
+
}.to_json
|
162
|
+
end
|
163
|
+
|
164
|
+
def missing_data_response
|
165
|
+
{
|
166
|
+
:successful => false,
|
167
|
+
:response => {},
|
168
|
+
:test => true,
|
169
|
+
:errors => ["Card Number is required"]
|
170
|
+
}.to_json
|
171
|
+
end
|
172
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ActiveMerchant-FatZebra
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matthew Savage
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activemerchant
|
16
|
+
requirement: &70121469611100 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70121469611100
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &70121469610340 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70121469610340
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: mocha
|
38
|
+
requirement: &70121469608900 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.11.3
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70121469608900
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rails
|
49
|
+
requirement: &70121469645180 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.3.11
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70121469645180
|
58
|
+
description: Fat Zebra support for Active Merchant - temporary gem until support is
|
59
|
+
combined into active_merchant core.
|
60
|
+
email:
|
61
|
+
- matthew.savage@fatzebra.com.au
|
62
|
+
executables: []
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- .gitignore
|
67
|
+
- ActiveMerchant-FatZebra.gemspec
|
68
|
+
- Gemfile
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- lib/ActiveMerchant-FatZebra.rb
|
72
|
+
- lib/active_merchant/billing/gateways/fat_zebra.rb
|
73
|
+
- test/comm_stub.rb
|
74
|
+
- test/fixtures.yml
|
75
|
+
- test/remote/gateways/remote_fat_zebra_test.rb
|
76
|
+
- test/test_helper.rb
|
77
|
+
- test/unit/gateways/fat_zebra_test.rb
|
78
|
+
homepage: https://www.fatzebra.com.au
|
79
|
+
licenses: []
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
hash: 3400427441090863407
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
hash: 3400427441090863407
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project: ActiveMerchant-FatZebra
|
104
|
+
rubygems_version: 1.8.10
|
105
|
+
signing_key:
|
106
|
+
specification_version: 3
|
107
|
+
summary: Fat Zebra support for Active Merchant, as a gem
|
108
|
+
test_files:
|
109
|
+
- test/comm_stub.rb
|
110
|
+
- test/fixtures.yml
|
111
|
+
- test/remote/gateways/remote_fat_zebra_test.rb
|
112
|
+
- test/test_helper.rb
|
113
|
+
- test/unit/gateways/fat_zebra_test.rb
|