activemerchant 1.14.0 → 1.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +18 -0
  3. data/CONTRIBUTORS +4 -0
  4. data/README.rdoc +1 -0
  5. data/lib/active_merchant.rb +1 -1
  6. data/lib/active_merchant/billing/credit_card.rb +53 -42
  7. data/lib/active_merchant/billing/gateway.rb +6 -6
  8. data/lib/active_merchant/billing/gateways/authorize_net.rb +4 -2
  9. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +1 -1
  10. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +3 -3
  11. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +1 -1
  12. data/lib/active_merchant/billing/gateways/bogus.rb +12 -0
  13. data/lib/active_merchant/billing/gateways/braintree_blue.rb +77 -16
  14. data/lib/active_merchant/billing/gateways/eway.rb +0 -4
  15. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +1 -1
  17. data/lib/active_merchant/billing/gateways/orbital.rb +1 -1
  18. data/lib/active_merchant/billing/gateways/pay_junction.rb +1 -1
  19. data/lib/active_merchant/billing/gateways/payflow.rb +22 -8
  20. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +10 -10
  21. data/lib/active_merchant/billing/gateways/payflow_express.rb +115 -36
  22. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -1
  23. data/lib/active_merchant/billing/gateways/qbms.rb +1 -1
  24. data/lib/active_merchant/billing/gateways/realex.rb +7 -20
  25. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +5 -5
  26. data/lib/active_merchant/billing/gateways/viaklix.rb +1 -1
  27. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +18 -10
  28. data/lib/active_merchant/billing/integrations/directebanking.rb +47 -0
  29. data/lib/active_merchant/billing/integrations/directebanking/helper.rb +90 -0
  30. data/lib/active_merchant/billing/integrations/directebanking/notification.rb +120 -0
  31. data/lib/active_merchant/billing/integrations/directebanking/return.rb +11 -0
  32. data/lib/active_merchant/billing/integrations/helper.rb +4 -4
  33. data/lib/active_merchant/billing/integrations/notification.rb +1 -1
  34. data/lib/active_merchant/common/post_data.rb +1 -1
  35. data/lib/active_merchant/common/posts_data.rb +1 -1
  36. data/lib/active_merchant/common/validateable.rb +20 -15
  37. data/lib/active_merchant/version.rb +1 -1
  38. metadata +20 -21
  39. metadata.gz.sig +0 -0
@@ -17,7 +17,7 @@ module ActiveMerchant #:nodoc:
17
17
  # The name of the gateway
18
18
  self.display_name = 'SecurePay'
19
19
 
20
- class_inheritable_accessor :request_timeout
20
+ class_attribute :request_timeout
21
21
  self.request_timeout = 60
22
22
 
23
23
  self.money_format = :cents
@@ -58,20 +58,20 @@ module ActiveMerchant #:nodoc:
58
58
  commit :authorization, build_purchase_request(money, credit_card, options)
59
59
  end
60
60
 
61
- def capture(money, reference)
61
+ def capture(money, reference, options = {})
62
62
  commit :capture, build_reference_request(money, reference)
63
63
  end
64
64
 
65
- def refund(money, reference)
65
+ def refund(money, reference, options = {})
66
66
  commit :refund, build_reference_request(money, reference)
67
67
  end
68
68
 
69
- def credit(money, reference)
69
+ def credit(money, reference, options = {})
70
70
  deprecated CREDIT_DEPRECATION_MESSAGE
71
71
  refund(money, reference)
72
72
  end
73
73
 
74
- def void(reference)
74
+ def void(reference, options = {})
75
75
  commit :void, build_reference_request(nil, reference)
76
76
  end
77
77
 
@@ -1,7 +1,7 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class ViaklixGateway < Gateway
4
- class_inheritable_accessor :test_url, :live_url, :delimiter, :actions
4
+ class_attribute :test_url, :live_url, :delimiter, :actions
5
5
 
6
6
  self.test_url = 'https://demo.viaklix.com/process.asp'
7
7
  self.live_url = 'https://www.viaklix.com/process.asp'
