activemerchant 1.29.3 → 1.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +39 -0
  3. data/CONTRIBUTORS +19 -0
  4. data/README.md +43 -41
  5. data/lib/active_merchant/billing/check.rb +15 -11
  6. data/lib/active_merchant/billing/credit_card.rb +5 -1
  7. data/lib/active_merchant/billing/credit_card_formatting.rb +8 -8
  8. data/lib/active_merchant/billing/gateway.rb +1 -1
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +9 -1
  10. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +15 -4
  11. data/lib/active_merchant/billing/gateways/balanced.rb +9 -3
  12. data/lib/active_merchant/billing/gateways/banwire.rb +15 -1
  13. data/lib/active_merchant/billing/gateways/beanstream.rb +26 -24
  14. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +6 -2
  15. data/lib/active_merchant/billing/gateways/braintree_blue.rb +5 -2
  16. data/lib/active_merchant/billing/gateways/cyber_source.rb +55 -22
  17. data/lib/active_merchant/billing/gateways/eway.rb +114 -171
  18. data/lib/active_merchant/billing/gateways/eway_managed.rb +52 -22
  19. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +222 -0
  20. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +13 -2
  21. data/lib/active_merchant/billing/gateways/litle.rb +50 -19
  22. data/lib/active_merchant/billing/gateways/merchant_ware.rb +44 -9
  23. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +190 -0
  24. data/lib/active_merchant/billing/gateways/moneris.rb +2 -4
  25. data/lib/active_merchant/billing/gateways/nab_transact.rb +20 -3
  26. data/lib/active_merchant/billing/gateways/netbilling.rb +1 -0
  27. data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
  28. data/lib/active_merchant/billing/gateways/optimal_payment.rb +18 -3
  29. data/lib/active_merchant/billing/gateways/orbital.rb +9 -5
  30. data/lib/active_merchant/billing/gateways/payment_express.rb +62 -1
  31. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -1
  32. data/lib/active_merchant/billing/gateways/paypal_express.rb +2 -0
  33. data/lib/active_merchant/billing/gateways/pin.rb +157 -0
  34. data/lib/active_merchant/billing/gateways/qbms.rb +3 -2
  35. data/lib/active_merchant/billing/gateways/quickpay.rb +66 -28
  36. data/lib/active_merchant/billing/gateways/sage_pay.rb +6 -0
  37. data/lib/active_merchant/billing/gateways/smart_ps.rb +1 -1
  38. data/lib/active_merchant/billing/gateways/spreedly_core.rb +235 -0
  39. data/lib/active_merchant/billing/gateways/stripe.rb +1 -0
  40. data/lib/active_merchant/billing/gateways/wirecard.rb +15 -9
  41. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +4 -1
  42. data/lib/active_merchant/billing/integrations/paypal/notification.rb +39 -31
  43. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +13 -10
  44. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +14 -14
  45. data/lib/active_merchant/version.rb +1 -1
  46. metadata +109 -49
  47. metadata.gz.sig +0 -0
@@ -89,7 +89,18 @@ module ActiveMerchant #:nodoc:
89
89
  commit("ProcessPayment", post)
90
90
  end
91
91
 
92
- # TODO: eWay API also provides QueryCustomer
92
+ # Get customer's stored credit card details given by billing_id
93
+ #
94
+ # ==== Parameters
95
+ #
96
+ # * <tt>billing_id</tt> -- The eWay provided card/customer token to charge (managedCustomerID)
97
+ def retrieve(billing_id)
98
+ post = {}
99
+ post[:managedCustomerID] = billing_id.to_s
100
+
101
+ commit("QueryCustomer", post)
102
+ end
103
+
93
104
  # TODO: eWay API also provides QueryPayment
94
105
 
95
106
  private
@@ -146,25 +157,29 @@ module ActiveMerchant #:nodoc:
146
157
  # Successful payment
147
158
  reply=parse_purchase(root)
148
159
  else
149
- if root = REXML::XPath.first(xml, '//CreateCustomerResult') then
150
- reply[:message]='OK'
151
- reply[:CreateCustomerResult]=root.text
152
- reply[:success]=true
160
+ if root = REXML::XPath.first(xml, '//QueryCustomerResult') then
161
+ reply=parse_query_customer(root)
153
162
  else
