activemerchant 1.41.0 → 1.42.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 (36) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG +23 -1
  3. data/CONTRIBUTORS +12 -0
  4. data/README.md +3 -0
  5. data/lib/active_merchant/billing/gateway.rb +8 -1
  6. data/lib/active_merchant/billing/gateways/app55.rb +185 -0
  7. data/lib/active_merchant/billing/gateways/authorize_net.rb +12 -2
  8. data/lib/active_merchant/billing/gateways/balanced.rb +9 -3
  9. data/lib/active_merchant/billing/gateways/card_stream_modern.rb +2 -1
  10. data/lib/active_merchant/billing/gateways/elavon.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/eway_rapid.rb +2 -2
  12. data/lib/active_merchant/billing/gateways/litle.rb +5 -5
  13. data/lib/active_merchant/billing/gateways/mercury.rb +22 -0
  14. data/lib/active_merchant/billing/gateways/orbital.rb +25 -2
  15. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +187 -0
  16. data/lib/active_merchant/billing/gateways/paymill.rb +60 -28
  17. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +1 -1
  18. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +4 -2
  19. data/lib/active_merchant/billing/gateways/spreedly_core.rb +4 -2
  20. data/lib/active_merchant/billing/gateways/stripe.rb +35 -15
  21. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +158 -0
  22. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +62 -46
  23. data/lib/active_merchant/billing/gateways/webpay.rb +1 -2
  24. data/lib/active_merchant/billing/integrations/bit_pay.rb +2 -2
  25. data/lib/active_merchant/billing/integrations/bit_pay/helper.rb +15 -19
  26. data/lib/active_merchant/billing/integrations/bit_pay/notification.rb +38 -20
  27. data/lib/active_merchant/billing/integrations/notification.rb +2 -2
  28. data/lib/active_merchant/billing/integrations/wirecard_checkout_page.rb +39 -0
  29. data/lib/active_merchant/billing/integrations/wirecard_checkout_page/common.rb +104 -0
  30. data/lib/active_merchant/billing/integrations/wirecard_checkout_page/helper.rb +145 -0
  31. data/lib/active_merchant/billing/integrations/wirecard_checkout_page/notification.rb +101 -0
  32. data/lib/active_merchant/billing/integrations/wirecard_checkout_page/return.rb +35 -0
  33. data/lib/active_merchant/version.rb +1 -1
  34. metadata +16 -59
  35. data.tar.gz.sig +0 -0
  36. metadata.gz.sig +0 -0
