tlconnor-activemerchant 1.20.4 → 1.23.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 (58) hide show
  1. data/CHANGELOG +86 -6
  2. data/CONTRIBUTORS +33 -0
  3. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +2 -0
  4. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +4 -4
  5. data/lib/active_merchant/billing/gateways/blue_pay.rb +492 -11
  6. data/lib/active_merchant/billing/gateways/braintree_blue.rb +46 -19
  7. data/lib/active_merchant/billing/gateways/certo_direct.rb +1 -1
  8. data/lib/active_merchant/billing/gateways/elavon.rb +2 -0
  9. data/lib/active_merchant/billing/gateways/epay.rb +3 -1
  10. data/lib/active_merchant/billing/gateways/itransact.rb +450 -0
  11. data/lib/active_merchant/billing/gateways/litle.rb +275 -0
  12. data/lib/active_merchant/billing/gateways/migs.rb +259 -0
  13. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  14. data/lib/active_merchant/billing/gateways/moneris.rb +4 -30
  15. data/lib/active_merchant/billing/gateways/moneris_us.rb +211 -0
  16. data/lib/active_merchant/billing/gateways/nab_transact.rb +1 -1
  17. data/lib/active_merchant/billing/gateways/ogone.rb +104 -12
  18. data/lib/active_merchant/billing/gateways/orbital.rb +15 -6
  19. data/lib/active_merchant/billing/gateways/paybox_direct.rb +1 -4
  20. data/lib/active_merchant/billing/gateways/payflow.rb +8 -3
  21. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +4 -1
  22. data/lib/active_merchant/billing/gateways/payflow_express.rb +4 -2
  23. data/lib/active_merchant/billing/gateways/payment_express.rb +60 -13
  24. data/lib/active_merchant/billing/gateways/paypal.rb +3 -18
  25. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +333 -3
  26. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +245 -0
  27. data/lib/active_merchant/billing/gateways/paypal_digital_goods.rb +43 -0
  28. data/lib/active_merchant/billing/gateways/paypal_express.rb +14 -65
  29. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +8 -3
  30. data/lib/active_merchant/billing/gateways/realex.rb +5 -7
  31. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +3 -2
  32. data/lib/active_merchant/billing/gateways/stripe.rb +1 -9
  33. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +2 -2
  34. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -5
  35. data/lib/active_merchant/billing/gateways/viaklix.rb +7 -2
  36. data/lib/active_merchant/billing/gateways/vindicia.rb +359 -0
  37. data/lib/active_merchant/billing/integrations/dotpay.rb +22 -0
  38. data/lib/active_merchant/billing/integrations/dotpay/helper.rb +77 -0
  39. data/lib/active_merchant/billing/integrations/dotpay/notification.rb +86 -0
  40. data/lib/active_merchant/billing/integrations/dotpay/return.rb +11 -0
  41. data/lib/active_merchant/billing/integrations/epay.rb +21 -0
  42. data/lib/active_merchant/billing/integrations/epay/helper.rb +55 -0
  43. data/lib/active_merchant/billing/integrations/epay/notification.rb +110 -0
  44. data/lib/active_merchant/billing/integrations/paypal/notification.rb +2 -1
  45. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +2 -3
  46. data/lib/active_merchant/billing/integrations/robokassa.rb +49 -0
  47. data/lib/active_merchant/billing/integrations/robokassa/common.rb +19 -0
  48. data/lib/active_merchant/billing/integrations/robokassa/helper.rb +50 -0
  49. data/lib/active_merchant/billing/integrations/robokassa/notification.rb +55 -0
  50. data/lib/active_merchant/billing/integrations/robokassa/return.rb +17 -0
  51. data/lib/active_merchant/billing/integrations/two_checkout.rb +25 -3
  52. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +58 -26
  53. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +71 -46
  54. data/lib/active_merchant/billing/integrations/verkkomaksut.rb +20 -0
  55. data/lib/active_merchant/billing/integrations/verkkomaksut/helper.rb +87 -0
  56. data/lib/active_merchant/billing/integrations/verkkomaksut/notification.rb +59 -0
  57. data/lib/active_merchant/version.rb +1 -1
  58. metadata +28 -5
