activemerchant 1.57.0 → 1.58.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 763b8ca398277831841d2b263a482a46e2f8793b
4
- data.tar.gz: 6acb014413ea74d8b36ebe9a2a40ae04e60de312
3
+ metadata.gz: da3af9432d84476a6bb80c8c2ce551def7cb9aad
4
+ data.tar.gz: f0fca6c6c9aea70ec6fe8ba476aee0e0087bab14
5
5
  SHA512:
6
- metadata.gz: fce7249de3a503a61123f020bf72a374d5fe3fc9368c786e939135995ff5f4d5a392f31026325d7844ffd6458bc832d7c845e56d1f892c21b4e567ce322b5ec7
7
- data.tar.gz: 204553110484e103828c9767405bf11cd704810a5689f1150bace201316445168ce9f523ba67278f9e3ff2010906ca2237cd8a6d58ddd99192671cffd08d36d3
6
+ metadata.gz: d2d71c5586712ab174a2aea4da55ce52e10e4df6d11207ee46d85e3ea337feb698137e5b9c80b9f764f33e7d157bd31f15e3f097e5aaa866c60df2c48960525e
7
+ data.tar.gz: 831bc61882f81730b31b6f46d29e45a4d41e239c15209ff40a20abca24fe89bee9a1b04e31df0a900ccab1edd0a12ad501b46c7ae818c19dadcde21930c00da9
data/CHANGELOG CHANGED
@@ -1,5 +1,20 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
+ == Version 1.58.0 (March 1, 2016)
4
+ * Move Electron check out of CreditCard into CreditCardMethods [ThereExistsX]
5
+ * CardStream: Add AED and NZD currencies [sdball]
6
+ * App55: Remove Gateway [ThereExistsX]
7
+ * Mercury: Stripping the start and end sentinels on card-present track data for max-length track1 requests [ryanbalsdon]
8
+ * SagePay: Update VISA Electron ranges [sdball]
9
+ * Clearhaus: Make request signing more transparent & robust [sdball]
10
+ * NCRSecurePay: Fix production URL [rwdaigle]
11
+ * Add ACH support to Stripe [sdball]
12
+ * PayPal Express: Fixing list of currencies without fractions [Krystosterone]
13
+ * Cashnet: Default custcode option and proper redirect handling [rwdaigle]
14
+ * TransFirst: Fix missing address and remove CC only fields for ACH [davidsantoso]
15
+ * More prominent links to contribution docs [rwdaigle]
16
+
17
+
3
18
  == Version 1.57.0 (February 1, 2016)
4
19
  * AuthorizeNetCim: Add unmaskExpirationDate option [RamilGilmanov]
5
20
  * Element: Add gateway support [davidsantoso]
@@ -37,6 +52,12 @@
37
52
  * Stripe: Add `stripe_account` header option [anellis]
38
53
  * Cardstream: Add AVS code and message [anellis]
39
54
  * Barclaycard Smartpay: New gateway support [curiousepic]
55
+ * Transfirst: Fix missing address and remove CC only fields for ACH [davidsantoso]
56
+ * Stripe: Support ACH payments [sdball]
57
+ * NCRSecurePay: Fix production URL [rwdaigle]
58
+ * Clearhaus: Make request signing more transparent & robust [sdball]
59
+ * SagePay: Properly detect Electron brand [sdball]
60
+ * Mercury: Fix for max-length track 1 [ryanbalsdon]
40
61
 
41
62
 
42
63
  == Version 1.56.0 (December 1, 2015)
data/README.md CHANGED
@@ -17,6 +17,8 @@ from an ever-growing set of contributors.
17
17
  See [GettingStarted.md](GettingStarted.md) if you want to learn more about using Active Merchant in your
18
18
  applications.
19
19
 
20
+ If you'd like to contribute to Active Merchant, please start with our [contribution guide](CONTRIBUTING.md).
21
+
20
22
  ## Installation
21
23
 
22
24
  ### From Git