154
- if root = REXML::XPath.first(xml, '//UpdateCustomerResult') then
155
- if root.text.downcase == 'true' then
156
- reply[:message]='OK'
157
- reply[:success]=true
163
+ if root = REXML::XPath.first(xml, '//CreateCustomerResult') then
164
+ reply[:message]='OK'
165
+ reply[:CreateCustomerResult]=root.text
166
+ reply[:success]=true
167
+ else
168
+ if root = REXML::XPath.first(xml, '//UpdateCustomerResult') then
169
+ if root.text.downcase == 'true' then
170
+ reply[:message]='OK'
171
+ reply[:success]=true
172
+ else
173
+ # ERROR: This state should never occur. If there is a problem,
174
+ # a soap:Fault will be returned. The presence of this
175
+ # element always means a success.
176
+ raise StandardError, "Unexpected \"false\" in UpdateCustomerResult"
177
+ end
158
178
  else
159
- # ERROR: This state should never occur. If there is a problem,
160
- # a soap:Fault will be returned. The presence of this
161
- # element always means a success.
162
- raise StandardError, "Unexpected \"false\" in UpdateCustomerResult"
179
+ # ERROR: This state should never occur currently. We have handled
180
+ # responses for all the methods which we support.
181
+ raise StandardError, "Unexpected response"
163
182
  end
164
- else
165
- # ERROR: This state should never occur currently. We have handled
166
- # responses for all the methods which we support.
167
- raise StandardError, "Unexpected response"
168
183
  end
169
184
  end
170
185
  end
@@ -188,6 +203,17 @@ module ActiveMerchant #:nodoc:
188
203
  reply
189
204
  end
190
205
 
206
+ def parse_query_customer(node)
207
+ reply={}
208
+ reply[:message]='OK'
209
+ reply[:success]=true
210
+ reply[:CCNumber]=REXML::XPath.first(node, '//CCNumber').text
211
+ reply[:CCName]=REXML::XPath.first(node, '//CCName').text
212
+ reply[:CCExpiryMonth]=REXML::XPath.first(node, '//CCExpiryMonth').text
213
+ reply[:CCExpiryYear]=REXML::XPath.first(node, '//CCExpiryYear').text
214
+ reply
215
+ end
216
+
191
217
  def commit(action, post)
192
218
  raw = begin
193
219
  ssl_post(test? ? self.test_url : self.live_url, soap_request(post, action), 'Content-Type' => 'application/soap+xml; charset=utf-8')
@@ -206,11 +232,15 @@ module ActiveMerchant #:nodoc:
206
232
  def soap_request(arguments, action)
207
233
  # eWay demands all fields be sent, but contain an empty string if blank
208
234
  post = case action
209
- when 'ProcessPayment'
210
- default_payment_fields.merge(arguments)
211
- else
212
- default_customer_fields.merge(arguments)
213
- end
235
+ when 'QueryCustomer'
236
+ arguments
237
+ when 'ProcessPayment'
238
+ default_payment_fields.merge(arguments)
239
+ when 'CreateCustomer'
240
+ default_customer_fields.merge(arguments)
241
+ when 'UpdateCustomer'
242
+ default_customer_fields.merge(arguments)
243
+ end
214
244
 
215
245
  xml = Builder::XmlMarkup.new :indent => 2
216
246
  xml.instruct!