@@ -0,0 +1,187 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PacNetRavenGateway < Gateway
4
+ self.test_url = 'https://demo.deepcovelabs.com/realtime/'
5
+ self.live_url = 'https://raven.pacnetservices.com/realtime/'
6
+
7
+ self.supported_countries = ['US']
8
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
9
+ self.money_format = :cents
10
+ self.default_currency = 'USD'
11
+ self.homepage_url = 'http://www.pacnetservices.com/'
12
+ self.display_name = 'Raven PacNet'
13
+
14
+ def initialize(options = {})
15
+ requires!(options, :user, :secret, :prn)
16
+ super
17
+ end
18
+
19
+ def authorize(money, creditcard, options = {})
20
+ post = {}
21
+ add_creditcard(post, creditcard)
22
+ add_currency_code(post, money, options)
23
+ add_address(post, options)
24
+ post['PRN'] = @options[:prn]
25
+
26
+ commit('cc_preauth', money, post)
27
+ end
28
+
29
+ def purchase(money, creditcard, options = {})
30
+ post = {}
31
+ add_currency_code(post, money, options)
32
+ add_creditcard(post, creditcard)
33
+ add_address(post, options)
34
+ post['PRN'] = @options[:prn]
35
+
36
+ commit('cc_debit', money, post)
37
+ end
38
+
39
+ def void(authorization, options = {})
40
+ post = {}
41
+ post['TrackingNumber'] = authorization
42
+ post['PymtType'] = options[:pymt_type] || 'cc_debit'
43
+
44
+ commit('void', nil, post)
45
+ end
46
+
47
+ def capture(money, authorization, options = {})
48
+ post = {}
49
+ post['PreauthNumber'] = authorization
50
+ post['PRN'] = @options[:prn]
51
+ add_currency_code(post, money, options)
52
+
53
+ commit('cc_settle', money, post)
54
+ end
55
+
56
+ def refund(money, template_number, options = {})
57
+ post = {}
58
+ post['PRN'] = @options[:prn]
59
+ post['TemplateNumber'] = template_number
60
+ add_currency_code(post, money, options)
61
+
62
+ commit('cc_refund', money, post)
63
+ end
64
+
65
+ private
66
+
67
+ def add_creditcard(post, creditcard)
68
+ post['CardNumber'] = creditcard.number
69
+ post['Expiry'] = expdate(creditcard)
70
+ post['CVV2'] = creditcard.verification_value if creditcard.verification_value
71
+ end
72
+
73
+ def add_currency_code(post, money, options)
74
+ post['Currency'] = options[:currency] || currency(money)
75
+ end
76
+
77
+ def add_address(post, options)
78
+ if address = options[:billing_address] || options[:address]
79
+ post['BillingStreetAddressLineOne'] = address[:address1].to_s
80
+ post['BillingStreetAddressLineFour'] = address[:address2].to_s
81
+ post['BillingPostalCode'] = address[:zip].to_s
82
+ end
83
+ end
84
+
85
+ def parse(body)
86
+ Hash[body.split('&').map{|x| x.split('=').map{|x| CGI.unescape(x)}}]
87
+ end
88
+
89
+ def commit(action, money, parameters)
90
+ parameters['Amount'] = amount(money) unless action == 'void'
91
+
92
+ data = ssl_post url(action), post_data(action, parameters)
93
+
94
+ response = parse(data)
95
+ response[:action] = action
96
+
97
+ message = message_from(response)
98
+
99
+ test_mode = test? || message =~ /TESTMODE/
100
+
101
+ Response.new(success?(response), message, response,
102
+ :test => test_mode,
103
+ :authorization => response['TrackingNumber'],
104
+ :fraud_review => fraud_review?(response),
105
+ :avs_result => { :postal_match => response['AVSPostalResponseCode'], :street_match => response['AVSAddressResponseCode'] },
106
+ :cvv_result => response['CVV2ResponseCode']
107
+ )
108
+ end
109
+
110
+ def url(action)
111
+ (test? ? self.test_url : self.live_url) + endpoint(action)
112
+ end
113
+
114
+ def endpoint(action)
115
+ return 'void' if action == 'void'
116
+ 'submit'
117
+ end
118
+
119
+ def fraud_review?(response)
120
+ false
121
+ end
122
+
123
+ def success?(response)
124
+ if %w(cc_settle cc_debit cc_preauth cc_refund).include?(response[:action])
125
+ !response['ApprovalCode'].nil? and response['ErrorCode'].nil? and response['Status'] == 'Approved'
126
+ elsif response[:action] = 'void'
127
+ !response['ApprovalCode'].nil? and response['ErrorCode'].nil? and response['Status'] == 'Voided'
128
+ end
129
+ end
130
+
131
+ def message_from(response)
132
+ return response['Message'] if response['Message']
133
+
134
+ if response['Status'] == 'Approved'
135
+ "This transaction has been approved"
136
+ elsif response['Status'] == 'Declined'
137
+ "This transaction has been declined"
138
+ elsif response['Status'] == 'Voided'
139
+ "This transaction has been voided"
140
+ else
141
+ response['Status']
142
+ end
143
+ end
144
+
145
+ def post_data(action, parameters = {})
146
+ post = {}
147
+
148
+ post['PymtType'] = action
149
+ post['RAPIVersion'] = '2'
150
+ post['UserName'] = @options[:user]
151
+ post['Timestamp'] = timestamp
152
+ post['RequestID'] = request_id
153
+ post['Signature'] = signature(action, post, parameters)
154
+
155
+ request = post.merge(parameters).collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
156
+ request
157
+ end
158
+
159
+ def timestamp
160
+ Time.now.strftime("%Y-%m-%dT%H:%M:%S.Z")
161
+ end
162
+
163
+ def request_id
164
+ (0...21).map{(65+rand(26)).chr}.join.downcase
165
+ end
166
+
167
+ def signature(action, post, parameters = {})
168
+ string = if %w(cc_settle cc_debit cc_preauth cc_refund).include?(action)
169
+ post['UserName'] + post['Timestamp'] + post['RequestID'] + post['PymtType'] + parameters['Amount'].to_s + parameters['Currency']
170
+ elsif action == 'void'
171
+ post['UserName'] + post['Timestamp'] + post['RequestID'] + parameters['TrackingNumber']
172
+ else
173
+ post['UserName']
174
+ end
175
+ Digest::HMAC.hexdigest(string, @options[:secret], Digest::SHA1)
176
+ end
177
+
178
+ def expdate(creditcard)
179
+ year = sprintf("%.4i", creditcard.year)
180
+ month = sprintf("%.2i", creditcard.month)
181
+
182
+ "#{month}#{year[-2..-1]}"
183
+ end
184
+ end
185
+ end
186
+ end
187
+
@@ -17,27 +17,11 @@ module ActiveMerchant #:nodoc:
17
17
  end