@@ -0,0 +1,100 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ module MigsCodes
4
+ TXN_RESPONSE_CODES = {
5
+ '?' => 'Response Unknown',
6
+ '0' => 'Transaction Successful',
7
+ '1' => 'Transaction Declined - Bank Error',
8
+ '2' => 'Bank Declined Transaction',
9
+ '3' => 'Transaction Declined - No Reply from Bank',
10
+ '4' => 'Transaction Declined - Expired Card',
11
+ '5' => 'Transaction Declined - Insufficient funds',
12
+ '6' => 'Transaction Declined - Error Communicating with Bank',
13
+ '7' => 'Payment Server Processing Error - Typically caused by invalid input data such as an invalid credit card number. Processing errors can also occur',
14
+ '8' => 'Transaction Declined - Transaction Type Not Supported',
15
+ '9' => 'Bank Declined Transaction (Do not contact Bank)',
16
+ 'A' => 'Transaction Aborted',
17
+ 'C' => 'Transaction Cancelled',
18
+ 'D' => 'Deferred Transaction',
19
+ 'E' => 'Issuer Returned a Referral Response',
20
+ 'F' => '3D Secure Authentication Failed',
21
+ 'I' => 'Card Security Code Failed',
22
+ 'L' => 'Shopping Transaction Locked (This indicates that there is another transaction taking place using the same shopping transaction number)',
23
+ 'N' => 'Cardholder is not enrolled in 3D Secure (Authentication Only)',
24
+ 'P' => 'Transaction is Pending',
25
+ 'R' => 'Retry Limits Exceeded, Transaction Not Processed',
26
+ 'S' => 'Duplicate OrderInfo used. (This is only relevant for Payment Servers that enforce the uniqueness of this field)',
27
+ 'U' => 'Card Security Code Failed'
28
+ }
29
+
30
+ ISSUER_RESPONSE_CODES = {
31
+ '00' => 'Approved',
32
+ '01' => 'Refer to Card Issuer',
33
+ '02' => 'Refer to Card Issuer',
34
+ '03' => 'Invalid Merchant',
35
+ '04' => 'Pick Up Card',
36
+ '05' => 'Do Not Honor',
37
+ '07' => 'Pick Up Card',
38
+ '12' => 'Invalid Transaction',
39
+ '14' => 'Invalid Card Number (No such Number)',
40
+ '15' => 'No Such Issuer',
41
+ '33' => 'Expired Card',
42
+ '34' => 'Suspected Fraud',
43
+ '36' => 'Restricted Card',
44
+ '39' => 'No Credit Account',
45
+ '41' => 'Card Reported Lost',
46
+ '43' => 'Stolen Card',
47
+ '51' => 'Insufficient Funds',
48
+ '54' => 'Expired Card',
49
+ '57' => 'Transaction Not Permitted',
50
+ '59' => 'Suspected Fraud',
51
+ '62' => 'Restricted Card',
52
+ '65' => 'Exceeds withdrawal frequency limit',
53
+ '91' => 'Cannot Contact Issuer'
54
+ }
55
+
56
+ VERIFIED_3D_CODES = {
57
+ 'Y' => 'The cardholder was successfully authenticated.',
58
+ 'E' => 'The cardholder is not enrolled.',
59
+ 'N' => 'The cardholder was not verified.',
60
+ 'U' => 'The cardholder\'s Issuer was unable to authenticate due to a system error at the Issuer.',
61
+ 'F' => 'An error exists in the format of the request from the merchant. For example, the request did not contain all required fields, or the format of some fields was invalid.',
62
+ 'A' => 'Authentication of your Merchant ID and Password to the Directory Server Failed (see "What does a Payment Authentication Status of "A" mean?" on page 85).',
63
+ 'D' => 'Error communicating with the Directory Server, for example, the Payment Server could not connect to the directory server or there was a versioning mismatch.',
64
+ 'C' => 'The card type is not supported for authentication.',
65
+ 'M' => 'This indicates that attempts processing was used. Verification is marked with status M - ACS attempts processing used. Payment is performed with authentication. Attempts is when a cardholder has successfully passed the directory server but decides not to continue with the authentication process and cancels.',
66
+ 'S' => 'The signature on the response received from the Issuer could not be validated. This should be considered a failure.',
67
+ 'T' => 'ACS timed out. The Issuer\'s ACS did not respond to the Authentication request within the time out period.',
68
+ 'P' => 'Error parsing input from Issuer.',
69
+ 'I' => 'Internal Payment Server system error. This could be caused by a temporary DB failure or an error in the security module or by some error in an internal system.'
70
+ }
71
+
72
+ class CreditCardType
73
+ attr_accessor :am_code, :migs_code, :migs_long_code, :name
74
+ def initialize(am_code, migs_code, migs_long_code, name)
75
+ @am_code = am_code
76
+ @migs_code = migs_code
77
+ @migs_long_code = migs_long_code
78
+ @name = name
79
+ end
80
+ end
81
+
82
+ CARD_TYPES = [
83
+ # The following are 4 different representations of credit card types
84
+ # am_code: The active merchant code
85
+ # migs_code: Used in response for purchase/authorize/status
86
+ # migs_long_code: Used to pre-select card for server_purchase_url
87
+ # name: The nice display name
88
+ %w(american_express AE Amex American\ Express),
89
+ %w(diners_club DC Dinersclub Diners\ Club),
90
+ %w(jcb JC JCB JCB\ Card),
91
+ %w(maestro MS Maestro Maestro\ Card),
92
+ %w(master MC Mastercard MasterCard),
93
+ %w(na PL PrivateLabelCard Private\ Label\ Card),
94
+ %w(visa VC Visa Visa\ Card')
95
+ ].map do |am_code, migs_code, migs_long_code, name|
96
+ CreditCardType.new(am_code, migs_code, migs_long_code, name)
97
+ end
98
+ end
99
+ end
100
+ end
@@ -101,9 +101,7 @@ module ActiveMerchant #:nodoc:
101
101
  :amount => amount(money),
102
102
  :pan => creditcard.number,
103
103
  :expdate => expdate(creditcard),
104
- :crypt_type => options[:crypt_type] || @options[:crypt_type],
105
- :cvv => creditcard.verification_value,
106
- :billing_address => options[:address] || options[:billing_address]
104
+ :crypt_type => options[:crypt_type] || @options[:crypt_type]
107
105
  }
