figpay_gateway 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.
- checksums.yaml +7 -0
- data/.env.sample +10 -0
- data/.github/workflows/ci.yml +48 -0
- data/.gitignore +27 -0
- data/.tool-versions +2 -0
- data/Gemfile +4 -0
- data/Guardfile +42 -0
- data/LICENSE.txt +21 -0
- data/README.md +491 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/test_all_methods +330 -0
- data/docs/FigPay-Direct-Post-API.pdf +0 -0
- data/figpay_gateway.gemspec +47 -0
- data/lib/figpay_gateway/version.rb +3 -0
- data/lib/figpay_gateway.rb +3 -0
- data/lib/nmi_gateway/api.rb +195 -0
- data/lib/nmi_gateway/customer_vault.rb +38 -0
- data/lib/nmi_gateway/data.rb +15 -0
- data/lib/nmi_gateway/error.rb +20 -0
- data/lib/nmi_gateway/recurring.rb +54 -0
- data/lib/nmi_gateway/response.rb +175 -0
- data/lib/nmi_gateway/result/action.rb +38 -0
- data/lib/nmi_gateway/result/customer.rb +63 -0
- data/lib/nmi_gateway/result/transaction.rb +74 -0
- data/lib/nmi_gateway/transaction.rb +110 -0
- data/lib/nmi_gateway.rb +29 -0
- metadata +266 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require "pry"
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'figpay_gateway'
|
|
5
|
+
require 'dotenv'
|
|
6
|
+
Dotenv.load
|
|
7
|
+
|
|
8
|
+
# Use demo credentials if not set
|
|
9
|
+
ENV['NMI_SECURITY_KEY'] ||= '6457Thfj624V5r7WUwc5v6a68Zsd6YEm'
|
|
10
|
+
|
|
11
|
+
puts "=" * 80
|
|
12
|
+
puts "FigPay Gateway - Comprehensive Method Test"
|
|
13
|
+
puts "=" * 80
|
|
14
|
+
puts "Using Security Key: #{ENV['NMI_SECURITY_KEY'][0..10]}..."
|
|
15
|
+
puts "=" * 80
|
|
16
|
+
puts
|
|
17
|
+
|
|
18
|
+
# Store results
|
|
19
|
+
results = {}
|
|
20
|
+
customer_vault_id = nil
|
|
21
|
+
subscription_id = nil
|
|
22
|
+
transaction_id = nil
|
|
23
|
+
auth_transaction_id = nil
|
|
24
|
+
|
|
25
|
+
# Helper method to display results
|
|
26
|
+
def display_result(name, result)
|
|
27
|
+
puts "\n#{'-' * 80}"
|
|
28
|
+
puts "Test: #{name}"
|
|
29
|
+
|
|
30
|
+
if result.success?
|
|
31
|
+
puts "Status: ✓ SUCCESS"
|
|
32
|
+
puts "Response Code: #{result.response_code}" if result.respond_to?(:response_code)
|
|
33
|
+
puts "Response Message: #{result.response_message}" if result.respond_to?(:response_message) && result.response_message
|
|
34
|
+
puts "Transaction ID: #{result.transactionid}" if result.respond_to?(:transactionid) && result.transactionid
|
|
35
|
+
puts "Customer Vault ID: #{result.customer_vault_id}" if result.respond_to?(:customer_vault_id) && result.customer_vault_id
|
|
36
|
+
puts "Subscription ID: #{result.subscription_id}" if result.respond_to?(:subscription_id) && result.subscription_id
|
|
37
|
+
puts "Auth Code: #{result.authcode}" if result.respond_to?(:authcode) && result.authcode
|
|
38
|
+
puts "Response: #{result.response}" if result.respond_to?(:response) && result.response
|
|
39
|
+
else
|
|
40
|
+
puts "Status: ✗ FAILED"
|
|
41
|
+
puts "Error: #{result.response_text}" if result.respond_to?(:response_text)
|
|
42
|
+
puts "Response Code: #{result.response_code}" if result.respond_to?(:response_code)
|
|
43
|
+
puts "Response Message: #{result.response_message}" if result.respond_to?(:response_message) && result.response_message
|
|
44
|
+
puts "Response: #{result.response}" if result.respond_to?(:response) && result.response
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
puts "-" * 80
|
|
48
|
+
result
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
puts "\n### TRANSACTION TESTS ###\n"
|
|
52
|
+
|
|
53
|
+
# Test 1: Validate Card
|
|
54
|
+
puts "\n1. Testing Card Validation..."
|
|
55
|
+
result = FigpayGateway::Transaction.new.validate(
|
|
56
|
+
ccnumber: '4111111111111111',
|
|
57
|
+
ccexp: '1225',
|
|
58
|
+
first_name: 'John',
|
|
59
|
+
last_name: 'Doe',
|
|
60
|
+
email: 'john@example.com'
|
|
61
|
+
)
|
|
62
|
+
results[:validate] = display_result("Card Validation", result)
|
|
63
|
+
|
|
64
|
+
# Test 2: Sale Transaction
|
|
65
|
+
puts "\n2. Testing Sale Transaction..."
|
|
66
|
+
result = FigpayGateway::Transaction.new.sale(
|
|
67
|
+
ccnumber: '4111111111111111',
|
|
68
|
+
ccexp: '1225',
|
|
69
|
+
cvv: '999',
|
|
70
|
+
amount: 10.00,
|
|
71
|
+
first_name: 'John',
|
|
72
|
+
last_name: 'Doe',
|
|
73
|
+
address1: '123 Main St',
|
|
74
|
+
city: 'Beverly Hills',
|
|
75
|
+
state: 'CA',
|
|
76
|
+
zip: '90210',
|
|
77
|
+
country: 'US',
|
|
78
|
+
email: 'john@example.com'
|
|
79
|
+
)
|
|
80
|
+
results[:sale] = display_result("Sale Transaction", result)
|
|
81
|
+
transaction_id = result.transactionid if result.success? && result.respond_to?(:transactionid)
|
|
82
|
+
|
|
83
|
+
# Test 3: Authorization
|
|
84
|
+
puts "\n3. Testing Authorization..."
|
|
85
|
+
result = FigpayGateway::Transaction.new.authorize(
|
|
86
|
+
ccnumber: '4111111111111111',
|
|
87
|
+
ccexp: '1225',
|
|
88
|
+
amount: 25.00,
|
|
89
|
+
first_name: 'John',
|
|
90
|
+
last_name: 'Doe',
|
|
91
|
+
email: 'john@example.com'
|
|
92
|
+
)
|
|
93
|
+
results[:authorize] = display_result("Authorization", result)
|
|
94
|
+
auth_transaction_id = result.transactionid if result.success? && result.respond_to?(:transactionid)
|
|
95
|
+
|
|
96
|
+
# Test 4: Capture (if authorization succeeded)
|
|
97
|
+
if auth_transaction_id
|
|
98
|
+
puts "\n4. Testing Capture..."
|
|
99
|
+
result = FigpayGateway::Transaction.new.capture(
|
|
100
|
+
transactionid: auth_transaction_id,
|
|
101
|
+
amount: 25.00
|
|
102
|
+
)
|
|
103
|
+
results[:capture] = display_result("Capture", result)
|
|
104
|
+
else
|
|
105
|
+
puts "\n4. Skipping Capture (no auth transaction ID)"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Test 5: Credit (Standalone)
|
|
109
|
+
puts "\n5. Testing Standalone Credit..."
|
|
110
|
+
result = FigpayGateway::Transaction.new.credit(
|
|
111
|
+
ccnumber: '4111111111111111',
|
|
112
|
+
ccexp: '1225',
|
|
113
|
+
amount: 5.00,
|
|
114
|
+
first_name: 'John',
|
|
115
|
+
last_name: 'Doe',
|
|
116
|
+
email: 'john@example.com'
|
|
117
|
+
)
|
|
118
|
+
results[:credit] = display_result("Standalone Credit", result)
|
|
119
|
+
|
|
120
|
+
# Test 6: Find Transaction (if we have a transaction ID)
|
|
121
|
+
if transaction_id
|
|
122
|
+
puts "\n6. Testing Transaction Query..."
|
|
123
|
+
result = FigpayGateway::Transaction.new.find(
|
|
124
|
+
transactionid: transaction_id
|
|
125
|
+
)
|
|
126
|
+
results[:find_transaction] = display_result("Transaction Query", result)
|
|
127
|
+
else
|
|
128
|
+
puts "\n6. Skipping Transaction Query (no transaction ID)"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Test 7: Update Transaction (if we have a transaction ID)
|
|
132
|
+
if transaction_id
|
|
133
|
+
puts "\n7. Testing Transaction Update..."
|
|
134
|
+
result = FigpayGateway::Transaction.new.update(
|
|
135
|
+
transactionid: transaction_id,
|
|
136
|
+
orderid: 'TEST-ORDER-123',
|
|
137
|
+
order_description: 'Test order updated'
|
|
138
|
+
)
|
|
139
|
+
results[:update_transaction] = display_result("Transaction Update", result)
|
|
140
|
+
else
|
|
141
|
+
puts "\n7. Skipping Transaction Update (no transaction ID)"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Test 8: Refund (if we have a transaction ID)
|
|
145
|
+
if transaction_id
|
|
146
|
+
puts "\n8. Testing Refund..."
|
|
147
|
+
result = FigpayGateway::Transaction.new.refund(
|
|
148
|
+
transactionid: transaction_id,
|
|
149
|
+
amount: 5.00
|
|
150
|
+
)
|
|
151
|
+
results[:refund] = display_result("Refund", result)
|
|
152
|
+
else
|
|
153
|
+
puts "\n8. Skipping Refund (no transaction ID)"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Test 9: Void (create a new transaction to void)
|
|
157
|
+
puts "\n9. Testing Void..."
|
|
158
|
+
void_result = FigpayGateway::Transaction.new.sale(
|
|
159
|
+
ccnumber: '4111111111111111',
|
|
160
|
+
ccexp: '1225',
|
|
161
|
+
amount: 1.00,
|
|
162
|
+
first_name: 'John',
|
|
163
|
+
last_name: 'Doe',
|
|
164
|
+
email: 'john@example.com'
|
|
165
|
+
)
|
|
166
|
+
if void_result.success? && void_result.respond_to?(:transactionid)
|
|
167
|
+
result = FigpayGateway::Transaction.new.void(
|
|
168
|
+
transactionid: void_result.transactionid
|
|
169
|
+
)
|
|
170
|
+
results[:void] = display_result("Void Transaction", result)
|
|
171
|
+
else
|
|
172
|
+
puts "Skipping Void (could not create transaction to void)"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
puts "\n\n### CUSTOMER VAULT TESTS ###\n"
|
|
176
|
+
|
|
177
|
+
# Test 10: Create Customer
|
|
178
|
+
puts "\n10. Testing Customer Vault Create..."
|
|
179
|
+
result = FigpayGateway::CustomerVault.new.create(
|
|
180
|
+
ccnumber: '4111111111111111',
|
|
181
|
+
ccexp: '1225',
|
|
182
|
+
cvv: '999',
|
|
183
|
+
first_name: 'Jane',
|
|
184
|
+
last_name: 'Smith',
|
|
185
|
+
address1: '456 Oak Ave',
|
|
186
|
+
city: 'Los Angeles',
|
|
187
|
+
state: 'CA',
|
|
188
|
+
zip: '90001',
|
|
189
|
+
email: 'jane@example.com'
|
|
190
|
+
)
|
|
191
|
+
results[:create_customer] = display_result("Create Customer", result)
|
|
192
|
+
customer_vault_id = result.customer_vault_id if result.success? && result.respond_to?(:customer_vault_id)
|
|
193
|
+
|
|
194
|
+
# Test 11: Update Customer (if we have a customer vault ID)
|
|
195
|
+
if customer_vault_id
|
|
196
|
+
puts "\n11. Testing Customer Vault Update..."
|
|
197
|
+
result = FigpayGateway::CustomerVault.new.update(
|
|
198
|
+
customer_vault_id: customer_vault_id,
|
|
199
|
+
first_name: 'Jane',
|
|
200
|
+
last_name: 'Doe-Smith',
|
|
201
|
+
email: 'jane.smith@example.com'
|
|
202
|
+
)
|
|
203
|
+
results[:update_customer] = display_result("Update Customer", result)
|
|
204
|
+
else
|
|
205
|
+
puts "\n11. Skipping Customer Update (no customer vault ID)"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Test 12: Find Customer (if we have a customer vault ID)
|
|
209
|
+
if customer_vault_id
|
|
210
|
+
puts "\n12. Testing Customer Vault Query..."
|
|
211
|
+
result = FigpayGateway::CustomerVault.new.find(
|
|
212
|
+
customer_vault_id: customer_vault_id
|
|
213
|
+
)
|
|
214
|
+
results[:find_customer] = display_result("Find Customer", result)
|
|
215
|
+
else
|
|
216
|
+
puts "\n12. Skipping Customer Query (no customer vault ID)"
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Test 13: Charge Vaulted Customer (if we have a customer vault ID)
|
|
220
|
+
if customer_vault_id
|
|
221
|
+
puts "\n13. Testing Charge Vaulted Customer..."
|
|
222
|
+
result = FigpayGateway::Transaction.new.sale(
|
|
223
|
+
customer_vault_id: customer_vault_id,
|
|
224
|
+
amount: 15.00,
|
|
225
|
+
orderid: 'VAULT-ORDER-456'
|
|
226
|
+
)
|
|
227
|
+
results[:charge_vaulted] = display_result("Charge Vaulted Customer", result)
|
|
228
|
+
else
|
|
229
|
+
puts "\n13. Skipping Charge Vaulted Customer (no customer vault ID)"
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
puts "\n\n### RECURRING BILLING TESTS ###\n"
|
|
233
|
+
|
|
234
|
+
# Test 14: Create Billing Plan
|
|
235
|
+
puts "\n14. Testing Create Billing Plan..."
|
|
236
|
+
result = FigpayGateway::Recurring.new.create_plan(
|
|
237
|
+
plan_id: "test-plan-#{Time.now.to_i}",
|
|
238
|
+
plan_name: 'Test Monthly Plan',
|
|
239
|
+
plan_amount: 29.99,
|
|
240
|
+
month_frequency: 1,
|
|
241
|
+
day_of_month: 1
|
|
242
|
+
)
|
|
243
|
+
results[:create_plan] = display_result("Create Billing Plan", result)
|
|
244
|
+
plan_id = result.plan_id if result.success? && result.respond_to?(:plan_id)
|
|
245
|
+
|
|
246
|
+
# Test 15: Add Subscription to Plan (if we have both plan and customer)
|
|
247
|
+
if plan_id && customer_vault_id
|
|
248
|
+
puts "\n15. Testing Subscribe Customer to Plan..."
|
|
249
|
+
result = FigpayGateway::Recurring.new.add_subscription_to_plan(
|
|
250
|
+
plan_id: plan_id,
|
|
251
|
+
customer_vault_id: customer_vault_id
|
|
252
|
+
)
|
|
253
|
+
results[:add_subscription] = display_result("Subscribe to Plan", result)
|
|
254
|
+
subscription_id = result.subscription_id if result.success? && result.respond_to?(:subscription_id)
|
|
255
|
+
elsif !plan_id
|
|
256
|
+
puts "\n15. Skipping Subscribe to Plan (no plan ID)"
|
|
257
|
+
elsif !customer_vault_id
|
|
258
|
+
puts "\n15. Skipping Subscribe to Plan (no customer vault ID)"
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Test 16: Create Custom Subscription (if we have customer vault ID)
|
|
262
|
+
if customer_vault_id && !subscription_id
|
|
263
|
+
puts "\n16. Testing Create Custom Subscription..."
|
|
264
|
+
result = FigpayGateway::Recurring.new.add_custom_subscription(
|
|
265
|
+
customer_vault_id: customer_vault_id,
|
|
266
|
+
plan_amount: 19.99,
|
|
267
|
+
month_frequency: 1,
|
|
268
|
+
day_of_month: 15
|
|
269
|
+
)
|
|
270
|
+
results[:custom_subscription] = display_result("Create Custom Subscription", result)
|
|
271
|
+
subscription_id = result.subscription_id if result.success? && result.respond_to?(:subscription_id)
|
|
272
|
+
else
|
|
273
|
+
puts "\n16. Skipping Custom Subscription (no customer or already subscribed)"
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Test 17: Update Subscription (if we have subscription ID)
|
|
277
|
+
if subscription_id
|
|
278
|
+
puts "\n17. Testing Update Subscription..."
|
|
279
|
+
result = FigpayGateway::Recurring.new.update_subscription(
|
|
280
|
+
subscription_id: subscription_id,
|
|
281
|
+
plan_amount: 24.99
|
|
282
|
+
)
|
|
283
|
+
results[:update_subscription] = display_result("Update Subscription", result)
|
|
284
|
+
else
|
|
285
|
+
puts "\n17. Skipping Update Subscription (no subscription ID)"
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Test 18: Cancel Subscription (if we have subscription ID)
|
|
289
|
+
if subscription_id
|
|
290
|
+
puts "\n18. Testing Cancel Subscription..."
|
|
291
|
+
result = FigpayGateway::Recurring.new.delete_subscription(
|
|
292
|
+
subscription_id: subscription_id
|
|
293
|
+
)
|
|
294
|
+
results[:delete_subscription] = display_result("Cancel Subscription", result)
|
|
295
|
+
else
|
|
296
|
+
puts "\n18. Skipping Cancel Subscription (no subscription ID)"
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Test 19: Delete Customer (if we have customer vault ID)
|
|
300
|
+
if customer_vault_id
|
|
301
|
+
puts "\n19. Testing Delete Customer..."
|
|
302
|
+
result = FigpayGateway::CustomerVault.new.destroy(
|
|
303
|
+
customer_vault_id: customer_vault_id
|
|
304
|
+
)
|
|
305
|
+
results[:delete_customer] = display_result("Delete Customer", result)
|
|
306
|
+
else
|
|
307
|
+
puts "\n19. Skipping Delete Customer (no customer vault ID)"
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Summary
|
|
311
|
+
puts "\n\n"
|
|
312
|
+
puts "=" * 80
|
|
313
|
+
puts "TEST SUMMARY"
|
|
314
|
+
puts "=" * 80
|
|
315
|
+
|
|
316
|
+
successful = results.select { |k, v| v.success? }.count
|
|
317
|
+
total = results.count
|
|
318
|
+
|
|
319
|
+
puts "Total Tests Run: #{total}"
|
|
320
|
+
puts "Successful: #{successful}"
|
|
321
|
+
puts "Failed: #{total - successful}"
|
|
322
|
+
puts "\nSuccess Rate: #{(successful.to_f / total * 100).round(2)}%"
|
|
323
|
+
|
|
324
|
+
puts "\nDetailed Results:"
|
|
325
|
+
results.each do |name, result|
|
|
326
|
+
status = result.success? ? "✓ PASS" : "✗ FAIL"
|
|
327
|
+
puts " #{status} - #{name}"
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
puts "=" * 80
|
|
Binary file
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'figpay_gateway/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "figpay_gateway"
|
|
8
|
+
spec.version = FigpayGateway::VERSION
|
|
9
|
+
spec.authors = ["Ben Eggett"]
|
|
10
|
+
spec.email = ["beneggett@gmail.com"]
|
|
11
|
+
|
|
12
|
+
spec.summary = %q{ FigPay Gateway (NMI) Api }
|
|
13
|
+
spec.description = %q{ Ruby wrapper for interacting with FigPay Gateway. FigPay is a white label gateway for NMI. Built with portability in mind for other NMI white-label providers. }
|
|
14
|
+
spec.homepage = "https://github.com/beneggett/figpay_gateway"
|
|
15
|
+
spec.license = "MIT"
|
|
16
|
+
|
|
17
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
18
|
+
|
|
19
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
|
20
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
|
21
|
+
if spec.respond_to?(:metadata)
|
|
22
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
|
23
|
+
else
|
|
24
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
28
|
+
spec.bindir = "exe"
|
|
29
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
30
|
+
spec.require_paths = ["lib"]
|
|
31
|
+
|
|
32
|
+
spec.add_development_dependency "bundler", ">= 2.0"
|
|
33
|
+
spec.add_development_dependency "rake", ">= 13.0"
|
|
34
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
|
35
|
+
spec.add_development_dependency "vcr"
|
|
36
|
+
spec.add_development_dependency "webmock"
|
|
37
|
+
spec.add_development_dependency "pry"
|
|
38
|
+
spec.add_development_dependency "guard"
|
|
39
|
+
spec.add_development_dependency "guard-minitest"
|
|
40
|
+
spec.add_development_dependency "coveralls"
|
|
41
|
+
spec.add_development_dependency "simplecov"
|
|
42
|
+
spec.add_development_dependency "minitest-focus"
|
|
43
|
+
spec.add_development_dependency "dotenv"
|
|
44
|
+
spec.add_dependency "httparty", ">= 0.14.0"
|
|
45
|
+
spec.add_dependency "activesupport"
|
|
46
|
+
|
|
47
|
+
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
module NMIGateway
|
|
2
|
+
class Api
|
|
3
|
+
TRANSACTION_URL = ENV.fetch("NMI_TRANSACTION_URL", "https://figpay.transactiongateway.com/api/transact.php")
|
|
4
|
+
QUERY_URL = ENV.fetch("NMI_QUERY_URL", "https://figpay.transactiongateway.com/api/query.php")
|
|
5
|
+
include HTTParty
|
|
6
|
+
|
|
7
|
+
attr_accessor :security_key, :query
|
|
8
|
+
|
|
9
|
+
def initialize(options = {})
|
|
10
|
+
@security_key = options[:security_key] || ENV["NMI_SECURITY_KEY"]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get(options={})
|
|
14
|
+
response = self.class.get(QUERY_URL, query: options.merge(credentials), timeout: 30, headers: headers)
|
|
15
|
+
api_type = options[:type] || options[:report_type]
|
|
16
|
+
handle_response(response, api_type)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def post(options={})
|
|
20
|
+
response = self.class.get(TRANSACTION_URL, query: options.merge(credentials), timeout: 30, headers: headers)
|
|
21
|
+
api_type = options[:type] || options[:customer_vault]
|
|
22
|
+
handle_response(response, api_type)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def handle_response(response, api_type)
|
|
28
|
+
if response.success?
|
|
29
|
+
NMIGateway::Response.new(response, api_type)
|
|
30
|
+
else
|
|
31
|
+
NMIGateway::Error.new(response.code, response.message, response)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def headers
|
|
36
|
+
{'Content-Type' => "application/xml", 'Accept' => "application/xml"}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def credentials
|
|
40
|
+
creds = {security_key: security_key}
|
|
41
|
+
creds[:test_mode] = ENV['NMI_TEST_MODE'] if ENV['NMI_TEST_MODE']
|
|
42
|
+
creds
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def set_query(options = {})
|
|
46
|
+
query = {}
|
|
47
|
+
query[:customer_vault] = options[:customer_vault] # add_customer or update_customer
|
|
48
|
+
query[:customer_vault_id] = options[:customer_vault_id]
|
|
49
|
+
query[:transactionid] = options[:transactionid]
|
|
50
|
+
query[:billing_id] = options[:billing_id]
|
|
51
|
+
|
|
52
|
+
# Query params
|
|
53
|
+
query[:condition] = options[:condition] # pending, pendingsettlement, failed, canceled, complete, unknown. Can be stringed together.
|
|
54
|
+
query[:transaction_type] = options[:transaction_type] # cc, ck
|
|
55
|
+
query[:action_type] = options[:action_type] # sale, refund, credit, auth, capture, void, return
|
|
56
|
+
|
|
57
|
+
query[:transaction_id] = options[:transaction_id] # can me multiple together
|
|
58
|
+
query[:cc_number] = options[:cc_number] # You can use either the full number or the last 4 digits of the credit card number.
|
|
59
|
+
query[:end_date] = options[:end_date] # YYYYMMDDhhmmss
|
|
60
|
+
query[:report_type] = options[:report_type] # receipt or customer_vault
|
|
61
|
+
query[:mobile_device_license] = options[:mobile_device_license]
|
|
62
|
+
query[:mobile_device_nickname] = options[:mobile_device_nickname]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Credit Cards
|
|
66
|
+
query[:ccnumber] = options[:ccnumber]
|
|
67
|
+
query[:ccexp] = options[:ccexp] # FORMAT MMYY
|
|
68
|
+
query[:cvv] = options[:cvv] # Recommended
|
|
69
|
+
|
|
70
|
+
# Payment
|
|
71
|
+
query[:amount] = options[:amount]
|
|
72
|
+
query[:currency] = options[:currency] || 'USD'
|
|
73
|
+
query[:payment] = options[:payment] || 'creditcard' # creditcard or check
|
|
74
|
+
query[:processor_id] = options[:processor_id]
|
|
75
|
+
query[:dup_seconds] = options[:dup_seconds]
|
|
76
|
+
query[:billing_method] = options[:billing_method] # recurring
|
|
77
|
+
query[:customer_receipt] = options[:customer_receipt] # true, false
|
|
78
|
+
|
|
79
|
+
# Advanced Payment
|
|
80
|
+
query[:tax] = options[:tax]
|
|
81
|
+
query[:shipping] = options[:shipping]
|
|
82
|
+
|
|
83
|
+
# Recurring Billing
|
|
84
|
+
query[:recurring] = options[:recurring ] # add_subscription
|
|
85
|
+
query[:plan_id] = options [:plan_id]
|
|
86
|
+
query[:plan_name] = options [:plan_name]
|
|
87
|
+
query[:plan_payments] = options [:plan_payments] # 0 until canceled
|
|
88
|
+
query[:plan_amount] = options [:plan_amount]
|
|
89
|
+
query[:day_frequency] = options [:day_frequency]
|
|
90
|
+
query[:month_frequency] = options [:month_frequency] # 1-24
|
|
91
|
+
query[:day_of_month] = options [:day_of_month] # 1-31
|
|
92
|
+
query[:start_date] = options [:start_date] #YYYYMMDD
|
|
93
|
+
query[:subscription_id] = options [:subscription_id]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# Customer Billing
|
|
97
|
+
query[:first_name] = options[:first_name]
|
|
98
|
+
query[:last_name] = options[:last_name]
|
|
99
|
+
query[:company] = options[:company]
|
|
100
|
+
query[:address1] = options[:address1]
|
|
101
|
+
query[:address2] = options[:address2]
|
|
102
|
+
query[:city] = options[:city]
|
|
103
|
+
query[:state] = options[:state]
|
|
104
|
+
query[:zip] = options[:zip]
|
|
105
|
+
query[:country] = options[:country]
|
|
106
|
+
query[:phone] = options[:phone]
|
|
107
|
+
query[:fax] = options[:fax]
|
|
108
|
+
query[:email] = options[:email]
|
|
109
|
+
query[:ipaddress] = options[:ipaddress] # Recommended
|
|
110
|
+
|
|
111
|
+
# Additional Customer Info
|
|
112
|
+
query[:social_security_number] = options[:social_security_number]
|
|
113
|
+
query[:drivers_license_number] = options[:drivers_license_number]
|
|
114
|
+
query[:drivers_license_dob] = options[:drivers_license_dob]
|
|
115
|
+
query[:drivers_license_state] = options[:drivers_license_state]
|
|
116
|
+
|
|
117
|
+
# Customer Shipping
|
|
118
|
+
query[:shipping_id] = options[:shipping_id]
|
|
119
|
+
query[:shipping_first_name] = options[:shipping_first_name]
|
|
120
|
+
query[:shipping_last_name] = options[:shipping_last_name]
|
|
121
|
+
query[:shipping_company] = options[:shipping_company]
|
|
122
|
+
query[:shipping_address1] = options[:shipping_address1]
|
|
123
|
+
query[:shipping_address2] = options[:shipping_address2]
|
|
124
|
+
query[:shipping_city] = options[:shipping_city]
|
|
125
|
+
query[:shipping_state] = options[:shipping_state]
|
|
126
|
+
query[:shipping_zip] = options[:shipping_zip]
|
|
127
|
+
query[:shipping_country] = options[:shipping_country]
|
|
128
|
+
query[:shipping_phone] = options[:shipping_phone]
|
|
129
|
+
query[:shipping_fax] = options[:shipping_fax]
|
|
130
|
+
query[:shipping_email] = options[:shipping_email]
|
|
131
|
+
|
|
132
|
+
# For ACH
|
|
133
|
+
query[:checkname] = options[:check_name]
|
|
134
|
+
query[:checkaba] = options[:check_routing_number] # routing_number
|
|
135
|
+
query[:checkaccount] = options[:check_account_number]
|
|
136
|
+
query[:account_holder_type] = options[:account_holder_type] # personal or 'business'
|
|
137
|
+
query[:account_type] = options[:account_type] # checking or savings
|
|
138
|
+
query[:sec_code] = options[:sec_code] # 'PPD', 'WEB', 'TEL', or 'CCD'
|
|
139
|
+
|
|
140
|
+
# Optional order fields
|
|
141
|
+
query[:orderid] = options[:orderid]
|
|
142
|
+
query[:order_description] = options[:order_description]
|
|
143
|
+
query[:order_date] = options[:order_date]
|
|
144
|
+
query[:ponumber] = options[:ponumber]
|
|
145
|
+
query[:tracking_number] = options[:tracking_number]
|
|
146
|
+
|
|
147
|
+
# order update fields
|
|
148
|
+
query[:shipping_carrier] = options[:shipping_carrier] # ups, fedex, dhl, or usps
|
|
149
|
+
query[:shipping_postal] = options[:shipping_postal]
|
|
150
|
+
query[:ship_from_postal] = options[:ship_from_postal]
|
|
151
|
+
query[:shipping_date] = options[:shipping_date]
|
|
152
|
+
query[:summary_commodity_code] = options[:summary_commodity_code]
|
|
153
|
+
query[:duty_amount] = options[:duty_amount]
|
|
154
|
+
query[:discount_amount] = options[:discount_amount]
|
|
155
|
+
query[:tax] = options[:tax]
|
|
156
|
+
query[:national_tax_amount] = options[:national_tax_amount]
|
|
157
|
+
query[:alternate_tax_amount] = options[:alternate_tax_amount]
|
|
158
|
+
query[:alternate_tax_id] = options[:alternate_tax_id]
|
|
159
|
+
query[:vat_tax_amount] = options[:vat_tax_amount]
|
|
160
|
+
query[:vat_tax_rate] = options[:vat_tax_rate] # 1% = 1.00.
|
|
161
|
+
query[:vat_invoice_reference_number] = options[:vat_invoice_reference_number]
|
|
162
|
+
query[:merchant_vat_registration] = options[:merchant_vat_registration]
|
|
163
|
+
# merchant_defined_field_# Merchant Defined Fields.
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# Descriptor fields
|
|
168
|
+
query[:descriptor] = options[:descriptor]
|
|
169
|
+
query[:descriptor_phone] = options[:descriptor_phone]
|
|
170
|
+
query[:descriptor_address] = options[:descriptor_address]
|
|
171
|
+
query[:descriptor_city] = options[:descriptor_city]
|
|
172
|
+
query[:descriptor_state] = options[:descriptor_state]
|
|
173
|
+
query[:descriptor_postal] = options[:descriptor_postal]
|
|
174
|
+
query[:descriptor_country] = options[:descriptor_country]
|
|
175
|
+
query[:descriptor_mcc] = options[:descriptor_mcc]
|
|
176
|
+
query[:descriptor_merchant_id] = options[:descriptor_merchant_id]
|
|
177
|
+
query[:descriptor_url] = options[:descriptor_url]
|
|
178
|
+
|
|
179
|
+
1..20.to_a.each do |i|
|
|
180
|
+
query["merchant_defined_field_#{i}".to_sym] = options["merchant_defined_field_#{i}".to_sym]
|
|
181
|
+
end if options.select {|k,v| k.to_s.starts_with? 'merchant_defined_field' }.any?
|
|
182
|
+
|
|
183
|
+
@query = query.select {|k,v| !v.nil?}
|
|
184
|
+
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def require_fields(*fields)
|
|
188
|
+
if query.values_at(*fields).include?(nil)
|
|
189
|
+
missing_fields = fields - query.keys
|
|
190
|
+
raise Error::MissingParameters.new("Missing required keys: #{missing_fields.to_s.gsub(']', '').gsub('[', '')}") if missing_fields.any?
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module NMIGateway
|
|
2
|
+
class CustomerVault < Api
|
|
3
|
+
|
|
4
|
+
# NMIGateway::CustomerVault.new.create ccnumber: '4111111111111111', ccexp: "0219", first_name: "John", last_name: "Doe"
|
|
5
|
+
def create(options = {})
|
|
6
|
+
query = set_query(options)
|
|
7
|
+
query[:customer_vault] = 'add_customer'
|
|
8
|
+
|
|
9
|
+
require_fields(:ccnumber, :ccexp)
|
|
10
|
+
post query
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# NMIGateway::CustomerVault.new.update customer_vault_id: 481397475, ccnumber: '4111111111111111', ccexp: "0220", first_name: "Jane", last_name: "Doe"
|
|
14
|
+
def update(options = {})
|
|
15
|
+
query = set_query(options)
|
|
16
|
+
query[:customer_vault] = 'update_customer'
|
|
17
|
+
require_fields(:customer_vault_id)
|
|
18
|
+
post query
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# NMIGateway::CustomerVault.new.destroy customer_vault_id: 481397475
|
|
22
|
+
def destroy(options = {})
|
|
23
|
+
query = set_query(options)
|
|
24
|
+
query[:customer_vault] = 'delete_customer'
|
|
25
|
+
require_fields(:customer_vault_id)
|
|
26
|
+
post query
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# NMIGateway::CustomerVault.new.find customer_vault_id: 481397475
|
|
30
|
+
def find(options = {})
|
|
31
|
+
query = set_query(options)
|
|
32
|
+
query[:report_type] = 'customer_vault'
|
|
33
|
+
require_fields(:customer_vault_id)
|
|
34
|
+
get query
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module NMIGateway
|
|
2
|
+
class Data
|
|
3
|
+
|
|
4
|
+
# Attributes that have a value
|
|
5
|
+
def attributes
|
|
6
|
+
all_attributes.select {|x| !self.send(x).nil? }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def all_attributes
|
|
10
|
+
self.instance_variables.map{|attribute| attribute.to_s.gsub('@', '').to_sym }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module NMIGateway
|
|
2
|
+
class Error
|
|
3
|
+
class AuthenticationError < StandardError; end
|
|
4
|
+
class NoApiKey < AuthenticationError; end
|
|
5
|
+
class InvalidResponseFormat < TypeError; end
|
|
6
|
+
class MissingParameters < TypeError; end
|
|
7
|
+
class Timeout < Timeout::Error ; end
|
|
8
|
+
class NoData < EOFError; end
|
|
9
|
+
|
|
10
|
+
attr_reader :message, :code, :response
|
|
11
|
+
|
|
12
|
+
def initialize( code, message, response)
|
|
13
|
+
@code = response_code
|
|
14
|
+
@message = response_message
|
|
15
|
+
@response = response
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|