18
18
 
19
19
  def purchase(money, payment_method, options = {})
20
- case payment_method
21
- when String
22
- purchase_with_token(money, payment_method, options)
23
- else
24
- MultiResponse.run do |r|
25
- r.process { save_card(payment_method) }
26
- r.process { purchase_with_token(money, r.authorization, options) }
27
- end
28
- end
20
+ action_with_token(:purchase, money, payment_method, options)
29
21
  end
30
22
 
31
23
  def authorize(money, payment_method, options = {})
32
- case payment_method
33
- when String
34
- authorize_with_token(money, payment_method, options)
35
- else
36
- MultiResponse.run do |r|
37
- r.process { save_card(payment_method) }
38
- r.process { authorize_with_token(money, r.authorization, options) }
39
- end
40
- end
24
+ action_with_token(:authorize, money, payment_method, options)
41
25
  end
42
26
 
43
27
  def capture(money, authorization, options = {})
@@ -110,6 +94,18 @@ module ActiveMerchant #:nodoc:
110
94
  ].join(";")
111
95
  end
112
96
 
97
+ def action_with_token(action, money, payment_method, options)
98
+ case payment_method
99
+ when String
100
+ self.send("#{action}_with_token", money, payment_method, options)
101
+ else
102
+ MultiResponse.run do |r|
103
+ r.process { save_card(payment_method) }
104
+ r.process { self.send("#{action}_with_token", money, r.authorization, options) }
105
+ end
106
+ end
107
+ end
108
+
113
109
  def purchase_with_token(money, card_token, options)
114
110
  post = {}
115
111
 
@@ -147,17 +143,12 @@ module ActiveMerchant #:nodoc:
147
143
  def response_for_save_from(raw_response)
148
144
  options = { :test => test? }
149
145
 
150
- parsed = JSON.parse(raw_response.sub(/jsonPFunction\(/, '').sub(/\)\z/, ''))
151
- if parsed['error']
152
- succeeded = false
153
- message = parsed['error']['message']
154
- else
155
- succeeded = parsed['transaction']['processing']['result'] == 'ACK'
156
- message = parsed['transaction']['processing']['return']['message']
157
- options[:authorization] = parsed['transaction']['identification']['uniqueId'] if succeeded
158
- end
146
+ parser = ResponseParser.new(raw_response, options)
147
+ parser.generate_response
148
+ end
159
149
 
160
- Response.new(succeeded, message, parsed, options)
150
+ def parse_reponse(response)
151
+ JSON.parse(response.sub(/jsonPFunction\(/, '').sub(/\)\z/, ''))
161
152
  end
162
153
 
163
154
  def save_card_url
@@ -183,6 +174,47 @@ module ActiveMerchant #:nodoc:
183
174
  def transaction_id(authorization)
184
175
  authorization.split(';').first
185
176
  end
177
+
178
+ class ResponseParser
179
+ def initialize(raw_response="", options={})
180
+ @raw_response = raw_response
181
+ @options = options
182
+ end
183
+
184
+ def generate_response
185
+ parse_response
186
+ if parsed['error']
187
+ handle_response_parse_error
188
+ else
189
+ handle_response_correct_parsing
190
+ end
191
+
192
+ Response.new(succeeded, message, parsed, options)
193
+ end
194
+
195
+ private
196
+ attr_reader :raw_response, :parsed, :succeeded, :message, :options
197
+
198
+ def parse_response
199
+ @parsed = JSON.parse(raw_response.sub(/jsonPFunction\(/, '').sub(/\)\z/, ''))
200
+ end
201
+
202
+ def handle_response_parse_error
203
+ @succeeded = false
204
+ @message = parsed['error']['message']
205
+ end
206
+
207
+ def handle_response_correct_parsing
208
+ @message = parsed['transaction']['processing']['return']['message']
209
+ if @succeeded = is_ack?
210
+ @options[:authorization] = parsed['transaction']['identification']['uniqueId']
211
+ end
212
+ end
213
+
214
+ def is_ack?
215
+ parsed['transaction']['processing']['result'] == 'ACK'
216
+ end
217
+ end
186
218
  end
