activemerchant 1.24.0 → 1.25.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.
@@ -10,6 +10,46 @@ raise "Need braintree gem 2.x.y. Run `gem install braintree --version '~>2.0'` t
10
10
 
11
11
  module ActiveMerchant #:nodoc:
12
12
  module Billing #:nodoc:
13
+ # For more information on the Braintree Gateway please visit their
14
+ # {Developer Portal}[https://www.braintreepayments.com/developers]
15
+ #
16
+ # ==== About this implementation
17
+ #
18
+ # This implementation leverages the Braintree-authored ruby gem:
19
+ # https://github.com/braintree/braintree_ruby
20
+ #
21
+ # ==== Debugging Information
22
+ #
23
+ # Setting an ActiveMerchant +wiredump_device+ will automatically
24
+ # configure the Braintree logger (via the Braintree gem's
25
+ # configuration) when the BraintreeBlueGateway is instantiated.
26
+ # Additionally, the log level will be set to +DEBUG+. Therefore,
27
+ # all you have to do is set the +wiredump_device+ and you'll
28
+ # get your debug output from your HTTP interactions with the
29
+ # remote gateway. (Don't enable this in production.)
30
+ #
31
+ # For example:
32
+ #
33
+ # ActiveMerchant::Billing::BraintreeBlueGateway.wiredump_device = Logger.new(STDOUT)
34
+ # # => #<Logger:0x107d385f8 ...>
35
+ #
36
+ # Braintree::Configuration.logger
37
+ # # => (some other logger, created by default by the gem)
38
+ #
39
+ # Braintree::Configuration.logger.level
40
+ # # => 1 (INFO)
41
+ #
42
+ # ActiveMerchant::Billing::BraintreeBlueGateway.new(:merchant_id => 'x', :public_key => 'x', :private_key => 'x')
43
+ #
44
+ # Braintree::Configuration.logger
45
+ # # => #<Logger:0x107d385f8 ...>
46
+ #
47
+ # Braintree::Configuration.logger.level
48
+ # # => 0 (DEBUG)
49
+ #
50
+ # Alternatively, you can avoid setting the +wiredump_device+
51
+ # and set +Braintree::Configuration.logger+ and/or
52
+ # +Braintree::Configuration.logger.level+ directly.
13
53
  class BraintreeBlueGateway < Gateway
14
54
  include BraintreeCommon
15
55
 
@@ -23,8 +63,11 @@ module ActiveMerchant #:nodoc:
23
63
  Braintree::Configuration.public_key = options[:public_key]
24
64
  Braintree::Configuration.private_key = options[:private_key]
25
65
  Braintree::Configuration.environment = (options[:environment] || (test? ? :sandbox : :production)).to_sym
26
- Braintree::Configuration.logger.level = Logger::ERROR if Braintree::Configuration.logger
27
66
  Braintree::Configuration.custom_user_agent = "ActiveMerchant #{ActiveMerchant::VERSION}"
67
+ if wiredump_device
68
+ Braintree::Configuration.logger = wiredump_device
69
+ Braintree::Configuration.logger.level = Logger::DEBUG
70
+ end
28
71
  super
29
72
  end
30
73
 
