vantiv 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 (128) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +4 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +2 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +354 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +19 -0
  12. data/bin/generate_sandbox_fixtures +23 -0
  13. data/bin/paypage_server +20 -0
  14. data/bin/setup +7 -0
  15. data/cert_fixtures/L_AC_1.json +27 -0
  16. data/cert_fixtures/L_AC_1A.json +8 -0
  17. data/cert_fixtures/L_AC_2.json +27 -0
  18. data/cert_fixtures/L_AC_2A.json +8 -0
  19. data/cert_fixtures/L_AC_3.json +26 -0
  20. data/cert_fixtures/L_AC_3A.json +8 -0
  21. data/cert_fixtures/L_AC_4.json +25 -0
  22. data/cert_fixtures/L_AC_4A.json +8 -0
  23. data/cert_fixtures/L_AC_5.json +18 -0
  24. data/cert_fixtures/L_AC_5A.json +8 -0
  25. data/cert_fixtures/L_AC_6.json +27 -0
  26. data/cert_fixtures/L_AC_7.json +26 -0
  27. data/cert_fixtures/L_AC_8.json +26 -0
  28. data/cert_fixtures/L_AC_9.json +26 -0
  29. data/cert_fixtures/L_AR_1.json +27 -0
  30. data/cert_fixtures/L_AR_1A.json +9 -0
  31. data/cert_fixtures/L_AR_1B.json +8 -0
  32. data/cert_fixtures/L_AR_2.json +27 -0
  33. data/cert_fixtures/L_AR_2A.json +8 -0
  34. data/cert_fixtures/L_AR_3.json +26 -0
  35. data/cert_fixtures/L_AR_3A.json +8 -0
  36. data/cert_fixtures/L_AR_4.json +25 -0
  37. data/cert_fixtures/L_AR_4A.json +9 -0
  38. data/cert_fixtures/L_AR_4B.json +9 -0
  39. data/cert_fixtures/L_AR_5.json +17 -0
  40. data/cert_fixtures/L_AR_5A.json +9 -0
  41. data/cert_fixtures/L_EP_1.json +15 -0
  42. data/cert_fixtures/L_EP_2.json +15 -0
  43. data/cert_fixtures/L_EP_3.json +11 -0
  44. data/cert_fixtures/L_EP_4.json +15 -0
  45. data/cert_fixtures/L_EP_5.json +15 -0
  46. data/cert_fixtures/L_EP_6.json +15 -0
  47. data/cert_fixtures/L_EP_7.json +15 -0
  48. data/cert_fixtures/L_RC_1.json +27 -0
  49. data/cert_fixtures/L_RC_1A.json +8 -0
  50. data/cert_fixtures/L_RC_1B.json +8 -0
  51. data/cert_fixtures/L_RC_2.json +27 -0
  52. data/cert_fixtures/L_RC_2A.json +8 -0
  53. data/cert_fixtures/L_RC_2B.json +8 -0
  54. data/cert_fixtures/L_RC_3.json +26 -0
  55. data/cert_fixtures/L_RC_3A.json +8 -0
  56. data/cert_fixtures/L_RC_3B.json +8 -0
  57. data/cert_fixtures/L_RC_4.json +25 -0
  58. data/cert_fixtures/L_RC_4A.json +8 -0
  59. data/cert_fixtures/L_RC_4B.json +8 -0
  60. data/cert_fixtures/L_RC_5.json +18 -0
  61. data/cert_fixtures/L_RC_5A.json +8 -0
  62. data/cert_fixtures/L_RC_5B.json +8 -0
  63. data/cert_fixtures/L_RC_6.json +27 -0
  64. data/cert_fixtures/L_RC_6A.json +8 -0
  65. data/cert_fixtures/L_RC_7.json +26 -0
  66. data/cert_fixtures/L_RC_7A.json +8 -0
  67. data/cert_fixtures/L_RC_8.json +18 -0
  68. data/cert_fixtures/L_S_1.json +27 -0
  69. data/cert_fixtures/L_S_2.json +27 -0
  70. data/cert_fixtures/L_S_3.json +26 -0
  71. data/cert_fixtures/L_S_4.json +25 -0
  72. data/cert_fixtures/L_S_5.json +18 -0
  73. data/cert_fixtures/L_S_6.json +26 -0
  74. data/cert_fixtures/L_S_7.json +26 -0
  75. data/cert_fixtures/L_S_8.json +26 -0
  76. data/cert_fixtures/L_S_9.json +26 -0
  77. data/cert_fixtures/L_T_1.json +11 -0
  78. data/cert_fixtures/L_T_10.json +19 -0
  79. data/cert_fixtures/L_T_11.json +19 -0
  80. data/cert_fixtures/L_T_2.json +11 -0
  81. data/cert_fixtures/L_T_3.json +12 -0
  82. data/cert_fixtures/L_T_4.json +11 -0
  83. data/cert_fixtures/L_T_6.json +19 -0
  84. data/cert_fixtures/L_T_7.json +19 -0
  85. data/cert_fixtures/L_T_8.json +19 -0
  86. data/cert_fixtures/L_T_9.json +20 -0
  87. data/cert_fixtures/L_V_1.json +27 -0
  88. data/cert_fixtures/L_V_1A.json +8 -0
  89. data/cert_fixtures/L_V_1B.json +8 -0
  90. data/cert_fixtures/L_V_1C.json +8 -0
  91. data/cert_fixtures/L_V_2.json +27 -0
  92. data/cert_fixtures/L_V_2A.json +8 -0
  93. data/cert_fixtures/L_V_3.json +18 -0
  94. data/cert_fixtures/L_V_3A.json +8 -0
  95. data/cert_fixtures/L_V_4.json +26 -0
  96. data/cert_fixtures/L_V_4A.json +8 -0
  97. data/exe/vantiv-certify-app +23 -0
  98. data/lib/vantiv.rb +152 -0
  99. data/lib/vantiv/api.rb +10 -0
  100. data/lib/vantiv/api/capture_response.rb +26 -0
  101. data/lib/vantiv/api/endpoints.rb +15 -0
  102. data/lib/vantiv/api/live_transaction_response.rb +50 -0
  103. data/lib/vantiv/api/request.rb +75 -0
  104. data/lib/vantiv/api/request_body.rb +111 -0
  105. data/lib/vantiv/api/request_body_generator.rb +43 -0
  106. data/lib/vantiv/api/response.rb +50 -0
  107. data/lib/vantiv/api/tied_transaction_response.rb +30 -0
  108. data/lib/vantiv/api/tokenization_response.rb +45 -0
  109. data/lib/vantiv/certification/cert_request_body_compiler.rb +38 -0
  110. data/lib/vantiv/certification/paypage_driver.rb +54 -0
  111. data/lib/vantiv/certification/paypage_server.rb +70 -0
  112. data/lib/vantiv/certification/response_cache.rb +29 -0
  113. data/lib/vantiv/certification/validation_test_runner.rb +103 -0
  114. data/lib/vantiv/certification/views/index.html.erb +76 -0
  115. data/lib/vantiv/environment.rb +14 -0
  116. data/lib/vantiv/mocked_sandbox.rb +29 -0
  117. data/lib/vantiv/mocked_sandbox/api_request.rb +53 -0
  118. data/lib/vantiv/mocked_sandbox/fixture_generator.rb +48 -0
  119. data/lib/vantiv/mocked_sandbox/fixtures/tokenize_by_direct_post--4457002100000005.json.erb +23 -0
  120. data/lib/vantiv/mocked_sandbox/fixtures/tokenize_by_direct_post--4457010000000009.json.erb +23 -0
  121. data/lib/vantiv/mocked_sandbox/fixtures/tokenize_by_direct_post--4457010010900010.json.erb +21 -0
  122. data/lib/vantiv/mocked_sandbox/fixtures/tokenize_by_direct_post--5112001600000006.json.erb +23 -0
  123. data/lib/vantiv/mocked_sandbox/fixtures/tokenize_by_direct_post--5112001900000003.json.erb +23 -0
  124. data/lib/vantiv/paypage.rb +7 -0
  125. data/lib/vantiv/test_card.rb +94 -0
  126. data/lib/vantiv/version.rb +3 -0
  127. data/vantiv-ruby.gemspec +29 -0
  128. metadata +271 -0
