activemerchant 1.31.0 → 1.32.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 (33) hide show
  1. data/CHANGELOG +29 -0
  2. data/CONTRIBUTORS +8 -0
  3. data/README.md +2 -0
  4. data/lib/active_merchant/billing/credit_card.rb +1 -1
  5. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +2 -1
  6. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +1 -0
  7. data/lib/active_merchant/billing/gateways/braintree_blue.rb +1 -0
  8. data/lib/active_merchant/billing/gateways/cc5.rb +160 -0
  9. data/lib/active_merchant/billing/gateways/cyber_source.rb +1 -1
  10. data/lib/active_merchant/billing/gateways/data_cash.rb +3 -3
  11. data/lib/active_merchant/billing/gateways/finansbank.rb +22 -0
  12. data/lib/active_merchant/billing/gateways/iridium.rb +8 -2
  13. data/lib/active_merchant/billing/gateways/litle.rb +289 -101
  14. data/lib/active_merchant/billing/gateways/ogone.rb +1 -1
  15. data/lib/active_merchant/billing/gateways/optimal_payment.rb +26 -16
  16. data/lib/active_merchant/billing/gateways/orbital.rb +6 -6
  17. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +2 -1
  18. data/lib/active_merchant/billing/gateways/paymill.rb +13 -9
  19. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +14 -9
  20. data/lib/active_merchant/billing/gateways/pin.rb +13 -5
  21. data/lib/active_merchant/billing/gateways/spreedly_core.rb +15 -17
  22. data/lib/active_merchant/billing/gateways/stripe.rb +25 -12
  23. data/lib/active_merchant/billing/gateways/webpay.rb +8 -0
  24. data/lib/active_merchant/billing/gateways/worldpay.rb +44 -22
  25. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +3 -2
  26. data/lib/active_merchant/billing/integrations/pxpay/helper.rb +1 -0
  27. data/lib/active_merchant/billing/integrations/robokassa/common.rb +1 -1
  28. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +5 -1
  29. data/lib/active_merchant/billing/integrations/world_pay.rb +15 -8
  30. data/lib/active_merchant/version.rb +1 -1
  31. data.tar.gz.sig +0 -0
  32. metadata +82 -26
  33. metadata.gz.sig +0 -0
@@ -8,31 +8,36 @@ module ActiveMerchant #:nodoc:
8
8
  def info
9
9
  (@params['PayerInfo']||{})
10
10
  end
11
-
11
+
12
+ def details
13
+ (@params['PaymentDetails']||{})
14
+ end
15
+
12
16
  def name
13
17
  payer = (info['PayerName']||{})
14
18
  [payer['FirstName'], payer['MiddleName'], payer['LastName']].compact.join(' ')
15
19
  end
16
-
20
+
17
21
  def token
18
22
  @params['Token']
19
23
  end
20
-
24
+
21
25
  def payer_id
22
26
  info['PayerID']
23
27
  end
24
-
28
+
25
29
  def payer_country
26
30
  info['PayerCountry']
27
31
  end
28
-
29
- # PayPal returns a contact telephone number only if your Merchant account profile settings require that the buyer enter one.
32
+
33
+ # PayPal returns a contact telephone number only if your Merchant account
34
+ # profile settings require that the buyer enter one.
30
35
  def contact_phone
31
36
  @params['ContactPhone']
32
37
  end
33
-
38
+
34
39
  def address
35
- address = (@params['PaymentDetails']||{})['ShipToAddress']
40
+ address = (details['ShipToAddress']||{})
36
41
  { 'name' => address['Name'],
37
42
  'company' => info['PayerBusiness'],
38
43
  'address1' => address['Street1'],
@@ -44,7 +49,7 @@ module ActiveMerchant #:nodoc:
44
49
  'phone' => (contact_phone || address['Phone'])
45
50
  }
46
51
  end
47
-
52
+
48
53
  def shipping
49
54
  shipping = (@params['UserSelectedOptions']||{})
50
55
  { 'amount' => shipping['ShippingOptionAmount'],
@@ -78,7 +78,7 @@ module ActiveMerchant #:nodoc:
78
78
  end
79
79
 
80
80
  def add_invoice(post, options)
81
- post[:description] = options[:description]
81
+ post[:description] = options[:description] || "Active Merchant Purchase"
82
82
  end
83
83
 
84
84
  def add_creditcard(post, creditcard)
@@ -93,22 +93,30 @@ module ActiveMerchant #:nodoc:
93
93
  :name => "#{creditcard.first_name} #{creditcard.last_name}"
94
94
  )