@@ -64,8 +64,7 @@ module ActiveMerchant #:nodoc:
64
64
 
65
65
 
66
66
  def customer(params = {})
67
- full_name = "#{params[:first_name]} #{params[:last_name]}"
68
- add_field(mappings[:customer][:name], full_name)
67
+ add_field(mappings[:customer][:name], full_name(params))
69
68
  add_field(mappings[:customer][:email], params[:email])
70
69
  end
71
70
 
@@ -79,13 +78,11 @@ module ActiveMerchant #:nodoc:
79
78
  end
80
79
 
81
80
  def shipping_address(params = {})
82
- update_address(:shipping_address, params)
83
- super(params.dup)
81
+ super(update_address(:shipping_address, params))
84
82
  end
85
83
 
86
84
  def billing_address(params = {})
87
- update_address(:billing_address, params)
88
- super(params.dup)
85
+ super(update_address(:billing_address, params))
89
86
  end
90
87
 
91
88
  def form_fields
@@ -121,15 +118,20 @@ module ActiveMerchant #:nodoc:
121
118
  end
122
119
 
123
120
  def update_address(address_type, params)
121
+ params = params.dup
124
122
  address = params[:address1]
125
- address << " #{params[:address2]}" if params[:address2]
126
- params[:address1] = address
123
+ address = "#{address} #{params[:address2]}" if params[:address2].present?
124
+ address = "#{params[:company]} #{address}" if params[:company].present?
125
+ params[:address1] = address
126
+
127
127
  params[:phone] = normalize_phone_number(params[:phone])
128
128
  add_land_line_phone_for(address_type, params)
129
129
 
130
- if address_type == :shipping_address && params[:name].blank?
131
- add_field(mappings[:shipping_address][:name], fields[mappings[:customer][:name]])
130
+ if address_type == :shipping_address
131
+ shipping_name = full_name(params) || fields[mappings[:customer][:name]]
132
+ add_field(mappings[:shipping_address][:name], shipping_name)
132
133
  end
134
+ params
133
135
  end
134
136
 
135
137
  # Split a single phone number into the country code, area code and local number as best as possible
@@ -185,6 +187,12 @@ module ActiveMerchant #:nodoc:
185
187
  def phone_code_for_country(country)
186
188
  PHONE_CODES[country]
187
189
  end
190
+
191
+ def full_name(params)
192
+ return if params[:name].blank? && params[:first_name].blank? && params[:last_name].blank?
193
+
194
+ params[:name] || "#{params[:first_name]} #{params[:last_name]}"
195
+ end
188
196
  end
189
197
  end
190
198
  end