108
106
  end
109
107
 
@@ -131,9 +129,7 @@ module ActiveMerchant #:nodoc:
131
129
 
132
130
  Response.new(successful?(response), message_from(response[:message]), response,
133
131
  :test => test?,
134
- :authorization => authorization_from(response),
135
- :avs_result => {:code => response[:avs_result_code]},
136
- :cvv_result => response[:cvd_result_code] && response[:cvd_result_code].last
132
+ :authorization => authorization_from(response)
137
133
  )
138
134
  end
139
135
 
@@ -176,32 +172,10 @@ module ActiveMerchant #:nodoc:
176
172
  actions[action].each do |key|
177
173
  transaction.add_element(key.to_s).text = parameters[key] unless parameters[key].blank?
178
174
  end
179
-
180
- add_avs_info(transaction, parameters)
181
- add_cvd_info(transaction, parameters)
182
-
175
+
183
176
  xml.to_s
184
177
  end
185
-
186
- def add_avs_info(transaction, parameters)
187
- if address = parameters[:billing_address]
188
- street_number, street_name = address[:address1].split(' ', 2)
189
-
190
- avs_info = transaction.add_element('avs_info')
191
- avs_info.add_element('avs_street_number').text = street_number
192
- avs_info.add_element('avs_street_name').text = street_name
193
- avs_info.add_element('avs_zipcode').text = address[:zip]
194
- end
195
- end
196
-
197
- def add_cvd_info(transaction, parameters)
198
- if cvd_value = parameters[:cvv]
199
- cvd_info = transaction.add_element('cvd_info')
200
- cvd_info.add_element('cvd_indicator').text = '1'
201
- cvd_info.add_element('cvd_value').text = cvd_value
202
- end
203
- end
204
-
178
+
205
179
  def message_from(message)
206
180
  return 'Unspecified error' if message.blank?