95
95
  elsif creditcard.kind_of?(String)
96
- post[:customer_token] = creditcard
96
+ if creditcard =~ /^card_/
97
+ post[:card_token] = creditcard
98
+ else
99
+ post[:customer_token] = creditcard
100
+ end
97
101
  end
98
102
  end
99
103
 
100
- def headers
101
- {
104
+ def headers(params = {})
105
+ result = {
102
106
  "Content-Type" => "application/json",
103
107
  "Authorization" => "Basic #{Base64.strict_encode64(options[:api_key] + ':').strip}"
104
108
  }
109
+
110
+ result['X-Partner-Key'] = params[:partner_key] if params[:partner_key]
111
+ result['X-Safe-Card'] = params[:safe_card] if params[:safe_card]
112
+ result
105
113
  end
106
114
 
107
115
  def commit(action, params)
108
116
  url = "#{test? ? test_url : live_url}/#{action}"
109
117
 
110
118
  begin
111
- body = parse(ssl_post(url, post_data(params), headers))
119
+ body = parse(ssl_post(url, post_data(params), headers(params)))
112
120
  rescue ResponseError => e
113
121
  body = parse(e.response.body)
114
122
  end
@@ -3,11 +3,11 @@ require 'nokogiri'
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
5
5
  # Public: This gateway allows you to interact with any gateway you've
6
- # created in Spreedly Core (https://spreedlycore.com). It's an adapter
7
- # which can be particularly useful if you already have code interacting with
8
- # ActiveMerchant and want to easily take advantage of Core's vault.
6
+ # created in Spreedly (https://spreedly.com). It's an adapter which can be
7
+ # particularly useful if you already have code interacting with
8
+ # ActiveMerchant and want to easily take advantage of Spreedly's vault.
9
9
  class SpreedlyCoreGateway < Gateway
10
- self.live_url = 'https://spreedlycore.com/v1'
10
+ self.live_url = 'https://core.spreedly.com/v1'
11
11
 
12
12
  self.supported_countries = %w(AD AE AT AU BD BE BG BN CA CH CY CZ DE DK EE EG ES FI FR GB
13
13
  GI GR HK HU ID IE IL IM IN IS IT JO KW LB LI LK LT LU LV MC
@@ -15,18 +15,18 @@ module ActiveMerchant #:nodoc:
15
15
  TR TT UM US VA VN ZA)
16
16
 
17
17
  self.supported_cardtypes = [:visa, :master, :american_express, :discover]
18
- self.homepage_url = 'https://spreedlycore.com'
19
- self.display_name = 'Spreedly Core'
18
+ self.homepage_url = 'https://spreedly.com'
19
+ self.display_name = 'Spreedly'
20
20
  self.money_format = :cents
21
21
  self.default_currency = 'USD'
22
22
 
23
- # Public: Create a new Spreedly Core Gateway.
23
+ # Public: Create a new Spreedly gateway.
24
24
  #
25
25
  # options - A hash of options:
26
- # :login - Your Spreedly Core API login.
27
- # :password - Your Spreedly Core API secret.
26
+ # :login - The environment key.
27
+ # :password - The access secret.
28
28
  # :gateway_token - The token of the gateway you've created in
29
- # Spreedly Core.
29
+ # Spreedly.
30
30
  def initialize(options = {})
31
31
  requires!(options, :login, :password, :gateway_token)
32
32
  super
@@ -35,8 +35,7 @@ module ActiveMerchant #:nodoc:
35
35
  # Public: Run a purchase transaction.
36
36
  #
37
37
  # money - The monetary amount of the transaction in cents.
38
- # payment_method - The CreditCard or the Spreedly Core payment method
39
- # token.
38
+ # payment_method - The CreditCard or the Spreedly payment method token.
40
39
  # options - A standard ActiveMerchant options hash
41
40
  def purchase(money, payment_method, options = {})
42
41
  if payment_method.is_a?(String)
@@ -52,8 +51,7 @@ module ActiveMerchant #:nodoc:
52
51
  # Public: Run an authorize transaction.
53
52
  #
54
53
  # money - The monetary amount of the transaction in cents.
55
- # payment_method - The CreditCard or the Spreedly Core payment method
56
- # token.
54
+ # payment_method - The CreditCard or the Spreedly payment method token.
57
55
  # options - A standard ActiveMerchant options hash
58
56
  def authorize(money, payment_method, options = {})
59
57
  if payment_method.is_a?(String)
@@ -86,7 +84,7 @@ module ActiveMerchant #:nodoc:
86
84
  commit("transactions/#{authorization}/void.xml", '')
87
85
  end
88
86
 
89
- # Public: Store a credit card in the Spreedly Core vault and retain it.
87
+ # Public: Store a credit card in the Spreedly vault and retain it.
90
88
  #
91
89
  # credit_card - The CreditCard to store
92
90
  # options - A standard ActiveMerchant options hash
@@ -94,8 +92,8 @@ module ActiveMerchant #:nodoc:
94
92
  save_card(true, credit_card, options)
95
93
  end
96
94
 
97
- # Public: Redact the CreditCard in Spreedly Core. This wipes the
98
- # sensitive payment information from the card.
95
+ # Public: Redact the CreditCard in Spreedly. This wipes the sensitive
96
+ # payment information from the card.
99
97
  #
100
98
  # credit_card - The CreditCard to store
101
99
  # options - A standard ActiveMerchant options hash
@@ -35,6 +35,14 @@ module ActiveMerchant #:nodoc:
35
35
  super
36
36
  end
37
37
 
38
+ def authorize(money, creditcard, options = {})
39
+ post = create_post_for_auth_or_purchase(money, creditcard, options)
40
+ post[:capture] = "false"
41
+ meta = generate_meta(options)
42
+
43
+ commit(:post, 'charges', post, meta)
44
+ end
45
+
38
46
  # To create a charge on a card or a token, call
39
47
  #
40
48
  # purchase(money, card_hash_or_token, { ... })
@@ -43,23 +51,16 @@ module ActiveMerchant #:nodoc:
43
51
  #
44
52
  # purchase(money, nil, { :customer => id, ... })
45
53
  def purchase(money, creditcard, options = {})
46
- post = {}
47
-
48
- add_amount(post, money, options)
49
- add_creditcard(post, creditcard, options)
50
- add_customer(post, options)
51
- add_customer_data(post,options)
52
- post[:description] = options[:description] || options[:email]
53
- post[:application_fee] = options[:application_fee] if options[:application_fee]
54
- add_flags(post, options)
55
-
54
+ post = create_post_for_auth_or_purchase(money, creditcard, options)
56
55
  meta = generate_meta(options)
57
56
 
58
- raise ArgumentError.new("Customer or Credit Card required.") if !post[:card] && !post[:customer]
59
-
60
57
  commit(:post, 'charges', post, meta)
61
58
  end
62
59
 
60
+ def capture(money, authorization, options = {})
61
+ commit(:post, "charges/#{CGI.escape(authorization)}/capture", {:amount => amount(money)})
62
+ end
63
+
63
64
  def void(identification, options = {})
64
65
  commit(:post, "charges/#{CGI.escape(identification)}/refund", {})
65
66
  end
@@ -101,6 +102,18 @@ module ActiveMerchant #:nodoc:
101
102
 
102
103
  private
103
104
 
105
+ def create_post_for_auth_or_purchase(money, creditcard, options)
106
+ post = {}
107
+ add_amount(post, money, options)
108
+ add_creditcard(post, creditcard, options)
109
+ add_customer(post, options)
110
+ add_customer_data(post,options)
111
+ post[:description] = options[:description] || options[:email]
112
+ post[:application_fee] = options[:application_fee] if options[:application_fee]
113
+ add_flags(post, options)
114
+ post
115
+ end
116
+
104
117
  def add_amount(post, money, options)
105
118
  post[:amount] = amount(money)
106
119
  post[:currency] = (options[:currency] || currency(money)).downcase
@@ -13,6 +13,14 @@ module ActiveMerchant #:nodoc:
13
13
  self.homepage_url = 'https://webpay.jp/'
14
14
  self.display_name = 'WebPay'
15
15
 
16
+ def authorize(money, credit_card, options = {})
17
+ raise NotImplementedError.new
18
+ end
19
+
20
+ def capture(money, credit_card, options = {})
21
+ raise NotImplementedError.new
22
+ end
23
+
16
24
  def json_error(raw_response)
17
25
  msg = 'Invalid response received from the WebPay API. Please contact support@webpay.jp if you continue to receive this message.'
18
26
  msg += " (The raw response returned by the API was #{raw_response.inspect})"
@@ -58,7 +58,7 @@ module ActiveMerchant #:nodoc:
58
58
 
59
59
  def refund(money, authorization, options = {})
60
60
  MultiResponse.run do |r|
61
- r.process{inquire_request(authorization, options, "CAPTURED")}
61
+ r.process{inquire_request(authorization, options, "CAPTURED", "SETTLED")}
62
62
  r.process{refund_request(money, authorization, options)}
63
63
  end
64
64
  end
@@ -77,12 +77,12 @@ module ActiveMerchant #:nodoc:
77
77
  commit('cancel', build_void_request(authorization, options), :ok)
78
78
  end
79
79
 
80
- def inquire_request(authorization, options, success_criteria)
81
- commit('inquiry', build_order_inquiry_request(authorization, options), success_criteria)
80
+ def inquire_request(authorization, options, *success_criteria)
81
+ commit('inquiry', build_order_inquiry_request(authorization, options), *success_criteria)
82
82
  end
83
83
 
84
84
  def refund_request(money, authorization, options)
85
- commit('inquiry', build_refund_request(money, authorization, options), :ok)
85
+ commit('refund', build_refund_request(money, authorization, options), :ok)
86
86
  end
87
87
 
88
88
  def build_request
@@ -149,7 +149,7 @@ module ActiveMerchant #:nodoc:
149
149
  def build_refund_request(money, authorization, options)
150
150
  build_order_modify_request(authorization) do |xml|
151
151
  xml.tag! 'refund' do
152
- add_amount(xml, money, options)
152
+ add_amount(xml, money, options.merge(:debit_credit_indicator => "credit"))
153
153
  end
154
154
  end
155
155
  end
@@ -158,7 +158,17 @@ module ActiveMerchant #:nodoc:
158
158
  currency = options[:currency] || currency(money)
159
159
  amount = localized_amount(money, currency)
160
160
 
161
- xml.tag! 'amount', :value => amount, 'currencyCode' => currency, 'exponent' => 2
161
+ amount_hash = {
162
+ :value => amount,
163
+ 'currencyCode' => currency,
164
+ 'exponent' => 2
165
+ }
166
+
167
+ if options[:debit_credit_indicator]
168
+ amount_hash.merge!('debitCreditIndicator' => options[:debit_credit_indicator])
169
+ end
170
+
171
+ xml.tag! 'amount', amount_hash
162
172
  end
163
173
 
164
174
  def add_payment_method(xml, amount, payment_method, options)
@@ -226,20 +236,18 @@ module ActiveMerchant #:nodoc:
226
236
  raw
227
237
  end
228
238
 
229
- def commit(action, request, success_criteria)
230
- xmr = ssl_post((test? ? self.test_url : self.live_url),
231
- request,
232
- 'Content-Type' => 'text/xml',
233
- 'Authorization' => encoded_credentials)
234
-
239
+ def commit(action, request, *success_criteria)
240
+ xmr = ssl_post(url, request, 'Content-Type' => 'text/xml', 'Authorization' => encoded_credentials)
235
241
  raw = parse(action, xmr)
242
+ success, message = success_and_message_from(raw, success_criteria)
236
243
 
237
244
  Response.new(
238
- success_from(raw, success_criteria),
239
- message_from(raw),
245
+ success,
246
+ message,
240
247
  raw,
241
248
  :authorization => authorization_from(raw),
242
249
  :test => test?)
250
+
243
251
  rescue ActiveMerchant::ResponseError => e
244
252
  if e.response.code.to_s == "401"
245
253
  return Response.new(false, "Invalid credentials", {}, :test => test?)
@@ -248,15 +256,29 @@ module ActiveMerchant #:nodoc:
248
256
  end
249
257
  end
250
258
 
251
- def success_from(raw, success_criteria)
252
- (raw[:last_event] == success_criteria ||
253
- raw[:ok].present?)
259
+ def url
260
+ test? ? self.test_url : self.live_url
254
261
  end
255
262
 
256
- def message_from(raw)
257
- (raw[:iso8583_return_code_description] ||
258
- raw[:error] ||
259
- "SUCCESS")
263
+ # success_criteria can be:
264
+ # - a string or an array of strings (if one of many responses)
265
+ # - An array of strings if one of many responses could be considered a
266
+ # success.
267
+ def success_and_message_from(raw, success_criteria)
268
+ success = (success_criteria.include?(raw[:last_event]) || raw[:ok].present?)
269
+ if success
270
+ message = "SUCCESS"
271
+ else
272
+ message = (raw[:iso8583_return_code_description] || raw[:error] || required_status_message(raw, success_criteria))
273
+ end
274
+
275
+ [ success, message ]
276
+ end
277
+
278
+ def required_status_message(raw, success_criteria)
279
+ if(!success_criteria.include?(raw[:last_event]))
280
+ "A transaction status of #{success_criteria.collect{|c| "'#{c}'"}.join(" or ")} is required."
281
+ end
260
282
  end
261
283
 
262
284
  def authorization_from(raw)
@@ -272,7 +294,7 @@ module ActiveMerchant #:nodoc:
272
294
  def localized_amount(money, currency)
273
295
  amount = amount(money)
274
296
  return amount unless CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s)
