agms 0.1.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.
Files changed (58) hide show
  1. checksums.yaml +15 -0
  2. data/.travis.yml +3 -0
  3. data/Gemfile +4 -0
  4. data/README.md +51 -0
  5. data/Rakefile +40 -0
  6. data/agms.gemspec +29 -0
  7. data/bin/console +14 -0
  8. data/bin/setup +7 -0
  9. data/example/hpp.rb +18 -0
  10. data/example/invoicing.rb +23 -0
  11. data/example/recurring.rb +23 -0
  12. data/example/report.rb +16 -0
  13. data/example/safe.rb +17 -0
  14. data/example/transaction.rb +17 -0
  15. data/init.yml +9 -0
  16. data/lib/agms.rb +59 -0
  17. data/lib/agms/agms.rb +109 -0
  18. data/lib/agms/configuration.rb +71 -0
  19. data/lib/agms/connect.rb +176 -0
  20. data/lib/agms/error/agms_error.rb +9 -0
  21. data/lib/agms/error/authentication_error.rb +7 -0
  22. data/lib/agms/error/authorization_error.rb +7 -0
  23. data/lib/agms/error/client_error_error.rb +7 -0
  24. data/lib/agms/error/configuation_error.rb +7 -0
  25. data/lib/agms/error/down_for_maintenance_error.rb +7 -0
  26. data/lib/agms/error/forged_query_String_error.rb +7 -0
  27. data/lib/agms/error/invalid_parameter_error.rb +7 -0
  28. data/lib/agms/error/invalid_request_error.rb +7 -0
  29. data/lib/agms/error/invalid_signature_error.rb +7 -0
  30. data/lib/agms/error/not_found_error.rb +7 -0
  31. data/lib/agms/error/request_validation_error.rb +7 -0
  32. data/lib/agms/error/response_error.rb +7 -0
  33. data/lib/agms/error/server_error_error.rb +7 -0
  34. data/lib/agms/error/ssl_certificate_error.rb +7 -0
  35. data/lib/agms/error/unexpected_error.rb +7 -0
  36. data/lib/agms/error/upgrade_required_error.rb +7 -0
  37. data/lib/agms/hpp.rb +65 -0
  38. data/lib/agms/invoicing.rb +63 -0
  39. data/lib/agms/recurring.rb +76 -0
  40. data/lib/agms/report.rb +53 -0
  41. data/lib/agms/request/hpp_request.rb +181 -0
  42. data/lib/agms/request/invoicing_request.rb +8 -0
  43. data/lib/agms/request/recurring_request.rb +115 -0
  44. data/lib/agms/request/report_request.rb +106 -0
  45. data/lib/agms/request/request.rb +455 -0
  46. data/lib/agms/request/safe_request.rb +211 -0
  47. data/lib/agms/request/transaction_request.rb +217 -0
  48. data/lib/agms/response/hpp_response.rb +39 -0
  49. data/lib/agms/response/invoicing_response.rb +10 -0
  50. data/lib/agms/response/recurring_response.rb +28 -0
  51. data/lib/agms/response/report_response.rb +141 -0
  52. data/lib/agms/response/response.rb +48 -0
  53. data/lib/agms/response/safe_response.rb +51 -0
  54. data/lib/agms/response/transaction_response.rb +50 -0
  55. data/lib/agms/safe.rb +66 -0
  56. data/lib/agms/transaction.rb +37 -0
  57. data/lib/agms/version.rb +3 -0
  58. metadata +156 -0