207
181
  message.gsub(/[^\w]/, ' ').split.join(" ").capitalize
@@ -0,0 +1,211 @@
1
+ require 'rexml/document'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+
6
+ # To learn more about the Moneris (US) gateway, please contact
7
+ # ussales@moneris.com for a copy of their integration guide. For
8
+ # information on remote testing, please see "Test Environment Penny Value
9
+ # Response Table", and "Test Environment eFraud (AVS and CVD) Penny
10
+ # Response Values", available at Moneris' {eSelect Plus Documentation
11
+ # Centre}[https://www3.moneris.com/connect/en/documents/index.html].
12
+ class MonerisUsGateway < Gateway
13
+ TEST_URL = 'https://esplusqa.moneris.com/gateway_us/servlet/MpgRequest'
14
+ LIVE_URL = 'https://esplus.moneris.com/gateway_us/servlet/MpgRequest'
15
+
16
+ self.supported_countries = ['US']
17
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :discover]
18
+ self.homepage_url = 'http://www.monerisusa.com/'
19
+ self.display_name = 'Moneris (US)'
20
+
21
+ # login is your Store ID
22
+ # password is your API Token
23
+ def initialize(options = {})
24
+ requires!(options, :login, :password)
25
+ @options = { :crypt_type => 7 }.update(options)
26
+ super
27
+ end
28
+
29
+ # Referred to as "PreAuth" in the Moneris integration guide, this action
30
+ # verifies and locks funds on a customer's card, which then must be
31
+ # captured at a later date.
32
+ #
33
+ # Pass in +order_id+ and optionally a +customer+ parameter.
34
+ def authorize(money, creditcard, options = {})
35
+ debit_commit 'us_preauth', money, creditcard, options
36
+ end
37
+
38
+ # This action verifies funding on a customer's card, and readies them for
39
+ # deposit in a merchant's account.
40
+ #
41
+ # Pass in <tt>order_id</tt> and optionally a <tt>customer</tt> parameter
42
+ def purchase(money, creditcard, options = {})
43
+ debit_commit 'us_purchase', money, creditcard, options
44
+ end
45
+
46
+ # This method retrieves locked funds from a customer's account (from a
47
+ # PreAuth) and prepares them for deposit in a merchant's account.
48
+ #
49
+ # Note: Moneris requires both the order_id and the transaction number of
50
+ # the original authorization. To maintain the same interface as the other
51
+ # gateways the two numbers are concatenated together with a ; separator as
52
+ # the authorization number returned by authorization
53
+ def capture(money, authorization, options = {})
54
+ commit 'us_completion', crediting_params(authorization, :comp_amount => amount(money))
55
+ end
56
+
57
+ # Voiding requires the original transaction ID and order ID of some open
58
+ # transaction. Closed transactions must be refunded. Note that the only
59
+ # methods which may be voided are +capture+ and +purchase+.
60
+ #
61
+ # Concatenate your transaction number and order_id by using a semicolon
62
+ # (';'). This is to keep the Moneris interface consistent with other
63
+ # gateways. (See +capture+ for details.)
64
+ def void(authorization, options = {})
65
+ commit 'us_purchasecorrection', crediting_params(authorization)
66
+ end
67
+
68
+ # Performs a refund. This method requires that the original transaction
69
+ # number and order number be included. Concatenate your transaction
70
+ # number and order_id by using a semicolon (';'). This is to keep the
71
+ # Moneris interface consistent with other gateways. (See +capture+ for
72
+ # details.)
73
+ def credit(money, authorization, options = {})
74
+ deprecated CREDIT_DEPRECATION_MESSAGE
75
+ refund(money, authorization, options)
76
+ end
77
+
78
+ def refund(money, authorization, options = {})
79
+ commit 'us_refund', crediting_params(authorization, :amount => amount(money))
80
+ end
81
+
82
+ def test?
83
+ @options[:test] || super
84
+ end
85
+ private # :nodoc: all
86
+
87
+ def expdate(creditcard)
88
+ sprintf("%.4i", creditcard.year)[-2..-1] + sprintf("%.2i", creditcard.month)
89
+ end
90
+
91
+ def debit_commit(commit_type, money, creditcard, options)
92
+ requires!(options, :order_id)
93
+ commit(commit_type, debit_params(money, creditcard, options))
94
+ end
95
+
96
+ # Common params used amongst the +purchase+ and +authorization+ methods
97
+ def debit_params(money, creditcard, options = {})
98
+ {
99
+ :order_id => options[:order_id],
100
+ :cust_id => options[:customer],
101
+ :amount => amount(money),
102
+ :pan => creditcard.number,
103
+ :expdate => expdate(creditcard),
104
+ :crypt_type => options[:crypt_type] || @options[:crypt_type]
105
+ }
106
+ end
107
+
108
+ # Common params used amongst the +credit+, +void+ and +capture+ methods
109
+ def crediting_params(authorization, options = {})
110
+ {
111
+ :txn_number => split_authorization(authorization).first,
112
+ :order_id => split_authorization(authorization).last,
113
+ :crypt_type => options[:crypt_type] || @options[:crypt_type]
114
+ }.merge(options)
115
+ end
116
+
117
+ # Splits an +authorization+ param and retrives the order id and
118
+ # transaction number in that order.
119
+ def split_authorization(authorization)
120
+ if authorization.nil? || authorization.empty? || authorization !~ /;/
121
+ raise ArgumentError, 'You must include a valid authorization code (e.g. "1234;567")'
122
+ else
123
+ authorization.split(';')
124
+ end
125
+ end
126
+
127
+ def commit(action, parameters = {})
128
+ response = parse(ssl_post(test? ? TEST_URL : LIVE_URL, post_data(action, parameters)))
129
+
130
+ Response.new(successful?(response), message_from(response[:message]), response,
131
+ :test => test?,
132
+ :authorization => authorization_from(response)
133
+ )
134
+ end
135
+
136
+ # Generates a Moneris authorization string of the form 'trans_id;receipt_id'.
137
+ def authorization_from(response = {})
138
+ if response[:trans_id] && response[:receipt_id]
139
+ "#{response[:trans_id]};#{response[:receipt_id]}"
140
+ end
141
+ end
142
+
143
+ # Tests for a successful response from Moneris' servers
144
+ def successful?(response)
145
+ response[:response_code] &&
146
+ response[:complete] &&
147
+ (0..49).include?(response[:response_code].to_i)
148
+ end
149
+
150
+ def parse(xml)
151
+ response = { :message => "Global Error Receipt", :complete => false }
152
+ hashify_xml!(xml, response)
153
+ response
154
+ end
155
+
156
+ def hashify_xml!(xml, response)
157
+ xml = REXML::Document.new(xml)
158
+ return if xml.root.nil?
159
+ xml.elements.each('//receipt/*') do |node|
160
+ response[node.name.underscore.to_sym] = normalize(node.text)
161
+ end
162
+ end
163
+
164
+ def post_data(action, parameters = {})
165
+ xml = REXML::Document.new
166
+ root = xml.add_element("request")
167
+ root.add_element("store_id").text = options[:login]
168
+ root.add_element("api_token").text = options[:password]
169
+ transaction = root.add_element(action)
170
+
171
+ # Must add the elements in the correct order
172
+ actions[action].each do |key|
173
+ transaction.add_element(key.to_s).text = parameters[key] unless parameters[key].blank?
174
+ end
175
+
176
+ xml.to_s
177
+ end
178
+
179
+ def message_from(message)
180
+ return 'Unspecified error' if message.blank?
181
+ message.gsub(/[^\w]/, ' ').split.join(" ").capitalize
182
+ end
183
+
184
+ # Make a Ruby type out of the response string
185
+ def normalize(field)
186
+ case field
187
+ when "true" then true
188
+ when "false" then false
189
+ when '', "null" then nil
190
+ else field
191
+ end
192
+ end
193
+
194
+ def actions
195
+ {
196
+ "us_purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
197
+ "us_preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
198
+ "us_refund" => [:order_id, :amount, :txn_number, :crypt_type],
199
+ "us_ind_refund" => [:order_id, :cust_id, :amount, :pan, :expdate, :crypt_type],
200
+ "us_completion" => [:order_id, :comp_amount, :txn_number, :crypt_type],
201
+ "us_purchasecorrection" => [:order_id, :txn_number, :crypt_type],
202
+ "us_cavv_purchase" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv],
203
+ "us_cavv_preauth" => [:order_id, :cust_id, :amount, :pan, :expdate, :cavv],
204
+ "us_batchcloseall" => [],
205
+ "us_opentotals" => [:ecr_number],
206
+ "us_batchclose" => [:ecr_number]
207
+ }
208
+ end
209
+ end
210
+ end
211
+ end
@@ -59,7 +59,7 @@ module ActiveMerchant #:nodoc:
59
59
  end