275
-
297
+
276
298
  amount.to_i / 100 * 100
277
299
  end
278
300
  end
@@ -32,14 +32,15 @@ module ActiveMerchant #:nodoc:
32
32
  :phone => 'phone',
33
33
  :name => 'name'
34
34
 
35
- mapping :customer, :name => 'name'
35
+ mapping :customer, { :first_name => 'first_name', :last_name => 'last_name' }
36
36
 
37
37
  def description(value)
38
38
  add_field('description', "#{value}".delete("#"))
39
39
  end
40
40
 
41
41
  def customer(params = {})
42
- add_field(mappings[:customer][:name], [params.delete(:first_name), params.delete(:last_name)].compact.join(' '))
42
+ add_field(mappings[:customer][:first_name], params[:first_name])
43
+ add_field(mappings[:customer][:last_name], params[:last_name])
43
44
  end
44
45
 
45
46
  def billing_address(params = {})
@@ -80,6 +80,7 @@ module ActiveMerchant #:nodoc:
80
80
  root = xml.add_element('GenerateRequest')
81
81
 
82
82
  @fields.each do | k, v |
83
+ v = v.slice(0, 50) if k == "MerchantReference"
83
84
  root.add_element(k).text = v
84
85
  end
85
86
 
@@ -6,7 +6,7 @@ module ActiveMerchant #:nodoc:
6
6
  def generate_signature_string