@@ -0,0 +1,47 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module Directebanking
5
+ autoload :Return, File.dirname(__FILE__) + '/directebanking/return.rb'
6
+ autoload :Helper, File.dirname(__FILE__) + '/directebanking/helper.rb'
7
+ autoload :Notification, File.dirname(__FILE__) + '/directebanking/notification.rb'
8
+
9
+ # Supported countries:
10
+ # Germany - DE
11
+ # Austria - AT
12
+ # Belgium - BE
13
+ # Netherlands - NL
14
+ # Switzerland - CH
15
+ # Great Britain - GB
16
+
17
+ # Overwrite this if you want to change the directebanking test url
18
+ mattr_accessor :test_url
19
+ self.test_url = 'https://www.directebanking.com/payment/start'
20
+
21
+ # Overwrite this if you want to change the directebanking production url
22
+ mattr_accessor :production_url
23
+ self.production_url = 'https://www.directebanking.com/payment/start'
24
+
25
+ def self.service_url
26
+ mode = ActiveMerchant::Billing::Base.integration_mode
27
+ case mode
28
+ when :production
29
+ self.production_url
30
+ when :test
31
+ self.test_url
32
+ else
33
+ raise StandardError, "Integration mode set to an invalid value: #{mode}"
34
+ end
35
+ end
36
+
37
+ def self.notification(post, options = {})
38
+ Notification.new(post, options)
39
+ end
40
+
41
+ def self.return(post, options = {})
42
+ Return.new(post, options)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,90 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module Directebanking
5
+ class Helper < ActiveMerchant::Billing::Integrations::Helper
6
+
7
+ # All credentials are mandatory and need to be set
8
+ #
9
+ # credential1: User ID
10
+ # credential2: Project ID
11
+ # credential3: Project Password (Algorithm: SH1)
12
+ # credential4: Notification Password (Algorithm: SH1)
13
+ def initialize(order, account, options = {})
14
+ super
15
+ add_field('user_variable_0', order)
16
+ add_field('project_id', options[:credential2])
17
+ @project_password = options[:credential3]
18
+ end
19
+
20
+ SIGNATURE_FIELDS = [
21
+ :user_id,
22
+ :project_id,
23
+ :sender_holder,
24
+ :sender_account_number,
25
+ :sender_bank_code,
26
+ :sender_country_id,
27
+ :amount,
28
+ :currency_id,
29
+ :reason_1,
30
+ :reason_2,
31
+ :user_variable_0,
32
+ :user_variable_1,
33
+ :user_variable_2,
34
+ :user_variable_3,
35
+ :user_variable_4,
36
+ :user_variable_5
37
+ ]
38
+
39
+ SIGNATURE_IGNORE_AT_METHOD_CREATION_FIELDS = [
40
+ :user_id,
41
+ :amount,
42
+ :project_id,
43
+ :currency_id,
44
+ :user_variable_0,
45
+ :user_variable_1,
46
+ :user_variable_2,
47
+ :user_variable_3
48
+ ]
49
+
50
+ SIGNATURE_FIELDS.each do |key|
51
+ if !SIGNATURE_IGNORE_AT_METHOD_CREATION_FIELDS.include?(key)
52
+ mapping "#{key}".to_sym, "#{key.to_s}"
53
+ end
54
+ end
55
+
56
+ # Need to format the amount to have 2 decimal places
57
+ def amount=(money)
58
+ cents = money.respond_to?(:cents) ? money.cents : money
59
+ if money.is_a?(String) or cents.to_i <= 0
60
+ raise ArgumentError, 'money amount must be either a Money object or a positive integer in cents.'
61
+ end
62
+ add_field mappings[:amount], sprintf("%.2f", cents.to_f/100)
63
+ end
64
+
65
+ def generate_signature_string
66
+ # format of signature: user_id|project_id|sender_holder|sender_account_number|sender_bank_code| sender_country_id|amount|currency_id|reason_1|reason_2|user_variable_0|user_variable_1|user_variable_2|user_variable_3|user_variable_4|user_variable_5|project_password
67
+ SIGNATURE_FIELDS.map {|key| @fields[key.to_s]} * "|" + "|#{@project_password}"
68
+ end
69
+
70
+ def generate_signature
71
+ Digest::SHA1.hexdigest(generate_signature_string)
72
+ end
73
+
74
+ def form_fields
75
+ @fields.merge('hash' => generate_signature)
76
+ end
77
+
78
+ mapping :account, 'user_id'
79
+ mapping :amount, 'amount'
80
+ mapping :currency, 'currency_id'
81
+ mapping :description, 'reason_1'
82
+
83
+ mapping :return_url, 'user_variable_1'
84
+ mapping :cancel_return_url, 'user_variable_2'
85
+ mapping :notify_url, 'user_variable_3'
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,120 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module Directebanking
5
+ class Notification < ActiveMerchant::Billing::Integrations::Notification
6
+
7
+ def initialize(data, options)
8
+ if options[:credential4].nil?
9
+ raise ArgumentError, "You need to provide the notification password (SH1) as the option :credential4 to verify that the notification originated from Directebanking (Payment Networks AG)"
10
+ end
11
+ super
12
+ end
13
+
14
+ def complete?
15
+ status == 'Completed'
16
+ end
17
+
18
+ def item_id
19
+ params['user_variable_0']
20
+ end
21
+
22
+ def transaction_id
23
+ params['transaction']
24
+ end
25
+
26
+ # When was this payment received by the client.
27
+ def received_at
28
+ Time.parse(params['created']) if params['created']
29
+ end
30
+
31
+ # the money amount we received in X.2 decimal.
32
+ def gross
33
+ "%.2f" % params['amount'].to_f
34
+ end
35
+
36
+ def status
37
+ 'Completed'
38
+ end
39
+
40
+ def currency
41
+ params['currency_id']
42
+ end
43
+
44
+ def test?
45
+ params['sender_bank_name'] == 'Testbank'
46
+ end
47
+
48
+ # for verifying the signature of the URL parameters
49
+ PAYMENT_HOOK_SIGNATURE_FIELDS = [
50
+ :transaction,
51
+ :user_id,
52
+ :project_id,
53
+ :sender_holder,
54
+ :sender_account_number,
55
+ :sender_bank_code,
56
+ :sender_bank_name,
57
+ :sender_bank_bic,
58
+ :sender_iban,
59
+ :sender_country_id,
60
+ :recipient_holder,
61
+ :recipient_account_number,
62
+ :recipient_bank_code,
63
+ :recipient_bank_name,
64
+ :recipient_bank_bic,
65
+ :recipient_iban,
66
+ :recipient_country_id,
67
+ :international_transaction,
68
+ :amount,
69
+ :currency_id,
70
+ :reason_1,
71
+ :reason_2,
72
+ :security_criteria,
73
+ :user_variable_0,
74
+ :user_variable_1,
75
+ :user_variable_2,
76
+ :user_variable_3,
77
+ :user_variable_4,
78
+ :user_variable_5,
79
+ :created
80
+ ]
81
+
82
+ PAYMENT_HOOK_IGNORE_AT_METHOD_CREATION_FIELDS = [
83
+ :transaction,
84
+ :amount,
85
+ :currency_id,
86
+ :user_variable_0,
87
+ :user_variable_1,
88
+ :user_variable_2,
89
+ :user_variable_3,
90
+ :created
91
+ ]
92
+
93
+ # Provide access to raw fields
94
+ PAYMENT_HOOK_SIGNATURE_FIELDS.each do |key|
95
+ if !PAYMENT_HOOK_IGNORE_AT_METHOD_CREATION_FIELDS.include?(key)
96
+ define_method(key.to_s) do
97
+ params[key.to_s]
98
+ end
99
+ end
100
+ end
101
+
102
+ def generate_signature_string
103
+ #format is: transaction|user_id|project_id|sender_holder|sender_account_number|sender_bank_code|sender_bank_name|sender_bank_bic|sender_iban|sender_country_id|recipient_holder|recipient_account_number|recipient_bank_code|recipient_bank_name|recipient_bank_bic|recipient_iban|recipient_country_id|international_transaction|amount|currency_id|reason_1|reason_2|security_criteria|user_variable_0|user_variable_1|user_variable_2|user_variable_3|user_variable_4|user_variable_5|created|notification_password
104
+ PAYMENT_HOOK_SIGNATURE_FIELDS.map {|key| params[key.to_s]} * "|" + "|#{@options[:credential4]}"
105
+ end
106
+
107
+ def generate_signature
108
+ Digest::SHA1.hexdigest(generate_signature_string)
109
+ end
110
+
111
+ def acknowledge
112
+ # signature_is_valid?
113
+ generate_signature.to_s == params['hash'].to_s
114
+ end
115
+
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module Directebanking
5
+ class Return < ActiveMerchant::Billing::Integrations::Return
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
11
+
@@ -3,14 +3,14 @@ module ActiveMerchant #:nodoc:
3
3
  module Integrations #:nodoc:
4
4
  class Helper #:nodoc:
5
5
  attr_reader :fields
6
- class_inheritable_accessor :service_url
7
- class_inheritable_hash :mappings
8
- class_inheritable_accessor :country_format
6
+ class_attribute :service_url
7
+ class_attribute :mappings
8
+ class_attribute :country_format
9
9
  self.country_format = :alpha2
10
10
 
11
11
  # The application making the calls to the gateway
12
12
  # Useful for things like the PayPal build notation (BN) id fields
13
- class_inheritable_accessor :application_id
13
+ class_attribute :application_id
14
14
  self.application_id = 'ActiveMerchant'
15
15
 
16
16
  def initialize(order, account, options = {})
@@ -6,7 +6,7 @@ module ActiveMerchant #:nodoc:
6
6
  attr_accessor :raw
7
7
 
8
8
  # set this to an array in the subclass, to specify which IPs are allowed to send requests
9
- class_inheritable_accessor :production_ips
9
+ class_attribute :production_ips
10
10
 
11
11
  def initialize(post, options = {})
12
12
  @options = options
@@ -2,7 +2,7 @@ require 'cgi'
2
2
 
3
3
  module ActiveMerchant
4
4
  class PostData < Hash
5
- class_inheritable_accessor :required_fields, :instance_writer => false
5
+ class_attribute :required_fields, :instance_writer => false
6
6
  self.required_fields = []