60
60
 
61
61
  def purchase(money, credit_card_or_stored_id, options = {})
62
- if credit_card_or_stored_id.is_a?(ActiveMerchant::Billing::CreditCard)
62
+ if credit_card_or_stored_id.respond_to?(:number)
63
63
  #Credit card for instant payment
64
64
  commit :purchase, build_purchase_request(money, credit_card_or_stored_id, options)
65
65
  else
@@ -9,17 +9,23 @@ module ActiveMerchant #:nodoc:
9
9
  # communication between Ogone systems and your e-commerce website.
10
10
  #
11
11
  # This implementation follows the specification provided in the DirectLink integration
12
- # guide version 4.2.0 (26 October 2011), available here:
12
+ # guide version 4.3.0 (25 April 2012), available here:
13
13
  # https://secure.ogone.com/ncol/Ogone_DirectLink_EN.pdf
14
14
  #
15
15
  # It also features aliases, which allow to store/unstore credit cards, as specified in
16
- # the Alias Manager Option guide version 3.2.0 (26 October 2011) available here:
16
+ # the Alias Manager Option guide version 3.2.1 (25 April 2012) available here:
17
17
  # https://secure.ogone.com/ncol/Ogone_Alias_EN.pdf
18
18
  #
19
- # It was last tested on Release 4.89 of Ogone DirectLink + AliasManager (26 October 2011).
19
+ # It also implements the 3-D Secure feature, as specified in the DirectLink with
20
+ # 3-D Secure guide version 3.0 (25 April 2012) available here:
21
+ # https://secure.ogone.com/ncol/Ogone_DirectLink-3-D_EN.pdf
22
+ #
23
+ # It was last tested on Release 4.92 of Ogone DirectLink + AliasManager + Direct Link 3D
24
+ # (25 April 2012).
20
25
  #
