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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +125 -0
- data/cardflex.gemspec +14 -0
- data/lib/cardflex.rb +32 -0
- data/lib/cardflex/base_module.rb +34 -0
- data/lib/cardflex/configuration.rb +96 -0
- data/lib/cardflex/customer_vault.rb +35 -0
- data/lib/cardflex/customer_vault_gateway.rb +26 -0
- data/lib/cardflex/error_response.rb +18 -0
- data/lib/cardflex/errors.rb +11 -0
- data/lib/cardflex/gateway.rb +35 -0
- data/lib/cardflex/http.rb +68 -0
- data/lib/cardflex/plan.rb +22 -0
- data/lib/cardflex/plan_gateway.rb +23 -0
- data/lib/cardflex/subscription.rb +24 -0
- data/lib/cardflex/subscription_gateway.rb +22 -0
- data/lib/cardflex/success_response.rb +15 -0
- data/lib/cardflex/test/test_values.rb +26 -0
- data/lib/cardflex/three_step.rb +42 -0
- data/lib/cardflex/three_step_gateway.rb +41 -0
- data/lib/cardflex/transaction.rb +52 -0
- data/lib/cardflex/transaction_gateway.rb +23 -0
- data/lib/cardflex/version.rb +9 -0
- data/lib/cardflex/xml.rb +11 -0
- data/lib/cardflex/xml/parser.rb +48 -0
- data/lib/cardflex/xml/serializer.rb +70 -0
- data/lib/ssl/ca-certificates.ca.crt +4190 -0
- data/spec/integration/cardflex/http_spec.rb +72 -0
- data/spec/integration/cardflex/plan_spec.rb +24 -0
- data/spec/integration/cardflex/three_step_gateway_spec.rb +47 -0
- data/spec/integration/cardflex/three_step_spec.rb +37 -0
- data/spec/integration/spec_helper.rb +24 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/ssl/certificate.crt +21 -0
- data/spec/ssl/geotrust_global.crt +20 -0
- data/spec/ssl/private_key.pem +30 -0
- data/spec/unit/cardflex/base_module_spec.rb +34 -0
- data/spec/unit/cardflex/configuration_spec.rb +61 -0
- data/spec/unit/cardflex/customer_vault_gateway_spec.rb +10 -0
- data/spec/unit/cardflex/errors_spec.rb +8 -0
- data/spec/unit/cardflex/gateway_spec.rb +11 -0
- data/spec/unit/cardflex/http_spec.rb +18 -0
- data/spec/unit/cardflex/three_step_gateway_spec.rb +29 -0
- data/spec/unit/cardflex/xml_spec.rb +90 -0
- data/spec/unit/spec_helper.rb +1 -0
- 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
|
data/lib/cardflex/xml.rb
ADDED
@@ -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
|