@@ -0,0 +1,106 @@
1
+ module Agms
2
+ class ReportRequest < Request
3
+ # A class representing AGMS Report Request objects.
4
+
5
+ alias AgmsAutoValidate autoValidate
6
+
7
+ def initialize(op)
8
+ super(op)
9
+ @trans_fields = {
10
+ :TransactionType => {:setting => '', :value => ''},
11
+ :PaymentType => {:setting => '', :value => 'creditcard'},
12
+ :Amount => {:setting => '', :value => ''}, # Required for sale or auth
13
+ :FirstName => {:setting => '', :value => ''},
14
+ :LastName => {:setting => '', :value => ''},
15
+ :Company => {:setting => '', :value => ''},
16
+ :Address1 => {:setting => '', :value => ''},
17
+ :Address2 => {:setting => '', :value => ''},
18
+ :City => {:setting => '', :value => ''},
19
+ :State => {:setting => '', :value => ''},
20
+ :Zip => {:setting => '', :value => ''},
21
+ :Country => {:setting => '', :value => ''},
22
+ :Phone => {:setting => '', :value => ''},
23
+ :Fax => {:setting => '', :value => ''},
24
+ :EMail => {:setting => '', :value => ''},
25
+ :ProcessorID => {:setting => '', :value => ''},
26
+ :TransactionID => {:setting => '', :value => ''},
27
+ :SAFE_ID => {:setting => '', :value => ''},
28
+ :StartDate => {:setting => '', :value => ''},
29
+ :EndDate => {:setting => '', :value => ''},
30
+ :CreditCardLast4 => {:setting => '', :value => ''}
31
+ }
32
+
33
+ @safe_fields = {
34
+ :Active => {:setting => '', :value => ''},
35
+ :PaymentType => {:setting => '', :value => 'creditcard'},
36
+ :FirstName => {:setting => '', :value => ''},
37
+ :LastName => {:setting => '', :value => ''},
38
+ :Company => {:setting => '', :value => ''},
39
+ :EMail => {:setting => '', :value => ''},
40
+ :SafeID => {:setting => '', :value => ''},
41
+ :StartDate => {:setting => '', :value => ''},
42
+ :EndDate => {:setting => '', :value => ''},
43
+ :Expiring30 => {:setting => '', :value => ''}
44
+ }
45
+
46
+ @numeric = %w(Amount ProcessorID TransactionID CreditCardLast4)
47
+
48
+ @date = %w(StartDate EndDate)
49
+
50
+ @digit_2 = %w(State)
51
+
52
+ if @op == 'TransactionAPI'
53
+ @fields = @trans_fields
54
+ # Override mapping with api-specific field maps
55
+ @mapping[:safe_id] = :Safe_ID
56
+ @mapping[:gateway_username] = :GatewayUsername
57
+ elsif @op == 'QuerySAFE'
58
+ @fields = @safe_fields
59
+ # Override mapping with api-specific field maps
60
+ @mapping[:safe_id] = :SafeID
61
+ @mapping[:gateway_username] = :GatewayUsername
62
+ @mapping[:gateway_key] = :APIKey
63
+ else
64
+ raise InvalidRequestError, "Invalid op #{@op} in Request."
65
+ end
66
+
67
+ @needs_account = true
68
+ @needs_key = true
69
+
70
+ end
71
+
72
+ def validate
73
+ @required = []
74
+
75
+ error_array = AgmsAutoValidate();
76
+
77
+ errors = error_array['errors'];
78
+ messages = error_array['messages'];
79
+
80
+ @validate_errors = errors;
81
+ @validate_messages = messages;
82
+
83
+ if errors == 0
84
+ return {'errors' => errors, 'messages' => messages}
85
+ else
86
+ raise RequestValidationError, "Request validation failed with #{messages.join(' ')}."
87
+ end
88
+
89
+ end
90
+
91
+ def getFields
92
+ return getFieldArray
93
+ end
94
+
95
+ def getParams(request)
96
+ if @op == 'TransactionAPI'
97
+ return {:objparameters => request}
98
+ elsif @op == 'QuerySAFE'
99
+ return request
100
+ else
101
+ return {:objparameters => request}
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,455 @@
1
+ module Agms
2
+ class Request
3
+ def initialize(op)
4
+ @op = op
5
+ @validateErrors = 0
6
+ @validateMessages = nil
7
+ @fields = nil
8
+ @required = nil
9
+ @numeric = nil
10
+ @optionable = nil
11
+ @enums = nil
12
+ @date = nil
13
+ @time = nil
14
+ @boolean = nil
15
+ @digit_2 = nil
16
+ @amount = nil
17
+
18
+ @needs_account = nil
19
+ @needs_key = nil
20
+
21
+ @mapping_alias = nil
22
+
23
+ @mapping = {
24
+ :gateway_username => :GatewayUserName,
25
+ :gateway_password => :GatewayPassword,
26
+ :gateway_account => :AccountNumber,
27
+ :gateway_key => :TransactionAPIKey,
28
+ :amount => :Amount,
29
+ :description => :OrderDescription,
30
+ :order_description => :OrderDescription,
31
+ :return_url => :RetURL,
32
+ :enable_ach => :ACHEnabled,
33
+ :transaction_type => :TransactionType,
34
+ :payment_type => :PaymentType,
35
+ :enable_auto_add_to_safe => :AutoSAFE,
36
+ :processing_account_id => :ProcessorID,
37
+ :enable_donation => :Donation,
38
+ :max_link_uses => :UsageCount,
39
+ :cc_number => :CCNumber,
40
+ :cc_exp_date => :CCExpDate,
41
+ :cc_cvv => :CVV,
42
+ :cc_track_1 => :Track1,
43
+ :cc_track_2 => :Track2,
44
+ :cc_track_3 => :Track3,
45
+ :cc_encrypted_data => :MagData,
46
+ :cc_encrypted_hardware => :MagHardware,
47
+ :ach_name => :CheckName,
48
+ :ach_routing_number => :CheckABA,
49
+ :ach_account_number => :CheckAccount,
50
+ :ach_business_or_personal => :AccountHolderType,
51
+ :ach_checking_or_savings => :AccountType,
52
+ :ach_sec_code => :SecCode,
53
+ :safe_action => :SAFE_Action,
54
+ :safe_id => :SAFE_ID,
55
+ :first_name => :FirstName,
56
+ :last_name => :LastName,
57
+ :company_name => :Company,
58
+ :company => :Company,
59
+ :address => :Address1,
60
+ :address_1 => :Address1,
61
+ :address_2 => :Address2,
62
+ :city => :City,
63
+ :state => :State,
64
+ :zip => :Zip,
65
+ :country => :Country,
66
+ :phone => :Phone,
67
+ :fax => :Fax,
68
+ :email => :EMail,
69
+ :website => :Website,
70
+ :tax_amount => :Tax,
71
+ :shipping_amount => :Shipping,
72
+ :tip_amount => :TipAmount,
73
+ :order_id => :OrderID,
74
+ :po_number => :PONumber,
75
+ :clerk_id => :ClerkID,
76
+ :ip_address => :IPAddress,
77
+ :receipt_type => :ReceiptType,
78
+ :shipping_first_name => :ShippingFirstName,
79
+ :shipping_last_name => :ShippingLastName,
80
+ :shipping_company_name => :ShippingCompany,
81
+ :shipping_company => :ShippingCompany,
82
+ :shipping_address => :ShippingAddress1,
83
+ :shipping_address_1 => :ShippingAddress1,
84
+ :shipping_address_2 => :ShippingAddress2,
85
+ :shipping_city => :ShippingCity,
86
+ :shipping_state => :ShippingState,
87
+ :shipping_zip => :ShippingZip,
88
+ :shipping_country => :ShippingCountry,
89
+ :shipping_email => :ShippingEmail,
90
+ :shipping_phone => :ShippingPhone,
91
+ :shipping_fax => :ShippingFax,
92
+ :shipping_tracking_number => :ShippingTrackingNumber,
93
+ :shipping_carrier => :ShippingCarrier,
94
+ :custom_field_1 => :Custom_Field_1,
95
+ :custom_field_2 => :Custom_Field_2,
96
+ :custom_field_3 => :Custom_Field_3,
97
+ :custom_field_4 => :Custom_Field_4,
98
+ :custom_field_5 => :Custom_Field_5,
99
+ :custom_field_6 => :Custom_Field_6,
100
+ :custom_field_7 => :Custom_Field_7,
101
+ :custom_field_8 => :Custom_Field_8,
102
+ :custom_field_9 => :Custom_Field_9,
103
+ :custom_field_10 => :Custom_Field_10,
104
+ :custom_field_11 => :Custom_Field_11,
105
+ :custom_field_12 => :Custom_Field_12,
106
+ :custom_field_13 => :Custom_Field_13,
107
+ :custom_field_14 => :Custom_Field_14,
108
+ :custom_field_15 => :Custom_Field_15,
109
+ :custom_field_16 => :Custom_Field_16,
110
+ :custom_field_17 => :Custom_Field_17,
111
+ :custom_field_18 => :Custom_Field_18,
112
+ :custom_field_19 => :Custom_Field_19,
113
+ :custom_field_20 => :Custom_Field_20,
114
+ :expiring_in_30_days => :Expiring30,
115
+ :recurring_id => :RecurringID,
116
+ :merchant_id => :MerchantID,
117
+ :initial_amount => :InitialAmount,
118
+ :recurring_amount => :RecurringAmount,
119
+ :frequency => :Frequency,
120
+ :quantity => :Quantity,
121
+ :number_of_times_to_bill => :NumberOfOccurrences,
122
+ :number_of_retries => :NumberOfRetries,
123
+ :hpp_format => :HPPFormat,
124
+ :cc_last_4 => :CreditCardLast4,
125
+ :transaction_id => :TransactionID,
126
+ :start_date => :StartDate,
127
+ :end_date => :EndDate,
128
+ :start_time => :StartTime,
129
+ :end_time => :EndTime,
130
+ :suppress_safe_option => :SupressAutoSAFE
131
+ }
132
+
133
+ @states = %w(AL AK AS AZ AR CA CO CT DE DC FM FL GA GU HI ID IL IN IA KS KY LA ME MH MD MA MI MN MS MO MT NE NV NH NJ NM NY NC ND MP OH OK OR PW PA PR RI SC SD TN TX UT VT VI VA WA WV WI WY AE AA AP)
134
+ end
135
+
136
+ def get(username, password, account, api_key)
137
+ request_body = getFields()
138
+ request_body[:GatewayUserName] = username
139
+ request_body[:GatewayPassword] = password
140
+
141
+ # Adjust for a field name variation in the Reporting API
142
+ if @op == 'TransactionAPI' or @op == 'QuerySAFE'
143
+ request_body.delete(:GatewayUserName)
144
+ request_body[:GatewayUsername] = username
145
+ end
146
+
147
+ # Add Account # and API Key field to request when necessary for specific API
148
+ if @needs_account
149
+ request_body[:AccountNumber] = account
150
+ end
151
+
152
+ if @needs_key
153
+ # Adjust for a field name variation in the Reporting API
154
+ if @op == 'TransactionAPI'
155
+ request_body[:TransactionAPIKey] = api_key
156
+ elsif @op == 'QuerySAFE'
157
+ request_body[:APIKey] = api_key
158
+ end
159
+ end
160
+
161
+ return request_body
162
+ end
163
+
164
+ def setField(name, parameter, value)
165
+ field_name = mapToField(name)
166
+ # Fix for odd capitalization of Email
167
+ if field_name == :Email
168
+ field_name = :EMail
169
+ end
170
+
171
+ # Check that field exists
172
+ if not @fields.has_key?(field_name)
173
+ raise InvalidParameterError, "Invalid field name #{name}."
174
+ end
175
+
176
+ # Ensure that setting parameters are forced to all lowercase and are case insensitive
177
+ if parameter == :setting
178
+ value = value.downcase
179
+ end
180
+
181
+ # Check that it is a valid setting
182
+ if ( parameter == :setting and
183
+ value != '' and
184
+ value != 'required' and
185
+ value != 'disabled' and
186
+ value != 'visible' and
187
+ value != 'excluded' and
188
+ value != 'hidden' )
189
+ raise InvalidParameterError, "Invalid parameter #{parameter} for #{name}."
190
+ end
191
+
192
+ if parameter == :setting
193
+ @fields[field_name][:setting] = value
194
+ return true
195
+ elsif parameter == :value
196
+ @fields[field_name][:value] = value
197
+ return true
198
+ else
199
+ raise InvalidParameterError, "Invalid parameter #{parameter} for #{name}."
200
+ end
201
+ end
202
+
203
+ def getField(name)
204
+ field_name = mapToField(name)
205
+ return @fields[field_name]
206
+ end
207
+
208
+ def getValidationErrors
209
+ return @validateErrors
210
+ end
211
+
212
+
213
+ def getValidationMessages
214
+ return @validateMessages
215
+ end
216
+
217
+ protected
218
+ def autoValidate
219
+ errors = 0
220
+ messages = Array.new
221
+
222
+ if @required
223
+ @required.each do | field_name |
224
+ if @fields[field_name][:value] == ''
225
+ errors +=1
226
+ messages.push("Missing required field #{field_name}.")
227
+ end
228
+ end
229
+ end
230
+
231
+ # Validate enumerated types
232
+ if @enums
233
+ @enums.each do | field_name, valid_values |
234
+
235
+ if @fields.has_key?(field_name) and
236
+ @fields[field_name][:value] != '' and
237
+ not valid_values.include?@fields[field_name][:value]
238
+ errors += 1
239
+ messages.push("Invalid #{field_name}, value " + @fields[field_name][:value] + ', must be one of ' + valid_values.join(' ') + '.')
240
+ end
241
+ end
242
+ end
243
+
244
+ # Validate numeric fields
245
+ if @numeric
246
+ @numeric.each do | field_name |
247
+ if @fields.has_key?(field_name) and
248
+ @fields[field_name][:value] != '' and
249
+ not isNumber(@fields[field_name][:value])
250
+ errors += 1
251
+ messages.push("Field #{field_name} has value " + @fields[field_name][:value] + ' must be numeric.')
252
+ end
253
+ end
254
+ end
255
+
256
+ # Validate optionable fields
257
+ if @optionable
258
+ @optionable.each do | field_name |
259
+ if ( @fields.has_key?(field_name) and
260
+ @fields[field_name][:setting] != '' and
261
+ @fields[field_name][:setting] != :required and
262
+ @fields[field_name][:setting] != :disabled and
263
+ @fields[field_name][:setting] != :visible and
264
+ @fields[field_name][:setting] != :excluded and
265
+ @fields[field_name][:setting] != :hidden )
266
+ errors += 1
267
+ messages.push("Field #{field_name} has setting " + @fields[field_name][:value] + ', must be required, disabled, visible, hidden, or empty.')
268
+ end
269
+ end
270
+ end
271
+
272
+ # Validate date fields
273
+ if @date
274
+ @date.each do | field_name |
275
+
276
+ if @fields.has_key?(field_name) and
277
+ @fields[field_name][:value] != '' and
278
+ not /[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/.match(@fields[field_name][:value])
279
+ errors += 1
280
+ messages.push("Field #{field_name} has setting " + @fields[field_name][:value] + ', must be in date format YYYY-MM-DD.')
281
+ end
282
+ end
283
+ end
284
+
285
+
286
+ # Validate time fields
287
+ if @time
288
+ @time.each do | field_name |
289
+ if not @fields[field_name][:value] != '' and
290
+ not /([01][0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])/.match(@fields[field_name][:value])
291
+ errors += 1
292
+ messages.push("Field #{field_name} has setting " + @fields[field_name]['value'] + ', must be in 24h time format HH:MM:SS or HH:MM.')
293
+ end
294
+ end
295
+ end
296
+
297
+
298
+ # Validate boolean fields
299
+ if @boolean
300
+ @boolean.each do | field_name |
301
+ if ( @fields.has_key?(field_name) and
302
+ not @fields[field_name][:value] != '' and
303
+ not @fields[field_name][:value] != true and
304
+ @fields[field_name][:value] != false and
305
+ not @fields[field_name][:value] != 'TRUE' and
306
+ @fields[field_name][:value] != 'FALSE'
307
+ )
308
+ errors += 1
309
+ messages.push("Field #{field_name} has setting " + @fields[field_name][:value] + ', must be boolean TRUE or FALSE.')
310
+ end
311
+ end
312
+ end
313
+
314
+ # Validate state code fields
315
+ if @digit_2
316
+ @digit_2.each do | field_name |
317
+ if @fields.has_key?(field_name) and
318
+ @fields[field_name][:value] != '' and
319
+ not @digit_2.has_key?@fields[field_name][:value]
320
+ errors += 1
321
+ messages.push("Field #{field_name} has setting " + @fields[field_name][:value] + ', must be valid 2 digit US State code.')
322
+ end
323
+ end
324
+ end
325
+
326
+ # Validate amount fields
327
+ if @amount
328
+ @amount.each do | field_name |
329
+ if @fields.key?(field_name) and
330
+ @fields[field_name][:value] != '' and
331
+ @fields[field_name][:value] > Configuration.max_amount
332
+ errors += 1
333
+ messages.push("Field #{field_name} amount " + @fields[field_name][:value] + ", is above maximum allowable value of #{Configuration.max_amount}")
334
+ end
335
+ if @fields.key?(field_name) and
336
+ @fields[field_name][:value] != '' and
337
+ @fields[field_name][:value] < Configuration.min_amount
338
+ errors += 1
339
+ messages.push("Field #{field_name} amount " + @fields[field_name][:value] + ", is below minimum allowable value of #{Configuration.min_amount}")
340
+ end
341
+ end
342
+ end
343
+
344
+ return {'errors' => errors, 'messages' => messages}
345
+ end
346
+
347
+
348
+ def getFieldArray
349
+ request = {}
350
+
351
+ # Call validation, which ensures we've validated and done so against current data
352
+ validate()
353
+
354
+ if @validateErrors > 0
355
+ raise RequestValidationError , 'Request validation failed with ' + ' '.join(@validateMessages) + '.'
356
+ end
357
+
358
+ @fields.each do | field_name, settings |
359
+ if settings[:setting] == :required
360
+ request[field_name] = ''
361
+ request[field_name + '_Visible'] = true
362
+ request[field_name + '_Required'] = true
363
+ if field_name == :EMail
364
+ request['Email_Disabled'] = false
365
+ else
366
+ request[field_name + '_Disabled'] = false
367
+ end
368
+
369
+ elsif settings[:setting] == :disabled
370
+ request[field_name] = ''
371
+ request[field_name + '_Visible'] = true
372
+ request[field_name + '_Required'] =true
373
+ if field_name == :EMail
374
+ request['Email_Disabled'] = false
375
+ else
376
+ request[field_name + '_Disabled'] = false
377
+ end
378
+
379
+ elsif settings[:setting] == :visible
380
+ request[field_name] = ''
381
+ request[field_name + '_Visible'] = true
382
+ request[field_name + '_Required'] = true
383
+ if field_name == :EMail
384
+ request['Email_Disabled'] = false
385
+ else
386
+ request[field_name + '_Disabled'] = false
387
+ end
388
+
389
+ elsif settings[:setting] == :hidden
390
+ next
391
+
392
+ elsif settings[:setting] == :excluded
393
+ next
394
+
395
+ else
396
+ if @optionable and @optionable.include?(field_name)
397
+ request[field_name + '_Visible'] = true
398
+ request[field_name + '_Required'] = true
399
+ if field_name == :EMail
400
+ request['Email_Disabled'] = false
401
+ else
402
+ request[field_name + '_Disabled'] = false
403
+ end
404
+ end
405
+ end
406
+
407
+ if settings[:value]
408
+ if settings[:value].upcase == 'TRUE'
409
+ request[field_name] = true
410
+ elsif settings[:value].upcase == 'FALSE'
411
+ request[field_name] = false
412
+ else
413
+ request[field_name] = settings[:value]
414
+ end
415
+ end
416
+ end
417
+ return request
418
+ end
419
+
420
+
421
+ def mapToField(field_name)
422
+ if @mapping.has_key?(field_name)
423
+ return @mapping[field_name]
424
+ elsif @mapping_alias and @mapping_alias.has_key?(field_name)
425
+ return @mapping_alias[field_name]
426
+ elsif @fields.has_key?(field_name)
427
+ return field_name
428
+ else
429
+ raise InvalidParameterError, "Invalid field name #{field_name}."
430
+ end
431
+ end
432
+
433
+ def mapToName(field_name)
434
+ if @mapping.has_key?(field_name)
435
+ return @mapping[field_name]
436
+ elsif @mapping_alias and @mapping_alias.has_key?(field_name)
437
+ return @mapping_alias[field_name]
438
+ elsif @fields.has_key?(field_name)
439
+ return field_name
440
+ else
441
+ raise InvalidParameterError, "Invalid field name #{field_name}."
442
+ end
443
+ end
444
+
445
+
446
+ def isNumber(str)
447
+ begin
448
+ !!Integer(str)
449
+ rescue ArgumentError, TypeError
450
+ false
451
+ end
452
+ end
453
+
454
+ end
455
+ end