@@ -105,6 +148,7 @@ module ActiveMerchant #:nodoc:
105
148
  credit_card_params = merge_credit_card_options({
106
149
  :credit_card => {
107
150
  :number => creditcard.number,
151
+ :cvv => creditcard.verification_value,
108
152
  :expiration_month => creditcard.month.to_s.rjust(2, "0"),
109
153
  :expiration_year => creditcard.year.to_s
110
154
  }
@@ -17,6 +17,8 @@ module ActiveMerchant #:nodoc:
17
17
  class CyberSourceGateway < Gateway
18
18
  TEST_URL = 'https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor'
19
19
  LIVE_URL = 'https://ics2ws.ic3.com/commerce/1.x/transactionProcessor'
20
+
21
+ XSD_VERSION = "1.69"
20
22
 
21
23
  # visa, master, american_express, discover
22
24
  self.supported_cardtypes = [:visa, :master, :american_express, :discover]
@@ -104,10 +106,10 @@ module ActiveMerchant #:nodoc:
104
106
  # Request an authorization for an amount from CyberSource
105
107
  #
106
108
  # You must supply an :order_id in the options hash
107
- def authorize(money, creditcard, options = {})
108
- requires!(options, :order_id, :email)
109
+ def authorize(money, creditcard_or_reference, options = {})
110
+ requires!(options, :order_id)
109
111
  setup_address_hash(options)
110
- commit(build_auth_request(money, creditcard, options), options )
112
+ commit(build_auth_request(money, creditcard_or_reference, options), options )
111
113
  end
112
114
 
113
115
  def auth_reversal(money, identification, options = {})
@@ -122,10 +124,10 @@ module ActiveMerchant #:nodoc:
122
124
 
123
125
  # Purchase is an auth followed by a capture
124
126
  # You must supply an order_id in the options hash
125
- def purchase(money, creditcard, options = {})
126
- requires!(options, :order_id, :email)
127
+ def purchase(money, creditcard_or_reference, options = {})
128
+ requires!(options, :order_id)
127
129
  setup_address_hash(options)
128
- commit(build_purchase_request(money, creditcard, options), options)
130
+ commit(build_purchase_request(money, creditcard_or_reference, options), options)
129
131
  end
130
132
 
131
133
  def void(identification, options = {})
@@ -141,6 +143,33 @@ module ActiveMerchant #:nodoc:
141
143
  refund(money, identification, options)
142
144
  end
143
145
 
146
+ # Stores a customer subscription/profile with type "on-demand".
147
+ # To charge the card while creating a profile, pass options[:setup_fee] => money
148
+ def store(creditcard, options = {})
149
+ requires!(options, :order_id)
150
+ setup_address_hash(options)
151
+ commit(build_create_subscription_request(creditcard, options), options)
152
+ end
153
+
154
+ # Updates a customer subscription/profile
155
+ def update(reference, creditcard, options = {})
156
+ requires!(options, :order_id)
157
+ setup_address_hash(options)
158
+ commit(build_update_subscription_request(reference, creditcard, options), options)
159
+ end
160
+
161
+ # Removes a customer subscription/profile
162
+ def unstore(reference, options = {})
163
+ requires!(options, :order_id)
164
+ commit(build_delete_subscription_request(reference, options), options)
165
+ end
166
+
167
+ # Retrieves a customer subscription/profile
168
+ def retrieve(reference, options = {})
169
+ requires!(options, :order_id)
170
+ commit(build_retrieve_subscription_request(reference, options), options)
171
+ end
172
+
144
173
  # CyberSource requires that you provide line item information for tax calculations
145
174
  # If you do not have prices for each item or want to simplify the situation then pass in one fake line item that costs the subtotal of the order
146
175
  #
@@ -171,18 +200,17 @@ module ActiveMerchant #:nodoc:
171
200
  commit(build_tax_calculation_request(creditcard, options), options)
172
201
  end
173
202
 
174
- private
203
+ private
204
+
175
205
  # Create all address hash key value pairs so that we still function if we were only provided with one or two of them
176
206
  def setup_address_hash(options)
177
207
  options[:billing_address] = options[:billing_address] || options[:address] || {}
178
208
  options[:shipping_address] = options[:shipping_address] || {}
179
209
  end
180
210
 
181
- def build_auth_request(money, creditcard, options)
211
+ def build_auth_request(money, creditcard_or_reference, options)
182
212
  xml = Builder::XmlMarkup.new :indent => 2
183
- add_address(xml, creditcard, options[:billing_address], options)
184
- add_purchase_data(xml, money, true, options)
185
- add_creditcard(xml, creditcard)
213
+ add_creditcard_or_subscription(xml, money, creditcard_or_reference, options)
186
214
  add_auth_service(xml)
187
215
  add_business_rules_data(xml)
188
216
  xml.target!
@@ -210,11 +238,9 @@ module ActiveMerchant #:nodoc:
210
238
  xml.target!
211
239
  end
212
240
 
213
- def build_purchase_request(money, creditcard, options)
241
+ def build_purchase_request(money, creditcard_or_reference, options)
214
242
  xml = Builder::XmlMarkup.new :indent => 2
215
- add_address(xml, creditcard, options[:billing_address], options)
216
- add_purchase_data(xml, money, true, options)
217
- add_creditcard(xml, creditcard)
243
+ add_creditcard_or_subscription(xml, money, creditcard_or_reference, options)
218
244
  add_purchase_service(xml, options)
219
245
  add_business_rules_data(xml)
220
246
  xml.target!
@@ -249,6 +275,47 @@ module ActiveMerchant #:nodoc:
249
275
  xml.target!
250
276
  end
251
277
 
278
+ def build_create_subscription_request(creditcard, options)
279
+ options[:subscription] = (options[:subscription] || {}).merge(:frequency => "on-demand", :amount => 0, :automatic_renew => false)
280
+
281
+ xml = Builder::XmlMarkup.new :indent => 2
282
+ add_address(xml, creditcard, options[:billing_address], options)
283
+ add_purchase_data(xml, options[:setup_fee] || 0, true, options)
284
+ add_creditcard(xml, creditcard)
285
+ add_creditcard_payment_method(xml)
286
+ add_subscription(xml, options)
287
+ add_purchase_service(xml, options) if options[:setup_fee]
288
+ add_subscription_create_service(xml, options)
289
+ add_business_rules_data(xml)
290
+ xml.target!
291
+ end
292
+
293
+ def build_update_subscription_request(reference, creditcard, options)
294
+ xml = Builder::XmlMarkup.new :indent => 2
295
+ add_address(xml, creditcard, options[:billing_address], options) unless options[:billing_address].blank?
296
+ add_purchase_data(xml, options[:setup_fee], true, options) unless options[:setup_fee].blank?
297
+ add_creditcard(xml, creditcard) if creditcard
298
+ add_creditcard_payment_method(xml) if creditcard
299
+ add_subscription(xml, options, reference)
300
+ add_subscription_update_service(xml, options)
301
+ add_business_rules_data(xml)
302
+ xml.target!
303
+ end
304
+
305
+ def build_delete_subscription_request(reference, options)
306
+ xml = Builder::XmlMarkup.new :indent => 2
307
+ add_subscription(xml, options, reference)
308
+ add_subscription_delete_service(xml, options)
309
+ xml.target!
310
+ end
311
+
312
+ def build_retrieve_subscription_request(reference, options)
313
+ xml = Builder::XmlMarkup.new :indent => 2
314
+ add_subscription(xml, options, reference)
315
+ add_subscription_retrieve_service(xml, options)
316
+ xml.target!
317
+ end
318
+
252
319
  def add_business_rules_data(xml)
253
320
  xml.tag! 'businessRules' do
254
321
  xml.tag!('ignoreAVSResult', 'true') if @options[:ignore_avs]
@@ -272,8 +339,8 @@ module ActiveMerchant #:nodoc:
272
339
  xml.tag! 'merchantID', @options[:login]
273
340
  xml.tag! 'merchantReferenceCode', options[:order_id]
274
341
  xml.tag! 'clientLibrary' ,'Ruby Active Merchant'
275
- xml.tag! 'clientLibraryVersion', '1.0'
276
- xml.tag! 'clientEnvironment' , 'Linux'
342
+ xml.tag! 'clientLibraryVersion', VERSION
343
+ xml.tag! 'clientEnvironment' , RUBY_PLATFORM
277
344
  end
278
345
 
279
346
  def add_purchase_data(xml, money = 0, include_grand_total = false, options={})
@@ -283,17 +350,24 @@ module ActiveMerchant #:nodoc:
283
350
  end
284
351
  end
285
352
 
286
- def add_address(xml, creditcard, address, options, shipTo = false)
353
+ def add_address(xml, creditcard, address, options, shipTo = false)
354
+ requires!(options, :email)
355
+
287
356
  xml.tag! shipTo ? 'shipTo' : 'billTo' do
288
- xml.tag! 'firstName', creditcard.first_name
289
- xml.tag! 'lastName', creditcard.last_name
290
- xml.tag! 'street1', address[:address1]
291
- xml.tag! 'street2', address[:address2]
292
- xml.tag! 'city', address[:city]
293
- xml.tag! 'state', address[:state]
294
- xml.tag! 'postalCode', address[:zip]
295
- xml.tag! 'country', address[:country]
296
- xml.tag! 'email', options[:email]
357
+ xml.tag! 'firstName', creditcard.first_name if creditcard
358
+ xml.tag! 'lastName', creditcard.last_name if creditcard
359
+ xml.tag! 'street1', address[:address1]
360
+ xml.tag! 'street2', address[:address2] unless address[:address2].blank?
361
+ xml.tag! 'city', address[:city]
362
+ xml.tag! 'state', address[:state]
363
+ xml.tag! 'postalCode', address[:zip]
364
+ xml.tag! 'country', address[:country]
365
+ xml.tag! 'company', address[:company] unless address[:company].blank?
366
+ xml.tag! 'companyTaxID', address[:companyTaxID] unless address[:company_tax_id].blank?
367
+ xml.tag! 'phoneNumber', address[:phone_number] unless address[:phone_number].blank?
368
+ xml.tag! 'email', options[:email]
369
+ xml.tag! 'driversLicenseNumber', options[:drivers_license_number] unless options[:drivers_license_number].blank?
370
+ xml.tag! 'driversLicenseState', options[:drivers_license_state] unless options[:drivers_license_state].blank?
297
371
  end
298
372
  end
299
373
 
@@ -329,7 +403,7 @@ module ActiveMerchant #:nodoc:
329
403
  xml.tag! 'ccAuthService', {'run' => 'true'}
330
404
  xml.tag! 'ccCaptureService', {'run' => 'true'}
331
405
  end
332
-
406
+
333
407
  def add_void_service(xml, request_id, request_token)
334
408
  xml.tag! 'voidService', {'run' => 'true'} do
335
409
  xml.tag! 'voidRequestID', request_id
@@ -351,6 +425,60 @@ module ActiveMerchant #:nodoc:
351
425
  end
352
426
  end
353
427
 
428
+ def add_subscription_create_service(xml, options)
429
+ xml.tag! 'paySubscriptionCreateService', {'run' => 'true'}
430
+ end
431
+
432
+ def add_subscription_update_service(xml, options)
433
+ xml.tag! 'paySubscriptionUpdateService', {'run' => 'true'}
434
+ end
435
+
436
+ def add_subscription_delete_service(xml, options)
437
+ xml.tag! 'paySubscriptionDeleteService', {'run' => 'true'}
438
+ end
439
+
440
+ def add_subscription_retrieve_service(xml, options)
441
+ xml.tag! 'paySubscriptionRetrieveService', {'run' => 'true'}
442
+ end
443
+
444
+ def add_subscription(xml, options, reference = nil)
445
+ options[:subscription] ||= {}
446
+
447
+ xml.tag! 'recurringSubscriptionInfo' do
448
+ if reference
449
+ reference_code, subscription_id, request_token = reference.split(";")
450
+ xml.tag! 'subscriptionID', subscription_id
451
+ end
452
+
453
+ xml.tag! 'status', options[:subscription][:status] if options[:subscription][:status]
454
+ xml.tag! 'amount', options[:subscription][:amount] if options[:subscription][:amount]
455
+ xml.tag! 'numberOfPayments', options[:subscription][:occurrences] if options[:subscription][:occurrences]
456
+ xml.tag! 'automaticRenew', options[:subscription][:automatic_renew] if options[:subscription][:automatic_renew]
457
+ xml.tag! 'frequency', options[:subscription][:frequency] if options[:subscription][:frequency]
458
+ xml.tag! 'startDate', options[:subscription][:start_date].strftime("%Y%m%d") if options[:subscription][:start_date]
459
+ xml.tag! 'endDate', options[:subscription][:end_date].strftime("%Y%m%d") if options[:subscription][:end_date]
460
+ xml.tag! 'approvalRequired', options[:subscription][:approval_required] || false
461
+ xml.tag! 'event', options[:subscription][:event] if options[:subscription][:event]
462
+ xml.tag! 'billPayment', options[:subscription][:bill_payment] if options[:subscription][:bill_payment]
463
+ end
464
+ end
465
+
466
+ def add_creditcard_payment_method(xml)
467
+ xml.tag! 'subscription' do
468
+ xml.tag! 'paymentMethod', "credit card"
469
+ end
470
+ end
471
+
472
+ def add_creditcard_or_subscription(xml, money, creditcard_or_reference, options)
473
+ if creditcard_or_reference.is_a?(String)
474
+ add_purchase_data(xml, money, true, options)
475
+ add_subscription(xml, options, creditcard_or_reference)
476
+ else
477
+ add_address(xml, creditcard_or_reference, options[:billing_address], options)
478
+ add_purchase_data(xml, money, true, options)
479
+ add_creditcard(xml, creditcard_or_reference)
480
+ end
481
+ end
354
482
 
355
483
  # Where we actually build the full SOAP request using builder
356
484
  def build_request(body, options)
@@ -366,7 +494,7 @@ module ActiveMerchant #:nodoc:
366
494
  end
367
495
  end
368
496
  xml.tag! 's:Body', {'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'} do
369
- xml.tag! 'requestMessage', {'xmlns' => 'urn:schemas-cybersource-com:transaction-data-1.32'} do
497
+ xml.tag! 'requestMessage', {'xmlns' => "urn:schemas-cybersource-com:transaction-data-#{XSD_VERSION}"} do
370
498
  add_merchant_data(xml, options)
371
499
  xml << body
372
500
  end
@@ -11,14 +11,18 @@ module ActiveMerchant #:nodoc:
11
11
  #
12
12
  # tendollar = 1000
13
13
  #
14
- # Next, create a credit card object using a TC approved test card.
14
+ # The transaction result is based on the cent value of the transaction. $10.15 will return a failed transaction
15
+ # with a response code of "15 – No Issuer", while $10.00 will return "00 – Transaction Approved."
16
+ #
17
+ # Next, create a credit card object using a eWay approved test card number (4444333322221111).
15
18
  #
16
19
  # creditcard = ActiveMerchant::Billing::CreditCard.new(
17
- # :number => '4111111111111111',
20
+ # :number => '4444333322221111',
18
21
  # :month => 8,
19
22
  # :year => 2006,
20
23
  # :first_name => 'Longbob',
21
- # :last_name => 'Longsen'
24
+ # :last_name => 'Longsen',
25
+ # :verification_value => '123'
22
26
  # )
23
27
  # options = {
24
28
  # :order_id => '1230123',
@@ -28,12 +32,12 @@ module ActiveMerchant #:nodoc:
28
32
  # :state => 'WA',
29
33
  # :country => 'Australia',
30
34
  # :zip => '2000'
31
- # }
35
+ # },
32
36
  # :description => 'purchased items'
33
37
  # }
34
38
  #
35
39
  # To finish setting up, create the active_merchant object you will be using, with the eWay gateway. If you have a
36
- # functional eWay account, replace :login with your account info.
40
+ # functional eWay account, replace :login with your Customer ID.
37
41
  #
38
42
  # gateway = ActiveMerchant::Billing::Base.gateway(:eway).new(:login => '87654321')
39
43
  #
@@ -41,7 +45,7 @@ module ActiveMerchant #:nodoc:
41
45
  #
42
46
  # response = gateway.purchase(tendollar, creditcard, options)
43
47
  #
44
- # Sending a transaction to TrustCommerce with active_merchant returns a Response object, which consistently allows you to:
48
+ # Sending a transaction to eWay with active_merchant returns a Response object, which consistently allows you to:
45
49
  #
46
50
  # 1) Check whether the transaction was successful
47
51
  #
@@ -131,7 +135,7 @@ module ActiveMerchant #:nodoc:
131
135
 
132
136
  self.money_format = :cents
133
137
  self.supported_countries = ['AU']
134
- self.supported_cardtypes = [:visa, :master, :american_express]
138
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
135
139
  self.homepage_url = 'http://www.eway.com.au/'
136
140
  self.display_name = 'eWAY'
137
141
 
@@ -143,7 +147,7 @@ module ActiveMerchant #:nodoc:
143
147
 
144
148
  # ewayCustomerEmail, ewayCustomerAddress, ewayCustomerPostcode
145
149
  def purchase(money, creditcard, options = {})
146
- requires!(options, :order_id)
150
+ requires_address!(options)
147
151
 
148
152
  post = {}
149
153
  add_creditcard(post, creditcard)
@@ -161,6 +165,11 @@ module ActiveMerchant #:nodoc:
161
165
  end
162
166
 
163
167
  private
168
+
169
+ def requires_address!(options)
170
+ raise ArgumentError.new("Missing eWay required parameters: address or billing_address") unless (options.has_key?(:address) or options.has_key?(:billing_address))
171
+ end
172
+
164
173
  def add_creditcard(post, creditcard)
165
174
  post[:CardNumber] = creditcard.number
166
175
  post[:CardExpiryMonth] = sprintf("%.2i", creditcard.month)
@@ -0,0 +1,152 @@
1
+ require 'json'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class FatZebraGateway < Gateway
6
+ LIVE_URL = "https://gateway.fatzebra.com.au/v1.0"
7
+ SANDBOX_URL = "https://gateway.sandbox.fatzebra.com.au/v1.0"
8
+
9
+ self.supported_countries = ['AU']
10
+ self.default_currency = 'AUD'
11
+ self.money_format = :cents
12
+ self.supported_cardtypes = [:visa, :master, :american_express, :jcb]
13
+
14
+ self.homepage_url = 'https://www.fatzebra.com.au/'
15
+ self.display_name = 'Fat Zebra'
16
+
17
+ # Setup a new instance of the gateway.
18
+ #
19
+ # The options hash should include :username and :token
20
+ # You can find your username and token at https://dashboard.fatzebra.com.au
21
+ # Under the Your Account section
22
+ def initialize(options = {})
23
+ requires!(options, :username)
24
+ requires!(options, :token)
25
+ @username = options[:username]
26
+ @token = options[:token]
27
+ super
28
+ end
29
+
30
+ # To create a purchase on a credit card use:
31
+ #
32
+ # purchase(money, creditcard , { ... })
33
+ #
34
+ # To charge a tokenized card
35
+ #
36
+ # purchase(money, {:token => "abzy87u", :cvv => "123"}, { ... }})
37
+ def purchase(money, creditcard, options = {})
38
+ post = {}
39
+
40
+ add_amount(post, money, options)
41
+ add_creditcard(post, creditcard, options)
42
+ post[:reference] = options[:order_id]
43
+ post[:customer_ip] = options[:ip]
44
+
45
+ commit(:post, 'purchases', post)
46
+ end
47
+
48
+ # Refund a transaction
49
+ #
50
+ # amount - Integer - the amount to refund
51
+ # txn_id - String - the original transaction to be refunded
52
+ # reference - String - your transaction reference
53
+ def refund(money, txn_id, reference)
54
+ post = {}
55
+
56
+ post[:amount] = money
57
+ post[:transaction_id] = txn_id
58
+ post[:reference] = reference
59
+
60
+ commit(:post, "refunds", post)
61
+ end
62
+
63
+ # Tokenize a credit card
64
+ def store(creditcard)
65
+ post = {}
66
+ add_creditcard(post, creditcard)
67
+
68
+ commit(:post, "credit_cards", post)
69
+ end
70
+
71
+ private
72
+ # Add the money details to the request
73
+ def add_amount(post, money, options)
74
+ post[:amount] = money
75
+ end
76
+
77
+ # Add the credit card details to the request
78
+ def add_creditcard(post, creditcard, options = {})
79
+ if creditcard.respond_to?(:number)
80
+ post[:card_number] = creditcard.number
81
+ post[:card_expiry] = "#{creditcard.month}/#{creditcard.year}"
82
+ post[:cvv] = creditcard.verification_value if creditcard.verification_value?
83
+ post[:card_holder] = creditcard.name if creditcard.name
84
+ else
85
+ post[:card_token] = creditcard[:token]
86
+ post[:cvv] = creditcard[:cvv]
87
+ end
88
+ end
89
+
90
+ # Post the data to the gateway
91
+ def commit(method, uri, parameters=nil)
92
+ raw_response = response = nil
93
+ success = false
94
+ begin
95
+ raw_response = ssl_request(method, get_url(uri), parameters.to_json, headers)
96
+ response = parse(raw_response)
97
+ success = response["successful"] && (response["response"]["successful"] || response["response"]["token"])
98
+ rescue ResponseError => e
99
+ if e.response.code == "401"
100
+ return Response.new(false, "Invalid Login")
101
+ end
102
+
103
+ raw_response = e.response.body
104
+ response = parse(raw_response)
105
+ rescue JSON::ParserError
106
+ response = json_error(raw_response)
107
+ end
108
+
109
+ message = response["response"]["message"]
110
+ unless response["successful"]
111
+ # There is an error, so we will show that instead
112
+ message = response["errors"].empty? ? "Unknown Error" : response["errors"].join(", ")
113
+ end
114
+
115
+ Response.new(success,
116
+ message,
117
+ response,
118
+ :test => response.has_key?("test") ? response["test"] : false,
119
+ :authorization => response["response"]["id"] || response["response"]["token"])
120
+ end
121
+
122
+ # Parse the returned JSON, if parse errors are raised then return a detailed error.
123
+ def parse(response)
124
+ begin
125
+ JSON.parse(response)
126
+ rescue JSON::ParserError
127
+ msg = 'Invalid JSON response received from Fat Zebra. Please contact support@fatzebra.com.au if you continue to receive this message.'
128
+ msg += " (The raw response returned by the API was #{response.inspect})"
129
+ {
130
+ "successful" => false,
131
+ "response" => {},
132
+ "errors" => [msg]
133
+ }
134
+ end
135
+ end
136
+
137
+ # Build the URL based on the AM mode and the URI
138
+ def get_url(uri)
139
+ base = test? ? SANDBOX_URL : LIVE_URL
140
+ base + "/" + uri
141
+ end
142
+
143
+ # Builds the auth and U-A headers for the request
144
+ def headers
145
+ {
146
+ "Authorization" => "Basic " + Base64.strict_encode64(@username.to_s + ":" + @token.to_s).strip,
147
+ "User-Agent" => "Fat Zebra v1.0/ActiveMerchant #{ActiveMerchant::VERSION}"
148
+ }
149
+ end
150
+ end
151
+ end
152
+ end