@@ -0,0 +1,222 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class FirstdataE4Gateway < Gateway
4
+ self.test_url = "https://api.demo.globalgatewaye4.firstdata.com/transaction"
5
+ self.live_url = "https://api.globalgatewaye4.firstdata.com/transaction"
6
+
7
+ TRANSACTIONS = {
8
+ :sale => "00",
9
+ :authorization => "01",
10
+ :capture => "32",
11
+ :void => "33",
12
+ :credit => "34"
13
+ }
14
+
15
+ POST_HEADERS = {
16
+ "Accepts" => "application/xml",
17
+ "Content-Type" => "application/xml"
18
+ }
19
+
20
+ SUCCESS = "true"
21
+
22
+ SENSITIVE_FIELDS = [:verification_str2, :expiry_date, :card_number]
23
+
24
+ self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :discover]
25
+ self.supported_countries = ["CA", "US"]
26
+ self.default_currency = "USD"
27
+ self.homepage_url = "http://www.firstdata.com"
28
+ self.display_name = "FirstData Global Gateway e4"
29
+
30
+ def initialize(options = {})
31
+ requires!(options, :login, :password)
32
+ @options = options
33
+
34
+ super
35
+ end
36
+
37
+ def authorize(money, credit_card, options = {})
38
+ commit(:authorization, build_sale_or_authorization_request(money, credit_card, options))
39
+ end
40
+
41
+ def purchase(money, credit_card, options = {})
42
+ commit(:sale, build_sale_or_authorization_request(money, credit_card, options))
43
+ end
44
+
45
+ def capture(money, authorization, options = {})
46
+ commit(:capture, build_capture_or_credit_request(money, authorization, options))
47
+ end
48
+
49
+ def void(authorization, options = {})
50
+ commit(:void, build_capture_or_credit_request(money_from_authorization(authorization), authorization, options))
51
+ end
52
+
53
+ def refund(money, authorization, options = {})
54
+ commit(:credit, build_capture_or_credit_request(money, authorization, options))
55
+ end
56
+
57
+ private
58
+
59
+ def build_request(action, body)
60
+ xml = Builder::XmlMarkup.new
61
+
62
+ xml.instruct!
63
+ xml.tag! "Transaction" do
64
+ add_credentials(xml)
65
+ add_transaction_type(xml, action)
66
+ xml << body
67
+ end
68
+
69
+ xml.target!
70
+ end
71
+
72
+ def build_sale_or_authorization_request(money, credit_card, options)
73
+ xml = Builder::XmlMarkup.new
74
+
75
+ add_amount(xml, money)
76
+ add_credit_card(xml, credit_card)
77
+ add_customer_data(xml, options)
78
+ add_invoice(xml, options)
79
+
80
+ xml.target!
81
+ end
82
+
83
+ def build_capture_or_credit_request(money, identification, options)
84
+ xml = Builder::XmlMarkup.new
85
+
86
+ add_identification(xml, identification)
87
+ add_amount(xml, money)
88
+ add_customer_data(xml, options)
89
+
90
+ xml.target!
91
+ end
92
+
93
+ def add_credentials(xml)
94
+ xml.tag! "ExactID", @options[:login]
95
+ xml.tag! "Password", @options[:password]
96
+ end
97
+
98
+ def add_transaction_type(xml, action)
99
+ xml.tag! "Transaction_Type", TRANSACTIONS[action]
100
+ end
101
+
102
+ def add_identification(xml, identification)
103
+ authorization_num, transaction_tag, _ = identification.split(";")
104
+
105
+ xml.tag! "Authorization_Num", authorization_num
106
+ xml.tag! "Transaction_Tag", transaction_tag
107
+ end
108
+
109
+ def add_amount(xml, money)
110
+ xml.tag! "DollarAmount", amount(money)
111
+ end
112
+
113
+ def add_credit_card(xml, credit_card)
114
+ xml.tag! "Card_Number", credit_card.number
115
+ xml.tag! "Expiry_Date", expdate(credit_card)
116
+ xml.tag! "CardHoldersName", credit_card.name
117
+ xml.tag! "CardType", credit_card.brand
118
+
119
+ if credit_card.verification_value?
120
+ xml.tag! "CVD_Presence_Ind", "1"
121
+ xml.tag! "VerificationStr2", credit_card.verification_value
122
+ end
123
+ end
124
+
125
+ def add_customer_data(xml, options)
126
+ xml.tag! "Customer_Ref", options[:customer] if options[:customer]
127
+ xml.tag! "Client_IP", options[:ip] if options[:ip]
128
+ xml.tag! "Client_Email", options[:email] if options[:email]
129
+ end
130
+
131
+ def add_address(xml, options)
132
+ if address = (options[:billing_address] || options[:address])
133
+ xml.tag! "ZipCode", address[:zip]
134
+ end
135
+ end
136
+
137
+ def add_invoice(xml, options)
138
+ xml.tag! "Reference_No", options[:order_id]
139
+ xml.tag! "Reference_3", options[:description] if options[:description]
140
+ end
141
+
142
+ def expdate(credit_card)
143
+ "#{format(credit_card.month, :two_digits)}#{format(credit_card.year, :two_digits)}"
144
+ end
145
+
146
+ def commit(action, request)
147
+ url = (test? ? self.test_url : self.live_url)
148
+ begin
149
+ response = parse(ssl_post(url, build_request(action, request), POST_HEADERS))
150
+ rescue ResponseError => e
151
+ response = parse_error(e.response)
152
+ end
153
+
154
+ Response.new(successful?(response), message_from(response), response,
155
+ :test => test?,
156
+ :authorization => authorization_from(response),
157
+ :avs_result => {:code => response[:avs]},
158
+ :cvv_result => response[:cvv2]
159
+ )
160
+ end
161
+
162
+ def successful?(response)
163
+ response[:transaction_approved] == SUCCESS
164
+ end
165
+
166
+ def authorization_from(response)
167
+ if response[:authorization_num] && response[:transaction_tag]
168
+ [
169
+ response[:authorization_num],
170
+ response[:transaction_tag],
171
+ (response[:dollar_amount].to_f * 100).to_i
172
+ ].join(";")
173
+ else
174
+ ""
175
+ end
176
+ end
177
+
178
+ def money_from_authorization(auth)
179
+ _, _, amount = auth.split(/;/, 3)
180
+ amount.to_i # return the # of cents, no need to divide
181
+ end
182
+
183
+ def message_from(response)
184
+ if(response[:faultcode] && response[:faultstring])
185
+ response[:faultstring]
186
+ elsif(response[:error_number] != "0")
187
+ response[:error_description]
188
+ else
189
+ result = (response[:exact_message] || "")
190
+ result << " - #{response[:bank_message]}" if response[:bank_message].present?
191
+ result
192
+ end
193
+ end
194
+
195
+ def parse_error(error)
196
+ {
197
+ :transaction_approved => "false",
198
+ :error_number => error.code,
199
+ :error_description => error.body
200
+ }
201
+ end
202
+
203
+ def parse(xml)
204
+ response = {}
205
+ xml = REXML::Document.new(xml)
206
+
207
+ if root = REXML::XPath.first(xml, "//TransactionResult")
208
+ parse_elements(response, root)
209
+ end
210
+
211
+ response.delete_if{ |k,v| SENSITIVE_FIELDS.include?(k) }
212
+ end
213
+
214
+ def parse_elements(response, root)
215
+ root.elements.to_a.each do |node|
216
+ response[node.name.gsub(/EXact/, "Exact").underscore.to_sym] = (node.text || "").strip
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
222
+
@@ -9,12 +9,23 @@ module ActiveMerchant #:nodoc:
9
9
  #