21
26
  # For any questions or comments, please contact one of the following:
22
- # - Nicolas Jacobeus (nj@belighted.com),
27
+ # - Joel Cogen (joel.cogen@belighted.com)
28
+ # - Nicolas Jacobeus (nicolas.jacobeus@belighted.com),
23
29
  # - Sébastien Grosjean (public@zencocoon.com),
24
30
  # - Rémy Coutable (remy@jilion.com).
25
31
  #
@@ -52,17 +58,46 @@ module ActiveMerchant #:nodoc:
52
58
  # puts response.success? # Check whether the transaction was successful
53
59
  # puts response.message # Retrieve the message returned by Ogone
54
60
  # puts response.authorization # Retrieve the unique transaction ID returned by Ogone
61
+ # puts response.order_id # Retrieve the order ID
55
62
  #
56
63
  # == Alias feature
57
64
  #
58
- # To use the alias feature, simply add :store in the options hash:
65
+ # To use the alias feature, simply add :billing_id in the options hash:
59
66
  #
60
67
  # # Associate the alias to that credit card
61
- # gateway.purchase(1000, creditcard, :order_id => "1", :store => "myawesomecustomer")
68
+ # gateway.purchase(1000, creditcard, :order_id => "1", :billing_id => "myawesomecustomer")
62
69
  #
63
70
  # # You can use the alias instead of the credit card for subsequent orders
64
71
  # gateway.purchase(2000, "myawesomecustomer", :order_id => "2")
65
72
  #
