cardflex-ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|