activemerchant 1.30.0 → 1.31.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,15 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
+ == Version 1.31.0 (February 20, 2013)
4
+
5
+ * Worldpay: XML encoding is required to be ISO-8859-1 [dougal]
6
+ * Worldpay: Add card code for more supported card types [dougal]
7
+ * Ogone: Add action option [pwoestelandt]
8
+ * PayPal Express gateway: Add support for BuyerEmailOptInEnable [chrisrbnelson]
9
+ * Add Paymill gateway [duff]
10
+ * Add EVO Canada gateway [alexdunae]
11
+ * Fixed credit card and check interface, used correct method for checking payment type [jduff]
12
+
3
13
  == Version 1.30.0 (February 13, 2013)
4
14
 
5
15
  * Add FirstData Global Gateway e4 [frobcode]
@@ -370,9 +370,17 @@ Pin gateway (February 2013)
370
370
 
371
371
  * Myles Eftos (madpilot)
372
372
 
373
- Merchant Warrior
373
+ Merchant Warrior (February 2013)
374
374
 
375
375
  * Ben Bruscella (benbruscella)
376
376
  * Дмитрий Василец (pronix)
377
377
  * Kirill Shirinkin (Fodoj)
378
378
  * Nathaniel Talbott (ntalbott)
379
+
380
+ Paymill (February 2013)
381
+
382
+ * Duff O'Melia (duff)
383
+
384
+ EVO Canada (February 2013)
385
+
386
+ * Alex Dunae (alexdunae)
data/README.md CHANGED
@@ -94,6 +94,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
94
94
  * [Efsnet](http://www.concordefsnet.com/) - US
95
95
  * [Elavon MyVirtualMerchant](http://www.elavon.com) - US, CA
96
96
  * [ePay](http://www.epay.dk/) - DK, SE, NO
97
+ * [EVO Canada](http://www.evocanada.com/) - CA
97
98
  * [eWAY](http://www.eway.com.au/) - AU
98
99
  * [eWay Rapid 3.0](http://www.eway.com.au/) - AU
99
100
  * [E-xact](http://www.e-xact.com) - CA, US
@@ -132,6 +133,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
132
133
  * [PayGate PayXML](http://paygate.co.za/) - US, ZA
133
134
  * [PayJunction](http://www.payjunction.com/) - US
134
135
  * [PaymentExpress](http://www.paymentexpress.com/) - AU, MY, NZ, SG, ZA, UK, US
136
+ * [Paymill](https://www.paymill.com) - AT, BE, CH, CZ, DE, DK, EE, ES, FI, FR, GB, HU, IE, IS, IT, LI, LU, LV, NL, NO, PL, PT, SE, SI, TR
135
137
  * [PayPal Express Checkout](https://www.paypal.com/cgi-bin/webscr?cmd=xpt/merchant/ExpressCheckoutIntro-outside) - US, CA, SG, AU
136
138
  * [PayPal Payflow Pro](https://www.paypal.com/cgi-bin/webscr?cmd=_payflow-pro-overview-outside) - US, CA, SG, AU
137
139
  * [PayPal Website Payments Pro (UK)](https://www.paypal.com/uk/cgi-bin/webscr?cmd=_wp-pro-overview-outside) - UK
@@ -45,10 +45,6 @@ module ActiveMerchant #:nodoc:
45
45
  'check'
46
46
  end
47
47
 
48
- def check?
49
- true
50
- end
51
-
52
48
  # Routing numbers may be validated by calculating a checksum and dividing it by 10. The
53
49
  # formula is:
54
50
  # (3(d1 + d4 + d7) + 7(d2 + d5 + d8) + 1(d3 + d6 + d9))mod 10 = 0
@@ -209,10 +209,6 @@ module ActiveMerchant #:nodoc:
209
209
  require_verification_value
210
210
  end
211
211
 
212
- def check?
213
- false
214
- end
215
-
216
212
  private
217
213
 
218
214
  def before_validate #:nodoc:
@@ -130,7 +130,7 @@ module ActiveMerchant #:nodoc:
130
130
 
131
131
  # Are we running in test mode?
132
132
  def test?
133
- (@options[:test] || Base.test?)
133
+ (@options.has_key?(:test) ? @options[:test] : Base.test?)
134
134
  end
135
135
 
136
136
  private # :nodoc: all
@@ -254,7 +254,7 @@ module ActiveMerchant #:nodoc:
254
254
  def build_purchase_request(money, payment_method_or_reference, options)
255
255
  xml = Builder::XmlMarkup.new :indent => 2
256
256
  add_payment_method_or_subscription(xml, money, payment_method_or_reference, options)
257
- if(payment_method_or_reference.respond_to?(:check?) && payment_method_or_reference.check?)
257
+ if card_brand(payment_method_or_reference) == 'check'
258
258
  add_check_service(xml)
259
259
  else
260
260
  add_purchase_service(xml, options)
@@ -308,7 +308,7 @@ module ActiveMerchant #:nodoc:
308
308
  xml = Builder::XmlMarkup.new :indent => 2
309
309
  add_address(xml, payment_method, options[:billing_address], options)
310
310
  add_purchase_data(xml, options[:setup_fee] || 0, true, options)
311
- if payment_method.check?
311
+ if card_brand(payment_method) == 'check'
312
312
  add_check(xml, payment_method)
313
313
  add_check_payment_method(xml)
314
314
  add_check_service(xml, options) if options[:setup_fee]
@@ -524,7 +524,7 @@ module ActiveMerchant #:nodoc:
524
524
  if payment_method_or_reference.is_a?(String)
525
525
  add_purchase_data(xml, money, true, options)
526
526
  add_subscription(xml, options, payment_method_or_reference)
527
- elsif payment_method_or_reference.check?
527
+ elsif card_brand(payment_method_or_reference) == 'check'
528
528
  add_address(xml, payment_method_or_reference, options[:billing_address], options)
529
529
  add_purchase_data(xml, money, true, options)
530
530
  add_check(xml, payment_method_or_reference)
@@ -0,0 +1,308 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ # === EVO Canada payment gateway.
4
+ #
5
+ # EVO returns two different identifiers for most transactions, the
6
+ # +authcode+ and the +transactionid+. Since +transactionid+ is used more
7
+ # often (i.e. for {#capture}, {#refund}, {#void} and {#update}) we store it in the
8
+ # Response#authorization attribute. The +authcode+ from the merchant
9
+ # account is accessible via {Response#params}.
10
+ #
11
+ # Two different but related response messages are also returned from EVO.
12
+ # The message indicated by EVO's <tt>response_code</tt> parameter is returned as
13
+ # {Response#message} (Those messages can be seen in the {MESSAGES} hash.)
14
+ # The other, shorter message is available via {Response#params}.
15
+ #
16
+ # It's recommended to save the contents of the {Response#params} in your
17
+ # transaction log for future reference.
18
+ #
19
+ # === Sample Use
20
+ #
21
+ # gateway = ActiveMerchant::Billing::EvoCaGateway.new(username: 'demo', password: 'password')
22
+ #
23
+ # response = gateway.authorize(1000, credit_card, options)
24
+ #
25
+ # puts response.authorization # the transactionid
26
+ # puts response.params['authcode'] # the authcode from the merchant account
27
+ # puts response.message # the 'pretty' response message
28
+ # puts response.params['responsetext'] # the 'terse' response message
29
+ #
30
+ # gateway.capture(1000, response.authorization)
31
+ # gateway.update(response.authorization, shipping_carrier: 'fedex')
32
+ # gateway.refund(500, response.authorization)
33
+ #
34
+ class EvoCaGateway < Gateway
35
+ self.test_url = 'https://secure.evoepay.com/api/transact.php'
36
+ self.live_url = 'https://secure.evoepay.com/api/transact.php'
37
+
38
+ self.supported_countries = ['CA']
39
+ self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :discover]
40
+ self.money_format = :dollars
41
+ self.homepage_url = 'http://www.evocanada.com/'
42
+ self.display_name = 'EVO Canada'
43
+
44
+ APPROVED, DECLINED, ERROR = 1, 2, 3
45
+
46
+ MESSAGES = {
47
+ 100 => 'Transaction was approved',
48
+ 200 => 'Transaction was declined by processor',
49
+ 201 => 'Do not honor',
50
+ 202 => 'Insufficient funds',
51
+ 203 => 'Over limit',
52
+ 204 => 'Transaction not allowed',
53
+ 220 => 'Incorrect payment data',
54
+ 221 => 'No such card issuer',
55
+ 222 => 'No card number on file with issuer',
56
+ 223 => 'Expired card',
57
+ 224 => 'Invalid expiration date',
58
+ 225 => 'Invalid card security code',
59
+ 240 => 'Call issuer for futher information',
60
+ 250 => 'Pick up card',
61
+ 251 => 'Lost card',
62
+ 252 => 'Stolen card',
63
+ 253 => 'Fraudulant card',
64
+ 260 => 'Declined with further instructions available',
65
+ 261 => 'Declined - stop all recurring payments',
66
+ 262 => 'Declined - stop this recurring program',
67
+ 263 => 'Declined - updated cardholder data available',
68
+ 264 => 'Declined - retry in a few days',
69
+ 300 => 'Transaction was rejected by gateway',
70
+ 400 => 'Transaction error returned by processor',
71
+ 410 => 'Invalid merchant configuration',
72
+ 411 => 'Merchant account is inactive',
73
+ 420 => 'Communication error',
74
+ 421 => 'Communication error with issuer',
75
+ 430 => 'Duplicate transaction at processor',
76
+ 440 => 'Processor format error',
77
+ 441 => 'Invalid transaction information',
78
+ 460 => 'Processor feature not available',
79
+ 461 => 'Unsupported card type'
80
+ }
81
+
82
+ # This gateway requires that a valid username and password be passed
83
+ # in the +options+ hash.
84
+ #
85
+ # === Required Options
86
+ #
87
+ # * <tt>:username</tt>
88
+ # * <tt>:password</tt>
89
+ def initialize(options = {})
90
+ requires!(options, :username, :password)
91
+ super
92
+ end
93
+
94
+ # Transaction sales are submitted and immediately flagged for settlement.
95
+ # These transactions will automatically be settled.
96
+ #
97
+ # Payment source can be either a {CreditCard} or {Check}.
98
+ #
99
+ # === Additional Options
100
+ # In addition to the standard options, this gateway supports
101
+ #
102
+ # * <tt>:tracking_number</tt> - Shipping tracking number
103
+ # * <tt>:shipping_carrier</tt> - ups/fedex/dhl/usps
104
+ # * <tt>:po_number</tt> - Purchase order
105
+ # * <tt>:tax</tt> - Tax amount
106
+ # * <tt>:shipping</tt> - Shipping cost
107
+ def purchase(money, credit_card_or_check, options = {})
108
+ post = {}
109
+ add_invoice(post, options)
110
+ add_order(post, options)
111
+ add_paymentmethod(post, credit_card_or_check)
112
+ add_address(post, options)
113
+ add_customer_data(post, options)
114
+ commit('sale', money, post)
115
+ end
116
+
117
+ # Transaction authorizations are authorized immediately but are not
118
+ # flagged for settlement. These transactions must be flagged for
119
+ # settlement using the _capture_ transaction type. Authorizations
120
+ # typically remain activate for three to seven business days.
121
+ #
122
+ # Payment source must be a {CreditCard}.
123
+ def authorize(money, credit_card, options = {})
124
+ post = {}
125
+ add_invoice(post, options)
126
+ add_order(post, options)
127
+ add_paymentmethod(post, credit_card)
128
+ add_address(post, options)
129
+ add_customer_data(post, options)
130
+ commit('auth', money, post)
131
+ end
132
+
133
+ # Transaction captures flag existing _authorizations_ for settlement. Only
134
+ # authorizations can be captured. Captures can be submitted for an amount
135
+ # equal to or less than the original authorization.
136
+ #
137
+ # The <tt>authorization</tt> parameter is the transaction ID, retrieved
138
+ # from Response#authorization. See EvoCaGateway#purchase for the
139
+ # <tt>options</tt>.
140
+ def capture(money, authorization, options = {})
141
+ post = {
142
+ :amount => amount(money),
143
+ :transactionid => authorization
144
+ }
145
+ add_order(post, options)
146
+ commit('capture', money, post)
147
+ end
148
+
149
+ # Transaction refunds will reverse a previously settled transaction. If
150
+ # the transaction has not been settled, it must be _voided_ instead of
151
+ # refunded.
152
+ #
153
+ # The <tt>identification</tt> parameter is the transaction ID, retrieved
154
+ # from {Response#authorization}.
155
+ def refund(money, identification)
156
+ post = {:transactionid => identification}
157
+ commit('refund', money, post)
158
+ end
159
+
160
+ # Transaction credits apply a negative amount to the cardholder's card.
161
+ # In most situations credits are disabled as transaction refunds should
162
+ # be used instead.
163
+ #
164
+ # Note that this is different from a {#refund} (which is usually what
165
+ # you'll be looking for).
166
+ def credit(money, credit_card, options = {})
167
+ post = {}
168
+ add_invoice(post, options)
169
+ add_order(post, options)
170
+ add_paymentmethod(post, credit_card)
171
+ add_address(post, options)
172
+ add_customer_data(post, options)
173
+ commit('credit', money, post)
174
+ end
175
+
176
+ # Transaction voids will cancel an existing sale or captured
177
+ # authorization. In addition, non-captured authorizations can be voided to
178
+ # prevent any future capture. Voids can only occur if the transaction has
179
+ # not been settled.
180
+ #
181
+ # The <tt>identification</tt> parameter is the transaction ID, retrieved
182
+ # from {Response#authorization}.
183
+ def void(identification)
184
+ post = {:transactionid => identification}
185
+ commit('void', nil, post)
186
+ end
187
+
188
+ # Transaction updates can be used to update previous transactions with
189
+ # specific order information, such as a tracking number and shipping
190
+ # carrier. See EvoCaGateway#purchase for <tt>options</tt>.
191
+ #
192
+ # The <tt>identification</tt> parameter is the transaction ID, retrieved
193
+ # from {Response#authorization}.
194
+ def update(identification, options)
195
+ post = {:transactionid => identification}
196
+ add_order(post, options)
197
+ commit('update', nil, post)
198
+ end
199
+
200
+ private
201
+
202
+ def add_customer_data(post, options)
203
+ post[:email] = options[:email]
204
+ post[:ipaddress] = options[:ip]
205
+ end
206
+
207
+ def add_address(post, options)
208
+ if address = options[:billing_address] || options[:address]
209
+ post[:firstname] = address[:first_name]
210
+ post[:lastname] = address[:last_name]
211
+ post[:address1] = address[:address1]
212
+ post[:address2] = address[:address2]
213
+ post[:company] = address[:company]
214
+ post[:phone] = address[:phone]
215
+ post[:city] = address[:city]
216
+ post[:state] = address[:state]
217
+ post[:zip] = address[:zip]
218
+ post[:country] = address[:country]
219
+ end
220
+
221
+ if address = options[:shipping_address]
222
+ post[:shipping_firstname] = address[:first_name]
223
+ post[:shipping_lastname] = address[:last_name]
224
+ post[:shipping_address1] = address[:address1]
225
+ post[:shipping_address2] = address[:address2]
226
+ post[:shipping_company] = address[:company]
227
+ post[:shipping_zip] = address[:zip]
228
+ post[:shipping_city] = address[:city]
229
+ post[:shipping_state] = address[:state]
230
+ post[:shipping_country] = address[:country]
231
+ end
232
+ end
233
+
234
+ def add_order(post, options)
235
+ post[:orderid] = options[:order_id]
236
+ post[:tracking_number] = options[:tracking_number]
237
+ post[:shipping_carrier] = options[:shipping_carrier]
238
+ end
239
+
240
+ def add_invoice(post, options)
241
+ post[:orderdescription] = options[:description]
242
+ post[:ponumber] = options[:po_number]
243
+ post[:shipping] = amount(options[:shipping])
244
+ post[:tax] = amount(options[:tax])
245
+ end
246
+
247
+ def add_paymentmethod(post, payment)
248
+ if card_brand(payment)=='check'
249
+ post[:payment] = 'check'
250
+ post[:checkname] = payment.name
251
+ post[:checkaba] = payment.routing_number
252
+ post[:checkaccount] = payment.account_number
253
+ post[:account_holder_type] = payment.account_holder_type
254
+ post[:account_type] = payment.account_type
255
+ else
256
+ post[:payment] = 'creditcard'
257
+ post[:ccnumber] = payment.number
258
+ post[:ccexp] = "#{format(payment.month, :two_digits)}#{format(payment.year, :two_digits)}"
259
+ post[:cvv] = payment.verification_value
260
+ end
261
+ end
262
+
263
+ def parse(body)
264
+ fields = {}
265
+ CGI::parse(body).each do |k, v|
266
+ fields[k.to_s] = v.kind_of?(Array) ? v[0] : v
267
+ end
268
+ fields
269
+ end
270
+
271
+ def success?(response)
272
+ response['response'].to_i == APPROVED
273
+ end
274
+
275
+ def commit(action, money, parameters)
276
+ parameters[:amount] = amount(money) unless action == 'void'
277
+
278
+ data = ssl_post self.live_url, post_data(action, parameters)
279
+ response = parse(data)
280
+ message = message_from(response)
281
+
282
+ Response.new(success?(response), message, response,
283
+ :test => test?,
284
+ :authorization => response['transactionid'],
285
+ :avs_result => { :code => response['avsresponse'] },
286
+ :cvv_result => response['cvvresponse']
287
+ )
288
+ end
289
+
290
+ def message_from(response)
291
+ MESSAGES.fetch(response['response_code'].to_i, false) || response['message']
292
+ end
293
+
294
+ def post_data(action, parameters = {})
295
+ post = {:type => action}
296
+
297
+ if test?
298
+ post[:username] = 'demo'
299
+ post[:password] = 'password'
300
+ else
301
+ post[:username] = options[:username]
302
+ post[:password] = options[:password]
303
+ end
304
+ post.merge(parameters).collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" unless value.nil? }.compact.join("&")
305
+ end
306
+ end
307
+ end
308
+ end
@@ -27,6 +27,16 @@ module ActiveMerchant #:nodoc:
27
27
  self.homepage_url = "http://www.firstdata.com"
28
28
  self.display_name = "FirstData Global Gateway e4"
29
29
 
30
+ # Create a new FirstdataE4Gateway
31
+ #
32
+ # The gateway requires that a valid login and password be passed
33
+ # in the +options+ hash.
34
+ #
35
+ # ==== Options
36
+ #
37
+ # * <tt>:login</tt> -- The EXACT ID. Also known as the Gateway ID.
38
+ # (Found in your administration terminal settings)
39
+ # * <tt>:password</tt> -- The terminal password (not your account password)
30
40
  def initialize(options = {})
31
41
  requires!(options, :login, :password)
32
42
  @options = options
@@ -154,23 +154,25 @@ module ActiveMerchant #:nodoc:
154
154
 
155
155
  # Verify and transfer the specified amount.
156
156
  def purchase(money, payment_source, options = {})
157
- post = {}
157
+ post = {}
158
+ action = options[:action] || 'SAL'
158
159
  add_invoice(post, options)
159
160
  add_payment_source(post, payment_source, options)
160
161
  add_address(post, payment_source, options)
161
162
  add_customer_data(post, options)
162
163
  add_money(post, money, options)
163
- commit('SAL', post)
164
+ commit(action, post)
164
165
  end
165
166
 
166
167
  # Complete a previously authorized transaction.
167
168
  def capture(money, authorization, options = {})
168
- post = {}
169
+ post = {}
170
+ action = options[:action] || 'SAL'
169
171
  add_authorization(post, reference_from(authorization))
170
172
  add_invoice(post, options)
171
173
  add_customer_data(post, options)
172
174
  add_money(post, money, options)
173
- commit('SAL', post)
175
+ commit(action, post)
174
176
  end
175
177
 
176
178
  # Cancels a previously authorized transaction.
@@ -0,0 +1,161 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PaymillGateway < Gateway
4
+ self.supported_countries = %w(AT BE CH CZ DE DK EE ES FI FR GB HU IE
5
+ IS IT LI LU LV NL NO PL PT SE SI TR)
6
+
7
+ self.supported_cardtypes = [:visa, :master]
8
+ self.homepage_url = 'https://paymill.com'
9
+ self.display_name = 'Paymill'
10
+ self.money_format = :cents
11
+ self.default_currency = 'EUR'
12
+
13
+ def initialize(options = {})
14
+ requires!(options, :public_key, :private_key)
15
+ super
16
+ end
17
+
18
+ def purchase(money, credit_card, options = {})
19
+ MultiResponse.run do |r|
20
+ r.process { save_card(credit_card) }
21
+ r.process { purchase_with_token(money, r.authorization, options) }
22
+ end
23
+ end
24
+
25
+ def authorize(money, credit_card, options = {})
26
+ MultiResponse.run do |r|
27
+ r.process { save_card(credit_card) }
28
+ r.process { authorize_with_token(money, r.authorization, options) }
29
+ end
30
+ end
31
+
32
+ def capture(money, authorization, options = {})
33
+ post = {}
34
+
35
+ add_amount(post, money, options)
36
+ post[:preauthorization] = preauth(authorization)
37
+ post[:description] = options[:description]
38
+ commit(:post, 'transactions', post)
39
+ end
40
+
41
+ def refund(money, authorization, options={})
42
+ post = {}
43
+
44
+ post[:amount] = amount(money)
45
+ post[:description] = options[:description]
46
+ commit(:post, "refunds/#{transaction_id(authorization)}", post)
47
+ end
48
+
49
+ private
50
+
51
+ def add_credit_card(post, credit_card)
52
+ post['account.number'] = credit_card.number
53
+ post['account.expiry.month'] = sprintf("%.2i", credit_card.month)
54
+ post['account.expiry.year'] = sprintf("%.4i", credit_card.year)
55
+ post['account.verification'] = credit_card.verification_value
56
+ end
57
+
58
+ def headers
59
+ { 'Authorization' => ('Basic ' + Base64.strict_encode64("#{@options[:private_key]}:X").chomp) }
60
+ end
61
+
62
+ def commit(method, url, parameters=nil)
63
+ begin
64
+ raw_response = ssl_request(method, "https://api.paymill.com/v2/#{url}", post_data(parameters), headers)
65
+ rescue ResponseError => e
66
+ parsed = JSON.parse(e.response.body)
67
+ return Response.new(false, parsed['error'], parsed, {})
68
+ end
69
+
70
+ response_from(raw_response)
71
+ end
72
+
73
+ def response_from(raw_response)
74
+ parsed = JSON.parse(raw_response)
75
+
76
+ options = {
77
+ :authorization => authorization_from(parsed),
78
+ :test => (parsed['mode'] == 'test'),
79
+ }
80
+
81
+ Response.new(true, 'Transaction approved', parsed, options)
82
+ end
83
+
84
+ def authorization_from(parsed_response)
85
+ [
86
+ parsed_response['data']['id'],
87
+ parsed_response['data']['preauthorization'].try(:[], 'id')
88
+ ].join(";")
89
+ end
90
+
91
+ def purchase_with_token(money, card_token, options)
92
+ post = {}
93
+
94
+ add_amount(post, money, options)
95
+ post[:token] = card_token
96
+ post[:description] = options[:description]
97
+ commit(:post, 'transactions', post)
98
+ end
99
+
100
+ def authorize_with_token(money, card_token, options)
101
+ post = {}
102
+
103
+ add_amount(post, money, options)
104
+ post[:token] = card_token
105
+ commit(:post, 'preauthorizations', post)
106
+ end
107
+
108
+ def save_card(credit_card)
109
+ post = {}
110
+
111
+ add_credit_card(post, credit_card)
112
+ post['channel.id'] = @options[:public_key]
113
+ post['jsonPFunction'] = 'jsonPFunction'
114
+ post['transaction.mode'] = (test? ? 'CONNECTOR_TEST' : 'LIVE')
115
+
116
+ begin
117
+ raw_response = ssl_request(:get, "#{save_card_url}?#{post_data(post)}", nil, {})
118
+ rescue ResponseError => e
119
+ return Response.new(false, e.response.body, e.response.body, {})
120
+ end
121
+
122
+ response_for_save_from(raw_response)
123
+ end
124
+
125
+ def response_for_save_from(raw_response)
126
+ parsed = JSON.parse(raw_response.sub(/jsonPFunction\(/, '').sub(/\)\z/, ''))
127
+ succeeded = parsed['transaction']['processing']['result'] == 'ACK'
128
+
129
+ options = { :test => test? }
130
+ if succeeded
131
+ options[:authorization] = parsed['transaction']['identification']['uniqueId']
132
+ end
133
+
134
+ message = parsed['transaction']['processing']['return']['message']
135
+ Response.new(succeeded, message, parsed, options)
136
+ end
137
+
138
+ def save_card_url
139
+ (test? ? 'https://test-token.paymill.de' : 'https://token-v2.paymill.de')
140
+ end
141
+
142
+ def post_data(params)
143
+ no_blanks = params.reject { |key, value| value.blank? }
144
+ no_blanks.map { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
145
+ end
146
+
147
+ def add_amount(post, money, options)
148
+ post[:amount] = amount(money)
149
+ post[:currency] = (options[:currency] || currency(money))
150
+ end
151
+
152
+ def preauth(authorization)
153
+ authorization.split(";").last
154
+ end
155
+
156
+ def transaction_id(authorization)
157
+ authorization.split(';').first
158
+ end
159
+ end
160
+ end
161
+ end
@@ -30,16 +30,16 @@ module ActiveMerchant #:nodoc:
30
30
  self.supported_countries = ['US']
31
31
  self.homepage_url = 'https://www.paypal.com/cgi-bin/webscr?cmd=xpt/merchant/ExpressCheckoutIntro-outside'
32
32
  self.display_name = 'PayPal Express Checkout'
33
-
33
+
34
34
  def setup_authorization(money, options = {})
35
35
  requires!(options, :return_url, :cancel_return_url)
36
-
36
+
37
37
  commit 'SetExpressCheckout', build_setup_request('Authorization', money, options)
38
38
  end
39
-
39
+
40
40
  def setup_purchase(money, options = {})
41
41
  requires!(options, :return_url, :cancel_return_url)
42
-
42
+
43
43
  commit 'SetExpressCheckout', build_setup_request('Sale', money, options)
44
44
  end
45
45
 
@@ -49,13 +49,13 @@ module ActiveMerchant #:nodoc:
49
49
 
50
50
  def authorize(money, options = {})
51
51
  requires!(options, :token, :payer_id)
52
-
52
+
53
53
  commit 'DoExpressCheckoutPayment', build_sale_or_authorization_request('Authorization', money, options)
54
54
  end
55
55
 
56
56
  def purchase(money, options = {})
57
57
  requires!(options, :token, :payer_id)
58
-
58
+
59
59
  commit 'DoExpressCheckoutPayment', build_sale_or_authorization_request('Sale', money, options)
60
60
  end
61
61
 
@@ -77,10 +77,10 @@ module ActiveMerchant #:nodoc:
77
77
 
78
78
  xml.target!
79
79
  end
80
-
80
+
81
81
  def build_sale_or_authorization_request(action, money, options)
82
82
  currency_code = options[:currency] || currency(money)
83
-
83
+
84
84
  xml = Builder::XmlMarkup.new :indent => 2
85
85
  xml.tag! 'DoExpressCheckoutPaymentReq', 'xmlns' => PAYPAL_NAMESPACE do
86
86
  xml.tag! 'DoExpressCheckoutPaymentRequest', 'xmlns:n2' => EBAY_NAMESPACE do
@@ -141,7 +141,7 @@ module ActiveMerchant #:nodoc:
141
141
  xml.tag! 'n2:AllowNote', options[:allow_note] ? '1' : '0'
142
142
  end
143
143
  xml.tag! 'n2:CallbackURL', options[:callback_url] unless options[:callback_url].blank?
144
-
144
+
145
145
  add_payment_details(xml, with_money_default(money), currency_code, options)
146
146
  if options[:shipping_options]
147
147
  options[:shipping_options].each do |shipping_option|
@@ -155,16 +155,20 @@ module ActiveMerchant #:nodoc:
155
155
 
156
156
  xml.tag! 'n2:CallbackTimeout', options[:callback_timeout] unless options[:callback_timeout].blank?
157
157
  xml.tag! 'n2:CallbackVersion', options[:callback_version] unless options[:callback_version].blank?
158
+
159
+ if options.has_key?(:allow_buyer_optin)
160
+ xml.tag! 'n2:BuyerEmailOptInEnable', (options[:allow_buyer_optin] ? '1' : '0')
161
+ end
158
162
  end
159
163
  end
160
164
  end
161
165
 
162
166
  xml.target!
163
167
  end
164
-
168
+
165
169
  def build_reference_transaction_request(action, money, options)
166
170
  currency_code = options[:currency] || currency(money)
167
-
171
+
168
172
  # I am not sure why it's set like this for express gateway
169
173
  # but I don't want to break the existing behavior
170
174
  xml = Builder::XmlMarkup.new :indent => 2
@@ -16,6 +16,9 @@ module ActiveMerchant #:nodoc:
16
16
  'master' => 'ECMC-SSL',
17
17
  'discover' => 'DISCOVER-SSL',
18
18
  'american_express' => 'AMEX-SSL',
19
+ 'jcb' => 'JCB-SSL',
20
+ 'maestro' => 'MAESTRO-SSL',
21
+ 'laser' => 'LASER-SSL'
19
22
  }
20
23
 
21
24
  def initialize(options = {})
@@ -84,7 +87,7 @@ module ActiveMerchant #:nodoc:
84
87
 
85
88
  def build_request
86
89
  xml = Builder::XmlMarkup.new :indent => 2
87
- xml.instruct!
90
+ xml.instruct! :xml, :encoding => 'ISO-8859-1'
88
91
  xml.declare! :DOCTYPE, :paymentService, :PUBLIC, "-//WorldPay//DTD WorldPay PaymentService v1//EN", "http://dtd.wp3.rbsworldpay.com/paymentService_v1.dtd"
89
92
  xml.tag! 'paymentService', 'version' => "1.4", 'merchantCode' => @options[:login] do
90
93
  yield xml
@@ -1,3 +1,3 @@
1
1
  module ActiveMerchant
2
- VERSION = "1.30.0"
2
+ VERSION = "1.31.0"
3
3
  end
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemerchant
3
3
  version: !ruby/object:Gem::Version
4
+ version: 1.31.0
4
5
  prerelease:
5
- version: 1.30.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Tobias Luetke
@@ -37,184 +37,129 @@ cert_chain:
37
37
  Z1BvU1BxN25rK3MyRlFVQko5VVpGSzFsZ016aG8vNGZaZ3pKd2J1K2NPOFNO
38
38
  dWFMUy9iagpoUGFTVHlWVTB5Q1Nudz09Ci0tLS0tRU5EIENFUlRJRklDQVRF
39
39
  LS0tLS0K
40
- date: 2013-02-13 00:00:00.000000000 Z
40
+ date: 2013-02-20 00:00:00.000000000 Z
41
41
  dependencies:
42
42
  - !ruby/object:Gem::Dependency
43
- version_requirements: !ruby/object:Gem::Requirement
43
+ name: activesupport
44
+ requirement: &70206840641520 !ruby/object:Gem::Requirement
45
+ none: false
44
46
  requirements:
45
47
  - - ! '>='
46
48
  - !ruby/object:Gem::Version
47
49
  version: 2.3.14
48
- none: false
49
- name: activesupport
50
50
  type: :runtime
51
51
  prerelease: false
52
- requirement: !ruby/object:Gem::Requirement
53
- requirements:
54
- - - ! '>='
55
- - !ruby/object:Gem::Version
56
- version: 2.3.14
57
- none: false
52
+ version_requirements: *70206840641520
58
53
  - !ruby/object:Gem::Dependency
59
- version_requirements: !ruby/object:Gem::Requirement
54
+ name: i18n
55
+ requirement: &70206840641100 !ruby/object:Gem::Requirement
56
+ none: false
60
57
  requirements:
61
58
  - - ! '>='
62
59
  - !ruby/object:Gem::Version
63
60
  version: '0'
64
- none: false
65
- name: i18n
66
61
  type: :runtime
67
62
  prerelease: false
68
- requirement: !ruby/object:Gem::Requirement
69
- requirements:
70
- - - ! '>='
71
- - !ruby/object:Gem::Version
72
- version: '0'
73
- none: false
63
+ version_requirements: *70206840641100
74
64
  - !ruby/object:Gem::Dependency
75
- version_requirements: !ruby/object:Gem::Requirement
65
+ name: money
66
+ requirement: &70206840640640 !ruby/object:Gem::Requirement
67
+ none: false
76
68
  requirements:
77
69
  - - ! '>='
78
70
  - !ruby/object:Gem::Version
79
71
  version: '0'
80
- none: false
81
- name: money
82
72
  type: :runtime
83
73
  prerelease: false
84
- requirement: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - ! '>='
87
- - !ruby/object:Gem::Version
88
- version: '0'
89
- none: false
74
+ version_requirements: *70206840640640
90
75
  - !ruby/object:Gem::Dependency
91
- version_requirements: !ruby/object:Gem::Requirement
76
+ name: builder
77
+ requirement: &70206840640080 !ruby/object:Gem::Requirement
78
+ none: false
92
79
  requirements:
93
80
  - - ! '>='
94
81
  - !ruby/object:Gem::Version
95
82
  version: 2.0.0
96
- none: false
97
- name: builder
98
83
  type: :runtime
99
84
  prerelease: false
100
- requirement: !ruby/object:Gem::Requirement
101
- requirements:
102
- - - ! '>='
103
- - !ruby/object:Gem::Version
104
- version: 2.0.0
105
- none: false
85
+ version_requirements: *70206840640080
106
86
  - !ruby/object:Gem::Dependency
107
- version_requirements: !ruby/object:Gem::Requirement
87
+ name: json
88
+ requirement: &70206840639440 !ruby/object:Gem::Requirement
89
+ none: false
108
90
  requirements:
109
91
  - - ! '>='
110
92
  - !ruby/object:Gem::Version
111
93
  version: 1.5.1
112
- none: false
113
- name: json
114
94
  type: :runtime
115
95
  prerelease: false
116
- requirement: !ruby/object:Gem::Requirement
117
- requirements:
118
- - - ! '>='
119
- - !ruby/object:Gem::Version
120
- version: 1.5.1
121
- none: false
96
+ version_requirements: *70206840639440
122
97
  - !ruby/object:Gem::Dependency
123
- version_requirements: !ruby/object:Gem::Requirement
98
+ name: active_utils
99
+ requirement: &70206840638860 !ruby/object:Gem::Requirement
100
+ none: false
124
101
  requirements:
125
102
  - - ! '>='
126
103
  - !ruby/object:Gem::Version
127
104
  version: 1.0.2
128
- none: false
129
- name: active_utils
130
105
  type: :runtime
131
106
  prerelease: false
132
- requirement: !ruby/object:Gem::Requirement
133
- requirements:
134
- - - ! '>='
135
- - !ruby/object:Gem::Version
136
- version: 1.0.2
137
- none: false
107
+ version_requirements: *70206840638860
138
108
  - !ruby/object:Gem::Dependency
139
- version_requirements: !ruby/object:Gem::Requirement
109
+ name: nokogiri
110
+ requirement: &70206840638080 !ruby/object:Gem::Requirement
111
+ none: false
140
112
  requirements:
141
113
  - - ! '>='
142
114
  - !ruby/object:Gem::Version
143
115
  version: '0'
144
- none: false
145
- name: nokogiri
146
116
  type: :runtime
147
117
  prerelease: false
148
- requirement: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ! '>='
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
- none: false
118
+ version_requirements: *70206840638080
154
119
  - !ruby/object:Gem::Dependency
155
- version_requirements: !ruby/object:Gem::Requirement
120
+ name: rake
121
+ requirement: &70206840637200 !ruby/object:Gem::Requirement
122
+ none: false
156
123
  requirements:
157
124
  - - ! '>='
158
125
  - !ruby/object:Gem::Version
159
126
  version: '0'
160
- none: false
161
- name: rake
162
127
  type: :development
163
128
  prerelease: false
164
- requirement: !ruby/object:Gem::Requirement
165
- requirements:
166
- - - ! '>='
167
- - !ruby/object:Gem::Version
168
- version: '0'
169
- none: false
129
+ version_requirements: *70206840637200
170
130
  - !ruby/object:Gem::Dependency
171
- version_requirements: !ruby/object:Gem::Requirement
131
+ name: mocha
132
+ requirement: &70206840636380 !ruby/object:Gem::Requirement
133
+ none: false
172
134
  requirements:
173
135
  - - ~>
174
136
  - !ruby/object:Gem::Version
175
137
  version: 0.11.3
176
- none: false
177
- name: mocha
178
138
  type: :development
179
139
  prerelease: false
180
- requirement: !ruby/object:Gem::Requirement
181
- requirements:
182
- - - ~>
183
- - !ruby/object:Gem::Version
184
- version: 0.11.3
185
- none: false
140
+ version_requirements: *70206840636380
186
141
  - !ruby/object:Gem::Dependency
187
- version_requirements: !ruby/object:Gem::Requirement
142
+ name: rails
143
+ requirement: &70206840635740 !ruby/object:Gem::Requirement
144
+ none: false
188
145
  requirements:
189
146
  - - ! '>='
190
147
  - !ruby/object:Gem::Version
191
148
  version: 2.3.14
192
- none: false
193
- name: rails
194
149
  type: :development
195
150
  prerelease: false
196
- requirement: !ruby/object:Gem::Requirement
197
- requirements:
198
- - - ! '>='
199
- - !ruby/object:Gem::Version
200
- version: 2.3.14
201
- none: false
151
+ version_requirements: *70206840635740
202
152
  - !ruby/object:Gem::Dependency
203
- version_requirements: !ruby/object:Gem::Requirement
153
+ name: thor
154
+ requirement: &70206840684480 !ruby/object:Gem::Requirement
155
+ none: false
204
156
  requirements:
205
157
  - - ! '>='
206
158
  - !ruby/object:Gem::Version
207
159
  version: '0'
208
- none: false
209
- name: thor
210
160
  type: :development
211
161
  prerelease: false
212
- requirement: !ruby/object:Gem::Requirement
213
- requirements:
214
- - - ! '>='
215
- - !ruby/object:Gem::Version
216
- version: '0'
217
- none: false
162
+ version_requirements: *70206840684480
218
163
  description: Active Merchant is a simple payment abstraction library used in and sponsored
219
164
  by Shopify. It is written by Tobias Luetke, Cody Fauser, and contributors. The aim
220
165
  of the project is to feel natural to Ruby users and to abstract as many parts as
@@ -261,6 +206,7 @@ files:
261
206
  - lib/active_merchant/billing/gateways/efsnet.rb
262
207
  - lib/active_merchant/billing/gateways/elavon.rb
263
208
  - lib/active_merchant/billing/gateways/epay.rb
209
+ - lib/active_merchant/billing/gateways/evo_ca.rb
264
210
  - lib/active_merchant/billing/gateways/eway.rb
265
211
  - lib/active_merchant/billing/gateways/eway_managed.rb
266
212
  - lib/active_merchant/billing/gateways/eway_rapid.rb
@@ -316,6 +262,7 @@ files:
316
262
  - lib/active_merchant/billing/gateways/payflow_express_uk.rb
317
263
  - lib/active_merchant/billing/gateways/payflow_uk.rb
318
264
  - lib/active_merchant/billing/gateways/payment_express.rb
265
+ - lib/active_merchant/billing/gateways/paymill.rb
319
266
  - lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb
320
267
  - lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb
321
268
  - lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb
@@ -512,21 +459,22 @@ rdoc_options: []
512
459
  require_paths:
513
460
  - lib
514
461
  required_ruby_version: !ruby/object:Gem::Requirement
462
+ none: false
515
463
  requirements:
516
464
  - - ! '>='
517
465
  - !ruby/object:Gem::Version
518
466
  version: '0'
519
- none: false
520
467
  required_rubygems_version: !ruby/object:Gem::Requirement
468
+ none: false
521
469
  requirements:
522
470
  - - ! '>='
523
471
  - !ruby/object:Gem::Version
524
472
  version: '0'
525
- none: false
526
473
  requirements: []
527
474
  rubyforge_project: activemerchant
528
- rubygems_version: 1.8.23
475
+ rubygems_version: 1.8.11
529
476
  signing_key:
530
477
  specification_version: 3
531
478
  summary: Framework and tools for dealing with credit card transactions.
532
479
  test_files: []
480
+ has_rdoc:
metadata.gz.sig CHANGED
Binary file