Mockerize 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.
- checksums.yaml +7 -0
- data/.gitignore +47 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/LICENSE.txt +22 -0
- data/Mockerize.gemspec +28 -0
- data/README.md +60 -0
- data/Rakefile +2 -0
- data/init.rb +22 -0
- data/lib/Mockerize.rb +13 -0
- data/lib/Mockerize/mock_authorize_net_cim_gateway.rb +480 -0
- data/lib/Mockerize/version.rb +3 -0
- data/spec/mock_authorize_net_cim_gateway_spec.rb +11 -0
- data/spec/spec_helper.rb +100 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1eac6bf29a2a958983034165f01567f38797a9c9
|
4
|
+
data.tar.gz: c140fd9cde582209c3d9115840d106d1108f2332
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 573766276d04451b6e62bb431d4ff0716a9222a804ae0974b3079e6828fb6b977e6d5c91e541d6a82cfe5eedb3887af524dddf8a99f3fbc5cb741cfc52c95059
|
7
|
+
data.tar.gz: 75462793ecbc1ff971b3814a6251eae622e4a9818b88c5a42006a6e53794d80a29a7791b5e3d0475bdb144fe1d2407c66661d0ce7b2a351e0572f6c659d75858
|
data/.gitignore
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/Gemfile.lock
|
4
|
+
/_yardoc/
|
5
|
+
/coverage/
|
6
|
+
/doc/
|
7
|
+
/pkg/
|
8
|
+
/spec/reports/
|
9
|
+
/tmp/
|
10
|
+
*.bundle
|
11
|
+
*.so
|
12
|
+
*.o
|
13
|
+
*.a
|
14
|
+
mkmf.log
|
15
|
+
.idea
|
16
|
+
*.rbc
|
17
|
+
capybara-*.html
|
18
|
+
.rspec
|
19
|
+
/log
|
20
|
+
/db/*.sqlite3
|
21
|
+
/public/system
|
22
|
+
/coverage/
|
23
|
+
/spec/tmp
|
24
|
+
**.orig
|
25
|
+
rerun.txt
|
26
|
+
pickle-email-*.html
|
27
|
+
|
28
|
+
# TODO Comment out these rules if you are OK with secrets being uploaded to the repo
|
29
|
+
config/initializers/secret_token.rb
|
30
|
+
config/secrets.yml
|
31
|
+
|
32
|
+
## Environment normalisation:
|
33
|
+
/.bundle
|
34
|
+
/vendor/bundle
|
35
|
+
|
36
|
+
# these should all be checked in to normalise the environment:
|
37
|
+
# Gemfile.lock, .ruby-version, .ruby-gemset
|
38
|
+
|
39
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
40
|
+
.rvmrc
|
41
|
+
|
42
|
+
# if using bower-rails ignore default bower_components path bower.json files
|
43
|
+
/vendor/assets/bower_components
|
44
|
+
*.bowerrc
|
45
|
+
bower.json
|
46
|
+
|
47
|
+
bin
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
mockerize
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.2.0
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Miguel Alonso Jr
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Miguel Alonso Jr
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Mockerize.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'Mockerize/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'Mockerize'
|
8
|
+
spec.version = Mockerize::VERSION
|
9
|
+
spec.authors = ['Miguel Alonso Jr']
|
10
|
+
spec.email = ['drmiguelalonsojr@gmail.com']
|
11
|
+
spec.summary = %q{Mockerize is a mock authorize.net customer information management (CIM) class for Rails.}
|
12
|
+
spec.description = %q{Mockerize is a mock authorize.net customer information management (CIM) class for Rails. It is based on the active-merchant (http://activemerchant.org) authorize.net CIM gateway. It use Redis as a data store to simulate the authorize.net CIM service.}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
22
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
spec.add_development_dependency 'activemerchant'
|
24
|
+
spec.add_development_dependency 'redis'
|
25
|
+
spec.add_development_dependency 'rspec'
|
26
|
+
spec.add_development_dependency 'shoulda-matchers'
|
27
|
+
# spec.add_development_dependecy 'null_logger'
|
28
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Mockerize
|
2
|
+
|
3
|
+
Mockerize is a mock authorize.net customer information management (CIM) for Rails. It is based on the active-merchant
|
4
|
+
(http://activemerchant.org) authorize.net CIM gateway. It use Redis as a data store to simulate the authorize.net
|
5
|
+
CIM service.
|
6
|
+
|
7
|
+
|
8
|
+
## Dependencies
|
9
|
+
|
10
|
+
1. Redis
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'Mockerize'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle install
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install Mockerize
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
### Development
|
31
|
+
|
32
|
+
To use the mock server in development mode, add
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
ENV['MOCK_AUTH_NET'] = 'true'
|
36
|
+
```
|
37
|
+
|
38
|
+
to your development.rb environment file.
|
39
|
+
|
40
|
+
### Test
|
41
|
+
|
42
|
+
To use the auth.ent servers in test mode, add
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
ENV['FORCE_AUTH_NET'] = 'true'
|
46
|
+
```
|
47
|
+
|
48
|
+
to your test.rb environment file.
|
49
|
+
|
50
|
+
## Attribution
|
51
|
+
|
52
|
+
Original idea: http://engineering.harrys.com/2014/04/15/mock-authorize.net-gateway.html
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
|
56
|
+
1. Fork it ( https://github.com/[my-github-username]/Mockerize/fork )
|
57
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
58
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
59
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
60
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/init.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Initializer where we inject the mock gateway into global variable AUTHNET_GATEWAY or setup the real client
|
2
|
+
|
3
|
+
if !Rails.env.production?
|
4
|
+
ActiveMerchant::Billing::Base.mode = :test
|
5
|
+
end
|
6
|
+
|
7
|
+
$using_mock_auth_net_gateway = false
|
8
|
+
|
9
|
+
# Use FORCE_AUTH_NET to use the real Auth.net API in tests
|
10
|
+
# Use MOCK_AUTH_NET to use the mock Auth.net API even in development (good on a plane!)
|
11
|
+
if Rails.env.test? && !ENV.has_key?("FORCE_AUTH_NET") || ENV.has_key?("MOCK_AUTH_NET")
|
12
|
+
MockAuthorizeNetCimGateway::LOGGER.info "Test env: using mock auth net"
|
13
|
+
::AUTHNET_GATEWAY = MockAuthorizeNetCimGateway.new
|
14
|
+
$using_mock_auth_net_gateway = true
|
15
|
+
|
16
|
+
else
|
17
|
+
::AUTHNET_GATEWAY = ActiveMerchant::Billing::AuthorizeNetCimGateway.new(
|
18
|
+
:login => ENV["AUTH_NET_LOGIN"],
|
19
|
+
:password => ENV["AUTH_NET_API_TOKEN"],
|
20
|
+
:test_requests => false
|
21
|
+
)
|
22
|
+
end
|
data/lib/Mockerize.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'Mockerize/version'
|
2
|
+
require 'Mockerize/mock_authorize_net_cim_gateway'
|
3
|
+
|
4
|
+
module Mockerize
|
5
|
+
def self.deprecated(message, caller=Kernel.caller[1])
|
6
|
+
warning = caller + ": " + message
|
7
|
+
if(respond_to?(:logger) && logger.present?)
|
8
|
+
logger.warn(warning)
|
9
|
+
else
|
10
|
+
warn(warning)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,480 @@
|
|
1
|
+
# Class that will mock out functionality of the AuthNet test sandbox for their CIM API.
|
2
|
+
|
3
|
+
require 'active_merchant'
|
4
|
+
# TODO: Setup null_logger for optional logging
|
5
|
+
# require 'null_logger'
|
6
|
+
|
7
|
+
class MockAuthorizeNetCimGateway < ActiveMerchant::Billing::AuthorizeNetCimGateway
|
8
|
+
|
9
|
+
# attr_accessor :logger
|
10
|
+
# private :logger=, :logger
|
11
|
+
|
12
|
+
## Special Numbers
|
13
|
+
FAILURE_CREDIT_CARD_NUMBER = "4222222222222" # If this card is entered, we will reject the payment.
|
14
|
+
EXCEPTION_CREDIT_CARD_NUMBER = "4041378749540103" # if this card is entered, payments will throw exceptions
|
15
|
+
UNVOIDABLE_TRANSACTION_ID = "98712498798247" # If this transaction is voided, it will fail
|
16
|
+
|
17
|
+
## Redis key prefixes
|
18
|
+
CUSTOMER_PREFIX = "mockcim__customer__"
|
19
|
+
TRANSACTION_PREFIX = "mockcim__transaction__"
|
20
|
+
|
21
|
+
def initialize(options = {})
|
22
|
+
# self.logger = options[:logger] || Nulllogger.instance
|
23
|
+
@redis_url = ENV["REDISCLOUD_URL"] || ENV["REDISTOGO_URL"] || "localhost:6379"
|
24
|
+
end
|
25
|
+
|
26
|
+
def redis
|
27
|
+
if @redis.nil?
|
28
|
+
if @redis_url.start_with?("redis://")
|
29
|
+
# logger.debug "New Redis from redis://"
|
30
|
+
@redis = Redis.new(url: @redis_url)
|
31
|
+
else
|
32
|
+
# logger.debug "New Redis from host/port"
|
33
|
+
host, port = @redis_url.split(":")
|
34
|
+
@redis = Redis.new(host: host, port: port)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
# Force reconnect to deal with after-fork errors
|
38
|
+
#@redis.client.reconnect
|
39
|
+
end
|
40
|
+
return @redis
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def reset
|
45
|
+
# logger.debug "MOCK: reset MockAuthNetCimGateway"
|
46
|
+
keys = redis.keys("#{CUSTOMER_PREFIX}*")
|
47
|
+
if !keys.empty?
|
48
|
+
ret = redis.del(keys)
|
49
|
+
# logger.debug("Deleted #{ret} customers")
|
50
|
+
end
|
51
|
+
keys = redis.keys("#{TRANSACTION_PREFIX}*")
|
52
|
+
if !keys.empty?
|
53
|
+
ret = redis.del(keys)
|
54
|
+
# logger.debug("Deleted #{ret} transactions")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# CIM API call
|
59
|
+
def create_customer_profile(options)
|
60
|
+
# logger.debug "MOCK: create_customer_profile #{options[:profile]}"
|
61
|
+
# Copied 'requires' from base class
|
62
|
+
requires!(options, :profile)
|
63
|
+
requires!(options[:profile], :email) unless options[:profile][:merchant_customer_id] || options[:profile][:description]
|
64
|
+
requires!(options[:profile], :description) unless options[:profile][:email] || options[:profile][:merchant_customer_id]
|
65
|
+
requires!(options[:profile], :merchant_customer_id) unless options[:profile][:description] || options[:profile][:email]
|
66
|
+
|
67
|
+
customer_profile_id = generate_profile_id
|
68
|
+
email = options[:profile][:email]
|
69
|
+
|
70
|
+
# Check for duplicate emails
|
71
|
+
if !get_customer_by_email(email).nil?
|
72
|
+
puts "Found duplicate:"
|
73
|
+
puts get_customer_by_email(email).inspect
|
74
|
+
return ActiveMerchant::Billing::Response.new(false, "duplicate record", {})
|
75
|
+
end
|
76
|
+
|
77
|
+
profile = {
|
78
|
+
"customer_profile_id" => customer_profile_id,
|
79
|
+
"email" => options[:profile][:email],
|
80
|
+
"payment_profiles" => {}
|
81
|
+
}
|
82
|
+
set_customer(profile)
|
83
|
+
message = "Customer profile created."
|
84
|
+
return ActiveMerchant::Billing::Response.new(true, message, profile)
|
85
|
+
end
|
86
|
+
|
87
|
+
# CIM API call
|
88
|
+
def get_customer_profile(options)
|
89
|
+
requires!(options, :customer_profile_id)
|
90
|
+
id = options[:customer_profile_id]
|
91
|
+
# logger.debug "MOCK: get_customer_profile: #{id}"
|
92
|
+
customer = get_customer(options[:customer_profile_id])
|
93
|
+
|
94
|
+
# Authnet returns either a hash (if only one profile) or an array of profiles.
|
95
|
+
# Yeah, I'm not fond of that either.
|
96
|
+
payment_profiles = nil
|
97
|
+
if customer["payment_profiles"].keys.length == 1
|
98
|
+
key = customer["payment_profiles"].keys[0]
|
99
|
+
# logger.debug "MOCK: found single payment profile: #{key}"
|
100
|
+
payment_profiles = mask_cc(customer["payment_profiles"][key])
|
101
|
+
payment_profiles["customer_payment_profile_id"] = key
|
102
|
+
elsif customer["payment_profiles"].keys.length > 1
|
103
|
+
# logger.debug "MOCK: found multiple (#{customer["payment_profiles"].keys.length}) payment profiles"
|
104
|
+
payment_profiles = []
|
105
|
+
customer["payment_profiles"].each do |this_id, profile|
|
106
|
+
profile["customer_payment_profile_id"] = this_id
|
107
|
+
payment_profiles << mask_cc(profile)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
response = {
|
112
|
+
"profile" => {}
|
113
|
+
}
|
114
|
+
if !payment_profiles.nil?
|
115
|
+
response = {
|
116
|
+
"profile" => {
|
117
|
+
"email" => customer["email"],
|
118
|
+
"payment_profiles" => payment_profiles
|
119
|
+
}
|
120
|
+
}
|
121
|
+
end
|
122
|
+
return ActiveMerchant::Billing::Response.new(true, "", response)
|
123
|
+
end
|
124
|
+
|
125
|
+
# CIM API call
|
126
|
+
def update_customer_profile(options)
|
127
|
+
requires!(options, :profile)
|
128
|
+
requires!(options[:profile], :customer_profile_id)
|
129
|
+
customer_id = options[:profile][:customer_profile_id]
|
130
|
+
# logger.debug "MOCK update_customer_profile: #{customer_id}"
|
131
|
+
customer = get_customer(customer_id)
|
132
|
+
if customer.nil?
|
133
|
+
return ActiveMerchant::Billing::Response.new(false, "customer not found", {})
|
134
|
+
end
|
135
|
+
|
136
|
+
# logger.debug "Trying to update customer profile with #{options[:profile]}"
|
137
|
+
customer["email"] = options[:profile][:email]
|
138
|
+
set_customer(customer)
|
139
|
+
return ActiveMerchant::Billing::Response.new(true, "Customer profile updated.", customer)
|
140
|
+
end
|
141
|
+
|
142
|
+
# CIM API call
|
143
|
+
def delete_customer_profile(options)
|
144
|
+
requires!(options, :customer_profile_id)
|
145
|
+
key = "#{CUSTOMER_PREFIX}#{options[:customer_profile_id]}"
|
146
|
+
if redis.exists(key)
|
147
|
+
redis.del(key)
|
148
|
+
return ActiveMerchant::Billing::Response.new(true, "", {})
|
149
|
+
else
|
150
|
+
return ActiveMerchant::Billing::Response.new(false, "notfound", {})
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# CIM API call
|
155
|
+
def create_customer_payment_profile(options)
|
156
|
+
# logger.debug "MOCK: create_customer_payment_profile: #{options.inspect}"
|
157
|
+
requires!(options, :customer_profile_id)
|
158
|
+
requires!(options, :payment_profile)
|
159
|
+
requires!(options[:payment_profile], :payment)
|
160
|
+
|
161
|
+
payment_profile_id = generate_profile_id
|
162
|
+
begin
|
163
|
+
set_payment_profile(options[:customer_profile_id], payment_profile_id, options[:payment_profile])
|
164
|
+
response = {
|
165
|
+
"customer_payment_profile_id" => payment_profile_id
|
166
|
+
}
|
167
|
+
return ActiveMerchant::Billing::Response.new(true, "", response)
|
168
|
+
rescue Exception => e
|
169
|
+
return ActiveMerchant::Billing::Response.new(false,"#{e}", {})
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# CIM API call
|
174
|
+
def update_customer_payment_profile(options)
|
175
|
+
requires!(options, :customer_profile_id, :payment_profile)
|
176
|
+
requires!(options[:payment_profile], :customer_payment_profile_id)
|
177
|
+
profile_id = options[:payment_profile][:customer_payment_profile_id].to_s
|
178
|
+
# logger.debug "MOCK: update_customer_payment_profile: #{profile_id}"
|
179
|
+
payment_profile = options[:payment_profile]
|
180
|
+
begin
|
181
|
+
set_payment_profile(options[:customer_profile_id], profile_id, payment_profile)
|
182
|
+
response = {
|
183
|
+
"customer_payment_profile_id" => profile_id
|
184
|
+
}
|
185
|
+
return ActiveMerchant::Billing::Response.new(true, "", response)
|
186
|
+
rescue Exception => e
|
187
|
+
# logger.error "Exception updating billing profile: #{e}"
|
188
|
+
return ActiveMerchant::Billing::Response.new(false, "#{e}", {})
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# CIM API call
|
193
|
+
def delete_customer_payment_profile(options)
|
194
|
+
requires!(options, :customer_profile_id)
|
195
|
+
requires!(options, :customer_payment_profile_id)
|
196
|
+
payment_profile_id = options[:customer_payment_profile_id].to_s
|
197
|
+
# logger.debug "MOCK: delete customer payment profile #{payment_profile_id}"
|
198
|
+
customer = get_customer(options[:customer_profile_id])
|
199
|
+
if !customer["payment_profiles"].has_key?(payment_profile_id)
|
200
|
+
return ActiveMerchant::Billing::Response.new(false, "Not found", {})
|
201
|
+
end
|
202
|
+
|
203
|
+
customer["payment_profiles"].delete(payment_profile_id)
|
204
|
+
set_customer(customer)
|
205
|
+
return ActiveMerchant::Billing::Response.new(true, "Successful.", {})
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
# CIM API call
|
210
|
+
def create_customer_profile_transaction(options)
|
211
|
+
requires!(options, :transaction)
|
212
|
+
requires!(options[:transaction], :type)
|
213
|
+
case options[:transaction][:type]
|
214
|
+
when :void
|
215
|
+
return void_transaction(options)
|
216
|
+
when :refund
|
217
|
+
return refund_transaction(options)
|
218
|
+
when :prior_auth_capture
|
219
|
+
return prior_auth_capture(options)
|
220
|
+
else
|
221
|
+
return auth_capture(options)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Method for inspecting the mock
|
226
|
+
def get_transaction_by_invoice_number(invoice_number)
|
227
|
+
redis.keys("#{TRANSACTION_PREFIX}*").each do |key|
|
228
|
+
transaction = JSON.parse(redis.get(key))
|
229
|
+
if transaction["invoice_number"] == invoice_number
|
230
|
+
return transaction
|
231
|
+
end
|
232
|
+
end
|
233
|
+
return nil
|
234
|
+
end
|
235
|
+
|
236
|
+
def get_customer_by_email(email)
|
237
|
+
redis.keys("#{CUSTOMER_PREFIX}*").each do |key|
|
238
|
+
customer = JSON.parse(redis.get(key))
|
239
|
+
if customer["email"] == email
|
240
|
+
return customer
|
241
|
+
end
|
242
|
+
end
|
243
|
+
return nil
|
244
|
+
end
|
245
|
+
|
246
|
+
# Method for getting transaction by transaction id. Only used for testing.
|
247
|
+
|
248
|
+
def get_transaction_by_id(transaction_id)
|
249
|
+
return get_transaction(transaction_id)
|
250
|
+
end
|
251
|
+
|
252
|
+
private
|
253
|
+
# Implement ':void' in customer profile transaction
|
254
|
+
def void_transaction(options)
|
255
|
+
requires!(options[:transaction], :trans_id)
|
256
|
+
id = options[:transaction][:trans_id]
|
257
|
+
# logger.debug "MOCK: voiding transaction #{id}"
|
258
|
+
|
259
|
+
# Verify that transaction exists and is not already voided
|
260
|
+
if id == UNVOIDABLE_TRANSACTION_ID
|
261
|
+
return ActiveMerchant::Billing::Response.new(false, "Forced void failure", {})
|
262
|
+
end
|
263
|
+
|
264
|
+
transaction = get_transaction(id)
|
265
|
+
# Verify that transaction exists and is not already voided
|
266
|
+
if transaction.nil?
|
267
|
+
return ActiveMerchant::Billing::Response.new(false, "Transaction not found", {})
|
268
|
+
end
|
269
|
+
|
270
|
+
if transaction["void"] == true
|
271
|
+
return ActiveMerchant::Billing::Response.new(true, "This transaction has already been voided", {})
|
272
|
+
end
|
273
|
+
|
274
|
+
transaction["void"] = true
|
275
|
+
return ActiveMerchant::Billing::Response.new(true, "This transaction has been voided.", {})
|
276
|
+
end
|
277
|
+
|
278
|
+
# Handle ':refund' in customer profile transaction (not used in our unit tests)
|
279
|
+
def refund_transaction(options)
|
280
|
+
requires!(options[:transaction], :trans_id) && (
|
281
|
+
(options[:transaction][:customer_profile_id] && options[:transaction][:customer_payment_profile_id]) ||
|
282
|
+
options[:transaction][:credit_card_number_masked] ||
|
283
|
+
(options[:transaction][:bank_routing_number_masked] && options[:transaction][:bank_account_number_masked])
|
284
|
+
)
|
285
|
+
# We don't use this in unit tests, because you can only refund after settlement
|
286
|
+
raise "Refunds not implemented"
|
287
|
+
end
|
288
|
+
|
289
|
+
# Handle ':prior_auth_capture' in customer profile transaction (not used in our unit tests)
|
290
|
+
def prior_auth_capture(options)
|
291
|
+
# We don't use this, sorry
|
292
|
+
raise "Prior auth capture not implemented"
|
293
|
+
end
|
294
|
+
|
295
|
+
# Handle ':auth_capture' in customer profile transaction i.e. actually charge user
|
296
|
+
def auth_capture(options)
|
297
|
+
requires!(options[:transaction], :amount, :customer_profile_id, :customer_payment_profile_id)
|
298
|
+
customer_profile_id = options[:transaction][:customer_profile_id]
|
299
|
+
payment_profile_id = options[:transaction][:customer_payment_profile_id].to_s
|
300
|
+
amount = options[:transaction][:amount]
|
301
|
+
if options[:transaction][:order] != nil
|
302
|
+
invoice_number = options[:transaction][:order][:invoice_number]
|
303
|
+
purchase_order_number = options[:transaction][:order][:purchase_order_number]
|
304
|
+
else
|
305
|
+
invoice_number = ''
|
306
|
+
purchase_order_number = ''
|
307
|
+
end
|
308
|
+
|
309
|
+
# logger.debug "MOCK auth_capture transaction for $#{amount}"
|
310
|
+
|
311
|
+
# Check that customer exists
|
312
|
+
customer = get_customer(customer_profile_id)
|
313
|
+
if customer.nil?
|
314
|
+
return ActiveMerchant::Billing::Response.new(false, "Customer not found", {})
|
315
|
+
end
|
316
|
+
|
317
|
+
# Check that billing profile exists
|
318
|
+
payment_profile = customer["payment_profiles"][payment_profile_id]
|
319
|
+
if payment_profile.nil?
|
320
|
+
return ActiveMerchant::Billing::Response.new(false, "Payment profile not found #{payment_profile_id}", {})
|
321
|
+
end
|
322
|
+
|
323
|
+
if payment_profile["payment"]["credit_card"]["card_number"] == FAILURE_CREDIT_CARD_NUMBER
|
324
|
+
# logger.debug "MOCK: failure cc number detected: returning error"
|
325
|
+
return ActiveMerchant::Billing::Response.new(false, "Forced test failure", {})
|
326
|
+
end
|
327
|
+
|
328
|
+
|
329
|
+
# Save transaction
|
330
|
+
transaction_id = generate_profile_id
|
331
|
+
approval_code = generate_profile_id
|
332
|
+
transaction = {
|
333
|
+
"id" => transaction_id,
|
334
|
+
"amount" => amount,
|
335
|
+
"customer_profile_id" => customer_profile_id,
|
336
|
+
"customer_payment_profile_id" => payment_profile_id,
|
337
|
+
"invoice_number" => invoice_number,
|
338
|
+
"purchase_order_number" => purchase_order_number,
|
339
|
+
"void" => false,
|
340
|
+
"approval_code" => approval_code
|
341
|
+
}
|
342
|
+
set_transaction(transaction_id, transaction)
|
343
|
+
|
344
|
+
if payment_profile["payment"]["credit_card"]["card_number"] == EXCEPTION_CREDIT_CARD_NUMBER
|
345
|
+
# logger.debug "MOCK: exception cc number detected: throwing exception"
|
346
|
+
raise Exception.new("FORCED EXCEPTION IN AUTH NET MOCK")
|
347
|
+
end
|
348
|
+
|
349
|
+
params = {
|
350
|
+
"direct_response" => {
|
351
|
+
"transaction_id" => transaction_id,
|
352
|
+
"approval_code" => approval_code,
|
353
|
+
"raw" => "Andy wuz ere"
|
354
|
+
}
|
355
|
+
}
|
356
|
+
|
357
|
+
return ActiveMerchant::Billing::Response.new(true, "", params)
|
358
|
+
end
|
359
|
+
|
360
|
+
# Helper method for finding customers in fake gateway store
|
361
|
+
def get_customer(customer_id)
|
362
|
+
customer = redis.get("#{CUSTOMER_PREFIX}#{customer_id}")
|
363
|
+
if !customer.nil?
|
364
|
+
customer = JSON.parse(customer)
|
365
|
+
end
|
366
|
+
return customer
|
367
|
+
end
|
368
|
+
|
369
|
+
def set_customer(customer)
|
370
|
+
redis.set("#{CUSTOMER_PREFIX}#{customer["customer_profile_id"]}", customer.to_json)
|
371
|
+
end
|
372
|
+
|
373
|
+
def get_transaction(transaction_id)
|
374
|
+
transaction = redis.get("#{TRANSACTION_PREFIX}#{transaction_id}")
|
375
|
+
if !transaction.nil?
|
376
|
+
transaction = JSON.parse(transaction)
|
377
|
+
end
|
378
|
+
return transaction
|
379
|
+
end
|
380
|
+
|
381
|
+
def set_transaction(transaction_id, transaction)
|
382
|
+
redis.set("#{TRANSACTION_PREFIX}#{transaction_id}", transaction.to_json)
|
383
|
+
end
|
384
|
+
|
385
|
+
def find_transaction_by_invoice_number(invoice_number)
|
386
|
+
end
|
387
|
+
|
388
|
+
# Helper method for storing payment profiles
|
389
|
+
def set_payment_profile(customer_id, payment_profile_id, payment_profile)
|
390
|
+
customer = get_customer(customer_id)
|
391
|
+
payment_profile = payment_profile.with_indifferent_access
|
392
|
+
# logger.debug "Setting payment profile #{payment_profile_id}: #{payment_profile.inspect}"
|
393
|
+
if customer["payment_profiles"].has_key?(payment_profile_id)
|
394
|
+
customer["payment_profiles"][payment_profile_id] = update_payment_profile(
|
395
|
+
customer["payment_profiles"][payment_profile_id],
|
396
|
+
payment_profile)
|
397
|
+
else
|
398
|
+
|
399
|
+
customer["payment_profiles"].each do |payment_id|
|
400
|
+
if payment_id != nil && payment_id[1]["payment"]["credit_card"]["card_number"] == payment_profile["payment"]["credit_card"].number
|
401
|
+
raise "A duplicate customer payment profile already exists."
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
if payment_profile.has_key?("payment")
|
406
|
+
payment_profile["payment"]["credit_card"] = cc_to_hash(payment_profile["payment"]["credit_card"])
|
407
|
+
end
|
408
|
+
customer["payment_profiles"][payment_profile_id] = payment_profile
|
409
|
+
end
|
410
|
+
set_customer(customer)
|
411
|
+
end
|
412
|
+
|
413
|
+
# Helper method for selectively updating payment profiles
|
414
|
+
def update_payment_profile(existing_profile, new_profile)
|
415
|
+
profile = {}
|
416
|
+
if new_profile.has_key?("bill_to")
|
417
|
+
profile["bill_to"] = new_profile["bill_to"]
|
418
|
+
elsif existing_profile.has_key?("bill_to")
|
419
|
+
profile["bill_to"] = existing_profile["bill_to"]
|
420
|
+
end
|
421
|
+
|
422
|
+
# If the user included the credit card, we'll set it
|
423
|
+
if new_profile.has_key?("payment")
|
424
|
+
new_profile["payment"]["credit_card"] = cc_to_hash(new_profile["payment"]["credit_card"])
|
425
|
+
profile["payment"] = new_profile["payment"]
|
426
|
+
new_number = new_profile["payment"]["credit_card"]["card_number"]
|
427
|
+
existing_number = existing_profile["payment"]["credit_card"]["card_number"]
|
428
|
+
# But if the user send a masked credit card, we'll check it's correct, then keep the existing one.
|
429
|
+
if new_number[0] == "X"
|
430
|
+
# logger.debug "Found blanked CC"
|
431
|
+
if existing_number[-4..-1] != new_number[-4..-1]
|
432
|
+
raise "does not match the original value"
|
433
|
+
end
|
434
|
+
profile["payment"]["credit_card"]["card_number"] = existing_number
|
435
|
+
end
|
436
|
+
elsif existing_profile.has_key?("payment")
|
437
|
+
profile["payment"] = existing_profile["payment"]
|
438
|
+
end
|
439
|
+
return profile
|
440
|
+
end
|
441
|
+
|
442
|
+
# Convert a ActiveMervchant credit card object to a hash (which is what we store and return)
|
443
|
+
def cc_to_hash(credit_card)
|
444
|
+
if credit_card.is_a?(Hash)
|
445
|
+
return credit_card
|
446
|
+
end
|
447
|
+
if credit_card.nil?
|
448
|
+
return nil
|
449
|
+
end
|
450
|
+
# logger.debug "Converting CC to hash: #{credit_card.inspect}"
|
451
|
+
hash = {
|
452
|
+
"card_number" => credit_card.number,
|
453
|
+
"expiration_date" => credit_card.year.to_s + credit_card.month.to_s
|
454
|
+
}
|
455
|
+
return hash
|
456
|
+
end
|
457
|
+
|
458
|
+
# When returning the credit card, we mask the number and expiration
|
459
|
+
def mask_cc(payment_profile)
|
460
|
+
payment_profile = payment_profile.with_indifferent_access
|
461
|
+
masked = payment_profile.deep_dup
|
462
|
+
if masked["payment"].nil?
|
463
|
+
return masked
|
464
|
+
end
|
465
|
+
cc = cc_to_hash(masked["payment"]["credit_card"])
|
466
|
+
if !cc.nil?
|
467
|
+
masked["payment"]["credit_card"] = {
|
468
|
+
"card_number" => "XXXX" + cc["card_number"][-4..-1],
|
469
|
+
"expiration_date" => "XXXX" }
|
470
|
+
end
|
471
|
+
return masked
|
472
|
+
end
|
473
|
+
|
474
|
+
# Generate realistic-looking auth.net customer profile ids
|
475
|
+
def generate_profile_id(length=9)
|
476
|
+
num = SecureRandom.random_number(10**length - 1)
|
477
|
+
return (num + 1).to_s
|
478
|
+
end
|
479
|
+
|
480
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'Mockerize'
|
3
|
+
|
4
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
5
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
6
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
7
|
+
# this file to always be loaded, without a need to explicitly require it in any
|
8
|
+
# files.
|
9
|
+
#
|
10
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
11
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
12
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
13
|
+
# individual file that may not need all of that loaded. Instead, consider making
|
14
|
+
# a separate helper file that requires the additional dependencies and performs
|
15
|
+
# the additional setup, and require it from the spec files that actually need
|
16
|
+
# it.
|
17
|
+
#
|
18
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
19
|
+
# users commonly want.
|
20
|
+
#
|
21
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
22
|
+
RSpec.configure do |config|
|
23
|
+
|
24
|
+
config.before(:each) do
|
25
|
+
# Wipe the auth.net mock data to avoid O(N) slowdown looking for existing email addresses
|
26
|
+
::AUTHNET_GATEWAY.reset if $using_mock_auth_net_gateway
|
27
|
+
end
|
28
|
+
|
29
|
+
# rspec-expectations config goes here. You can use an alternate
|
30
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
31
|
+
# assertions if you prefer.
|
32
|
+
config.expect_with :rspec do |expectations|
|
33
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
34
|
+
# and `failure_message` of custom matchers include text for helper methods
|
35
|
+
# defined using `chain`, e.g.:
|
36
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
37
|
+
# # => "be bigger than 2 and smaller than 4"
|
38
|
+
# ...rather than:
|
39
|
+
# # => "be bigger than 2"
|
40
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
41
|
+
end
|
42
|
+
|
43
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
44
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
45
|
+
config.mock_with :rspec do |mocks|
|
46
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
47
|
+
# a real object. This is generally recommended, and will default to
|
48
|
+
# `true` in RSpec 4.
|
49
|
+
mocks.verify_partial_doubles = true
|
50
|
+
end
|
51
|
+
|
52
|
+
# The settings below are suggested to provide a good initial experience
|
53
|
+
# with RSpec, but feel free to customize to your heart's content.
|
54
|
+
=begin
|
55
|
+
# These two settings work together to allow you to limit a spec run
|
56
|
+
# to individual examples or groups you care about by tagging them with
|
57
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
58
|
+
# get run.
|
59
|
+
config.filter_run :focus
|
60
|
+
config.run_all_when_everything_filtered = true
|
61
|
+
|
62
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
63
|
+
# recommended. For more details, see:
|
64
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
65
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
66
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
67
|
+
config.disable_monkey_patching!
|
68
|
+
|
69
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
70
|
+
# be too noisy due to issues in dependencies.
|
71
|
+
config.warnings = true
|
72
|
+
|
73
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
74
|
+
# file, and it's useful to allow more verbose output when running an
|
75
|
+
# individual spec file.
|
76
|
+
if config.files_to_run.one?
|
77
|
+
# Use the documentation formatter for detailed output,
|
78
|
+
# unless a formatter has already been configured
|
79
|
+
# (e.g. via a command-line flag).
|
80
|
+
config.default_formatter = 'doc'
|
81
|
+
end
|
82
|
+
|
83
|
+
# Print the 10 slowest examples and example groups at the
|
84
|
+
# end of the spec run, to help surface which specs are running
|
85
|
+
# particularly slow.
|
86
|
+
config.profile_examples = 10
|
87
|
+
|
88
|
+
# Run specs in random order to surface order dependencies. If you find an
|
89
|
+
# order dependency and want to debug it, you can fix the order by providing
|
90
|
+
# the seed, which is printed after each run.
|
91
|
+
# --seed 1234
|
92
|
+
config.order = :random
|
93
|
+
|
94
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
95
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
96
|
+
# test failures related to randomization by passing the same `--seed` value
|
97
|
+
# as the one that triggered the failure.
|
98
|
+
Kernel.srand config.seed
|
99
|
+
=end
|
100
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: Mockerize
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Miguel Alonso Jr
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activemerchant
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: redis
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: shoulda-matchers
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Mockerize is a mock authorize.net customer information management (CIM)
|
98
|
+
class for Rails. It is based on the active-merchant (http://activemerchant.org)
|
99
|
+
authorize.net CIM gateway. It use Redis as a data store to simulate the authorize.net
|
100
|
+
CIM service.
|
101
|
+
email:
|
102
|
+
- drmiguelalonsojr@gmail.com
|
103
|
+
executables: []
|
104
|
+
extensions: []
|
105
|
+
extra_rdoc_files: []
|
106
|
+
files:
|
107
|
+
- ".gitignore"
|
108
|
+
- ".ruby-gemset"
|
109
|
+
- ".ruby-version"
|
110
|
+
- Gemfile
|
111
|
+
- LICENSE
|
112
|
+
- LICENSE.txt
|
113
|
+
- Mockerize.gemspec
|
114
|
+
- README.md
|
115
|
+
- Rakefile
|
116
|
+
- init.rb
|
117
|
+
- lib/Mockerize.rb
|
118
|
+
- lib/Mockerize/mock_authorize_net_cim_gateway.rb
|
119
|
+
- lib/Mockerize/version.rb
|
120
|
+
- spec/mock_authorize_net_cim_gateway_spec.rb
|
121
|
+
- spec/spec_helper.rb
|
122
|
+
homepage: ''
|
123
|
+
licenses:
|
124
|
+
- MIT
|
125
|
+
metadata: {}
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
129
|
+
- lib
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
requirements: []
|
141
|
+
rubyforge_project:
|
142
|
+
rubygems_version: 2.4.5
|
143
|
+
signing_key:
|
144
|
+
specification_version: 4
|
145
|
+
summary: Mockerize is a mock authorize.net customer information management (CIM) class
|
146
|
+
for Rails.
|
147
|
+
test_files:
|
148
|
+
- spec/mock_authorize_net_cim_gateway_spec.rb
|
149
|
+
- spec/spec_helper.rb
|