nmi_direct_post 0.2.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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.simplecov +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +83 -0
- data/Rakefile +1 -0
- data/lib/nmi_direct_post.rb +7 -0
- data/lib/nmi_direct_post/base.rb +102 -0
- data/lib/nmi_direct_post/customer_vault.rb +245 -0
- data/lib/nmi_direct_post/logger.rb +17 -0
- data/lib/nmi_direct_post/transaction.rb +168 -0
- data/lib/nmi_direct_post/version.rb +3 -0
- data/nmi_direct_post.gemspec +30 -0
- data/spec/base_spec.rb +37 -0
- data/spec/customer_vault_spec.rb +243 -0
- data/spec/logger_spec.rb +77 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/credentials.rb.example +13 -0
- data/spec/support/test_credentials.rb +11 -0
- data/spec/transaction_spec.rb +303 -0
- metadata +217 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d376bc5d9401a3274444f713f3ef40f04a4a41e3
|
4
|
+
data.tar.gz: 383d6fb624a33d30f1121904b76c5b77eb0af761
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d40ddf52eceb8174336a6b9675d8a3a0e7161135cbdaed6effd7c1a361c54ebcb84432e08391a9b8829ffed9dd366401123cd750ecbe488e5b9cccf13b77e8a4
|
7
|
+
data.tar.gz: 9ac20fafa098b00e4069d05b994f0d15175033cd6b7408c617c10cd32e0318f1c6984039db20d95606fdb7375803c5413bef446914629ef29d74120cd3a53229
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.simplecov
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Isaac Betesh
|
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/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# NmiDirectPost
|
2
|
+
|
3
|
+
NmiDirectPost is a gem that encapsulates the NMI Direct Post API in an ActiveRecord-like syntax.
|
4
|
+
For more information on the NMI Direct Post API, see:
|
5
|
+
https://secure.nmi.com/merchants/resources/integration/integration_portal.php
|
6
|
+
|
7
|
+
To mimic ActivRecord syntax, it is necessary to blur, from the client's standpoint, the boundary between NMI's Direct Post API and its Query API. This fuzziness is part of the encapsulation.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'nmi_direct_post'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install nmi_direct_post
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
1) Before you can query or post, establish the connection:
|
26
|
+
|
27
|
+
NmiDirectPost::Base.establish_connection("MY_NMI_USERNAME", "MY_NMI_PASSWORD")
|
28
|
+
|
29
|
+
Theoretically, you can use a different connection for NmiDirectPost::Transaction or NmiDirectPost::CustomerVault by calling establish_connection on either of those derived classes, instead of on Base.
|
30
|
+
However, it's hard to imagine a case where this would be useful; the option is only present to mimic the syntax of ActiveRecord.
|
31
|
+
|
32
|
+
2) Query the API:
|
33
|
+
|
34
|
+
NmiDirectPost::Transaction.find_by_transaction_id(123456789)
|
35
|
+
NmiDirectPost::CustomerVault.find_by_customer_vault_id(123123123)
|
36
|
+
|
37
|
+
3) Create a CustomerVault:
|
38
|
+
|
39
|
+
george = NmiDirectPostCustomerVault.new(:first_name => 'George', :last_name => 'Washington', :cc_number => '4111111111111111', :cc_exp => '03/17')
|
40
|
+
george.create
|
41
|
+
|
42
|
+
4) Update a CustomerVault:
|
43
|
+
|
44
|
+
george.update!(:email => 'el_primero_presidente@whitehouse.gov', :address_1 => '1600 Pennsylvania Ave NW', :city => 'Washington', :state => 'DC', :postal_code => '20500')
|
45
|
+
|
46
|
+
ALTERNATIVELY:
|
47
|
+
|
48
|
+
george.email = 'el_primero_presidente@whitehouse.gov'
|
49
|
+
george.address_1 = '1600 Pennsylvania Ave NW'
|
50
|
+
george.city = 'Washington'
|
51
|
+
george.state = 'DC'
|
52
|
+
george.postal_code = '20500'
|
53
|
+
george.save! # Returns true
|
54
|
+
|
55
|
+
5) Delete a CustomerVault:
|
56
|
+
|
57
|
+
george.destroy # Returns the CustomerVault
|
58
|
+
|
59
|
+
6) Reload a CustomerVault:
|
60
|
+
|
61
|
+
george.email = 'el_primero_presidente@whitehouse.gov'
|
62
|
+
george.reload # Returns the Customer Vault
|
63
|
+
george.email # Returns the previously set email
|
64
|
+
|
65
|
+
7) CustomerVault class methods:
|
66
|
+
|
67
|
+
NmiDirectPost::CustomerVault.all_ids # Returns array of `customer_vault_id`s
|
68
|
+
NmiDirectPost::CustomerVault.first
|
69
|
+
NmiDirectPost::CustomerVault.last
|
70
|
+
NmiDirectPost::CustomerVault.all # Returns very, very big array. This method had very poor performance and could be optimized significantly in a future version of this gem.
|
71
|
+
|
72
|
+
8) Create a Transaction:
|
73
|
+
|
74
|
+
parking_ticket = NmiDirectPost::Transaction(:type => :sale, :amount => 150.01, :customer_vault_id => george.customer_vault_id)
|
75
|
+
parking_ticket.save! # Returns true
|
76
|
+
|
77
|
+
## Contributing
|
78
|
+
|
79
|
+
1. Fork it
|
80
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
81
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
82
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
83
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
require 'active_support/concern'
|
5
|
+
require 'active_support/callbacks'
|
6
|
+
require 'active_model/conversion'
|
7
|
+
require 'active_model/validator'
|
8
|
+
require 'active_model/callbacks'
|
9
|
+
require 'active_support/core_ext/module/delegation'
|
10
|
+
require 'active_model/naming'
|
11
|
+
require 'active_model/translation'
|
12
|
+
require 'active_model/validations'
|
13
|
+
require 'active_model/errors'
|
14
|
+
require 'active_support/core_ext/object/blank'
|
15
|
+
require 'addressable/uri'
|
16
|
+
require_relative 'logger'
|
17
|
+
|
18
|
+
module NmiDirectPost
|
19
|
+
class Base
|
20
|
+
POST_URI = "https://secure.nmi.com/api/transact.php"
|
21
|
+
GET_URI = "https://secure.nmi.com/api/query.php"
|
22
|
+
|
23
|
+
AUTH_PARAMS = [:username, :password]
|
24
|
+
attr_reader *AUTH_PARAMS
|
25
|
+
attr_reader :response, :response_text, :response_code
|
26
|
+
|
27
|
+
include ActiveModel::Validations
|
28
|
+
include ActiveModel::Conversion
|
29
|
+
validates_presence_of :username, :password
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@username, @password = self.class.username, self.class.password
|
33
|
+
end
|
34
|
+
|
35
|
+
def persisted?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
def success?
|
40
|
+
1 == self.response
|
41
|
+
end
|
42
|
+
|
43
|
+
def logger
|
44
|
+
NmiDirectPost.logger
|
45
|
+
end
|
46
|
+
|
47
|
+
class << self
|
48
|
+
NO_CONNECTION = "Please set a username by calling NmiDirectPost::Base.establish_connection(ENV['NMI_USERNAME'], ENV['NMI_PASSWORD'])"
|
49
|
+
def establish_connection(username, password)
|
50
|
+
@username, @password = username, password
|
51
|
+
end
|
52
|
+
|
53
|
+
def username
|
54
|
+
@username || (in_base? ? raise_no_connection_error : superclass.username).tap { |_| raise_no_connection_error if _.blank? }
|
55
|
+
end
|
56
|
+
|
57
|
+
def password
|
58
|
+
@password || (in_base? ? raise_no_connection_error : superclass.password).tap { |_| raise_no_connection_error if _.blank? }
|
59
|
+
end
|
60
|
+
|
61
|
+
def generate_query_string(attributes, target = self)
|
62
|
+
((attributes.reject { |attr| target.__send__(attr).blank? }).collect { |attr| "#{attr}=#{Addressable::URI.escape(target.__send__(attr).to_s)}"}).join('&')
|
63
|
+
end
|
64
|
+
|
65
|
+
def get(query)
|
66
|
+
uri = [GET_URI, query].join('?')
|
67
|
+
data = get_http_response(uri).body
|
68
|
+
Hash.from_xml(data)["nm_response"]
|
69
|
+
end
|
70
|
+
|
71
|
+
def post(query)
|
72
|
+
uri = [POST_URI, query].join('?')
|
73
|
+
data = get_http_response(uri)
|
74
|
+
Addressable::URI.parse([POST_URI, data.body].join('?')).query_values
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
def get_http_response(uri)
|
79
|
+
request = Net::HTTP::Get.new(uri)
|
80
|
+
url = URI.parse(uri)
|
81
|
+
http = Net::HTTP.new(url.host, url.port)
|
82
|
+
http.use_ssl = true
|
83
|
+
http.ssl_version = :TLSv1
|
84
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
85
|
+
http.request(request)
|
86
|
+
end
|
87
|
+
|
88
|
+
def in_base?
|
89
|
+
'Object' == superclass.name
|
90
|
+
end
|
91
|
+
|
92
|
+
def raise_no_connection_error
|
93
|
+
raise(StandardError, NO_CONNECTION)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
def generate_query_string(attributes)
|
99
|
+
self.class.generate_query_string(attributes, self)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
require 'active_model/serialization'
|
2
|
+
require 'active_model/serializers/xml'
|
3
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
4
|
+
require_relative 'base'
|
5
|
+
|
6
|
+
module NmiDirectPost
|
7
|
+
class CustomerVaultNotFoundError < StandardError; end
|
8
|
+
|
9
|
+
class CustomerVaultInvalidPostActionError < StandardError; end
|
10
|
+
|
11
|
+
module MassAssignmentSecurity
|
12
|
+
class Error < StandardError; end
|
13
|
+
end
|
14
|
+
|
15
|
+
class CustomerVault < Base
|
16
|
+
private
|
17
|
+
def self.attr_accessor_with_tracking_of_changes(*list)
|
18
|
+
list.each do |attr|
|
19
|
+
attr_reader attr
|
20
|
+
define_method("#{attr}=") do |val|
|
21
|
+
(@attributes_to_save ||=[]) << attr
|
22
|
+
instance_variable_set("@#{attr}", val)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
public
|
27
|
+
READ_ONLY_ATTRIBUTES ||= [:check_hash, :cc_hash]
|
28
|
+
attr_reader *READ_ONLY_ATTRIBUTES
|
29
|
+
attr_reader :customer_vault_id, :customer_vault, :report_type
|
30
|
+
|
31
|
+
MERCHANT_DEFINED_FIELDS ||= 20.times.collect { |i| :"merchant_defined_field_#{i+1}" }
|
32
|
+
WHITELIST_ATTRIBUTES ||= [:id, :first_name, :last_name, :address_1, :address_2, :company, :city, :state, :postal_code, :country, :email, :phone, :fax, :cell_phone, :customertaxid, :website, :shipping_first_name, :shipping_last_name, :shipping_address_1, :shipping_address_2, :shipping_company, :shipping_city, :shipping_state, :shipping_postal_code, :shipping_country, :shipping_email, :shipping_carrier, :tracking_number, :shipping_date, :shipping, :cc_number, :cc_exp, :cc_issue_number, :check_account, :check_aba, :check_name, :account_holder_type, :account_type, :sec_code, :processor_id, :cc_bin, :cc_start_date] + MERCHANT_DEFINED_FIELDS
|
33
|
+
attr_accessor_with_tracking_of_changes *WHITELIST_ATTRIBUTES
|
34
|
+
|
35
|
+
validate :billing_information_present?, :if => Proc.new { |record| :add_customer == record.customer_vault }
|
36
|
+
validates_presence_of :customer_vault_id, :message => "You must specify a %{attribute} ID when looking up an individual customer vault", :if => Proc.new { |record| :customer_vault == record.report_type }
|
37
|
+
validates_presence_of :customer_vault_id, :message => "You must specify a %{attribute} ID when updating a customer vault", :if => Proc.new { |record| :update_customer == record.customer_vault }
|
38
|
+
validates_inclusion_of :customer_vault_id, :in => [nil], :message => "You cannot specify a %{attribute} ID when creating a new customer vault. NMI will assign one upon creating the record",
|
39
|
+
:if => Proc.new { |record| :add_customer == record.customer_vault }
|
40
|
+
|
41
|
+
def initialize(attributes)
|
42
|
+
super()
|
43
|
+
if attributes[:customer_vault_id].blank?
|
44
|
+
set_attributes(attributes.dup) unless attributes.empty?
|
45
|
+
else
|
46
|
+
@customer_vault_id = attributes[:customer_vault_id].to_i
|
47
|
+
reload
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def create
|
52
|
+
post_action(:add)
|
53
|
+
self.success?
|
54
|
+
end
|
55
|
+
|
56
|
+
def update!(attributes)
|
57
|
+
begin
|
58
|
+
set_attributes(attributes)
|
59
|
+
post_action(:update)
|
60
|
+
ensure
|
61
|
+
@attributes_to_save.delete_if {|v| @attributes_to_update.include?(v) } if @attributes_to_save
|
62
|
+
@attributes_to_update = nil
|
63
|
+
end
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def save!
|
68
|
+
post_action(:update)
|
69
|
+
reload
|
70
|
+
self.success?
|
71
|
+
end
|
72
|
+
|
73
|
+
def destroy
|
74
|
+
post_action(:delete)
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def reload
|
79
|
+
@report_type = :customer_vault
|
80
|
+
if invalid?
|
81
|
+
@report_type = nil
|
82
|
+
return self
|
83
|
+
end
|
84
|
+
begin
|
85
|
+
safe_params = customer_vault_instance_params
|
86
|
+
logger.debug { "Loading NMI customer vault from customer_vault_id(#{customer_vault_id}) using query: #{safe_params}" }
|
87
|
+
response = self.class.get(self.class.all_params(safe_params))["customer_vault"]
|
88
|
+
raise CustomerVaultNotFoundError, "No record found for customer vault ID #{self.customer_vault_id}" if response.nil?
|
89
|
+
attributes = response["customer"].with_indifferent_access
|
90
|
+
READ_ONLY_ATTRIBUTES.each do |a|
|
91
|
+
if attributes.key?(a)
|
92
|
+
val = attributes.delete(a)
|
93
|
+
instance_variable_set("@#{a}",val)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
set_attributes(attributes.tap { |_| _.delete(:customer_vault_id) })
|
97
|
+
ensure
|
98
|
+
@report_type = nil
|
99
|
+
@attributes_to_update = nil
|
100
|
+
@attributes_to_save = nil
|
101
|
+
end
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def credit_card?
|
106
|
+
!@cc_hash.blank?
|
107
|
+
end
|
108
|
+
|
109
|
+
def checking?
|
110
|
+
!@check_hash.blank?
|
111
|
+
end
|
112
|
+
|
113
|
+
def find!
|
114
|
+
begin
|
115
|
+
@report_type = :customer_vault
|
116
|
+
safe_params = generate_query_string(MERCHANT_DEFINED_FIELDS + [:last_name, :email, :report_type]) # These are the only fields you can use when looking up without a customer_vault_id
|
117
|
+
logger.info { "Querying NMI customer vault: #{safe_params}" }
|
118
|
+
@customer_vault_id = self.class.get(self.class.all_params(safe_params))['customer_vault'][0]['customer_vault_id'] # This assumes there is only 1 result.
|
119
|
+
# TODO: When there are multiple results, we don't know which one you want. Maybe raise an error in that case?
|
120
|
+
reload
|
121
|
+
ensure
|
122
|
+
@report_type = nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class << self
|
127
|
+
attr_reader :report_type
|
128
|
+
|
129
|
+
def find_by_customer_vault_id(customer_vault_id)
|
130
|
+
raise StandardError, "CustomerVaultID cannot be blank" if customer_vault_id.blank?
|
131
|
+
begin
|
132
|
+
new(:customer_vault_id => customer_vault_id)
|
133
|
+
rescue CustomerVaultNotFoundError
|
134
|
+
return nil
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def first(limit = 1)
|
139
|
+
limit(0, limit-1).first
|
140
|
+
end
|
141
|
+
|
142
|
+
def last(limit = 1)
|
143
|
+
limit(-limit, -1).first
|
144
|
+
end
|
145
|
+
|
146
|
+
def all_ids
|
147
|
+
@report_type = :customer_vault
|
148
|
+
safe_params = generate_query_string([:report_type])
|
149
|
+
NmiDirectPost.logger.debug { "Loading all NMI customer vaults using query: #{safe_params}" }
|
150
|
+
begin
|
151
|
+
customers = get(all_params(safe_params))["customer_vault"]
|
152
|
+
ensure
|
153
|
+
@report_type = nil
|
154
|
+
end
|
155
|
+
return [] if customers.nil?
|
156
|
+
customers = customers["customer"]
|
157
|
+
customers.collect { |customer| customer["customer_vault_id"].to_i }
|
158
|
+
end
|
159
|
+
|
160
|
+
def all
|
161
|
+
limit
|
162
|
+
end
|
163
|
+
|
164
|
+
def all_params(safe_params)
|
165
|
+
[safe_params, generate_query_string(Base::AUTH_PARAMS)].join('&')
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
def limit(first = 0, last = -1)
|
170
|
+
all_ids[first..last].collect { |id| new(:customer_vault_id => id) }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
def customer_vault_instance_params
|
176
|
+
generate_query_string([:customer_vault, :customer_vault_id, :report_type])
|
177
|
+
end
|
178
|
+
|
179
|
+
def post(safe_params)
|
180
|
+
logger.info { "Sending Direct Post to NMI: #{safe_params}" }
|
181
|
+
response = self.class.post(self.class.all_params(safe_params))
|
182
|
+
@response, @response_text, @response_code = response["response"].to_i, response["responsetext"], response["response_code"].to_i
|
183
|
+
@customer_vault_id = response["customer_vault_id"].to_i if :add_customer == self.customer_vault
|
184
|
+
end
|
185
|
+
|
186
|
+
def set_attributes(attributes)
|
187
|
+
attributes = attributes.with_indifferent_access
|
188
|
+
@attributes_to_update = []
|
189
|
+
merchant_defined_fields = []
|
190
|
+
if attributes.key?(:merchant_defined_field) && attributes[:merchant_defined_field].is_a?(String)
|
191
|
+
self.merchant_defined_field_1 = attributes.delete(:merchant_defined_field)
|
192
|
+
end
|
193
|
+
WHITELIST_ATTRIBUTES.each do |a|
|
194
|
+
if attributes.key?(a)
|
195
|
+
val = attributes.delete(a)
|
196
|
+
@attributes_to_update << a
|
197
|
+
end
|
198
|
+
merchant_defined_field_index = a.to_s.split('merchant_defined_field_')[1]
|
199
|
+
if (!merchant_defined_field_index.nil? && val.nil? && attributes.key?(:merchant_defined_field) && attributes[:merchant_defined_field].is_a?(Array))
|
200
|
+
index = merchant_defined_field_index.to_i - 1
|
201
|
+
if attributes[:merchant_defined_field].size > index
|
202
|
+
val = attributes[:merchant_defined_field][index]
|
203
|
+
attributes[:merchant_defined_field][index] = nil
|
204
|
+
@attributes_to_update << a
|
205
|
+
end
|
206
|
+
end
|
207
|
+
self.__send__("#{a}=", val) if @attributes_to_update.include?(a)
|
208
|
+
end
|
209
|
+
attributes.delete(:merchant_defined_field) unless attributes.key?(:merchant_defined_field) && attributes[:merchant_defined_field].any?
|
210
|
+
@id = @id.to_i if @id
|
211
|
+
raise MassAssignmentSecurity::Error, "Cannot mass-assign the following attributes: #{attributes.keys.join(", ")}" unless attributes.empty?
|
212
|
+
end
|
213
|
+
|
214
|
+
def billing_information_present?
|
215
|
+
self.errors.add(:billing_information, "Either :cc_number (a credit card number) and :cc_exp (the credit card expiration date), or :check_account, :check_aba (the routing number of the checking account) and :check_name (a nickname for the account), must be present") if (missing_cc_information? && missing_checking_information?)
|
216
|
+
end
|
217
|
+
|
218
|
+
def missing_checking_information?
|
219
|
+
self.check_account.blank? || self.check_aba.blank? || self.check_name.blank?
|
220
|
+
end
|
221
|
+
|
222
|
+
def missing_cc_information?
|
223
|
+
self.cc_exp.blank? || self.cc_number.blank?
|
224
|
+
end
|
225
|
+
|
226
|
+
def post_action(action)
|
227
|
+
@customer_vault = :"#{action}_customer"
|
228
|
+
safe_params = case action.to_sym
|
229
|
+
when :delete
|
230
|
+
customer_vault_instance_params
|
231
|
+
when :add
|
232
|
+
[customer_vault_instance_params, generate_query_string(WHITELIST_ATTRIBUTES)].join("&")
|
233
|
+
when :update
|
234
|
+
[customer_vault_instance_params, generate_query_string(@attributes_to_update || @attributes_to_save)].join("&")
|
235
|
+
else
|
236
|
+
raise CustomerVaultInvalidPostActionError, "#{action} is not a valid post action. NmiDirectPost allows the following post actions: :add, :update, :delete"
|
237
|
+
end
|
238
|
+
begin
|
239
|
+
post(safe_params) if valid?
|
240
|
+
ensure
|
241
|
+
@customer_vault = nil
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|