activemerchant 1.40.0 → 1.41.0

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig CHANGED
Binary file
data/CHANGELOG CHANGED
@@ -1,5 +1,12 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
+ == Version 1.41.0 (October 24th, 2013)
4
+
5
+ * Stripe: Payments won't fail when specifying a customer with a creditcard number [melari]
6
+ * Add Conekta gateway [leofischer]
7
+ * Wirecard: Add support for void and refund [duff]
8
+ * Orbital: Mandatory field fix [juicedM3, jduff]
9
+
3
10
  == Version 1.40.0 (October 18th, 2013)
4
11
 
5
12
  * Paymill: Revert Add support for specifying the :customer [melari]
data/CONTRIBUTORS CHANGED
@@ -420,3 +420,7 @@ MoneyMovers (September 2013)
420
420
  Be2Bill (September 2013)
421
421
 
422
422
  * Michaël Hoste (MichaelHoste)
423
+
424
+ Conekta (October 2013)
425
+
426
+ * Leo Fischer (leofischer)
data/README.md CHANGED
@@ -94,6 +94,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
94
94
  * [CardSave](http://www.cardsave.net/) - GB
95
95
  * [CardStream](http://www.cardstream.com/) - GB
96
96
  * [CertoDirect](http://www.certodirect.com/) - BE, BG, CZ, DK, DE, EE, IE, EL, ES, FR, IT, CY, LV, LT, LU, HU, MT, NL, AT, PL, PT, RO, SI, SK, FI, SE, GB
97
+ * [Conekta](https://conekta.io) - MX
97
98
  * [CyberSource](http://www.cybersource.com) - US, BR, CA, CN, DK, FI, FR, DE, JP, MX, NO, SE, GB, SG
98
99
  * [DataCash](http://www.datacash.com/) - GB
99
100
  * [Efsnet](http://www.concordefsnet.com/) - US
@@ -0,0 +1,233 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class ConektaGateway < Gateway
4
+ self.live_url = 'https://api.conekta.io/'
5
+
6
+ self.supported_countries = ['MX']
7
+ self.supported_cardtypes = [:visa, :master]
8
+ self.homepage_url = 'https://conekta.io/'
9
+ self.display_name = 'Conekta Gateway'
10
+ self.money_format = :cents
11
+ self.default_currency = 'MXN'
12
+
13
+ def initialize(options = {})
14
+ requires!(options, :key)
15
+ options[:version] ||= '0.2.0'
16
+ super
17
+ end
18
+
19
+ def purchase(money, payment_source, options = {})
20
+ post = {}
21
+
22
+ add_order(post, money, options)
23
+ add_payment_source(post, payment_source, options)
24
+ add_details_data(post, options)
25
+
26
+ commit(:post, 'charges', post)
27
+ end
28
+
29
+ def authorize(money, payment_source, options = {})
30
+ post = {}
31
+
32
+ add_order(post, money, options)
33
+ add_payment_source(post, payment_source, options)
34
+ add_details_data(post, options)
35
+
36
+ post[:capture] = false
37
+ commit(:post, "charges", post)
38
+ end
39
+
40
+ def capture(identifier, money, options = {})
41
+ post = {}
42
+
43
+ post[:order_id] = identifier
44
+ add_order(post, money, options)
45
+
46
+ commit(:post, "charges/#{identifier}/capture", post)
47
+ end
48
+
49
+ def refund(identifier, money, options)
50
+ post = {}
51
+
52
+ post[:order_id] = identifier
53
+ add_order(post, money, options)
54
+
55
+ commit(:post, "charges/#{identifier}/refund", post)
56
+ end
57
+
58
+ def store(creditcard, options = {})
59
+ post = {}
60
+ add_payment_source(post, creditcard, options)
61
+ post[:name] = options[:name]
62
+ post[:email] = options[:email]
63
+
64
+ path = if options[:customer]
65
+ "customers/#{CGI.escape(options[:customer])}"
66
+ else
67
+ 'customers'
68
+ end
69
+
70
+ commit(:post, path, post)
71
+ end
72
+
73
+ def unstore(customer_id, options = {})
74
+ commit(:delete, "customers/#{CGI.escape(customer_id)}", nil)
75
+ end
76
+
77
+ private
78
+
79
+ def add_order(post, money, options)
80
+ post[:description] = options[:description]
81
+ post[:reference_id] = options[:order_id]
82
+ post[:amount] = amount(money)
83
+ end
84
+
85
+ def add_details_data(post, options)
86
+ details = {}
87
+ details[:name] = options[:customer]
88
+ details[:email] = options[:email]
89
+ details[:phone] = options[:phone]
90
+ details[:device_fingerprint] = options[:device_fingerprint]
91
+ details[:ip] = options[:ip]
92
+ add_billing_address(details, options)
93
+ add_line_items(details, options)
94
+ add_shipment(details, options)
95
+
96
+ post[:details] = details
97
+ end
98
+
99
+ def add_shipment(post, options)
100
+ shipment = {}
101
+ shipment[:carrier] = options[:carrier]
102
+ shipment[:service] = options[:service]
103
+ shipment[:tracking_number] = options[:tracking_number]
104
+ shipment[:price] = options[:price]
105
+ add_shipment_address(shipment, options)
106
+ post[:shipment] = shipment
107
+ end
108
+
109
+ def add_shipment_address(post, options)
110
+ address = {}
111
+ address[:street1] = options[:address1]
112
+ address[:street2] = options[:address2]
113
+ address[:street3] = options[:address3]
114
+ address[:city] = options[:city]
115
+ address[:state] = options[:state]
116
+ address[:country] = options[:country]
117
+ address[:zip] = options[:zip]
118
+ post[:address] = address
119
+ end
120
+
121
+ def add_line_items(post, options)
122
+ post[:line_items] = (options[:line_items] || []).collect do |line_item|
123
+ line_item
124
+ end
125
+ end
126
+
127
+ def add_billing_address(post, options)
128
+ address = {}
129
+ address[:street1] = options[:address1]
130
+ address[:street2] = options[:address2]
131
+ address[:street3] = options[:address3]
132
+ address[:city] = options[:city]
133
+ address[:state] = options[:state]
134
+ address[:country] = options[:country]
135
+ address[:zip] = options[:zip]
136
+ address[:company_name] = options[:company_name]
137
+ address[:tax_id] = options[:tax_id]
138
+ address[:name] = options[:name]
139
+ address[:phone] = options[:phone]
140
+ address[:email] = options[:email]
141
+ post[:billing_address] = address
142
+ end
143
+
144
+ def add_address(post, options)
145
+ address = {}
146
+ address[:street1] = options[:address1]
147
+ address[:street2] = options[:address2]
148
+ address[:street3] = options[:address3]
149
+ address[:city] = options[:city]
150
+ address[:state] = options[:state]
151
+ address[:country] = options[:country]
152
+ address[:zip] = options[:zip]
153
+ post[:address] = address
154
+ end
155
+
156
+ def add_payment_source(post, payment_source, options)
157
+ if payment_source.kind_of?(String)
158
+ post[:card] = payment_source
159
+ elsif payment_source.respond_to?(:number)
160
+ card = {}
161
+ card[:name] = payment_source.name
162
+ card[:cvc] = payment_source.verification_value
163
+ card[:number] = payment_source.number
164
+ card[:exp_month] = "#{sprintf("%02d", payment_source.month)}"
165
+ card[:exp_year] = "#{"#{payment_source.year}"[-2, 2]}"
166
+ post[:card] = card
167
+ add_address(post[:card], options)
168
+ end
169
+ end
170
+
171
+ def parse(body)
172
+ return {} unless body
173
+ JSON.parse(body)
174
+ end
175
+
176
+ def headers(meta)
177
+ @@ua ||= JSON.dump({
178
+ :bindings_version => ActiveMerchant::VERSION,
179
+ :lang => 'ruby',
180
+ :lang_version => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})",
181
+ :platform => RUBY_PLATFORM,
182
+ :publisher => 'active_merchant'
183
+ })
184
+
185
+ {
186
+ "Accept" => "application/vnd.conekta-v#{options[:version]}+json",
187
+ "Authorization" => "Basic " + Base64.encode64("#{options[:key]}:"),
188
+ "RaiseHtmlError" => "false",
189
+ "User-Agent" => "Conekta ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
190
+ "X-Conekta-Client-User-Agent" => @@ua,
191
+ "X-Conekta-Client-User-Metadata" => meta.to_json
192
+ }
193
+ end
194
+
195
+ def commit(method, url, parameters, options = {})
196
+ success = false
197
+ begin
198
+ raw_response = parse(ssl_request(method, live_url + url, (parameters ? parameters.to_query : nil), headers(options[:meta])))
199
+ success = (raw_response.key?("object") && (raw_response["object"] != "error"))
200
+ rescue ResponseError => e
201
+ raw_response = response_error(e.response.body)
202
+ rescue JSON::ParserError
203
+ raw_response = json_error(raw_response)
204
+ end
205
+
206
+ Response.new(
207
+ success,
208
+ raw_response["message"],
209
+ raw_response,
210
+ :test => test?,
211
+ :authorization => raw_response["id"]
212
+ )
213
+ end
214
+
215
+ def response_error(raw_response)
216
+ begin
217
+ parse(raw_response)
218
+ rescue JSON::ParserError
219
+ json_error(raw_response)
220
+ end
221
+ end
222
+
223
+ def json_error(raw_response)
224
+ msg = 'Invalid response received from the Conekta API.'
225
+ msg += " (The raw response returned by the API was #{raw_response.inspect})"
226
+ {
227
+ "message" => msg
228
+ }
229
+ end
230
+ end
231
+ end
232
+ end
233
+
@@ -146,7 +146,7 @@ module ActiveMerchant #:nodoc:
146
146
  # A – Authorization request
147
147
  def authorize(money, creditcard, options = {})
148
148
  order = build_new_order_xml(AUTH_ONLY, money, options) do |xml|
149
- add_creditcard(xml, creditcard, options[:currency]) unless creditcard.nil? && options[:profile_txn]
149
+ add_creditcard(xml, creditcard, options[:currency])
150
150
  add_address(xml, creditcard, options)
151
151
  if @options[:customer_profiles]
152
152
  add_customer_data(xml, options)
@@ -159,7 +159,7 @@ module ActiveMerchant #:nodoc:
159
159
  # AC – Authorization and Capture
160
160
  def purchase(money, creditcard, options = {})
161
161
  order = build_new_order_xml(AUTH_AND_CAPTURE, money, options) do |xml|
162
- add_creditcard(xml, creditcard, options[:currency]) unless creditcard.nil? && options[:profile_txn]
162
+ add_creditcard(xml, creditcard, options[:currency])
163
163
  add_address(xml, creditcard, options)
164
164
  if @options[:customer_profiles]
165
165
  add_customer_data(xml, options)
@@ -327,8 +327,10 @@ module ActiveMerchant #:nodoc:
327
327
  end
328
328
 
329
329
  def add_creditcard(xml, creditcard, currency=nil)
330
- xml.tag! :AccountNum, creditcard.number
331
- xml.tag! :Exp, expiry_date(creditcard)
330
+ unless creditcard.nil?
331
+ xml.tag! :AccountNum, creditcard.number
332
+ xml.tag! :Exp, expiry_date(creditcard)
333
+ end
332
334
 
333
335
  xml.tag! :CurrencyCode, currency_code(currency)
334
336
  xml.tag! :CurrencyExponent, '2' # Will need updating to support currencies such as the Yen.
@@ -342,10 +344,12 @@ module ActiveMerchant #:nodoc:
342
344
  # Null-fill this attribute OR
343
345
  # Do not submit the attribute at all.
344
346
  # - http://download.chasepaymentech.com/docs/orbital/orbital_gateway_xml_specification.pdf
345
- if %w( visa discover ).include?(creditcard.brand)
346
- xml.tag! :CardSecValInd, (creditcard.verification_value? ? '1' : '9')
347
+ unless creditcard.nil?
348
+ if %w( visa discover ).include?(creditcard.brand)
349
+ xml.tag! :CardSecValInd, (creditcard.verification_value? ? '1' : '9')
350
+ end
351
+ xml.tag! :CardSecVal, creditcard.verification_value if creditcard.verification_value?
347
352
  end
348
- xml.tag! :CardSecVal, creditcard.verification_value if creditcard.verification_value?
349
353
  end
350
354
 
351
355
  def add_refund(xml, currency=nil)
@@ -127,7 +127,7 @@ module ActiveMerchant #:nodoc:
127
127
  post = {}
128
128
  add_amount(post, money, options)
129
129
  add_creditcard(post, creditcard, options)
130
- add_customer(post, options)
130
+ add_customer(post, creditcard, options)
131
131
  add_customer_data(post,options)
132
132
  post[:description] = options[:description] || options[:email]
133
133
  add_flags(post, options)
@@ -189,8 +189,8 @@ module ActiveMerchant #:nodoc:
189
189
  end
190
190
  end
191
191
 
192
- def add_customer(post, options)
193
- post[:customer] = options[:customer] if options[:customer]
192
+ def add_customer(post, creditcard, options)
193
+ post[:customer] = options[:customer] if options[:customer] && !creditcard.respond_to?(:number)
194
194
  end
195
195
 
196
196
  def add_flags(post, options)
@@ -48,8 +48,8 @@ module ActiveMerchant #:nodoc:
48
48
  post[:amount] = localized_amount(money, post[:currency].upcase)
49
49
  end
50
50
 
51
- def add_customer(post, options)
52
- post[:customer] = options[:customer] if options[:customer] && post[:card].blank?
51
+ def add_customer(post, creditcard, options)
52
+ post[:customer] = options[:customer] if options[:customer] && !creditcard.respond_to?(:number)
53
53
  end
54
54
 
55
55
  def json_error(raw_response)
@@ -3,10 +3,7 @@ require 'base64'
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
5
5
  class WirecardGateway < Gateway
6
- # Test server location
7
6
  self.test_url = 'https://c3-test.wirecard.com/secure/ssl-gateway'
8
-
9
- # Live server location
10
7
  self.live_url = 'https://c3.wirecard.com/secure/ssl-gateway'
11
8
 
12
9
  # The Namespaces are not really needed, because it just tells the System, that there's actually no namespace used.
@@ -29,57 +26,51 @@ module ActiveMerchant #:nodoc:
29
26
  # number 5551234 within area code 202 (country code 1).
30
27
  VALID_PHONE_FORMAT = /\+\d{1,3}(\(?\d{3}\)?)?\d{3}-\d{4}-\d{3}/
31
28
 
32
- # The countries the gateway supports merchants from as 2 digit ISO country codes
29
+ self.supported_cardtypes = [ :visa, :master, :american_express, :diners_club, :jcb, :switch ]
33
30
  self.supported_countries = %w(AD CY GI IM MT RO CH AT DK GR IT MC SM TR BE EE HU LV NL SK GB BG FI IS LI NO SI VA FR IL LT PL ES CZ DE IE LU PT SE)
34
-
35
- # Wirecard supports all major credit and debit cards:
36
- # Visa, Mastercard, American Express, Diners Club,
37
- # JCB, Switch, VISA Carte Bancaire, Visa Electron and UATP cards.
38
- # They also support the latest anti-fraud systems such as Verified by Visa or Master Secure Code.
39
- self.supported_cardtypes = [
40
- :visa, :master, :american_express, :diners_club, :jcb, :switch
41
- ]
42
-
43
- # The homepage URL of the gateway
44
31
  self.homepage_url = 'http://www.wirecard.com'
45
-
46
- # The name of the gateway
47
32
  self.display_name = 'Wirecard'
48
-
49
- # The currency should normally be EUROs
50
33
  self.default_currency = 'EUR'
51
-
52
- # 100 is 1.00 Euro
53
34
  self.money_format = :cents
54
35
 
36
+ # Public: Create a new Wirecard gateway.
37
+ #
38
+ # options - A hash of options:
39
+ # :login - The username
40
+ # :password - The password
41
+ # :signature - The BusinessCaseSignature
55
42
  def initialize(options = {})
56
- # verify that username and password are supplied
57
- requires!(options, :login, :password)
58
- # unfortunately Wirecard also requires a BusinessCaseSignature in the XML request
59
- requires!(options, :signature)
43
+ requires!(options, :login, :password, :signature)
60
44
  super
61
45
  end
62
46
 
63
- # Authorization
64
47
  def authorize(money, creditcard, options = {})
65
48
  options[:credit_card] = creditcard
66
49
  commit(:preauthorization, money, options)
67
50
  end
68
51
 
69
- # Capture Authorization
70
52
  def capture(money, authorization, options = {})
71
53
  options[:preauthorization] = authorization
72
54
  commit(:capture, money, options)
73
55
  end
74
56
 
75
- # Purchase
76
57
  def purchase(money, creditcard, options = {})
77
58
  options[:credit_card] = creditcard
78
59
  commit(:purchase, money, options)
79
60
  end
80
61
 
81
- private
62
+ def void(identification, options = {})
63
+ options[:preauthorization] = identification
64
+ commit(:reversal, nil, options)
65
+ end
66
+
67
+ def refund(money, identification, options = {})
68
+ options[:preauthorization] = identification
69
+ commit(:bookback, money, options)
70
+ end
82
71
 
72
+
73
+ private
83
74
  def prepare_options_hash(options)
84
75
  result = @options.merge(options)
85
76
  setup_address_hash!(result)
@@ -156,9 +147,11 @@ module ActiveMerchant #:nodoc:
156
147
  add_invoice(xml, money, options)
157
148
  add_creditcard(xml, options[:credit_card])
158
149
  add_address(xml, options[:billing_address])
159
- when :capture
150
+ when :capture, :bookback
160
151
  xml.tag! 'GuWID', options[:preauthorization]
161
152
  add_amount(xml, money)
153
+ when :reversal
154
+ xml.tag! 'GuWID', options[:preauthorization]
162
155
  end
163
156
  end
164
157
  end
@@ -46,12 +46,16 @@ module ActiveMerchant #:nodoc:
46
46
  add_field mappings[:signature], signature
47
47
  end
48
48
 
49
+ def amount_in_dollars
50
+ sprintf("%.2f", @amount_in_cents.to_f/100)
51
+ end
52
+
49
53
  def amount=(money)
50
54
  @amount_in_cents = money.respond_to?(:cents) ? money.cents : money
51
55
  if money.is_a?(String) or @amount_in_cents.to_i < 0
52
56
  raise ArgumentError, "money amount must be either a Money object or a positive integer in cents."
53
57
  end
54
- add_field mappings[:amount], sprintf("%.2f", @amount_in_cents.to_f/100)
58
+ add_field mappings[:amount], amount_in_dollars
55
59
  end
56
60
 
57
61
  def currency(symbol)
@@ -103,7 +107,7 @@ module ActiveMerchant #:nodoc:
103
107
  components = [merchant_key]
104
108
  components << fields[mappings[:account]]
105
109
  components << fields[mappings[:order]]
106
- components << amount_in_cents.to_s.gsub(/[.,]/, '')
110
+ components << amount_in_dollars.to_s.gsub(/0+$/, '').gsub(/[.,]/, '')
107
111
  components << fields[mappings[:currency]]
108
112
  components.join
109
113
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveMerchant
2
- VERSION = "1.40.0"
2
+ VERSION = "1.41.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemerchant
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.40.0
4
+ version: 1.41.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -37,7 +37,7 @@ cert_chain:
37
37
  Z1BvU1BxN25rK3MyRlFVQko5VVpGSzFsZ016aG8vNGZaZ3pKd2J1K2NPOFNO
38
38
  dWFMUy9iagpoUGFTVHlWVTB5Q1Nudz09Ci0tLS0tRU5EIENFUlRJRklDQVRF
39
39
  LS0tLS0K
40
- date: 2013-10-18 00:00:00.000000000 Z
40
+ date: 2013-10-24 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: activesupport
@@ -278,6 +278,7 @@ files:
278
278
  - lib/active_merchant/billing/gateways/card_stream_modern.rb
279
279
  - lib/active_merchant/billing/gateways/cc5.rb
280
280
  - lib/active_merchant/billing/gateways/certo_direct.rb
281
+ - lib/active_merchant/billing/gateways/conekta.rb
281
282
  - lib/active_merchant/billing/gateways/cyber_source.rb
282
283
  - lib/active_merchant/billing/gateways/data_cash.rb
283
284
  - lib/active_merchant/billing/gateways/efsnet.rb
metadata.gz.sig CHANGED
Binary file