@@ -0,0 +1,75 @@
1
+ module Vantiv
2
+ class Api::Request
3
+
4
+ attr_reader :body
5
+
6
+ def initialize(endpoint:, body:, response_object:)
7
+ @endpoint = endpoint
8
+ @body = body.to_json
9
+ @response_object = response_object
10
+ self.retry_count = 0
11
+ end
12
+
13
+ def run
14
+ vantiv_response = run_request_with_retries
15
+ response_object.load(vantiv_response)
16
+ response_object
17
+ end
18
+
19
+ def run_request
20
+ http = Net::HTTP.new(uri.host, uri.port)
21
+ http.use_ssl = true
22
+
23
+ request = Net::HTTP::Post.new(uri.request_uri, header)
24
+ request.body = body
25
+ raw_response = http.request(request)
26
+ parsed_body = JSON.parse(raw_response.body)
27
+ {
28
+ httpok: raw_response.code_type == Net::HTTPOK,
29
+ http_response_code: raw_response.code,
30
+ body: parsed_body
31
+ }
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :endpoint, :response_object
37
+ attr_accessor :retry_count
38
+
39
+ def header
40
+ {
41
+ "Content-Type" =>"application/json",
42
+ "Authorization" => "VANTIV license=\"#{Vantiv.license_id}\""
43
+ }
44
+ end
45
+
46
+ def uri
47
+ @uri ||= URI.parse("#{root_uri}/#{endpoint}")
48
+ end
49
+
50
+ def root_uri
51
+ if Vantiv::Environment.production?
52
+ "https://apis.vantiv.com"
53
+ elsif Vantiv::Environment.certification?
54
+ "https://apis.cert.vantiv.com"
55
+ end
56
+ end
57
+
58
+ def max_retries_exceeded?
59
+ retry_count > 3
60
+ end
61
+
62
+ def run_request_with_retries
63
+ begin
64
+ run_request
65
+ rescue JSON::ParserError => e
66
+ self.retry_count += 1
67
+ if max_retries_exceeded?
68
+ raise e
69
+ else
70
+ run_request_with_retries
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,111 @@
1
+ module Vantiv
2
+ module Api
3
+ module RequestBody
4
+ def self.for_auth_or_sale(amount:, customer_id:, order_id:, payment_account_id:)
5
+ RequestBodyGenerator.run(
6
+ transaction_element(
7
+ amount: amount,
8
+ order_id: order_id,
9
+ customer_id: customer_id
10
+ ),
11
+ payment_account_element(payment_account_id: payment_account_id)
12
+ )
13
+ end
14
+
15
+ def self.for_auth_reversal(transaction_id:, amount: nil)
16
+ RequestBodyGenerator.run(
17
+ tied_transaction_element(transaction_id: transaction_id, amount: amount)
18
+ )
19
+ end
20
+
21
+ def self.for_capture(transaction_id:, amount: nil)
22
+ RequestBodyGenerator.run(
23
+ tied_transaction_element(transaction_id: transaction_id, amount: amount)
24
+ )
25
+ end
26
+
27
+ def self.for_credit(transaction_id:, amount: nil)
28
+ RequestBodyGenerator.run(
29
+ tied_transaction_element(transaction_id: transaction_id, amount: amount)
30
+ )
31
+ end
32
+
33
+ def self.for_return(amount:, customer_id:, order_id:, payment_account_id:)
34
+ transaction = transaction_element(
35
+ amount: amount,
36
+ order_id: order_id,
37
+ customer_id: customer_id
38
+ )
39
+ transaction["Transaction"].delete("PartialApprovedFlag")
40
+ RequestBodyGenerator.run(
41
+ transaction,
42
+ payment_account_element(payment_account_id: payment_account_id)
43
+ )
44
+ end
45
+
46
+ def self.for_tokenization(paypage_registration_id:)
47
+ RequestBodyGenerator.run(
48
+ card_element_for_tokenization(paypage_registration_id)
49
+ )
50
+ end
51
+
52
+ def self.for_direct_post_tokenization(card_number:, expiry_month:, expiry_year:, cvv:)
53
+ RequestBodyGenerator.run(
54
+ {
55
+ "Card" => {
56
+ "AccountNumber" => card_number,
57
+ "ExpirationMonth" => expiry_month,
58
+ "ExpirationYear" => expiry_year,
59
+ "CVV" => cvv
60
+ }
61
+ }
62
+ )
63
+ end
64
+
65
+ def self.for_void(transaction_id:)
66
+ RequestBodyGenerator.run(tied_transaction_element(transaction_id: transaction_id))
67
+ end
68
+
69
+ def self.card_element_for_tokenization(paypage_registration_id)
70
+ {
71
+ "Card" => {
72
+ "PaypageRegistrationID" => paypage_registration_id
73
+ }
74
+ }
75
+ end
76
+
77
+ def self.tied_transaction_element(transaction_id:, amount: nil)
78
+ res = {
79
+ "Transaction" => {
80
+ "TransactionID" => transaction_id
81
+ }
82
+ }
83
+ if amount
84
+ res["Transaction"]["TransactionAmount"] = '%.2f' % (amount / 100.0)
85
+ end
86
+ res
87
+ end
88
+
89
+ def self.transaction_element(amount:, customer_id:, order_id:)
90
+ {
91
+ "Transaction" => {
92
+ "ReferenceNumber" => order_id,
93
+ "TransactionAmount" => '%.2f' % (amount / 100.0),
94
+ "OrderSource" => Vantiv.order_source,
95
+ "CustomerID" => customer_id,
96
+ "PartialApprovedFlag" => false
97
+ }
98
+ }
99
+ end
100
+
101
+ def self.payment_account_element(payment_account_id:)
102
+ {
103
+ "PaymentAccount" => {
104
+ "PaymentAccountID" => payment_account_id
105
+ }
106
+ }
107
+ end
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,43 @@
1
+ require 'securerandom'
2
+
3
+ module Vantiv
4
+ module Api
5
+ class RequestBodyGenerator
6
+ def self.run(*args)
7
+ new(*args).run
8
+ end
9
+
10
+ def initialize(*args)
11
+ @body_parts = args
12
+ end
13
+
14
+ def run
15
+ body = request_body_base
16
+ body_parts.each do |body_part|
17
+ body.merge!(body_part)
18
+ end
19
+ body
20
+ end
21
+
22
+ private
23
+
24
+ attr_accessor :body_parts
25
+
26
+ def request_body_base
27
+ {
28
+ "Credentials" => {
29
+ "AcceptorID" => Vantiv.acceptor_id
30
+ },
31
+ "Reports" => {
32
+ # NOTE: this is required by vantiv, so a default is left here.
33
+ # If a user wants to use this Vantiv feature, it can be made dynamic.
34
+ "ReportGroup" => Vantiv.default_report_group
35
+ },
36
+ "Application" => {
37
+ "ApplicationID" => SecureRandom.hex(12)
38
+ }
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,50 @@
1
+ module Vantiv
2
+ module Api
3
+ class Response
4
+ attr_reader :httpok, :http_response_code, :body
5
+
6
+ def load(httpok:, http_response_code:, body:)
7
+ @httpok = httpok
8
+ @http_response_code = http_response_code
9
+ @body = body
10
+ end
11
+
12
+ # Only returned by cert API?
13
+ def request_id
14
+ body["RequestID"]
15
+ end
16
+
17
+ def api_level_failure?
18
+ !httpok ||
19
+ # NOTE: this kind of sucks, but at the commit point, the DevHub
20
+ # Api sometimes gives 200OK when litle had a parse issue and returns
21
+ # 'Error validating xml data...' instead of an actual error
22
+ @body["litleOnlineResponse"]["@message"].match(/error/i)
23
+ end
24
+
25
+ def message
26
+ litle_transaction_response["message"]
27
+ end
28
+
29
+ def response_code
30
+ litle_transaction_response["response"]
31
+ end
32
+
33
+ def transaction_id
34
+ litle_transaction_response["TransactionID"]
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :transaction_response_name
40
+
41
+ def litle_response
42
+ api_level_failure? ? {} : body["litleOnlineResponse"]
43
+ end
44
+
45
+ def litle_transaction_response
46
+ api_level_failure? ? {} : litle_response[transaction_response_name]
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,30 @@
1
+ module Vantiv
2
+ module Api
3
+ class TiedTransactionResponse < Api::Response
4
+ RESPONSE_CODES = {
5
+ transaction_received: '001'
6
+ }
7
+ TIED_TRANSACTION_RESPONSE_NAMES = {
8
+ auth_reversal: 'authReversalResponse',
9
+ capture: "captureResponse",
10
+ credit: "creditResponse",
11
+ return: "creditResponse",
12
+ void: "voidResponse"
13
+ }
14
+
15
+ def initialize(transaction_name)
16
+ unless @transaction_response_name = TIED_TRANSACTION_RESPONSE_NAMES[transaction_name]
17
+ raise "Implementation Error: Tied transactions do not include #{transaction_name}"
18
+ end
19
+ end
20
+
21
+ def success?
22
+ !api_level_failure? && response_code == RESPONSE_CODES[:transaction_received]
23
+ end
24
+
25
+ def failure?
26
+ !success?
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,45 @@
1
+ module Vantiv
2
+ module Api
3
+ class TokenizationResponse < Api::Response
4
+ RESPONSE_CODES = {
5
+ account_successfully_registered: "801",
6
+ account_already_registered: "802",
7
+ credit_card_number_invalid: "820",
8
+ merchant_not_authorized_for_tokens: "821",
9
+ token_not_found: "822",
10
+ token_invalid: "823",
11
+ invalid_paypage_registration_id: "877",
12
+ expired_paypage_registration_id: "878",
13
+ generic_token_registration_error: "898",
14
+ generic_token_use_error: "899"
15
+ }
16
+
17
+ def success?
18
+ !api_level_failure? && tokenization_successful?
19
+ end
20
+
21
+ def failure?
22
+ !success?
23
+ end
24
+
25
+ def payment_account_id
26
+ success? ? litle_transaction_response["PaymentAccountID"] : nil
27
+ end
28
+
29
+ def invalid_card_number?
30
+ response_code == RESPONSE_CODES[:credit_card_number_invalid]
31
+ end
32
+
33
+ private
34
+
35
+ def tokenization_successful?
36
+ response_code == RESPONSE_CODES[:account_successfully_registered] ||
37
+ response_code == RESPONSE_CODES[:account_already_registered]
38
+ end
39
+
40
+ def transaction_response_name
41
+ "registerTokenResponse"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,38 @@
1
+ module Vantiv
2
+ module Certification
3
+ class CertRequestBodyCompiler
4
+ attr_accessor :matchers
5
+
6
+ def initialize(*matchers)
7
+ @matchers = matchers
8
+ end
9
+
10
+ def compile(hash)
11
+ dup = {}
12
+ hash.each do |key, value|
13
+ if value.is_a?(Hash)
14
+ dup[key] = compile(value)
15
+ else
16
+ dup[key] = compile_value(value)
17
+ end
18
+ end
19
+ dup
20
+ end
21
+
22
+ private
23
+
24
+ def compile_value(value)
25
+ matchers.each do |matcher|
26
+ if matches = matcher[:regex].match(value)
27
+ matches = matches.to_a
28
+ matches.shift
29
+ matches.each do |match|
30
+ value = matcher[:fetcher].call(value, match)
31
+ end
32
+ end
33
+ end
34
+ value
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,54 @@
1
+ require "vantiv/certification/paypage_server"
2
+ require "selenium-webdriver"
3
+
4
+ module Vantiv
5
+ module Certification
6
+ class PaypageDriver
7
+
8
+ def start
9
+ paypage_server.start
10
+ start_driver
11
+ self
12
+ end
13
+
14
+ def stop
15
+ paypage_server.stop
16
+ driver.quit
17
+ end
18
+
19
+ def get_paypage_registration_id(card_number, cvv = '123')
20
+ driver.navigate.to paypage_server.root_path
21
+
22
+ driver.switch_to.frame('vantiv-payframe')
23
+ driver.find_element(:id, 'accountNumber').send_keys card_number
24
+ driver.find_element(:id, 'cvv').send_keys cvv
25
+
26
+ driver.switch_to.default_content
27
+ button = driver.find_element(:id, 'submit')
28
+ button.click
29
+
30
+ wait = Selenium::WebDriver::Wait.new(:timeout => 10)
31
+ wait.until {
32
+ driver.find_element(:id, "request-status").text.include?("Request Complete")
33
+ }
34
+
35
+ driver.find_element(:id, 'temp-token').text
36
+ end
37
+
38
+ private
39
+
40
+ def start_driver
41
+ driver.manage.timeouts.implicit_wait = 10
42
+ end
43
+
44
+ def driver
45
+ @driver ||= Selenium::WebDriver.for :phantomjs
46
+ end
47
+
48
+ def paypage_server
49
+ @paypage_server ||= Vantiv::Certification::PaypageServer.new
50
+ end
51
+
52
+ end
53
+ end
54
+ end