10
10
  # ActiveMerchant expects the amounts to be given as an Integer in cents. In this case, 10 EUR becomes 1000.
11
11
  #
12
+ # Create certificates for authentication:
13
+ #
14
+ # The PEM file expected should contain both the certificate and the generated PEM file.
15
+ # Some sample shell commands to generate the certificates:
16
+ #
17
+ # openssl genrsa -aes128 -out priv.pem -passout pass:[YOUR PASSWORD] 1024
18
+ # openssl req -x509 -new -key priv.pem -passin pass:[YOUR PASSWORD] -days 3000 -out cert.cer
19
+ # cat cert.cer priv.pem > ideal.pem
20
+ #
21
+ # Following the steps above, upload cert.cer to the ideal web interface and pass the path of ideal.pem to the :pem option.
22
+ #
12
23
  # Configure the gateway using your iDEAL bank account info and security settings:
13
24
  #
14
25
  # Create gateway:
15
26
  # gateway = ActiveMerchant::Billing::IdealRabobankGateway.new(
16
- # :login => '123456789', # merchant number
17
- # :pem => File.read(RAILS_ROOT + '/config/ideal.pem'), # put certificate and PEM in this file
27
+ # :login => '123456789', # 9 digit merchant number
28
+ # :pem => File.read(Rails.root + 'config/ideal.pem'),
18
29
  # :password => 'password' # password for the PEM key
19
30
  # )
20
31
  #
@@ -71,13 +71,13 @@ module ActiveMerchant #:nodoc:
71
71
  super
72
72
  end
73
73
 
74
- def authorize(money, creditcard, options = {})
75
- to_pass = create_credit_card_hash(money, creditcard, options)
74
+ def authorize(money, creditcard_or_token, options = {})
75
+ to_pass = build_authorize_request(money, creditcard_or_token, options)
76
76
  build_response(:authorization, @litle.authorization(to_pass))
77
77
  end
78
78
 
79
- def purchase(money, creditcard, options = {})
80
- to_pass = create_credit_card_hash(money, creditcard, options)
79
+ def purchase(money, creditcard_or_token, options = {})
80
+ to_pass = build_purchase_request(money, creditcard_or_token, options)
81
81
  build_response(:sale, @litle.sale(to_pass))
82
82
  end