73
+ # # You can also create an alias without making a purchase using store
74
+ # gateway.store(creditcard, :billing_id => "myawesomecustomer")
75
+ #
76
+ # # When using store, you can also let Ogone generate the alias for you
77
+ # response = gateway.store(creditcard)
78
+ # puts response.billing_id # Retrieve the generated alias
79
+ #
80
+ # == 3-D Secure feature
81
+ #
82
+ # To use the 3-D Secure feature, simply add :d3d => true in the options hash:
83
+ # gateway.purchase(2000, "myawesomecustomer", :order_id => "2", :d3d => true)
84
+ #
85
+ # Specific 3-D Secure request options are (please refer to the documentation for more infos about these options):
86
+ # :win_3ds => :main_window (default), :pop_up or :pop_ix.
87
+ # :http_accept => "*/*" (default), or any other HTTP_ACCEPT header value.
88
+ # :http_user_agent => The cardholder's User-Agent string
89
+ # :accept_url => URL of the web page to show the customer when the payment is authorized.
90
+ # (or waiting to be authorized).
91
+ # :decline_url => URL of the web page to show the customer when the acquirer rejects the authorization
92
+ # more than the maximum permitted number of authorization attempts (10 by default, but can
93
+ # be changed in the "Global transaction parameters" tab, "Payment retry" section of the
94
+ # Technical Information page).
95
+ # :exception_url => URL of the web page to show the customer when the payment result is uncertain.
96
+ # :paramplus => Field to submit the miscellaneous parameters and their values that you wish to be
97
+ # returned in the post sale request or final redirection.
98
+ # :complus => Field to submit a value you wish to be returned in the post sale request or output.
99
+ # :language => Customer's language, for example: "en_EN"
100
+ #
66
101
  class OgoneGateway < Gateway
67
102
 