7
7
 
8
8
  def []=(key, value)
@@ -5,7 +5,7 @@ module ActiveMerchant #:nodoc:
5
5
  base.superclass_delegating_accessor :ssl_strict
6
6
  base.ssl_strict = true
7
7
 
8
- base.class_inheritable_accessor :retry_safe
8
+ base.class_attribute :retry_safe
9
9
  base.retry_safe = false
10
10
 
11
11
  base.superclass_delegating_accessor :open_timeout
@@ -1,4 +1,4 @@
1
- module ActiveMerchant #:nodoc:
1
+ module ActiveMerchant #:nodoc:
2
2
  module Validateable #:nodoc:
3
3
  def valid?
4
4
  errors.clear
@@ -7,13 +7,13 @@ module ActiveMerchant #:nodoc:
7
7
  validate if respond_to?(:validate, true)
8
8
 
9
9
  errors.empty?
10
- end
10
+ end
11
11
 
12
12
  def initialize(attributes = {})
13
13
  self.attributes = attributes
14
14
  end
15
15
 
16
- def errors
16
+ def errors
17
17
  @errors ||= Errors.new(self)
18
18
  end
19
19
 
@@ -22,46 +22,51 @@ module ActiveMerchant #:nodoc:
22
22
  def attributes=(attributes)
23
23
  unless attributes.nil?
24
24
  for key, value in attributes
25
- send("#{key}=", value )
25
+ send("#{key}=", value )
26
26
  end
27
27
  end
28
- end
28
+ end
29
29
 
30
30
  # This hash keeps the errors of the object
31
31
  class Errors < HashWithIndifferentAccess
32
32
 
33
33
  def initialize(base)
34
+ super() { |h, k| h[k] = [] ; h[k] }
34
35
  @base = base
35
36
  end
36
-
37
+
37
38
  def count
38
39
  size
39
40
  end
40
41
 
41
- # returns a specific fields error message.
42
- # if more than one error is available we will only return the first. If no error is available
42
+ def empty?
43
+ all? { |k, v| v && v.empty? }
44
+ end
45
+
46
+ # returns a specific fields error message.
47
+ # if more than one error is available we will only return the first. If no error is available
43
48
  # we return an empty string
44
49
  def on(field)
45
50
  self[field].to_a.first
46
51
  end
47
52
 
48
53
  def add(field, error)
49
- self[field] ||= []
50
54
  self[field] << error
51
- end
52
-
55
+ end
56
+
53
57
  def add_to_base(error)
54
58
  add(:base, error)
55
59
  end
56
60
 
57
61
  def each_full
58
- full_messages.each { |msg| yield msg }
62
+ full_messages.each { |msg| yield msg }
59
63
  end
60
64
 
61
65
  def full_messages
62
66
  result = []
63
67
 
64
- self.each do |key, messages|
68
+ self.each do |key, messages|
69
+ next if messages.blank?
65
70
  if key == 'base'
66
71
  result << "#{messages.first}"
67
72
  else
@@ -70,7 +75,7 @@ module ActiveMerchant #:nodoc:
70
75
  end
71
76
 
72
77
  result
73
- end
74
- end
78
+ end
79
+ end
75
80
  end
76
81
  end