187
219
  end
188
220
  end
@@ -12,7 +12,7 @@ module ActiveMerchant #:nodoc:
12
12
  # This transaction creates a recurring payment profile
13
13
  # ==== Parameters
14
14
  #
15
- # * <tt>money</tt> -- The amount to be charged to the customer at each interval as an Integer value in cents.
15
+ # * <tt>amount</tt> -- The amount to be charged to the customer at each interval as an Integer value in cents.
16
16
  # * <tt>credit_card</tt> -- The CreditCard details for the transaction.
17
17
  # * <tt>options</tt> -- A hash of parameters.
18
18
  #
@@ -108,8 +108,10 @@ module ActiveMerchant #:nodoc:
108
108
  def build_purchase_request(money, credit_card, options)
109
109
  xml = Builder::XmlMarkup.new
110
110
 
111
- xml.tag! 'amount', amount(money)
112
- xml.tag! 'currency', options[:currency] || currency(money)
111
+ currency = options[:currency] || currency(money)
112
+
113
+ xml.tag! 'amount', localized_amount(money, currency)
114
+ xml.tag! 'currency', currency
113
115
  xml.tag! 'purchaseOrderNo', options[:order_id].to_s.gsub(/[ ']/, '')
114
116
 
115
117
  xml.tag! 'CreditCardInfo' do
@@ -93,7 +93,8 @@ module ActiveMerchant #:nodoc:
93
93
  # credit_card - The CreditCard to store
94
94
  # options - A standard ActiveMerchant options hash
95
95
  def store(credit_card, options={})
96
- save_card(true, credit_card, options)
96
+ retain = (options.has_key?(:retain) ? options[:retain] : true)
97
+ save_card(retain, credit_card, options)
97
98
  end
98
99
 
99
100
  # Public: Redact the CreditCard in Spreedly. This wipes the sensitive
@@ -118,7 +119,7 @@ module ActiveMerchant #:nodoc:
118
119
 
119
120
  def purchase_with_token(money, payment_method_token, options)
120
121
  request = auth_purchase_request(money, payment_method_token, options)
121
- commit("gateways/#{@options[:gateway_token]}/purchase.xml", request)
122
+ commit("gateways/#{options[:gateway_token] || @options[:gateway_token]}/purchase.xml", request)
122
123
  end
123
124
 
124
125
  def authorize_with_token(money, payment_method_token, options)
@@ -137,6 +138,7 @@ module ActiveMerchant #:nodoc:
137
138
  def add_invoice(doc, money, options)
138
139
  doc.amount amount(money)
139
140
  doc.currency_code(options[:currency] || currency(money) || default_currency)
141
+ doc.order_id(options[:order_id])
140
142
  end
141
143
 
142
144
  def add_credit_card(doc, credit_card, options)
@@ -40,7 +40,7 @@ module ActiveMerchant #:nodoc:
40
40
  post = create_post_for_auth_or_purchase(money, creditcard, options)
41
41
  post[:capture] = "false"
42
42
 
43
- commit(:post, 'charges', post, generate_meta(options))
43
+ commit(:post, 'charges', post, generate_options(options))
44
44
  end
45
45
 
46
46
  # To create a charge on a card or a token, call
@@ -53,7 +53,7 @@ module ActiveMerchant #:nodoc:
53
53
  def purchase(money, creditcard, options = {})
54
54
  post = create_post_for_auth_or_purchase(money, creditcard, options)
55
55
 
56
- commit(:post, 'charges', post, generate_meta(options))
56
+ commit(:post, 'charges', post, generate_options(options))
57
57
  end
58
58
 
59
59
  def capture(money, authorization, options = {})
@@ -69,7 +69,7 @@ module ActiveMerchant #:nodoc:
69
69
 
70
70
  def refund(money, identification, options = {})
71
71
  post = {:amount => amount(money)}
72
- commit_options = generate_meta(options)
72
+ commit_options = generate_options(options)
73
73
 
74
74
  MultiResponse.run(:first) do |r|
75
75
  r.process { commit(:post, "charges/#{CGI.escape(identification)}/refund", post, commit_options) }
@@ -97,28 +97,42 @@ module ActiveMerchant #:nodoc:
97
97
  commit(:post, "application_fees/#{CGI.escape(identification)}/refund", post, options)
98
98
  end
99
99
 
100
+ # Note: creating a new credit card will not change the customer's existing default credit card (use :set_default => true)
100
101
  def store(creditcard, options = {})
101
102
  post = {}
102
103
  add_creditcard(post, creditcard, options)
103
104
  post[:description] = options[:description]
104
105
  post[:email] = options[:email]
105
106
 
106
- path = if options[:customer]
107
- "customers/#{CGI.escape(options[:customer])}"
107
+ commit_options = generate_options(options)
108
+ if options[:customer]
109
+ MultiResponse.run(:first) do |r|
110
+ r.process { commit(:post, "customers/#{CGI.escape(options[:customer])}/cards", post, commit_options) }
111
+
112
+ return r unless options[:set_default] and r.success? and !r.params["id"].blank?
113
+
114
+ r.process { update_customer(options[:customer], :default_card => r.params["id"]) }
115
+ end
108
116
  else
109
- 'customers'
117
+ commit(:post, 'customers', post, commit_options)
110
118
  end
111
-
112
- commit(:post, path, post, generate_meta(options))
113
119
  end
114
120
 
115
121
  def update(customer_id, creditcard, options = {})
116
- options = options.merge(:customer => customer_id)
122
+ options = options.merge(:customer => customer_id, :set_default => true)
117
123
  store(creditcard, options)
118
124
  end
119
125
 
120
- def unstore(customer_id, options = {})
121
- commit(:delete, "customers/#{CGI.escape(customer_id)}", nil, generate_meta(options))
126
+ def update_customer(customer_id, options = {})
127
+ commit(:post, "customers/#{CGI.escape(customer_id)}", options, generate_options(options))
128
+ end
129
+
130
+ def unstore(customer_id, card_id = nil, options = {})
131
+ if card_id.nil?
132
+ commit(:delete, "customers/#{CGI.escape(customer_id)}", nil, generate_options(options))
133
+ else
134
+ commit(:delete, "customers/#{CGI.escape(customer_id)}/cards/#{CGI.escape(card_id)}", nil, generate_options(options))
135
+ end
122
136
  end
123
137
 
124
138
  private
@@ -145,7 +159,7 @@ module ActiveMerchant #:nodoc:
145
159
  end
146
160
 
147
161
  def add_customer_data(post, options)
148
- metadata_options = [:description,:browser_ip,:user_agent,:referrer]
162
+ metadata_options = [:description, :ip, :user_agent, :referrer]
149
163
  post.update(options.slice(*metadata_options))
150
164
 
151
165
  post[:external_id] = options[:order_id]
@@ -224,6 +238,11 @@ module ActiveMerchant #:nodoc:
224
238
  end.compact.join("&")
225
239
  end
226
240
 
241
+ def generate_options(raw_options)
242
+ options = generate_meta(raw_options)
243
+ options.merge!(raw_options.slice(:version))
244
+ end
245
+
227
246
  def generate_meta(options)
228
247
  {:meta => {:ip => options[:ip]}}
229
248
  end
@@ -234,18 +253,19 @@ module ActiveMerchant #:nodoc:
234
253
  :lang => 'ruby',
235
254
  :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
236
255
  :platform => RUBY_PLATFORM,
237
- :publisher => 'active_merchant',
238
- :uname => (RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil)
256
+ :publisher => 'active_merchant'
239
257
  })
240
258
 
241
259
  key = options[:key] || @api_key
242
260
 
243
- {
261
+ headers = {
244
262
  "Authorization" => "Basic " + Base64.encode64(key.to_s + ":").strip,
245
263
  "User-Agent" => "Stripe/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
246
264
  "X-Stripe-Client-User-Agent" => @@ua,
247
265
  "X-Stripe-Client-User-Metadata" => options[:meta].to_json
248
266
  }
267
+ headers.merge!("Stripe-Version" => options[:version]) if options[:version]
268
+ headers
249
269
  end
250
270
 
251
271
  def commit(method, url, parameters=nil, options = {})