cardflex-ruby 0.0.1

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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +125 -0
  4. data/cardflex.gemspec +14 -0
  5. data/lib/cardflex.rb +32 -0
  6. data/lib/cardflex/base_module.rb +34 -0
  7. data/lib/cardflex/configuration.rb +96 -0
  8. data/lib/cardflex/customer_vault.rb +35 -0
  9. data/lib/cardflex/customer_vault_gateway.rb +26 -0
  10. data/lib/cardflex/error_response.rb +18 -0
  11. data/lib/cardflex/errors.rb +11 -0
  12. data/lib/cardflex/gateway.rb +35 -0
  13. data/lib/cardflex/http.rb +68 -0
  14. data/lib/cardflex/plan.rb +22 -0
  15. data/lib/cardflex/plan_gateway.rb +23 -0
  16. data/lib/cardflex/subscription.rb +24 -0
  17. data/lib/cardflex/subscription_gateway.rb +22 -0
  18. data/lib/cardflex/success_response.rb +15 -0
  19. data/lib/cardflex/test/test_values.rb +26 -0
  20. data/lib/cardflex/three_step.rb +42 -0
  21. data/lib/cardflex/three_step_gateway.rb +41 -0
  22. data/lib/cardflex/transaction.rb +52 -0
  23. data/lib/cardflex/transaction_gateway.rb +23 -0
  24. data/lib/cardflex/version.rb +9 -0
  25. data/lib/cardflex/xml.rb +11 -0
  26. data/lib/cardflex/xml/parser.rb +48 -0
  27. data/lib/cardflex/xml/serializer.rb +70 -0
  28. data/lib/ssl/ca-certificates.ca.crt +4190 -0
  29. data/spec/integration/cardflex/http_spec.rb +72 -0
  30. data/spec/integration/cardflex/plan_spec.rb +24 -0
  31. data/spec/integration/cardflex/three_step_gateway_spec.rb +47 -0
  32. data/spec/integration/cardflex/three_step_spec.rb +37 -0
  33. data/spec/integration/spec_helper.rb +24 -0
  34. data/spec/spec.opts +4 -0
  35. data/spec/spec_helper.rb +45 -0
  36. data/spec/ssl/certificate.crt +21 -0
  37. data/spec/ssl/geotrust_global.crt +20 -0
  38. data/spec/ssl/private_key.pem +30 -0
  39. data/spec/unit/cardflex/base_module_spec.rb +34 -0
  40. data/spec/unit/cardflex/configuration_spec.rb +61 -0
  41. data/spec/unit/cardflex/customer_vault_gateway_spec.rb +10 -0
  42. data/spec/unit/cardflex/errors_spec.rb +8 -0
  43. data/spec/unit/cardflex/gateway_spec.rb +11 -0
  44. data/spec/unit/cardflex/http_spec.rb +18 -0
  45. data/spec/unit/cardflex/three_step_gateway_spec.rb +29 -0
  46. data/spec/unit/cardflex/xml_spec.rb +90 -0
  47. data/spec/unit/spec_helper.rb +1 -0
  48. metadata +103 -0