@@ -82,7 +84,6 @@ For more in-depth documentation and tutorials, see [GettingStarted.md](GettingSt
82
84
 
83
85
  The [ActiveMerchant Wiki](http://github.com/activemerchant/active_merchant/wikis) contains a [table of features supported by each gateway](http://github.com/activemerchant/active_merchant/wikis/gatewayfeaturematrix).
84
86
 
85
- * [App55](https://www.app55.com/) - AU, BR, CA, CH, CL, CN, CO, CZ, DK, EU, GB, HK, HU, ID, IS, JP, KE, KR, MX, MY, NO, NZ, PH, PL, TH, TW, US, VN, ZA
86
87
  * [Authorize.Net CIM](http://www.authorize.net/) - US
87
88
  * [Authorize.Net](http://www.authorize.net/) - AD, AT, AU, BE, BG, CA, CH, CY, CZ, DE, DK, ES, FI, FR, GB, GB, GI, GR, HU, IE, IT, LI, LU, MC, MT, NL, NO, PL, PT, RO, SE, SI, SK, SM, TR, US, VA
88
89
  * [Axcess MS](http://www.axcessms.com/) - AD, AT, BE, BG, BR, CA, CH, CY, CZ, DE, DK, EE, ES, FI, FO, FR, GB, GI, GR, HR, HU, IE, IL, IM, IS, IT, LI, LT, LU, LV, MC, MT, MX, NL, NO, PL, PT, RO, RU, SE, SI, SK, TR, US, VA
@@ -17,6 +17,26 @@ module ActiveMerchant #:nodoc:
17
17
  'laser' => /^(6304|6706|6709|6771(?!89))\d{8}(\d{4}|\d{6,7})?$/
18
18
  }
19
19
 
20
+ # http://www.barclaycard.co.uk/business/files/bin_rules.pdf
21
+ ELECTRON_RANGES = [
22
+ [400115],
23
+ (400837..400839),
24
+ (412921..412923),
25
+ [417935],
26
+ (419740..419741),
27
+ (419773..419775),
28
+ [424519],
29
+ (424962..424963),
30
+ [437860],
31
+ [444000],
32
+ [459472],
33
+ (484406..484411),
34
+ (484413..484414),
35
+ (484418..484418),
36
+ (484428..484455),
37
+ (491730..491759),
38
+ ]
39
+
20
40
  def self.included(base)
21
41
  base.extend(ClassMethods)
22
42
  end
@@ -58,6 +78,11 @@ module ActiveMerchant #:nodoc:
58
78
  (number.to_s =~ /^\d{1,2}$/)
59
79
  end
60
80
 
81
+ # Returns if the card matches known Electron BINs
82
+ def electron?
83
+ self.class.electron?(number)
84
+ end
85
+
61
86
  module ClassMethods
62
87
  # Returns true if it validates. Optionally, you can pass a card brand as an argument and
63
88
  # make sure it is of the correct brand.
@@ -106,6 +131,17 @@ module ActiveMerchant #:nodoc:
106
131
  return nil
107
132
  end
108
133
 
134
+ def electron?(number)
135
+ return false unless [16, 19].include?(number.length)
136
+
137
+ # don't recalculate for each range
138
+ bank_identification_number = first_digits(number).to_i
139
+
140
+ ELECTRON_RANGES.any? do |range|
141
+ range.include?(bank_identification_number)
142
+ end
143
+ end
144
+
109
145
  def type?(number)
110
146
  ActiveMerchant.deprecated "CreditCard#type? is deprecated and will be removed from a future release of ActiveMerchant. Please use CreditCard#brand? instead."
111
147
  brand?(number)
@@ -13,21 +13,23 @@ module ActiveMerchant #:nodoc:
13
13
  self.display_name = 'CardStream'
14
14
 
15
15
  CURRENCY_CODES = {
16
- "AUD" => '036',
17
- "BRL" => '986',
18
- "CAD" => '124',
19
- "CZK" => '203',
20
- "DKK" => '208',
21
- "HKD" => '344',
22
- "ICK" => '352',
23
- "JPY" => '392',
24
- "NOK" => '578',
25
- "SGD" => '702',
26
- "SEK" => '752',
27
- "CHF" => '756',
28
- "GBP" => '826',
29
- "USD" => '840',
30
- "EUR" => '978'
16
+ "AED" => "784",
17
+ "AUD" => "036",
18
+ "BRL" => "986",
19
+ "CAD" => "124",
20
+ "CHF" => "756",
21
+ "CZK" => "203",
22
+ "DKK" => "208",
23
+ "EUR" => "978",
24
+ "GBP" => "826",
25
+ "HKD" => "344",
26
+ "ICK" => "352",
27
+ "JPY" => "392",
28
+ "NOK" => "578",
29
+ "NZD" => "554",
30
+ "SEK" => "752",
31
+ "SGD" => "702",
32
+ "USD" => "840",
31
33
  }
32
34
 
33
35
  CVV_CODE = {
@@ -44,6 +44,7 @@ module ActiveMerchant #:nodoc:
44
44
  # :signing_key - merchant's private key for optionally signing request
45
45
  def initialize(options={})
46
46
  requires!(options, :api_key)
47
+ options[:signing_key].strip! if options[:signing_key]
47
48
  super
48
49
  end
49
50
 
@@ -183,7 +184,11 @@ module ActiveMerchant #:nodoc:
183
184
  body = parameters.to_query
184
185
 
185
186
  if signing_key = @options[:signing_key]
186
- headers["Signature"] = generate_signature(@options[:api_key], signing_key, body)
187
+ begin
188
+ headers["Signature"] = generate_signature(@options[:api_key], signing_key, body)
189
+ rescue OpenSSL::PKey::RSAError => e
190
+ return Response.new(false, e.message)
191
+ end
187
192
  end
188
193
 
189
194
  response = begin
@@ -197,12 +197,20 @@ module ActiveMerchant #:nodoc:
197
197
  if credit_card.track_data.present?
198
198
  # Track 1 has a start sentinel (STX) of '%' and track 2 is ';'
199
199
  # Track 1 and 2 have identical end sentinels (ETX) of '?'
200
+ # Tracks may or may not have checksum (LRC) after the ETX
200
201
  # If the track has no STX or is corrupt, we send it as track 1, to let Mercury
201
202
  #handle with the validation error as it sees fit.
202
- # Track 2 requires having the start and end sentinels stripped. Track 1 does not.
203
- if credit_card.track_data[0] == ';' # track 2 start sentinel (STX)
204
- xml.tag! 'Track2', credit_card.track_data[1..-2]
205
- else # track 1 or a corrupt track
203
+ # Track 2 requires having the STX and ETX stripped. Track 1 does not.
204
+ # Max-length track 1s require having the STX and ETX stripped. Max is 79 bytes including LRC.
205
+ is_track_2 = credit_card.track_data[0] == ';'
206
+ etx_index = credit_card.track_data.rindex('?') || credit_card.track_data.length
207
+ is_max_track1 = etx_index >= 77
208
+
209
+ if is_track_2
210
+ xml.tag! 'Track2', credit_card.track_data[1...etx_index]
211
+ elsif is_max_track1
212
+ xml.tag! 'Track1', credit_card.track_data[1...etx_index]
213
+ else
206
214
  xml.tag! 'Track1', credit_card.track_data
207
215
  end
208
216
  else
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
5
5
  class NcrSecurePayGateway < Gateway
6
6
  self.test_url = 'https://testbox.monetra.com:8665/'
7
- self.live_url = 'https://ps.ncrsecurepay.com:8444/'
7
+ self.live_url = 'https://portal.ncrsecurepay.com:8444/'
8
8
 
9
9
  self.supported_countries = ['US']
10
10
  self.default_currency = 'USD'
@@ -26,7 +26,7 @@ module ActiveMerchant #:nodoc:
26
26
  'TW' => 'zh_TW'
27
27
  }
28
28
 
29
- CURRENCIES_WITHOUT_FRACTIONS = %w(BRL HUF JPY MYR TWD TRY)
29
+ CURRENCIES_WITHOUT_FRACTIONS = %w(HUF JPY TWD)
30
30
 
31
31
  self.test_redirect_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr'
32
32
  self.supported_countries = ['US']
@@ -36,8 +36,6 @@ module ActiveMerchant #:nodoc:
36
36
  :jcb => "JCB"
37
37
  }
38
38
 
39
- ELECTRON = /^(424519|42496[23]|450875|48440[6-8]|4844[1-5][1-5]|4917[3-5][0-9]|491880)\d{10}(\d{3})?$/
40
-
41
39
  AVS_CVV_CODE = {
42
40
  "NOTPROVIDED" => nil,
43
41
  "NOTCHECKED" => 'X',
@@ -292,8 +290,7 @@ module ActiveMerchant #:nodoc:
292
290
 
293
291
  card_type = card_brand(credit_card).to_sym
294
292
 
295
- # Check if it is an electron card
296
- if card_type == :visa && credit_card.number =~ ELECTRON
293
+ if card_type == :visa && credit_card.electron?
297
294
  CREDIT_CARDS[:electron]
298
295
  else
299
296
  CREDIT_CARDS[card_type]
@@ -46,6 +46,11 @@ module ActiveMerchant #:nodoc:
46
46
  'incorrect_pin' => STANDARD_ERROR_CODE[:incorrect_pin]
47
47
  }
48
48
 
49
+ BANK_ACCOUNT_HOLDER_TYPE_MAPPING = {
50
+ "personal" => "individual",
51
+ "business" => "company",
52
+ }
53
+
49
54
  def initialize(options = {})
50
55
  requires!(options, :login)
51
56
  @api_key = options[:login]
@@ -80,6 +85,11 @@ module ActiveMerchant #:nodoc:
80
85
  #
81
86
  # purchase(money, nil, { :customer => id, ... })
82
87
  def purchase(money, payment, options = {})
88
+ if ach?(payment)
89
+ direct_bank_error = "Direct bank account transactions are not supported. Bank accounts must be stored and verified before use."
90
+ return Response.new(false, direct_bank_error)
91
+ end
92
+
83
93
  MultiResponse.run do |r|
84
94
  if payment.is_a?(ApplePayPaymentToken)
85
95
  r.process { tokenize_apple_pay_token(payment) }
@@ -155,26 +165,34 @@ module ActiveMerchant #:nodoc:
155
165
 
156
166
  # Note: creating a new credit card will not change the customer's existing default credit card (use :set_default => true)
157
167
  def store(payment, options = {})
158
- card_params = {}
168
+ params = {}
159
169
  post = {}
160
170
 
161
- if payment.is_a?(ApplePayPaymentToken)
171
+ if card_brand(payment) == "check"
172
+ bank_token_response = tokenize_bank_account(payment)
173
+ if bank_token_response.success?
174
+ params = { source: bank_token_response.params["token"]["id"] }
175
+ else
176
+ return bank_token_response
177
+ end
178
+ elsif payment.is_a?(ApplePayPaymentToken)
162
179
  token_exchange_response = tokenize_apple_pay_token(payment)
163
- card_params = { card: token_exchange_response.params["token"]["id"] } if token_exchange_response.success?
180
+ params = { card: token_exchange_response.params["token"]["id"] } if token_exchange_response.success?
164
181
  else
165
- add_creditcard(card_params, payment, options)
182
+ add_creditcard(params, payment, options)
166
183
  end
167
184
 
168
185
  post[:validate] = options[:validate] unless options[:validate].nil?
169
186
  post[:description] = options[:description] if options[:description]
170
187
  post[:email] = options[:email] if options[:email]
188
+
171
189
  if options[:account]
172
- add_external_account(post, card_params, payment)
190
+ add_external_account(post, params, payment)
173
191
  commit(:post, "accounts/#{CGI.escape(options[:account])}/external_accounts", post, options)
174
192
  elsif options[:customer]
175
193
  MultiResponse.run(:first) do |r|
176
194
  # The /cards endpoint does not update other customer parameters.
177
- r.process { commit(:post, "customers/#{CGI.escape(options[:customer])}/cards", card_params, options) }
195
+ r.process { commit(:post, "customers/#{CGI.escape(options[:customer])}/cards", params, options) }
178
196
 
179
197
  if options[:set_default] and r.success? and !r.params['id'].blank?
180
198
  post[:default_card] = r.params['id']
@@ -185,7 +203,7 @@ module ActiveMerchant #:nodoc:
185
203
  end
186
204
  end
187
205
  else
188
- commit(:post, 'customers', post.merge(card_params), options)
206
+ commit(:post, 'customers', post.merge(params), options)
189
207
  end
190
208
  end
191
209
 
@@ -542,6 +560,39 @@ module ActiveMerchant #:nodoc:
542
560
  error_code ||= STANDARD_ERROR_CODE_MAPPING[code]
543
561
  error_code
544
562
  end
563
+
564
+ def tokenize_bank_account(bank_account, options = {})
565
+ account_holder_type = BANK_ACCOUNT_HOLDER_TYPE_MAPPING[bank_account.account_holder_type]
566
+
567
+ post = {
568
+ bank_account: {
569
+ account_number: bank_account.account_number,
570
+ country: 'US',
571
+ currency: 'usd',
572
+ routing_number: bank_account.routing_number,
573
+ name: bank_account.name,
574
+ account_holder_type: account_holder_type,
575
+ }
576
+ }
577
+
578
+ token_response = api_request(:post, "tokens?#{post_data(post)}")
579
+ success = token_response["error"].nil?
580
+
581
+ if success && token_response["id"]
582
+ Response.new(success, nil, token: token_response)
583
+ else
584
+ Response.new(success, token_response["error"]["message"])
585
+ end
586
+ end
587
+
588
+ def ach?(payment_method)
589
+ case payment_method
590
+ when String, nil
591
+ false
592
+ else
593
+ card_brand(payment_method) == "check"
594
+ end
595
+ end
545
596
  end
546
597
  end
547
598
  end
@@ -38,24 +38,27 @@ module ActiveMerchant #:nodoc:
38
38
  post = {}
39
39
 
40
40
  add_amount(post, money)
41
- add_invoice(post, options)
42
41
  add_payment(post, payment)
43
42
  add_address(post, options)
43
+ add_invoice(post, options) if payment.is_a?(CreditCard)
44
+ add_pair(post, :RefID, options[:order_id], required: true)
44
45
 
45
46
  commit((payment.is_a?(Check) ? :purchase_echeck : :purchase), post)
46
47
  end
47
48
 
48
49
  def refund(money, authorization, options={})
49
50
  post = {}
51
+
50
52
  transaction_id, payment_type = split_authorization(authorization)
51
53
  add_amount(post, money)
52
- add_invoice(post, options)
53
54
  add_pair(post, :TransID, transaction_id)
55
+
54
56
  commit((payment_type == "check" ? :refund_echeck : :refund), post)
55
57
  end
56
58
 
57
59
  def void(authorization, options={})
58
60
  post = {}
61
+
59
62
  transaction_id, _ = split_authorization(authorization)
60
63
  add_pair(post, :TransID, transaction_id)
61
64
 
@@ -85,13 +88,15 @@ module ActiveMerchant #:nodoc:
85
88
  address = options[:billing_address] || options[:address]
86
89
 
87
90
  if address
88
- add_pair(post, :Address, address[:address1])
89
- add_pair(post, :ZipCode, address[:zip])
91
+ add_pair(post, :Address, address[:address1], required: true)
92
+ add_pair(post, :ZipCode, address[:zip], required: true)
93
+ else
94
+ add_pair(post, :Address, "", required: true)
95
+ add_pair(post, :ZipCode, "", required: true)
90
96
  end
91
97
  end
92
98
 
93
99
  def add_invoice(post, options)
94
- add_pair(post, :RefID, options[:order_id], required: true)
95
100
  add_pair(post, :SECCCode, options[:invoice], required: true)
96
101
  add_pair(post, :PONumber, options[:invoice], required: true)
97
102
  add_pair(post, :SaleTaxAmount, amount(options[:tax] || 0))
@@ -130,8 +135,8 @@ module ActiveMerchant #:nodoc:
130
135
  return default_value
131
136
  end
132
137
 
133
- def add_unused_fields(post)
134
- return if post[:TransRoute]
138
+ def add_unused_fields(action, post)
139
+ return unless action == :purchase
135
140
 
136
141
  UNUSED_CREDIT_CARD_FIELDS.each do |f|
137
142
  post[f] = ""
@@ -163,7 +168,7 @@ module ActiveMerchant #:nodoc:
163
168
  end
164
169
 
165
170
  def commit(action, params)
166
- response = parse(ssl_post(url(action), post_data(params)))
171
+ response = parse(ssl_post(url(action), post_data(action, params)))
167
172
 
168
173
  Response.new(
169
174
  success_from(response),
@@ -208,8 +213,8 @@ module ActiveMerchant #:nodoc:
208
213
  end
209
214
  end
210
215
 
211
- def post_data(params = {})
212
- add_unused_fields(params)
216
+ def post_data(action, params = {})
217
+ add_unused_fields(action, params)
213
218
  params[:MerchantID] = @options[:login]
214
219
  params[:RegKey] = @options[:password]
215
220
 
@@ -1,3 +1,3 @@
1
1
  module ActiveMerchant
2
- VERSION = "1.57.0"
2
+ VERSION = "1.58.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemerchant
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.57.0
4
+ version: 1.58.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Luetke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-01 00:00:00.000000000 Z
11
+ date: 2016-03-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -162,7 +162,6 @@ files:
162
162
  - lib/active_merchant/billing/gateway.rb
163
163
  - lib/active_merchant/billing/gateways.rb
164
164
  - lib/active_merchant/billing/gateways/allied_wallet.rb
165
- - lib/active_merchant/billing/gateways/app55.rb
166
165
  - lib/active_merchant/billing/gateways/authorize_net.rb
167
166
  - lib/active_merchant/billing/gateways/authorize_net_arb.rb
168
167
  - lib/active_merchant/billing/gateways/authorize_net_cim.rb
@@ -1,176 +0,0 @@
1
- module ActiveMerchant #:nodoc:
2
- module Billing #:nodoc:
3
- class App55Gateway < Gateway
4
- self.test_url = 'https://sandbox.app55.com/v1/'
5
- self.live_url = 'https://api.app55.com/v1/'
6
-
7
- self.supported_countries = ['AU', 'BR', 'CA', 'CH', 'CL', 'CN', 'CO', 'CZ', 'DK', 'GB', 'HK', 'HU', 'ID', 'IS', 'JP', 'KE', 'KR', 'MX', 'MY', 'NO', 'NZ', 'PH', 'PL', 'TH', 'TW', 'US', 'VN', 'ZA']
8
- self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :maestro, :solo]
9
- self.default_currency = 'UKP'
10
- self.money_format = :dollars
11
- self.homepage_url = 'https://www.app55.com/'
12
- self.display_name = 'App55'
13
-
14
- # Create gateway
15
- #
16
- # options:
17
- # :api_key - merchants App55 API Key
18
- # :api_secret - merchants App55 Secret Key
19
- def initialize(options = {})
20
- requires!(options, :api_key, :api_secret)
21
- @api_key = options[:api_key]
22
- @api_secret = options[:api_secret]
23
- super
24
- end
25
-
26
- # Make a purchase (authorize and commit)
27
- #
28
- # money - The monetary amount of the transaction in cents.
29
- # payment_method - The CreditCard or the App55 card token.
30
- # options - A standard ActiveMerchant options hash
31
- def purchase(money, payment_method, options = {})
32
- authorize(money, payment_method, options.merge(commit: true))
33
- end
34
-
35
- # Authorize a transaction.
36
- #
37
- # money - The monetary amount of the transaction in cents.
38
- # payment_method - The CreditCard or the App55 card token.
39
- # options - A standard ActiveMerchant options hash
40
- def authorize(money, payment_method, options = {})
41
- post = {}
42
- add_creditcard(post, payment_method, options)
43
- add_transaction(post, money, options)
44
-
45
- commit(:post, 'transaction', post)
46
- end
47
-
48
- # Commit a pre-authorized transaction.
49
- #
50
- # money - The monetary amount of the transaction in cents.
51
- # authorization - The App55 transaction id string.
52
- # options - A standard ActiveMerchant options hash
53
- def capture(money, authorization, options = {})
54
- commit(:post, "transaction/#{authorization}")
55
- end
56
-
57
- private
58
-
59
- def add_customer_data(post, options)
60
- metadata_options = [:description, :browser_ip, :user_agent, :referrer]
61
- post.update(options.slice(*metadata_options))
62
- end
63
-
64
- def add_creditcard(post, creditcard, options)
65
- card = {}
66
- card[:number] = creditcard.number
67
- card[:expiry] = ("%02d". % creditcard.month) + '/' + creditcard.year.to_s
68
- card[:security_code] = creditcard.verification_value if creditcard.verification_value?
69
- card[:holder_name] = creditcard.name if creditcard.name
70
- add_address(card, options)
71
- post[:card] = card
72
- end
73
-
74
- def add_address(card, options)
75
- return unless card && card.kind_of?(Hash)
76
- address_hash = {}
77
- if address = (options[:billing_address] || options[:address])
78
- address_hash[:street] = address[:address1] if address[:address1]
79
- address_hash[:street2] = address[:address2] if address[:address2]
80
- address_hash[:country] = address[:country] if address[:country]
81
- address_hash[:postal_code] = address[:zip] if address[:zip]
82
- address_hash[:city] = address[:city] if address[:city]
83
- card[:address] = address_hash
84
- end
85
- end
86
-
87
- def add_transaction(post, money, options)
88
- transaction = {}
89
- add_amount(transaction, money, options)
90
- transaction[:description] = (options[:description] || options[:email])
91
- transaction[:commit] = options[:commit]
92
- post[:transaction] = transaction
93
- end
94
-
95
- def add_amount(obj, money, options)
96
- obj[:amount] = amount(money)
97
- obj[:currency] = (options[:currency] || currency(money))
98
- end
99
-
100
- def parse(body)
101
- JSON.parse(body)
102
- rescue JSON::ParserError
103
- json_error(raw_response)
104
- end
105
-
106
- def commit(method, resource, parameters=nil, meta={})
107
- success = false
108
- begin
109
- raw_response = ssl_request(
110
- method,
111
- url(resource),
112
- post_data(parameters),
113
- headers
114
- )
115
- response = parse(raw_response)
116
- success = response.key?("sig")
117
- rescue ResponseError => e
118
- response = parse(e.response.body)
119
- end
120
-
121
- Response.new(
122
- success,
123
- (success ? "OK" : response["error"]["message"]),
124
- response,
125
- test: test?,
126
- authorization: authorization_from(response)
127
- )
128
- end
129
-
130
- def authorization_from(response)
131
- if response.key?("transaction")
132
- response["transaction"]["id"]
133
- elsif response.key?("card")
134
- response["card"]["token"]
135
- end
136
- end
137
-
138
- def json_error(raw_response)
139
- msg = "Invalid response from app55 server: Received: #{raw_response.inspect})"
140
- {
141
- "error" => {
142
- "message" => msg
143
- }
144
- }
145
- end
146
-
147
- def url(resource)
148
- (test? ? self.test_url : self.live_url) + resource
149
- end
150
-
151
- def post_data(params)
152
- return nil unless params
153
-
154
- params.map do |key, value|
155
- next if value.blank?
156
- if value.is_a?(Hash)
157
- h = {}
158
- value.each do |k, v|
159
- h["#{key}.#{k}"] = v unless v.blank?
160
- end
161
- post_data(h)
162
- else
163
- "#{key}=#{CGI.escape(value.to_s)}"
164
- end
165
- end.compact.join("&")
166
- end
167
-
168
- def headers
169
- {
170
- "Authorization" => "Basic " + Base64.strict_encode64(@options[:api_key].to_s + ":" + @options[:api_secret].to_s),
171
- "User-Agent" => user_agent,
172
- }
173
- end
174
- end
175
- end
176
- end