VantivCnp 8.31.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 (120) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +139 -0
  3. data/CONTRIBUTORS +2 -0
  4. data/DESCRIPTION +5 -0
  5. data/LICENSE +22 -0
  6. data/README.md +72 -0
  7. data/Rakefile +89 -0
  8. data/SETUP.md +46 -0
  9. data/bin/Setup.rb +124 -0
  10. data/bin/sample_batch_driver.rb +123 -0
  11. data/bin/sample_driver.rb +49 -0
  12. data/lib/Communications.rb +85 -0
  13. data/lib/Configuration.rb +67 -0
  14. data/lib/EnvironmentVariables.rb +22 -0
  15. data/lib/LitleBatchRequest.rb +562 -0
  16. data/lib/LitleListeners.rb +142 -0
  17. data/lib/LitleOnline.rb +62 -0
  18. data/lib/LitleOnlineRequest.rb +297 -0
  19. data/lib/LitleRequest.rb +494 -0
  20. data/lib/LitleTransaction.rb +463 -0
  21. data/lib/LitleXmlMapper.rb +64 -0
  22. data/lib/XMLFields.rb +1869 -0
  23. data/lib/cacert.pem +3331 -0
  24. data/samples/Auth/LitleAuthReversalTransaction.rb +15 -0
  25. data/samples/Auth/LitleAuthorizationTransaction.rb +31 -0
  26. data/samples/Auth/LitlePaymentFullLifeCycle.rb +47 -0
  27. data/samples/Batch/AccountUpdate.rb +64 -0
  28. data/samples/Batch/SampleBatchDriver.rb +94 -0
  29. data/samples/Capture/LitleCaptureGivenAuthTransaction.rb +30 -0
  30. data/samples/Capture/LitleCaptureTransaction.rb +14 -0
  31. data/samples/Capture/LitleForceCaptureTransaction.rb +26 -0
  32. data/samples/Capture/LitlePartialCapture.rb +16 -0
  33. data/samples/Credit/LitleCreditTransaction.rb +16 -0
  34. data/samples/Credit/LitleRefundTransaction.rb +29 -0
  35. data/samples/Other/LitleAvsTransaction.rb +34 -0
  36. data/samples/Other/LitleVoidTransaction.rb +18 -0
  37. data/samples/Paypage/FullPaypageLifeCycle.rb +74 -0
  38. data/samples/Run_all.rb +17 -0
  39. data/samples/Sale/LitleSaleTransaction.rb +29 -0
  40. data/samples/Sale/SampleSaleTransaction.rb +24 -0
  41. data/test/certification/certTest1_base.rb +945 -0
  42. data/test/certification/certTest2_authenhanced.rb +573 -0
  43. data/test/certification/certTest3_authreversal.rb +185 -0
  44. data/test/certification/certTest4_echeck.rb +278 -0
  45. data/test/certification/certTest5_token.rb +204 -0
  46. data/test/certification/certTest_batchAll.rb +337 -0
  47. data/test/certification/ts_all.rb +33 -0
  48. data/test/functional/test_activate.rb +100 -0
  49. data/test/functional/test_activateReversal.rb +56 -0
  50. data/test/functional/test_auth.rb +298 -0
  51. data/test/functional/test_authReversal.rb +69 -0
  52. data/test/functional/test_balanceInquiry.rb +80 -0
  53. data/test/functional/test_batch.rb +164 -0
  54. data/test/functional/test_batchStream.rb +145 -0
  55. data/test/functional/test_cancelSubscription.rb +55 -0
  56. data/test/functional/test_capture.rb +84 -0
  57. data/test/functional/test_captureGivenAuth.rb +235 -0
  58. data/test/functional/test_configuration.rb +89 -0
  59. data/test/functional/test_createPlan.rb +85 -0
  60. data/test/functional/test_credit.rb +174 -0
  61. data/test/functional/test_deactivate.rb +80 -0
  62. data/test/functional/test_deactivateReversal.rb +56 -0
  63. data/test/functional/test_depositReversal.rb +56 -0
  64. data/test/functional/test_echeckCredit.rb +134 -0
  65. data/test/functional/test_echeckRedeposit.rb +88 -0
  66. data/test/functional/test_echeckSale.rb +177 -0
  67. data/test/functional/test_echeckVerification.rb +127 -0
  68. data/test/functional/test_echeckVoid.rb +41 -0
  69. data/test/functional/test_forceCapture.rb +183 -0
  70. data/test/functional/test_litle_requests.rb +356 -0
  71. data/test/functional/test_load.rb +82 -0
  72. data/test/functional/test_loadReversal.rb +56 -0
  73. data/test/functional/test_override.rb +64 -0
  74. data/test/functional/test_refundReversal.rb +56 -0
  75. data/test/functional/test_sale.rb +259 -0
  76. data/test/functional/test_token.rb +115 -0
  77. data/test/functional/test_unload.rb +82 -0
  78. data/test/functional/test_unloadReversal.rb +56 -0
  79. data/test/functional/test_updateCardValidationNumOnToken.rb +43 -0
  80. data/test/functional/test_updatePlan.rb +58 -0
  81. data/test/functional/test_updateSubscription.rb +76 -0
  82. data/test/functional/test_xmlfields.rb +427 -0
  83. data/test/functional/ts_all.rb +66 -0
  84. data/test/unit/test_LitleAUBatch.rb +216 -0
  85. data/test/unit/test_LitleBatchRequest.rb +643 -0
  86. data/test/unit/test_LitleOnlineRequest.rb +295 -0
  87. data/test/unit/test_LitleRequest.rb +316 -0
  88. data/test/unit/test_LitleTransaction.rb +397 -0
  89. data/test/unit/test_activate.rb +92 -0
  90. data/test/unit/test_activateReversal.rb +44 -0
  91. data/test/unit/test_auth.rb +421 -0
  92. data/test/unit/test_authReversal.rb +82 -0
  93. data/test/unit/test_balanceInquiry.rb +52 -0
  94. data/test/unit/test_cancelSubscription.rb +43 -0
  95. data/test/unit/test_capture.rb +73 -0
  96. data/test/unit/test_captureGivenAuth.rb +188 -0
  97. data/test/unit/test_createPlan.rb +52 -0
  98. data/test/unit/test_credit.rb +342 -0
  99. data/test/unit/test_deactivate.rb +52 -0
  100. data/test/unit/test_deactivateReversal.rb +44 -0
  101. data/test/unit/test_depositReversal.rb +44 -0
  102. data/test/unit/test_echeckCredit.rb +71 -0
  103. data/test/unit/test_echeckRedeposit.rb +94 -0
  104. data/test/unit/test_echeckSale.rb +71 -0
  105. data/test/unit/test_echeckVerification.rb +71 -0
  106. data/test/unit/test_echeckVoid.rb +54 -0
  107. data/test/unit/test_forceCapture.rb +145 -0
  108. data/test/unit/test_load.rb +53 -0
  109. data/test/unit/test_loadReversal.rb +44 -0
  110. data/test/unit/test_refundReversal.rb +44 -0
  111. data/test/unit/test_sale.rb +465 -0
  112. data/test/unit/test_token.rb +144 -0
  113. data/test/unit/test_unload.rb +53 -0
  114. data/test/unit/test_unloadReversal.rb +44 -0
  115. data/test/unit/test_updateCardValidationNumOnToken.rb +80 -0
  116. data/test/unit/test_updatePlan.rb +45 -0
  117. data/test/unit/test_updateSubscription.rb +172 -0
  118. data/test/unit/test_xmlfields.rb +2930 -0
  119. data/test/unit/ts_unit.rb +65 -0
  120. metadata +224 -0
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env ruby
2
+ =begin
3
+ Copyright (c) 2011 Litle & Co.
4
+
5
+ Permission is hereby granted, free of charge, to any person
6
+ obtaining a copy of this software and associated documentation
7
+ files (the "Software"), to deal in the Software without
8
+ restriction, including without limitation the rights to use,
9
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the
11
+ Software is furnished to do so, subject to the following
12
+ conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ OTHER DEALINGS IN THE SOFTWARE.
25
+ =end
26
+
27
+ #Sample Driver
28
+ require_relative '../lib/LitleOnline'
29
+
30
+ saleHash = {
31
+ 'reportGroup'=>'Planets',
32
+ 'id' => '006',
33
+ 'orderId'=>'12344',
34
+ 'amount'=>'6000',
35
+ 'orderSource'=>'ecommerce',
36
+ 'card'=>{
37
+ 'type'=>'VI',
38
+ 'number' =>'4100000000000001',
39
+ 'expDate' =>'1210'
40
+ }}
41
+
42
+ accountUpdateHash = {
43
+ 'reportGroup'=>'Planets',
44
+ 'id'=>'12345',
45
+ 'customerId'=>'0987',
46
+ 'orderId'=>'1234',
47
+ 'card'=>{
48
+ 'type'=>'VI',
49
+ 'number' =>'4100000000000001',
50
+ 'expDate' =>'1210'
51
+ }}
52
+
53
+ rfrHash = {
54
+ 'merchantId'=>101,
55
+ 'postDay'=>'2013-06-04'
56
+ }
57
+
58
+ path = Dir.pwd
59
+
60
+ request = LitleOnline::LitleRequest.new({'sessionId'=>'8675309'})
61
+
62
+ request.create_new_litle_request(path)
63
+ puts "Created new LitleRequest at location: " + path
64
+ start = Time::now
65
+ #create five batches, each with 10 sales
66
+ #5.times{
67
+ batch = LitleOnline::LitleBatchRequest.new
68
+ batch.create_new_batch(path)
69
+
70
+ #add the same sale ten times
71
+ #10.times{
72
+ batch.sale(saleHash)
73
+ #}
74
+ batch.account_update(accountUpdateHash)
75
+
76
+ #close the batch, indicating we plan to add no more transactions
77
+ batch.close_batch()
78
+ #add the batch to the LitleRequest
79
+ request.commit_batch(batch)
80
+ #}
81
+
82
+ request.add_rfr_request(rfrHash)
83
+
84
+
85
+
86
+ # puts "Finished adding batches to LitleRequest at " + request.get_path_to_batches
87
+ #finish the Litle Request, indicating we plan to add no more batches
88
+ request.finish_request
89
+ puts "Generated final XML markup of the LitleRequest"
90
+
91
+ #send the batch files at the given directory over sFTP
92
+ request.send_to_litle
93
+ puts "Dropped off the XML of the LitleRequest over FTP"
94
+ #grab the expected number of responses from the sFTP server and save them to the given path
95
+ request.get_responses_from_server()
96
+ puts "Received the LitleRequest responses from the server"
97
+ #process the responses from the server with a listener which applies the given block
98
+ start = Time::now
99
+ request.process_responses({:transaction_listener => LitleOnline::DefaultLitleListener.new do |transaction|
100
+ type = transaction["type"]
101
+ #if we're dealing with a saleResponse (check the Litle XML Reference Guide!)
102
+ if(type == "saleResponse") then
103
+ #grab an attribute of the parent of the response
104
+ puts "Report Group: " + transaction["reportGroup"]
105
+
106
+ #grab some child elements of the transaction
107
+ puts "Litle Txn Id: " + transaction["litleTxnId"]
108
+ puts "Order Id: " + transaction["orderId"]
109
+ puts "Response: " + transaction["response"]
110
+
111
+ #grab a child element of a child element of the transation
112
+ puts "AVS Result: " + transaction["fraudResult"]["avsResult"]
113
+ puts "Token Response Message: " + transaction["tokenResponse"]["tokenMessage"]
114
+ end
115
+
116
+ if(type == "RFRResponse") then
117
+ puts "RFR Response Code: " + transaction["response"]
118
+ puts "RFR Response Message: " + transaction["message"]
119
+ end
120
+ end})
121
+ stop = Time::now
122
+ puts "Total time: " + (stop - start).to_s
123
+
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin
4
+ Copyright (c) 2011 Litle & Co.
5
+
6
+ Permission is hereby granted, free of charge, to any person
7
+ obtaining a copy of this software and associated documentation
8
+ files (the "Software"), to deal in the Software without
9
+ restriction, including without limitation the rights to use,
10
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the
12
+ Software is furnished to do so, subject to the following
13
+ conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25
+ OTHER DEALINGS IN THE SOFTWARE.
26
+ =end
27
+
28
+ #Sample Driver
29
+ #require 'LitleOnline'
30
+ #include LitleOnline
31
+ require_relative '../lib/LitleOnline'
32
+ hash = {
33
+ 'reportGroup'=>'product1',
34
+ 'orderId'=>'12344',
35
+ 'card'=>{
36
+ 'type'=>'VI',
37
+ 'number' =>'4100000000000001',
38
+ 'expDate' =>'1210'
39
+ },
40
+ 'orderSource'=>'ecommerce',
41
+ 'amount'=>'106'
42
+ }
43
+
44
+ #perform credit transaction
45
+ response= LitleOnline::LitleOnlineRequest.new.credit(hash)
46
+
47
+ #display results
48
+ puts "Message: "+response.message
49
+ puts "Litle Transaction ID: "+response.creditResponse.litleTxnId
@@ -0,0 +1,85 @@
1
+ =begin
2
+ Copyright (c) 2011 Litle & Co.
3
+
4
+ Permission is hereby granted, free of charge, to any person
5
+ obtaining a copy of this software and associated documentation
6
+ files (the "Software"), to deal in the Software without
7
+ restriction, including without limitation the rights to use,
8
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the
10
+ Software is furnished to do so, subject to the following
11
+ 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
18
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
+ OTHER DEALINGS IN THE SOFTWARE.
24
+ =end
25
+
26
+ #
27
+ # Used for all transmission to Litle over HTTP or HTTPS
28
+ # works with or without an HTTP proxy
29
+ #
30
+ # URL and proxy server settings are derived from the configuration file
31
+ #
32
+ module LitleOnline
33
+ class Communications
34
+ ##For http or https post with or without a proxy
35
+ def Communications.http_post(post_data,config_hash)
36
+
37
+ proxy_addr = config_hash['proxy_addr']
38
+ proxy_port = config_hash['proxy_port']
39
+ litle_url = config_hash['url']
40
+
41
+ # setup https or http post
42
+ url = URI.parse(litle_url)
43
+
44
+ response_xml = nil
45
+ https = Net::HTTP.new(url.host, url.port, proxy_addr, proxy_port)
46
+ if(url.scheme == 'https')
47
+ https.use_ssl = url.scheme=='https'
48
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
49
+ https.ca_file = File.join(File.dirname(__FILE__), "cacert.pem")
50
+ end
51
+ https.start { |http|
52
+ response = http.request_post(url.path, post_data.to_s, {'Content-Type'=>'text/xml','Connection'=>'close'})
53
+ response_xml = response
54
+ }
55
+
56
+ # validate response, only an HTTP 200 will work, redirects are not followed
57
+ case response_xml
58
+ when Net::HTTPOK
59
+ return response_xml.body
60
+ else
61
+ raise("Error with http http_post_request, code:" + response_xml.header.code)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ =begin
68
+ NOTES ON HTTP TIMEOUT
69
+
70
+ Litle & Co. optimizes our systems to ensure the return of responses as quickly as possible, some portions of the process are beyond our control.
71
+ The round-trip time of an Authorization can be broken down into three parts, as follows:
72
+ 1. Transmission time (across the internet) to Litle & Co. and back to the merchant
73
+ 2. Processing time by the authorization provider
74
+ 3. Processing time by Litle
75
+ Under normal operating circumstances, the transmission time to and from Litle does not exceed 0.6 seconds
76
+ and processing overhead by Litle occurs in 0.1 seconds.
77
+ Typically, the processing time by the card association or authorization provider can take between 0.5 and 3 seconds,
78
+ but some percentage of transactions may take significantly longer.
79
+
80
+ Because the total processing time can vary due to a number of factors, Litle & Co. recommends using a minimum timeout setting of
81
+ 60 seconds to accomodate Sale transactions and 30 seconds if you are not utilizing Sale tranactions.
82
+
83
+ These settings should ensure that you do not frequently disconnect prior to receiving a valid authorization causing dropped orders
84
+ and/or re-auths and duplicate auths.
85
+ =end
@@ -0,0 +1,67 @@
1
+ =begin
2
+ Copyright (c) 2011 Litle & Co.
3
+
4
+ Permission is hereby granted, free of charge, to any person
5
+ obtaining a copy of this software and associated documentation
6
+ files (the "Software"), to deal in the Software without
7
+ restriction, including without limitation the rights to use,
8
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the
10
+ Software is furnished to do so, subject to the following
11
+ 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
18
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
+ OTHER DEALINGS IN THE SOFTWARE.
24
+ =end
25
+
26
+ require 'yaml'
27
+ #
28
+ # Loads the configuration from a file
29
+ #
30
+ module LitleOnline
31
+ class Configuration
32
+ class << self
33
+ # External logger, if specified
34
+ attr_accessor :logger
35
+ end
36
+
37
+ def config
38
+ begin
39
+ if !(ENV['LITLE_CONFIG_DIR'].nil?)
40
+ config_file = ENV['LITLE_CONFIG_DIR'] + "/.litle_SDK_config.yml"
41
+ else
42
+ config_file = ENV['HOME'] + "/.litle_SDK_config.yml"
43
+ end
44
+ #if Env variable exist, then just override the data from config file
45
+ if (File.exists?(config_file))
46
+ datas=YAML.load_file(config_file)
47
+ else
48
+ environments = EnvironmentVariables.new
49
+ datas={}
50
+ environments.instance_variables.each {|var| datas[var.to_s.delete("@")] = environments.instance_variable_get(var) }
51
+ end
52
+ datas.each {|key,value| setENV(key,datas)}
53
+ return datas
54
+ rescue
55
+ return {}
56
+ end
57
+
58
+ end
59
+ def setENV(key,datas)
60
+ if !(ENV['litle_'+key].nil?)
61
+ datas[key]=ENV['litle_'+key]
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,22 @@
1
+ module LitleOnline
2
+ class EnvironmentVariables
3
+ def initialize
4
+ #load configuration data
5
+ @user = ''
6
+ @password = ''
7
+ @currency_merchant_map = ''
8
+ @default_report_group = 'Default Report Group'
9
+ @url = ''
10
+ @proxy_addr = ''
11
+ @proxy_port = ''
12
+ @sftp_username = ''
13
+ @sftp_password = ''
14
+ @sftp_url = ''
15
+ @fast_url = ''
16
+ @fast_port = ''
17
+ @printxml = false
18
+ @timeout = 65
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,562 @@
1
+ =begin
2
+ Copyright (c) 2011 Litle & Co.
3
+
4
+ Permission is hereby granted, free of charge, to any person
5
+ obtaining a copy of this software and associated documentation
6
+ files (the "Software"), to deal in the Software without
7
+ restriction, including without limitation the rights to use,
8
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the
10
+ Software is furnished to do so, subject to the following
11
+ 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
18
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
+ OTHER DEALINGS IN THE SOFTWARE.
24
+ =end
25
+ require_relative 'Configuration'
26
+
27
+ #
28
+ # This class creates a new batch to which Litle XML transactions are added.
29
+ # The batch is stored in the local file system until it is ready to be sent
30
+ # to Litle.
31
+ #
32
+ module LitleOnline
33
+
34
+ class LitleBatchRequest
35
+ include XML::Mapping
36
+ def initialize
37
+ #load configuration data
38
+ @config_hash = Configuration.new.config
39
+
40
+ @txn_counts = { :id=>nil,
41
+ :merchantId=>nil,
42
+ :auth=>{ :numAuths=>0, :authAmount=>0 },
43
+ :sale=>{ :numSales=>0, :saleAmount=>0 },
44
+ :credit=>{ :numCredits=>0, :creditAmount=>0 },
45
+ :numTokenRegistrations=>0,
46
+ :captureGivenAuth=>{ :numCaptureGivenAuths=>0, :captureGivenAuthAmount=>0 },
47
+ :forceCapture=>{ :numForceCaptures=>0, :forceCaptureAmount=>0 },
48
+ :authReversal=>{ :numAuthReversals=>0, :authReversalAmount=>0 },
49
+ :capture=>{ :numCaptures=>0, :captureAmount=>0 },
50
+ :echeckVerification=>{ :numEcheckVerification=>0, :echeckVerificationAmount=>0 },
51
+ :echeckCredit=>{ :numEcheckCredit=>0, :echeckCreditAmount=>0 },
52
+ :numEcheckRedeposit=>0,
53
+ :echeckSale=>{ :numEcheckSales=>0, :echeckSalesAmount=>0 },
54
+ :numUpdateCardValidationNumOnTokens=>0,
55
+ :numAccountUpdates=>0,
56
+ :total=>0,
57
+ :numCancelSubscriptions=>0,
58
+ :numUpdateSubscriptions=>0,
59
+ :numCreatePlans=>0,
60
+ :numUpdatePlans=>0,
61
+ :activate=>{:numActivates=>0, :activateAmount=>0},
62
+ :numDeactivates=>0,
63
+ :load=>{:numLoads=>0, :loadAmount=>0},
64
+ :unload=>{:numUnloads=>0, :unloadAmount=>0},
65
+ :numBalanceInquirys=>0,
66
+ :merchantSdk=>nil
67
+ }
68
+ @litle_txn = LitleTransaction.new
69
+ @path_to_batch = nil
70
+ @txn_file = nil
71
+ @MAX_TXNS_IN_BATCH = 100000
72
+ @au_batch = nil
73
+ end
74
+
75
+ def create_new_batch(path)
76
+ ts = Time::now.to_i.to_s
77
+ begin
78
+ ts += Time::now.nsec.to_s
79
+ rescue NoMethodError # ruby 1.8.7 fix
80
+ ts += Time::now.usec.to_s
81
+ end
82
+ if(File.file?(path)) then
83
+ raise ArgumentError, "Entered a file not a path."
84
+ end
85
+
86
+ if(path[-1,1] != '/' && path[-1,1] != '\\') then
87
+ path = path + File::SEPARATOR
88
+ end
89
+ if(!File.directory?(path)) then
90
+ Dir.mkdir(path)
91
+ end
92
+
93
+ @path_to_batch = path + 'batch_' + ts
94
+ @txn_file = @path_to_batch + '_txns'
95
+ if(File.file?(@path_to_batch)) then
96
+ create_new_batch(path)
97
+ return
98
+ end
99
+ File.open(@path_to_batch, 'a+') do |file|
100
+ file.write("")
101
+ end
102
+ File.open(@txn_file, 'a+') do |file|
103
+ file.write("")
104
+ end
105
+ end
106
+
107
+ def has_transactions?
108
+ !@txn_counts[:total].eql?(0)
109
+ end
110
+
111
+ def open_existing_batch(pathToBatchFile)
112
+ if(!File.file?(pathToBatchFile)) then
113
+ raise ArgumentError, "No batch file exists at the passed location!"
114
+ end
115
+
116
+ if((pathToBatchFile =~ /batch_\d+.closed-\d+\z/) != nil) then
117
+ raise ArgumentError, "The passed batch file is closed!"
118
+ end
119
+
120
+ @txn_file = pathToBatchFile + '_txns'
121
+ @path_to_batch = pathToBatchFile
122
+ temp_counts = File.open(@path_to_batch, "rb") { |f| Marshal.load(f) }
123
+ # woops, they opened an AU batch
124
+ if(temp_counts[:numAccountUpdates] != 0) then
125
+ au_batch = LitleAUBatch.new
126
+ au_batch.open_existing_batch(pathToBatchFile)
127
+ initialize()
128
+ create_new_batch(File.dirname(pathToBatchFile))
129
+ @au_batch = au_batch
130
+ elsif
131
+ @txn_counts = temp_counts
132
+ end
133
+ end
134
+
135
+
136
+ def au_batch?
137
+ !@au_batch.nil?
138
+ end
139
+
140
+ def close_batch(txn_location = @txn_file)
141
+ header = build_batch_header(@txn_counts)
142
+ File.rename(@path_to_batch, @path_to_batch + '.closed-' + @txn_counts[:total].to_s)
143
+ @path_to_batch = @path_to_batch + '.closed-' + @txn_counts[:total].to_s
144
+ File.open(@path_to_batch, 'w') do |fo|
145
+ # fo.puts header
146
+ put_header = !au_batch? || has_transactions?
147
+ fo.puts header if put_header
148
+ File.foreach(txn_location) do |li|
149
+ fo.puts li
150
+ end
151
+ # fo.puts('</batchRequest>')
152
+ fo.puts('</batchRequest>') if put_header
153
+ end
154
+ File.delete(txn_location)
155
+ if(@au_batch != nil) then
156
+ @au_batch.close_batch
157
+ end
158
+
159
+ end
160
+
161
+ def authorization(options)
162
+ transaction = @litle_txn.authorization(options)
163
+ @txn_counts[:auth][:numAuths] += 1
164
+ @txn_counts[:auth][:authAmount] += options['amount'].to_i
165
+
166
+ add_txn_to_batch(transaction, :authorization, options)
167
+ end
168
+
169
+ def sale(options)
170
+ transaction = @litle_txn.sale(options)
171
+ @txn_counts[:sale][:numSales] += 1
172
+ @txn_counts[:sale][:saleAmount] += options['amount'].to_i
173
+
174
+ add_txn_to_batch(transaction, :sale, options)
175
+ end
176
+
177
+ def credit(options)
178
+ transaction = @litle_txn.credit(options)
179
+ @txn_counts[:credit][:numCredits] += 1
180
+ @txn_counts[:credit][:creditAmount] += options['amount'].to_i
181
+
182
+ add_txn_to_batch(transaction, :credit, options)
183
+ end
184
+
185
+ def auth_reversal(options)
186
+ transaction = @litle_txn.auth_reversal(options)
187
+ @txn_counts[:authReversal][:numAuthReversals] += 1
188
+ @txn_counts[:authReversal][:authReversalAmount] += options['amount'].to_i
189
+
190
+ add_txn_to_batch(transaction, :authReversal, options)
191
+ end
192
+
193
+ def cancel_subscription(options)
194
+ transaction = @litle_txn.cancel_subscription(options)
195
+ @txn_counts[:numCancelSubscriptions] += 1
196
+
197
+ add_txn_to_batch(transaction, :cancelSubscription, options)
198
+ end
199
+
200
+ def update_subscription(options)
201
+ transaction = @litle_txn.update_subscription(options)
202
+ @txn_counts[:numUpdateSubscriptions] += 1
203
+
204
+ add_txn_to_batch(transaction, :updateSubscription, options)
205
+ end
206
+
207
+ def create_plan(options)
208
+ transaction = @litle_txn.create_plan(options)
209
+ @txn_counts[:numCreatePlans] += 1
210
+
211
+ add_txn_to_batch(transaction, :createPlan, options)
212
+ end
213
+
214
+ def update_plan(options)
215
+ transaction = @litle_txn.update_plan(options)
216
+ @txn_counts[:numUpdatePlans] += 1
217
+
218
+ add_txn_to_batch(transaction, :updatePlan, options)
219
+ end
220
+
221
+ def activate(options)
222
+ transaction = @litle_txn.activate(options)
223
+ @txn_counts[:numActivates] += 1
224
+
225
+ add_txn_to_batch(transaction, :activate, options)
226
+ end
227
+
228
+ def deactivate(options)
229
+ transaction = @litle_txn.deactivate(options)
230
+ @txn_counts[:numDeactivates] += 1
231
+
232
+ add_txn_to_batch(transaction, :deactivate, options)
233
+ end
234
+
235
+ def load_request(options)
236
+ transaction = @litle_txn.load_request(options)
237
+ @txn_counts[:numLoads] += 1
238
+
239
+ add_txn_to_batch(transaction, :load, options)
240
+ end
241
+
242
+ def unload_request(options)
243
+ transaction = @litle_txn.unload_request(options)
244
+ @txn_counts[:numunLoads] += 1
245
+
246
+ add_txn_to_batch(transaction, :unload, options)
247
+ end
248
+
249
+ def balance_inquiry(options)
250
+ transaction = @litle_txn.balance_inquiry(options)
251
+ @txn_counts[:numBalanceInquirys] += 1
252
+
253
+ add_txn_to_batch(transaction, :balanceInquirys, options)
254
+ end
255
+ def register_token_request(options)
256
+ transaction = @litle_txn.register_token_request(options)
257
+ @txn_counts[:numTokenRegistrations] += 1
258
+
259
+ add_txn_to_batch(transaction, :numTokenRegistrations, options)
260
+ end
261
+
262
+ def update_card_validation_num_on_token(options)
263
+ transaction = @litle_txn.update_card_validation_num_on_token(options)
264
+ @txn_counts[:numUpdateCardValidationNumOnTokens] += 1
265
+
266
+ add_txn_to_batch(transaction, :numUpdateCardValidationNumOnTokens, options)
267
+ end
268
+
269
+ def force_capture(options)
270
+ transaction = @litle_txn.force_capture(options)
271
+ @txn_counts[:forceCapture][:numForceCaptures] += 1
272
+ @txn_counts[:forceCapture][:forceCaptureAmount] += options['amount'].to_i
273
+
274
+ add_txn_to_batch(transaction, :forceCapture, options)
275
+ end
276
+
277
+ def capture(options)
278
+ transaction = @litle_txn.capture(options)
279
+ @txn_counts[:capture][:numCaptures] += 1
280
+ @txn_counts[:capture][:captureAmount] += options['amount'].to_i
281
+
282
+ add_txn_to_batch(transaction, :capture, options)
283
+ end
284
+
285
+ def capture_given_auth(options)
286
+ transaction = @litle_txn.capture_given_auth(options)
287
+ @txn_counts[:captureGivenAuth][:numCaptureGivenAuths] += 1
288
+ @txn_counts[:captureGivenAuth][:captureGivenAuthAmount] += options['amount'].to_i
289
+
290
+ add_txn_to_batch(transaction, :captureGivenAuth, options)
291
+ end
292
+
293
+ def echeck_verification(options)
294
+ transaction = @litle_txn.echeck_verification(options)
295
+ @txn_counts[:echeckVerification][:numEcheckVerification] += 1
296
+ @txn_counts[:echeckVerification][:echeckVerificationAmount] += options['amount'].to_i
297
+
298
+ add_txn_to_batch(transaction, :echeckVerification, options)
299
+ end
300
+
301
+ def echeck_credit(options)
302
+ transaction = @litle_txn.echeck_credit(options)
303
+ @txn_counts[:echeckCredit][:numEcheckCredit] += 1
304
+ @txn_counts[:echeckCredit][:echeckCreditAmount] += options['amount'].to_i
305
+
306
+ add_txn_to_batch(transaction, :echeckCredit, options)
307
+ end
308
+
309
+ def echeck_redeposit(options)
310
+ transaction = @litle_txn.echeck_redeposit(options)
311
+ @txn_counts[:numEcheckRedeposit] += 1
312
+
313
+ add_txn_to_batch(transaction, :echeckRedeposit, options)
314
+ end
315
+
316
+ def echeck_sale(options)
317
+ transaction = @litle_txn.echeck_sale(options)
318
+ @txn_counts[:echeckSale][:numEcheckSales] += 1
319
+ @txn_counts[:echeckSale][:echeckSalesAmount] += options['amount'].to_i
320
+
321
+ add_txn_to_batch(transaction, :echeckSale, options)
322
+ end
323
+
324
+ def account_update(options)
325
+
326
+ if(@au_batch == nil) then
327
+ @au_batch = LitleAUBatch.new
328
+ @au_batch.create_new_batch(File.dirname(@path_to_batch))
329
+ end
330
+ @au_batch.account_update(options)
331
+ end
332
+
333
+ def get_counts_and_amounts
334
+ return @txn_counts
335
+ end
336
+ def get_batch_name
337
+ return @path_to_batch
338
+ end
339
+ def get_au_batch
340
+ return @au_batch
341
+ end
342
+
343
+
344
+ private
345
+
346
+ def add_txn_to_batch(transaction, type, options)
347
+ @txn_counts[:total] += 1
348
+ xml = transaction.save_to_xml.to_s
349
+ File.open(@txn_file, 'a+') do |file|
350
+ file.write(xml)
351
+ end
352
+ # save counts and amounts to batch file
353
+ File.open(@path_to_batch, 'wb'){|f| Marshal.dump(@txn_counts, f)}
354
+ if(@txn_counts[:total] >= @MAX_TXNS_IN_BATCH) then
355
+ close_batch()
356
+ path = File.dirname(@path_to_batch)
357
+ initialize
358
+ create_new_batch(path)
359
+ end
360
+ end
361
+
362
+ def build_batch_header(options)
363
+ request = BatchRequest.new
364
+
365
+ request.numAuths = @txn_counts[:auth][:numAuths]
366
+ request.authAmount = @txn_counts[:auth][:authAmount]
367
+ request.numSales = @txn_counts[:sale][:numSales]
368
+ request.saleAmount = @txn_counts[:sale][:saleAmount]
369
+ request.numCredits = @txn_counts[:credit][:numCredits]
370
+ request.creditAmount = @txn_counts[:credit][:creditAmount]
371
+ request.numTokenRegistrations = @txn_counts[:numTokenRegistrations]
372
+ request.numCaptureGivenAuths = @txn_counts[:captureGivenAuth][:numCaptureGivenAuths]
373
+ request.captureGivenAuthAmount = @txn_counts[:captureGivenAuth][:captureGivenAuthAmount]
374
+ request.numForceCaptures = @txn_counts[:forceCapture][:numForceCaptures]
375
+ request.forceCaptureAmount = @txn_counts[:forceCapture][:forceCaptureAmount]
376
+ request.numAuthReversals = @txn_counts[:authReversal][:numAuthReversals]
377
+ request.authReversalAmount = @txn_counts[:authReversal][:authReversalAmount]
378
+ request.numCaptures = @txn_counts[:capture][:numCaptures]
379
+ request.captureAmount = @txn_counts[:capture][:captureAmount]
380
+ request.numEcheckSales = @txn_counts[:echeckSale][:numEcheckSales]
381
+ request.echeckSalesAmount = @txn_counts[:echeckSale][:echeckSalesAmount]
382
+ request.numEcheckRedeposit = @txn_counts[:numEcheckRedeposit]
383
+ request.numEcheckCredit = @txn_counts[:echeckCredit][:numEcheckCredit]
384
+ request.echeckCreditAmount = @txn_counts[:echeckCredit][:echeckCreditAmount]
385
+ request.numEcheckVerification = @txn_counts[:echeckVerification][:numEcheckVerification]
386
+ request.echeckVerificationAmount = @txn_counts[:echeckVerification][:echeckVerificationAmount]
387
+ request.numAccountUpdates = @txn_counts[:numAccountUpdates]
388
+ request.merchantId = get_merchant_id(options)
389
+ request.id = @txn_counts[:id]
390
+ request.numUpdateCardValidationNumOnTokens = @txn_counts[:numUpdateCardValidationNumOnTokens]
391
+ request.numCancelSubscriptions =@txn_counts[:numCancelSubscriptions]
392
+ request.numUpdateSubscriptions =@txn_counts[:numUpdateSubscriptions]
393
+ request.numCreatePlans =@txn_counts[:numCreatePlans]
394
+ request.numUpdatePlans =@txn_counts[:numUpdatePlans]
395
+ request.numActivates =@txn_counts[:activate][:numActivates]
396
+ request.numDeactivates =@txn_counts[:numDeactivates]
397
+ request.activateAmount =@txn_counts[:activate][:activateAmount]
398
+ request.numLoads =@txn_counts[:load][:numLoads]
399
+ request.loadAmount =@txn_counts[:load][:loadAmount]
400
+ request.numUnloads =@txn_counts[:unload][:numLoads]
401
+ request.unloadAmount =@txn_counts[:unload][:unloadAmount]
402
+ request.numBalanceInquirys =@txn_counts[:numBalanceInquirys]
403
+ header = request.save_to_xml.to_s
404
+ header['/>']= '>'
405
+
406
+ return header
407
+ end
408
+
409
+ def get_config(field, options)
410
+ options[field.to_s] == nil ? @config_hash[field.to_s] : options[field.to_s]
411
+ end
412
+
413
+ def get_merchant_id(options)
414
+ options['merchantId'] || @config_hash['currency_merchant_map']['DEFAULT']
415
+ end
416
+ end
417
+
418
+ private
419
+
420
+ # IF YOU ARE A MERCHANT, DON'T LOOK HERE. IT'S SCARY!
421
+
422
+ class LitleAUBatch
423
+ include XML::Mapping
424
+ def initialize
425
+ #load configuration data
426
+ @config_hash = Configuration.new.config
427
+
428
+ @txn_counts = { :id=>nil,
429
+ :merchantId=>nil,
430
+ :numAccountUpdates=>0,
431
+ :total=>0
432
+ }
433
+ @litle_txn = LitleTransaction.new
434
+ @path_to_batch = nil
435
+ @txn_file = nil
436
+ @MAX_TXNS_IN_BATCH = 100000
437
+ end
438
+
439
+ def create_new_batch(path)
440
+ ts = Time::now.to_i.to_s
441
+ begin
442
+ ts += Time::now.nsec.to_s
443
+ rescue NoMethodError # ruby 1.8.7 fix
444
+ ts += Time::now.usec.to_s
445
+ end
446
+
447
+ if(File.file?(path)) then
448
+ raise ArgumentError, "Entered a file not a path."
449
+ end
450
+
451
+ if(path[-1,1] != '/' && path[-1,1] != '\\') then
452
+ path = path + File::SEPARATOR
453
+ end
454
+ if(!File.directory?(path)) then
455
+ Dir.mkdir(path)
456
+ end
457
+
458
+ @path_to_batch = path + 'batch_' + ts
459
+ @txn_file = @path_to_batch + '_txns'
460
+ if(File.file?(@path_to_batch)) then
461
+ create_new_batch(path)
462
+ return
463
+ end
464
+ File.open(@path_to_batch, 'a+') do |file|
465
+ file.write("")
466
+ end
467
+ File.open(@txn_file, 'a+') do |file|
468
+ file.write("")
469
+ end
470
+ end
471
+
472
+ def open_existing_batch(pathToBatchFile)
473
+ if(!File.file?(pathToBatchFile)) then
474
+ raise ArgumentError, "No batch file exists at the passed location!"
475
+ end
476
+
477
+ if((pathToBatchFile =~ /batch_\d+.closed-\d+\z/) != nil) then
478
+ raise ArgumentError, "The passed batch file is closed!"
479
+ end
480
+
481
+ @txn_file = pathToBatchFile + '_txns'
482
+ @path_to_batch = pathToBatchFile
483
+ temp_counts = File.open(@path_to_batch, "rb") { |f| Marshal.load(f) }
484
+ if(temp_counts.keys.size > 4) then
485
+ raise RuntimeException, "Tried to open an AU batch with a non-AU batch file"
486
+ end
487
+
488
+ @txn_counts[:id] = temp_counts[:id]
489
+ @txn_counts[:merchantId] = temp_counts[:merchantId]
490
+ @txn_counts[:numAccountUpdates] = temp_counts[:numAccountUpdates]
491
+ @txn_counts[:total] = temp_counts[:total]
492
+ end
493
+
494
+ def close_batch(txn_location = @txn_file)
495
+ header = build_batch_header(@txn_counts)
496
+
497
+ File.rename(@path_to_batch, @path_to_batch + '.closed-' + @txn_counts[:total].to_s)
498
+ @path_to_batch = @path_to_batch + '.closed-' + @txn_counts[:total].to_s
499
+ File.open(@path_to_batch, 'w') do |fo|
500
+ fo.puts header
501
+ File.foreach(txn_location) do |li|
502
+ fo.puts li
503
+ end
504
+ fo.puts('</batchRequest>')
505
+ end
506
+ File.delete(txn_location)
507
+ end
508
+
509
+ def account_update(options)
510
+ transaction = @litle_txn.account_update(options)
511
+ @txn_counts[:numAccountUpdates] += 1
512
+
513
+ add_txn_to_batch(transaction, :authorization, options)
514
+ end
515
+
516
+ def get_counts_and_amounts
517
+ return @txn_counts
518
+ end
519
+ def get_batch_name
520
+ return @path_to_batch
521
+ end
522
+
523
+ private
524
+
525
+ def add_txn_to_batch(transaction, type, options)
526
+ @txn_counts[:total] += 1
527
+ xml = transaction.save_to_xml.to_s
528
+ File.open(@txn_file, 'a+') do |file|
529
+ file.write(xml)
530
+ end
531
+ # save counts and amounts to batch file
532
+ File.open(@path_to_batch, 'wb'){|f| Marshal.dump(@txn_counts, f)}
533
+ if(@txn_counts[:total] >= @MAX_TXNS_IN_BATCH) then
534
+ close_batch()
535
+ path = File.dirname(@path_to_batch)
536
+ initialize
537
+ create_new_batch(path)
538
+ end
539
+ end
540
+
541
+ def build_batch_header(options)
542
+ request = BatchRequest.new
543
+
544
+ request.numAccountUpdates = @txn_counts[:numAccountUpdates]
545
+ request.merchantId = get_merchant_id(options)
546
+ request.id = @txn_counts[:id]
547
+
548
+ header = request.save_to_xml.to_s
549
+ header['/>']= '>'
550
+
551
+ return header
552
+ end
553
+
554
+ def get_config(field, options)
555
+ options[field.to_s] == nil ? @config_hash[field.to_s] : options[field.to_s]
556
+ end
557
+
558
+ def get_merchant_id(options)
559
+ options['merchantId'] || @config_hash['currency_merchant_map']['DEFAULT']
560
+ end
561
+ end
562
+ end