7
7
  custom_param_keys = params.keys.select {|key| key =~ /^shp/}.sort
8
8
  custom_params = custom_param_keys.map {|key| "#{key}=#{params[key]}"}
9
- [main_params, secret, custom_params].flatten.compact.join(':')
9
+ [main_params, secret, custom_params.compact].flatten.join(':')
10
10
  end
11
11
 
12
12
  def generate_signature
@@ -41,6 +41,8 @@ module ActiveMerchant #:nodoc:
41
41
  mapping :return_url, 'SuccessURL'
42
42
  mapping :description, 'Description'
43
43
 
44
+ class_attribute :referrer_id
45
+
44
46
  def shipping_address(params = {})
45
47
  @shipping_address_set = true unless params.empty?
46
48
 
@@ -74,12 +76,14 @@ module ActiveMerchant #:nodoc:
74
76
  key = fields['EncryptKey']
75
77
  @crypt ||= create_crypt_field(fields.except(*crypt_skip), key)
76
78
 
77
- {
79
+ result = {
78
80
  'VPSProtocol' => '2.23',
79
81
  'TxType' => 'PAYMENT',
80
82
  'Vendor' => @fields['Vendor'],
81
83
  'Crypt' => @crypt
82
84
  }
85
+ result['ReferrerID'] = referrer_id if referrer_id
86
+ result
83
87
  end
84
88
 
85
89
  private
@@ -4,20 +4,27 @@ require File.dirname(__FILE__) + '/world_pay/notification.rb'
4
4
  module ActiveMerchant #:nodoc:
5
5
  module Billing #:nodoc:
6
6
  module Integrations #:nodoc:
7
- module WorldPay
8
-
9
- # production and test have the same endpoint
10
- mattr_accessor :production_url
11
- self.production_url = 'https://secure.wp3.rbsworldpay.com/wcc/purchase'
12
-
7
+ module WorldPay
8
+
9
+ mattr_accessor :production_url, :test_url
10
+ self.production_url = 'https://secure.worldpay.com/wcc/purchase'
11
+ self.test_url = 'https://secure-test.worldpay.com/wcc/purchase'
12
+
13
13
  def self.service_url
14
- production_url
14
+ case ActiveMerchant::Billing::Base.integration_mode
15
+ when :production
16
+ self.production_url
17
+ when :test
18
+ self.test_url
19
+ else
20
+ raise StandardError, "Integration mode set to an invalid value: #{mode}"
21
+ end
15
22
  end
16
23
 
17
24
  def self.notification(post, options = {})
18
25
  Notification.new(post, options)
19
26
  end
20
-
27
+
21
28
  def self.return(post, options = {})
22
29
  Return.new(post, options)
23
30
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveMerchant
2
- VERSION = "1.31.0"
2
+ VERSION = "1.32.0"
3
3
  end
data.tar.gz.sig CHANGED
Binary file