@@ -0,0 +1,68 @@
1
+ module Cardflex
2
+ class Http
3
+ attr_reader :config
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def post(params={})
10
+ params = _add_api_key(params)
11
+ response = _do_http(Net::HTTP::Post, Xml.hash_to_xml(params))
12
+ Xml.parse(response.body)
13
+ end
14
+
15
+ def _do_http(http_verb, body=nil)
16
+ connection = Net::HTTP.new(@config.server, @config.port)
17
+ connection.open_timeout = 60
18
+ connection.read_timeout = 60
19
+ if @config.ssl?
20
+ connection.use_ssl = true
21
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
22
+ connection.ca_file = @config.ca_file
23
+ connection.verify_callback = proc { |preverify_ok, ssl_context| _verify_ssl_certificate(preverify_ok, ssl_context) }
24
+ end
25
+
26
+ connection.start do |http|
27
+ request = http_verb.new("#{@config.three_step_path}")
28
+ request['Accept'] = 'text/xml'
29
+ @config.logger.debug("[Cardflex] [#{_current_time}] #{request.method} as text/xml")
30
+ if body
31
+ request['Content-Type'] = 'text/xml'
32
+ request.body = body
33
+ @config.logger.debug _format_body_for_log(body)
34
+ end
35
+
36
+ response = http.request(request)
37
+ @config.logger.info "[Cardflex] [#{_current_time}] #{request.method} #{response.code}"
38
+ @config.logger.debug _format_body_for_log(response.body)
39
+ response
40
+ end
41
+ rescue OpenSSL::SSL::SSLError
42
+ raise Cardflex::SSLCertificateError
43
+ end
44
+
45
+ def _current_time
46
+ Time.now.utc.strftime("%d/%b/%Y %H:%M:%S %Z")
47
+ end
48
+
49
+ def _verify_ssl_certificate(preverify_ok, ssl_context)
50
+ if preverify_ok != true || ssl_context.error != 0
51
+ err_msg = "SSL Verification failed -- Preverify: #{preverify_ok}, Error: #{ssl_context.error_string} (#{ssl_context.error})"
52
+ @config.logger.error err_msg
53
+ false
54
+ else
55
+ true
56
+ end
57
+ end
58
+
59
+ def _add_api_key(params)
60
+ key = params.keys[0]
61
+ { key => params[key].merge(:api_key => @config.api_key) }
62
+ end
63
+
64
+ def _format_body_for_log(body)
65
+ body.gsub(/^/, "[Cardflex] ")
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,22 @@
1
+ module Cardflex
2
+ class Plan
3
+ include BaseModule
4
+
5
+ module Type
6
+ Add = 'add_plan'
7
+ end
8
+
9
+ attr_reader :result, :result_text, :result_code, :action_type
10
+ attr_reader :plan
11
+
12
+ def initialize(gateway, attributes)
13
+ @gateway = gateway
14
+ @type = attributes[:action_type]
15
+ set_instance_variables_from_hash(attributes)
16
+ end
17
+
18
+ def self.create(attributes)
19
+ Configuration.gateway.plan.request(:add_plan => attributes)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ module Cardflex
2
+ class PlanGateway
3
+ attr_reader :config
4
+
5
+ def initialize(gateway)
6
+ @gateway = gateway
7
+ @config = gateway.config
8
+ end
9
+
10
+ def request(attributes)
11
+ res = config.http.post(attributes)
12
+ _handle_response(res)
13
+ end
14
+
15
+ def _handle_response(res)
16
+ if res[:response][:result] == '1'
17
+ SuccessResponse.new(:plan => Plan.new(@gateway, res[:response]))
18
+ else
19
+ ErrorResponse.new(@gatway, res[:response])
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ module Cardflex
2
+ class Subscription
3
+ include BaseModule
4
+
5
+ module Type
6
+ Delete = 'delete_subscription'
7
+ end
8
+
9
+ attr_reader :result, :result_text, :result_code, :action_type
10
+ attr_reader :subscription_id
11
+ attr_reader :type, :plan, :billing, :shipping
12
+
13
+ def initialize(gateway, attributes)
14
+ @gateway = gateway
15
+ @type = attributes[:action_type]
16
+ set_instance_variables_from_hash(attributes)
17
+ end
18
+
19
+ def self.delete(subscription_id)
20
+ attributes = { :delete_subscription => { :subscription_id => subscription_id }}
21
+ Configuration.gateway.subscription.request(attributes)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ module Cardflex
2
+ class SubscriptionGateway
3
+ attr_reader :config
4
+
5
+ def initialize(gateway)
6
+ @gateway = gateway
7
+ @config = gateway.config
8
+ end
9
+
10
+ def request(attributes)
11
+ @config.http.post(attributes)
12
+ end
13
+
14
+ def _handle_response(res)
15
+ if res[:response][:result] == '1'
16
+ SuccessResponse.new(:subscription => Subscription.new(@gateway, res[:response]))
17
+ else
18
+ ErrorResponse.new(@gateway, res[:response])
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ module Cardflex
2
+ class SuccessResponse
3
+ include BaseModule
4
+
5
+ attr_reader :transaction, :three_step, :plan, :subscription, :customer_vault
6
+
7
+ def initialize(hash={})
8
+ set_instance_variables_from_hash(hash)
9
+ end
10
+
11
+ def success?
12
+ true
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module Cardflex
2
+ module Test
3
+ module Cards
4
+ Visa = "4111111111111111"
5
+ MasterCard = "5431111111111111"
6
+ Discover = "6011601160116611"
7
+ AmEx = "341111111111111"
8
+ ACHAccount = "123123123"
9
+ ACHRouting = "123123123"
10
+
11
+ Expiration = "10/25"
12
+
13
+ # Failures
14
+ Declined = "100"
15
+ CVVMatch = "999"
16
+
17
+ AVSMatchAddress1 = "888"
18
+ AVSMatchZip = "77777"
19
+ end
20
+
21
+ module Charges
22
+ Accepted = "1.50"
23
+ Declined = "0.50"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,42 @@
1
+ module Cardflex
2
+ class ThreeStep
3
+ include BaseModule
4
+
5
+ module Type
6
+ Sale = 'sale'
7
+ Auth = 'auth'
8
+ Credit = 'credit'
9
+ Validate = 'validate'
10
+ Offline = 'offline'
11
+ AddSubscription = 'add_subscription'
12
+ UpdateSubscription = 'update_subscription'
13
+ AddCustomer = 'add_customer'
14
+ UpdateCustomer = 'update_customer'
15
+ AddBilling = 'add_billing'
16
+ UpdateBilling = 'update_billing'
17
+ end
18
+
19
+ attr_reader :config
20
+ attr_reader :form_url
21
+ attr_reader :transaction_id
22
+ attr_reader :result, :result_text, :result_code
23
+
24
+ def initialize(gateway, attributes={})
25
+ @gateway = gateway
26
+ @config = gateway.config
27
+ set_instance_variables_from_hash(attributes)
28
+ end
29
+
30
+ def self.get_form_url(attributes)
31
+ Configuration.gateway.three_step.get_form_url(attributes)
32
+ end
33
+
34
+ def self.complete(token_id)
35
+ Configuration.gateway.three_step.complete(token_id)
36
+ end
37
+
38
+ create_helper_methods(Type) do |attributes|
39
+ get_form_url(attributes)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,41 @@
1
+ module Cardflex
2
+ class ThreeStepGateway
3
+ attr_reader :config
4
+
5
+ def initialize(gateway)
6
+ @gateway = gateway
7
+ @config = gateway.config
8
+ end
9
+
10
+ def get_form_url(attributes)
11
+ root = attributes.keys[0]
12
+ raise ArgumentError, 'missing :redirect_url' unless attributes[root][:redirect_url]
13
+
14
+ res = @config.http.post(attributes)
15
+ _handle_intermediate_response(res)
16
+ end
17
+
18
+ def complete(token_id)
19
+ raise ArgumentError, 'you must provide a token_id' unless token_id
20
+
21
+ res = @config.http.post(:complete_action => { :token_id => token_id })
22
+ _handle_response(res)
23
+ end
24
+
25
+ def _handle_response(res)
26
+ if res[:response][:result] == '1'
27
+ SuccessResponse.new(:transaction => Transaction.new(@gateway, res[:response]))
28
+ else
29
+ ErrorResponse.new(@gateway, res[:response])
30
+ end
31
+ end
32
+
33
+ def _handle_intermediate_response(res)
34
+ if res[:response][:result] == '1'
35
+ SuccessResponse.new(:three_step => ThreeStep.new(@gateway, res[:response]))
36
+ else
37
+ ErrorResponse.new(@gateway, res[:response])
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,52 @@
1
+ module Cardflex
2
+ class Transaction
3
+ include BaseModule
4
+
5
+ module Type
6
+ Capture = 'capture'
7
+ Void = 'void'
8
+ Refund = 'refund'
9
+ end
10
+
11
+ attr_reader :result, :result_code, :result_text, :authorization_code
12
+ attr_reader :avs_result, :cvv_result, :avs_reject, :cvv_reject
13
+ attr_reader :amount, :currency
14
+ attr_reader :ip_address
15
+ attr_reader :industry
16
+ attr_reader :billing_method
17
+ attr_reader :processor_id
18
+ attr_reader :sec_code
19
+ attr_reader :descriptor, :descriptor_phone, :descriptor_address, :descriptor_city, :descriptor_state,
20
+ :descriptor_postal, :descriptor_country, :descriptor_mcc, :descriptor_merchant_id, :descriptor_url
21
+ attr_reader :order_description, :order_date
22
+ attr_reader :customer_id, :customer_vault_id
23
+ attr_reader :merchant_receipt_email
24
+ attr_reader :customer_receipt
25
+ attr_reader :tracking_number, :shipping_carrier, :shipping_amount, :ship_from_postal
26
+ attr_reader :po_number, :order_id
27
+ attr_reader :tax_amount, :duty_amount, :national_tax_amount, :alternate_tax_amount, :alternate_tax_id
28
+ attr_reader :vat_tax_amount, :vat_tax_rate, :vat_invoice_reference_number, :customer_vat_registration,
29
+ :merchant_vat_registration
30
+ attr_reader :summary_commodity_code
31
+ attr_reader :discount_amount
32
+ attr_reader :cardholder_auth
33
+ attr_reader :eci
34
+ attr_reader :cavv, :xid
35
+ attr_reader :dup_seconds
36
+ attr_reader :type, :action_type, :transaction_id, :billing, :plan, :shipping, :product
37
+
38
+ def initialize(gateway, attributes)
39
+ @gateway = gateway
40
+ @type = attributes[:action_type]
41
+ set_instance_variables_from_hash(attributes)
42
+ end
43
+
44
+ def self.request(attributes)
45
+ Configuration.gateway.transaction.request(attributes)
46
+ end
47
+
48
+ create_helper_methods(Type) do |attributes|
49
+ request(attributes)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,23 @@
1
+ module Cardflex
2
+ class TransactionGateway
3
+ attr_reader :config
4
+
5
+ def initialize(gateway)
6
+ @gateway = gateway
7
+ @config = gateway.config
8
+ end
9
+
10
+ def request(attributes)
11
+ res = @config.http.post(attributes)
12
+ _handle_response(res)
13
+ end
14
+
15
+ def _handle_response(res)
16
+ if res[:response][:result] == '1'
17
+ SuccessResponse.new(:transaction => Transaction.new(@gateway, res[:response]))
18
+ else
19
+ ErrorResponse.new(@gateway, res[:response])
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ module Cardflex
2
+ module Version
3
+ Major = '0'
4
+ Minor = '0'
5
+ Tiny = '1'
6
+
7
+ Full = "#{Major}.#{Minor}.#{Tiny}"
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Cardflex
2
+ module Xml
3
+ def self.parse(xml)
4
+ Parser.xml_to_hash(xml)
5
+ end
6
+
7
+ def self.hash_to_xml(hash)
8
+ Serializer.hash_to_xml(hash)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,48 @@
1
+ module Cardflex
2
+ module Xml
3
+ module Parser
4
+ require 'rexml/document' unless defined? REXML::Document
5
+
6
+ def self.xml_to_hash(xml)
7
+ return {} if xml.nil?
8
+
9
+ document = REXML::Document.new(xml)
10
+ { _snakecase(document.root.name) => _parse_children(document.root) }
11
+ end
12
+
13
+ def self._parse_children(elem)
14
+ if elem.has_elements?
15
+ children = {}
16
+ elem.each_element do |child|
17
+ children[_snakecase(child.name)] = _parse_children(child)
18
+ end
19
+ children
20
+ else
21
+ elem.text
22
+ end
23
+ end
24
+
25
+ def self._snakecase(key)
26
+ key.tr('-', '_').to_sym
27
+ end
28
+
29
+ def self.parse(string)
30
+ return {} if string.nil?
31
+
32
+ doc = REXML::Document.new(string)
33
+ { _snakecase(doc.root.name).to_sym => _to_hash({}, doc.root) }
34
+ end
35
+
36
+ def self._to_hash(hash, element)
37
+ if element.has_elements?
38
+ element.each_element do |child|
39
+ hash[_snakecase(child.name).to_sym] = child.text
40
+ end
41
+ end
42
+
43
+ hash
44
+ end
45
+
46
+ end
47
+ end
48
+ end