68
103
  URLS = {
@@ -80,8 +115,16 @@ module ActiveMerchant #:nodoc:
80
115
 
81
116
  SUCCESS_MESSAGE = "The transaction was successful"
82
117
 
118
+ THREE_D_SECURE_DISPLAY_WAYS = { :main_window => 'MAINW', # display the identification page in the main window
119
+ # (default value).
120
+ :pop_up => 'POPUP', # display the identification page in a pop-up window
121
+ # and return to the main window at the end.
122
+ :pop_ix => 'POPIX' } # display the identification page in a pop-up window
123
+ # and remain in the pop-up window.
124
+
83
125
  OGONE_NO_SIGNATURE_DEPRECATION_MESSAGE = "Signature usage will be required from a future release of ActiveMerchant's Ogone Gateway. Please update your Ogone account to use it."
84
126
  OGONE_LOW_ENCRYPTION_DEPRECATION_MESSAGE = "SHA512 signature encryptor will be required from a future release of ActiveMerchant's Ogone Gateway. Please update your Ogone account to use it."
127
+ OGONE_STORE_OPTION_DEPRECATION_MESSAGE = "The 'store' option has been renamed to 'billing_id', and its usage is deprecated."
85
128
 
86
129
  self.supported_countries = ['BE', 'DE', 'FR', 'NL', 'AT', 'CH']
87
130
  # also supports Airplus and UATP
@@ -152,6 +195,14 @@ module ActiveMerchant #:nodoc:
152
195
  perform_reference_credit(money, reference, options)
153
196
  end
154
197
 
198
+ # Store a credit card by creating an Ogone Alias
199
+ def store(payment_source, options = {})
200
+ options.merge!(:alias_operation => 'BYOGONE') unless options.has_key?(:billing_id) || options.has_key?(:store)
201
+ response = authorize(1, payment_source, options)
202
+ void(response.authorization) if response.success?
203
+ response
204
+ end
205
+
155
206
  def test?
156
207
  @options[:test] || super
157
208
  end
@@ -164,7 +215,7 @@ module ActiveMerchant #:nodoc:
164
215
 
165
216
  def reference_transaction?(identifier)
166
217
  return false unless identifier.is_a?(String)
167
- reference, action = identifier.split(";")
218
+ _, action = identifier.split(";")
168
219
  !action.nil?
169
220
  end
170
221
 
@@ -188,21 +239,44 @@ module ActiveMerchant #:nodoc:
188
239
 
189
240
  def add_payment_source(post, payment_source, options)
190
241
  if payment_source.is_a?(String)
191
- add_alias(post, payment_source)
242
+ add_alias(post, payment_source, options[:alias_operation])
192
243
  add_eci(post, options[:eci] || '9')
193
244
  else
194
- add_alias(post, options[:store])
245
+ if options.has_key?(:store)
246
+ deprecated OGONE_STORE_OPTION_DEPRECATION_MESSAGE
247
+ options[:billing_id] ||= options[:store]
248
+ end
249
+ add_alias(post, options[:billing_id], options[:alias_operation])
195
250
  add_eci(post, options[:eci] || '7')
251
+ add_d3d(post, options) if options[:d3d]
196
252
  add_creditcard(post, payment_source)
197
253
  end
198
254
  end
199
255
 
256
+ def add_d3d(post, options)
257
+ add_pair post, 'FLAG3D', 'Y'
258
+ win_3ds = THREE_D_SECURE_DISPLAY_WAYS.key?(options[:win_3ds]) ?
259
+ THREE_D_SECURE_DISPLAY_WAYS[options[:win_3ds]] :
260
+ THREE_D_SECURE_DISPLAY_WAYS[:main_window]
261
+ add_pair post, 'WIN3DS', win_3ds
262
+
263
+ add_pair post, 'HTTP_ACCEPT', options[:http_accept] || "*/*"
264
+ add_pair post, 'HTTP_USER_AGENT', options[:http_user_agent] if options[:http_user_agent]
265
+ add_pair post, 'ACCEPTURL', options[:accept_url] if options[:accept_url]
266
+ add_pair post, 'DECLINEURL', options[:decline_url] if options[:decline_url]
267
+ add_pair post, 'EXCEPTIONURL', options[:exception_url] if options[:exception_url]
268
+ add_pair post, 'PARAMPLUS', options[:paramplus] if options[:paramplus]
269
+ add_pair post, 'COMPLUS', options[:complus] if options[:complus]
270
+ add_pair post, 'LANGUAGE', options[:language] if options[:language]
271
+ end
272
+
200
273
  def add_eci(post, eci)
201
274
  add_pair post, 'ECI', eci.to_s
202
275
  end
203
276
 
204
- def add_alias(post, _alias)
277
+ def add_alias(post, _alias, alias_operation = nil)
205
278
  add_pair post, 'ALIAS', _alias
279
+ add_pair post, 'ALIASOPERATION', alias_operation unless alias_operation.nil?
206
280
  end
207
281
 
208
282
  def add_authorization(post, authorization)
@@ -242,7 +316,15 @@ module ActiveMerchant #:nodoc:
242
316
 
243
317
  def parse(body)
244
318
  xml_root = REXML::Document.new(body).root
245
- convert_attributes_to_hash(xml_root.attributes)
319
+ response = convert_attributes_to_hash(xml_root.attributes)
320
+
321
+ # Add HTML_ANSWER element (3-D Secure specific to the response's params)
322
+ # Note: HTML_ANSWER is not an attribute so we add it "by hand" to the response
323
+ if html_answer = REXML::XPath.first(xml_root, "//HTML_ANSWER")
324
+ response["HTML_ANSWER"] = html_answer.text
325
+ end
326
+
327
+ response
246
328
  end
247
329
 
248
330
  def commit(action, parameters)
@@ -259,7 +341,7 @@ module ActiveMerchant #:nodoc:
259
341
  :avs_result => { :code => AVS_MAPPING[response["AAVCheck"]] },
260
342
  :cvv_result => CVV_MAPPING[response["CVCCheck"]]
261
343
  }
262
- Response.new(successful?(response), message_from(response), response, options)
344
+ OgoneResponse.new(successful?(response), message_from(response), response, options)
263
345
  end
264
346
 
265
347
  def successful?(response)
@@ -326,5 +408,15 @@ module ActiveMerchant #:nodoc:
326
408
  response_hash
327
409
  end
328
410
  end
411
+
412
+ class OgoneResponse < Response
413
+ def order_id
414
+ @params['orderID']
415
+ end
416
+
417
+ def billing_id
418
+ @params['ALIAS']
419
+ end
420
+ end
329
421
  end
330
422
  end