83
83
 
@@ -98,7 +98,7 @@ module ActiveMerchant #:nodoc:
98
98
 
99
99
  def store(creditcard, options = {})
100
100
  to_pass = create_token_hash(creditcard, options)
101
- build_response(:registerToken, @litle.register_token_request(to_pass), %w(801 802))
101
+ build_response(:registerToken, @litle.register_token_request(to_pass), %w(000 801 802))
102
102
  end
103
103
 
104
104
  private
@@ -142,11 +142,17 @@ module ActiveMerchant #:nodoc:
142
142
  if response['response'] == "0"
143
143
  detail = response["#{kind}Response"]
144
144
  fraud = fraud_result(detail)
145
+ authorization = case kind
146
+ when :registerToken
147
+ response['registerTokenResponse']['litleToken']
148
+ else
149
+ detail['litleTxnId']
150
+ end
145
151
  Response.new(
146
152
  valid_responses.include?(detail['response']),
147
153
  detail['message'],
148
154
  {:litleOnlineResponse => response},
149
- :authorization => detail['litleTxnId'],
155
+ :authorization => authorization,
150
156
  :avs_result => {:code => fraud['avs']},
151
157
  :cvv_result => fraud['cvv'],
152
158
  :test => test?
@@ -156,27 +162,52 @@ module ActiveMerchant #:nodoc:
156
162
  end
157
163
  end
158
164
 
159
- def create_credit_card_hash(money, creditcard, options)
160
- cc_type = CARD_TYPE[creditcard.brand]
165
+ def build_authorize_request(money, creditcard_or_token, options)
166
+ hash = create_hash(money, options)
167
+
168
+ add_credit_card_or_token_hash(hash, creditcard_or_token)
169
+
170
+ hash
171
+ end
172
+
173
+ def build_purchase_request(money, creditcard_or_token, options)
174
+ hash = create_hash(money, options)
175
+
176
+ add_credit_card_or_token_hash(hash, creditcard_or_token)
161
177
 
162
- exp_date_yr = creditcard.year.to_s()[2..3]
178
+ hash
179
+ end
163
180
 
164
- if( creditcard.month.to_s().length == 1 )
165
- exp_date_mo = '0' + creditcard.month.to_s()
181
+ def add_credit_card_or_token_hash(hash, creditcard_or_token)
182
+ if creditcard_or_token.is_a?(String)
183
+ add_token_hash(hash, creditcard_or_token)
166
184
  else
167
- exp_date_mo = creditcard.month.to_s()
185
+ add_credit_card_hash(hash, creditcard_or_token)
168
186
  end
187
+ end
169
188
 
170
- exp_date = exp_date_mo + exp_date_yr
189
+ def add_token_hash(hash, creditcard_or_token)
190
+ token_info = {
191
+ 'litleToken' => creditcard_or_token
192
+ }
193
+
194
+ hash['token'] = token_info
195
+ hash
196
+ end
197
+
198
+ def add_credit_card_hash(hash, creditcard)
199
+ cc_type = CARD_TYPE[creditcard.brand]
200
+ exp_date_yr = creditcard.year.to_s[2..3]
201
+ exp_date_mo = '%02d' % creditcard.month.to_i
202
+ exp_date = exp_date_mo + exp_date_yr
171
203
 
172
204
  card_info = {
173
- 'type' => cc_type,
174
- 'number' => creditcard.number,
175
- 'expDate' => exp_date,
176
- 'cardValidationNum' => creditcard.verification_value
205
+ 'type' => cc_type,
206
+ 'number' => creditcard.number,
207
+ 'expDate' => exp_date,
208
+ 'cardValidationNum' => creditcard.verification_value
177
209
  }
178
210
 
179
- hash = create_hash(money, options)
180
211
  hash['card'] = card_info
181
212
  hash
182
213
  end
@@ -258,7 +289,7 @@ module ActiveMerchant #:nodoc:
258
289
  'customerId' => options[:customer],
259
290
  'reportGroup' => (options[:merchant] || @options[:merchant]),
260
291
  'merchantId' => (options[:merchant_id] || @options[:merchant_id]),
261
- 'orderSource' => 'ecommerce',
292
+ 'orderSource' => (options[:order_source] || 'ecommerce'),
262
293
  'enhancedData' => enhanced_data,
263
294
  'fraudCheckType' => fraud_check_type,
264
295
  'user' => (options[:user] || @options[:user]),