activemerchant 1.42.9 → 1.43.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: baa3b2ef41f3fd7b7ee93ae111090938ffee872a
4
- data.tar.gz: 3104e5e77e0895fc86563e6ada9381b37d827f05
3
+ metadata.gz: bd0a074d4175458b2a017a44f74f0bc452e0f37e
4
+ data.tar.gz: 58c5170f7a74afe97dadca2da6c5cbe4466105eb
5
5
  SHA512:
6
- metadata.gz: 5eb2c499dad2b3d33e7c820ce3c2afa77ea7376e19a84a348e4577d7ec10f7b8bbc8c8c6facf881ed65ea32de8762b3a1638ad7e5e7d4019545455364c36e323
7
- data.tar.gz: 97f099bce96fa830eccc9be8d2b2a5934a01e58c4dcb94811827fff49321f48506f1e326f9d7797570af726298664d688241cfaf59e7627627727f1eff497455
6
+ metadata.gz: ee8290e0840f1b817d0f1c9de135bbee7ae677fe37509d246ddb99c85382fb108c044e76075be616db0608d7d14266b4916d4d4581ac0dc2ab8de61e7d07f8a1
7
+ data.tar.gz: cbaf9362768fa747a57b71b8aa26e271712d56611789f5ea57fb67d2d1087261e42144a6340acb74e1e55be70f566468b18eda05a1df2f85b5a4abaa5a9766b7
Binary file
data.tar.gz.sig CHANGED
Binary file
data/CHANGELOG CHANGED
@@ -1,9 +1,22 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
+ == Version 1.43.0 (April 24, 2014)
4
+
5
+ * PagSeguro: New offsite integration [celsodantas]
6
+ * Sage Pay: Fix amount parsing in notifications [berkcaputcu]
7
+ * Sage Pay: Use API v3.00 [bslobodin]
8
+ * BridgePay: Switch method of success detection [markabe]
9
+ * BridgePay: Use Return as TransType for refunds [markabe]
10
+ * IATS: Complete rewrite using first class API [rwdaigle]
11
+ * IATS: Fix invalid country code UK -> GB [rwdaigle]
12
+ * DataCash: Fix refund processing using original authorization [bslobodin]
13
+ * Transnational gateway renamed to Network Merchants [bslobodin]
14
+ * PayMill: Handle non-JSON server responses [bslobodin]
15
+
3
16
  == Version 1.42.9 (April 15, 2014)
4
17
 
5
18
  * Spreedly: Add ip, description and gateway_specific_fields [faizalzakaria]
6
- * Sage (US): Support store/unstore of cards. [rwdaigle]
19
+ * Sage (US): Support store/unstore of cards [rwdaigle]
7
20
  * Pin: Add american express to supported cards [nagash]
8
21
  * Raven: Update handling of CVV/AVS [bslobodin]
9
22
  * Raven: Use UUID for RequestID [bslobodin]
@@ -468,3 +468,7 @@ WePay (March 2014)
468
468
  FirstGiving (March 2014)
469
469
 
470
470
  * Faizal Zakaria (faizalzakaria)
471
+
472
+ BridgePay (April 2014)
473
+
474
+ * Mark Bennett (markabe)
data/README.md CHANGED
@@ -94,6 +94,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
94
94
  * [Beanstream.com](http://www.beanstream.com/) - CA, US
95
95
  * [BluePay](http://www.bluepay.com/) - US
96
96
  * [Braintree](http://www.braintreepaymentsolutions.com) - US, CA, AU, AD, AT, BE, BG, CY, CZ, DK, EE, FI, FR, GI, DE, GR, HU, IS, IM, IE, IT, LV, LI, LT, LU, MT, MC, NL, NO, PL, PT, RO, SM, SK, SI, ES, SE, CH, TR, GB
97
+ * [BridgePay](http://www.bridgepaynetwork.com/) - CA, US
97
98
  * [CardSave](http://www.cardsave.net/) - GB
98
99
  * [CardStream](http://www.cardstream.com/) - GB
99
100
  * [Cecabank](http://www.ceca.es/es/) - ES
@@ -116,7 +117,7 @@ The [ActiveMerchant Wiki](http://github.com/Shopify/active_merchant/wikis) conta
116
117
  * [FirstGiving](http://www.firstgiving.com/) - US
117
118
  * [Garanti Sanal POS](https://sanalposweb.garanti.com.tr) - US, TR
118
119
  * [HDFC](http://www.hdfcbank.com/sme/sme-details/merchant-services/guzh6m0i) - IN
119
- * [IATSPayments](http://www.iatspayments.com/) - US, CA, GB
120
+ * [iATS Payments](http://home.iatspayments.com/) - AU, CA, CH, DE, DK, ES, FI, FR, GR, HK, IE, IT, JP, NL, NO, NZ, PT, SE, SG, TR, UK, US
120
121
  * [Inspire Commerce](http://www.inspiregateway.com) - US
121
122
  * [InstaPay](http://www.instapayllc.com) - US
122
123
  * [Iridium](http://www.iridiumcorp.co.uk/) - GB, ES
@@ -0,0 +1,182 @@
1
+ require "nokogiri"
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class BridgePayGateway < Gateway
6
+ self.display_name = "BridgePay"
7
+ self.homepage_url = "http://www.bridgepaynetwork.com/"
8
+
9
+ self.test_url = "https://gatewaystage.itstgate.com/SmartPayments/transact.asmx/ProcessCreditCard"
10
+ self.live_url = "https://gateway.itstgate.com/SmartPayments/transact.asmx/ProcessCreditCard"
11
+
12
+ self.supported_countries = ["CA", "US"]
13
+ self.default_currency = "USD"
14
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
15
+
16
+
17
+ def initialize(options={})
18
+ requires!(options, :user_name, :password)
19
+ super
20
+ end
21
+
22
+ def purchase(amount, creditcard, options={})
23
+ post = post_required_fields("Sale")
24
+
25
+ # Allow the same amount in multiple transactions.
26
+ post[:ExtData] = "<Force>T</Force>"
27
+ add_invoice(post, amount, options)
28
+ add_creditcard(post, creditcard)
29
+ add_customer_data(post, options)
30
+
31
+ commit(post)
32
+ end
33
+
34
+ def authorize(amount, creditcard, options={})
35
+ post = post_required_fields("Auth")
36
+
37
+ add_invoice(post, amount, options)
38
+ add_creditcard(post, creditcard)
39
+ add_customer_data(post, options)
40
+
41
+ commit(post)
42
+ end
43
+
44
+ def capture(amount, authorization, options={})
45
+ post = post_required_fields("Force")
46
+
47
+ add_invoice(post, amount, options)
48
+ add_reference(post, authorization)
49
+ add_customer_data(post, options)
50
+
51
+ commit(post)
52
+ end
53
+
54
+ def refund(amount, authorization, options={})
55
+ post = post_required_fields("Return")
56
+
57
+ add_invoice(post, amount, options)
58
+ add_reference(post, authorization)
59
+
60
+ commit(post)
61
+ end
62
+
63
+ def void(authorization, options={})
64
+ post = post_required_fields("Void")
65
+
66
+ add_reference(post, authorization)
67
+
68
+ commit(post)
69
+ end
70
+
71
+ private
72
+
73
+ def post_required_fields(transaction_type)
74
+ post = {}
75
+ post[:TransType] = transaction_type
76
+ post[:Amount] = ""
77
+ post[:PNRef] = ""
78
+ post[:InvNum] = ""
79
+ post[:CardNum] = ""
80
+ post[:ExpDate] = ""
81
+ post[:MagData] = ""
82
+ post[:NameOnCard] = ""
83
+ post[:Zip] = ""
84
+ post[:Street] = ""
85
+ post[:CVNum] = ""
86
+ post[:MagData] = ""
87
+ post[:ExtData] = ""
88
+ post
89
+ end
90
+
91
+ def add_customer_data(post, options)
92
+ if(billing_address = (options[:billing_address] || options[:address]))
93
+ post[:Street] = billing_address[:address1]
94
+ post[:Zip] = billing_address[:zip]
95
+ end
96
+ end
97
+
98
+ def add_invoice(post, amount, options)
99
+ post[:Amount] = amount(amount)
100
+ post[:InvNum] = options[:order_id]
101
+ end
102
+
103
+ def add_creditcard(post, creditcard)
104
+ post[:NameOnCard] = creditcard.name if creditcard.name
105
+ post[:ExpDate] = expdate(creditcard)
106
+ post[:CardNum] = creditcard.number
107
+ post[:CVNum] = creditcard.verification_value
108
+ end
109
+
110
+ def expdate(creditcard)
111
+ "#{format(creditcard.month, :two_digits)}#{format(creditcard.year, :two_digits)}"
112
+ end
113
+
114
+ def parse(xml)
115
+ response = {}
116
+
117
+ doc = Nokogiri::XML(xml)
118
+ doc.root.xpath("*").each do |node|
119
+ if (node.elements.size == 0)
120
+ response[node.name.downcase.to_sym] = node.text
121
+ else
122
+ node.elements.each do |childnode|
123
+ name = "#{node.name.downcase}_#{childnode.name.downcase}"
124
+ response[name.to_sym] = childnode.text
125
+ end
126
+ end
127
+ end unless doc.root.nil?
128
+
129
+ response
130
+ end
131
+
132
+ def commit(parameters)
133
+ url = (test? ? test_url : live_url)
134
+ data = post_data(parameters)
135
+ raw = parse(ssl_post(url, data))
136
+
137
+ Response.new(
138
+ success_from(raw[:respmsg]),
139
+ message_from(raw),
140
+ raw,
141
+ authorization: authorization_from(raw),
142
+ test: test?
143
+ )
144
+ end
145
+
146
+ def success_from(result)
147
+ case result
148
+ when "Approved"
149
+ true
150
+ else
151
+ false
152
+ end
153
+ end
154
+
155
+ def message_from(response)
156
+ response[:respmsg]
157
+ end
158
+
159
+ def authorization_from(response)
160
+ [response[:authcode], response[:pnref]].join("|")
161
+ end
162
+
163
+ def split_authorization(authorization)
164
+ authcode, pnref = authorization.split("|")
165
+ [authcode, pnref]
166
+ end
167
+
168
+ def add_reference(post, authorization)
169
+ authcode, pnref = split_authorization(authorization)
170
+ post[:AuthCode] = authcode
171
+ post[:PNRef] = pnref
172
+ end
173
+
174
+ def post_data(post)
175
+ {
176
+ :UserName => @options[:user_name],
177
+ :Password => @options[:password]
178
+ }.merge(post).collect{|k,v| "#{k}=#{CGI.escape(v.to_s)}"}.join("&")
179
+ end
180
+ end
181
+ end
182
+ end
@@ -182,8 +182,7 @@ module ActiveMerchant
182
182
  # -Builder xml document
183
183
  #
184
184
  def build_void_or_capture_request(type, money, authorization, options)
185
- reference, auth_code, ca_reference = authorization.to_s.split(';')
186
-
185
+ parsed_authorization = parse_authorization_string(authorization)
187
186
  xml = Builder::XmlMarkup.new :indent => 2
188
187
  xml.instruct!
189
188
  xml.tag! :Request do
@@ -191,8 +190,8 @@ module ActiveMerchant
191
190
 
192
191
  xml.tag! :Transaction do
193
192
  xml.tag! :HistoricTxn do
194
- xml.tag! :reference, reference
195
- xml.tag! :authcode, auth_code
193
+ xml.tag! :reference, parsed_authorization[:reference]
194
+ xml.tag! :authcode, parsed_authorization[:auth_code]
196
195
  xml.tag! :method, type
197
196
  end
198
197
 
@@ -328,8 +327,8 @@ module ActiveMerchant
328
327
  # -xml: Builder document containing the markup
329
328
  #
330
329
  def build_purchase_or_authorization_request_with_continuous_authority_reference_request(type, money, authorization, options)
331
- reference, auth_code, ca_reference = authorization.to_s.split(';')
332
- raise ArgumentError, "The continuous authority reference is required for continuous authority transactions" if ca_reference.blank?
330
+ parsed_authorization = parse_authorization_string(authorization)
331
+ raise ArgumentError, "The continuous authority reference is required for continuous authority transactions" if parsed_authorization[:ca_reference].blank?
333
332
 
334
333
  xml = Builder::XmlMarkup.new :indent => 2
335
334
  xml.instruct!
@@ -338,7 +337,7 @@ module ActiveMerchant
338
337
  xml.tag! :Transaction do
339
338
  xml.tag! :ContAuthTxn, :type => 'historic'
340
339
  xml.tag! :HistoricTxn do
341
- xml.tag! :reference, ca_reference
340
+ xml.tag! :reference, parsed_authorization[:ca_reference]
342
341
  xml.tag! :method, type
343
342
  end
344
343
  xml.tag! :TxnDetails do
@@ -371,14 +370,15 @@ module ActiveMerchant
371
370
  # </Transaction>
372
371
  # </Request>
373
372
  #
374
- def build_transaction_refund_request(money, reference)
373
+ def build_transaction_refund_request(money, authorization)
374
+ parsed_authorization = parse_authorization_string(authorization)
375
375
  xml = Builder::XmlMarkup.new :indent => 2
376
376
  xml.instruct!
377
377
  xml.tag! :Request do
378
378
  add_authentication(xml)
379
379
  xml.tag! :Transaction do
380
380
  xml.tag! :HistoricTxn do
381
- xml.tag! :reference, reference
381
+ xml.tag! :reference, parsed_authorization[:reference]
382
382
  xml.tag! :method, TRANSACTION_REFUND_TYPE
383
383
  end
384
384
  unless money.nil?
@@ -588,6 +588,11 @@ module ActiveMerchant
588
588
  def format_reference_number(number)
589
589
  number.to_s.gsub(/[^A-Za-z0-9]/, '').rjust(6, "0").first(30)
590
590
  end
591
+
592
+ def parse_authorization_string(authorization)
593
+ reference, auth_code, ca_reference = authorization.to_s.split(';')
594
+ {:reference => reference, :auth_code => auth_code, :ca_reference => ca_reference}
595
+ end
591
596
  end
592
597
  end
593
598
  end
@@ -1,34 +1,217 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
- class IatsPaymentsGateway < AuthorizeNetGateway
4
- self.live_url = self.test_url = 'https://www.iatspayments.com/netgate/AEGateway.aspx'
3
+ class IatsPaymentsGateway < Gateway
4
+ class_attribute :live_na_url, :live_uk_url
5
5
 
6
- self.homepage_url = 'http://www.iatspayments.com/'
7
- self.display_name = 'IATSPayments'
6
+ self.live_na_url = 'https://www.iatspayments.com/NetGate/ProcessLink.asmx'
7
+ self.live_uk_url = 'https://www.uk.iatspayments.com/NetGate/ProcessLink.asmx'
8
8
 
9
- def authorize(money, paysource, options = {})
10
- raise NotImplementedError
9
+ self.supported_countries = %w(AU CA CH DE DK ES FI FR GR HK IE IT JP NL NO NZ PT SE SG TR GB US)
10
+ self.default_currency = 'USD'
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
12
+
13
+ self.homepage_url = 'http://home.iatspayments.com/'
14
+ self.display_name = 'iATS Payments'
15
+
16
+ def initialize(options={})
17
+ if(options[:login])
18
+ deprecated("The 'login' option is deprecated in favor of 'agent_code' and will be removed in a future version.")
19
+ options[:agent_code] = options[:login]
20
+ end
21
+
22
+ options[:region] = 'na' unless options[:region]
23
+
24
+ requires!(options, :agent_code, :password, :region)
25
+ super
26
+ end
27
+
28
+ def purchase(money, payment, options={})
29
+ post = {}
30
+ add_invoice(post, money, options)
31
+ add_payment(post, payment)
32
+ add_address(post, options)
33
+ add_customer_data(post, options)
34
+
35
+ commit('ProcessCreditCardV1', post)
11
36
  end
12
37
 
13
- def capture(money, authorization, options = {})
14
- raise NotImplementedError
38
+ def authorize(money, payment, options={})
39
+ post = {}
40
+ add_invoice(post, money, options)
41
+ add_payment(post, payment)
42
+ add_address(post, options)
43
+ add_customer_data(post, options)
44
+
45
+ commit('authonly', post)
15
46
  end
16
47
 
17
- def void(authorization, options = {})
18
- raise NotImplementedError
48
+ def capture(money, authorization, options={})
49
+ commit('capture', post)
19
50
  end
20
51
 
21
- def refund(money, identification, options = {})
22
- raise NotImplementedError
52
+ def refund(money, authorization, options={})
53
+ post = {}
54
+ post[:transaction_id] = authorization
55
+ add_customer_data(post, options)
56
+ add_invoice(post, -money, options)
57
+
58
+ commit('ProcessCreditCardRefundWithTransactionIdV1', post)
23
59
  end
24
60
 
25
- def credit(money, identification, options = {})
26
- raise NotImplementedError
61
+ def void(authorization, options={})
62
+ commit('void', post)
27
63
  end
28
64
 
29
65
  private
30
- def split(response)
31
- response.split(',')
66
+
67
+ def add_customer_data(post, options)
68
+ post[:customer_ip_address] = options[:ip] if options.has_key?(:ip)
69
+ end
70
+
71
+ def add_address(post, options)
72
+ billing_address = options[:billing_address] || options[:address]
73
+ if(billing_address)
74
+ post[:address] = billing_address[:address1]
75
+ post[:city] = billing_address[:city]
76
+ post[:state] = billing_address[:state]
77
+ post[:zip_code] = billing_address[:zip]
78
+ end
79
+ end
80
+
81
+ def add_invoice(post, money, options)
82
+ post[:invoice_num] = options[:order_id] if options[:order_id]
83
+ post[:total] = amount(money)
84
+ post[:comment] = options[:description] if options[:description]
85
+ end
86
+
87
+ def add_payment(post, payment)
88
+ post[:first_name] = payment.first_name
89
+ post[:last_name] = payment.last_name
90
+ post[:credit_card_num] = payment.number
91
+ post[:credit_card_expiry] = expdate(payment)
92
+ post[:cvv2] = payment.verification_value if payment.verification_value?
93
+ post[:mop] = creditcard_brand(payment.brand)
94
+ end
95
+
96
+ def expdate(creditcard)
97
+ year = sprintf("%.4i", creditcard.year)
98
+ month = sprintf("%.2i", creditcard.month)
99
+
100
+ "#{month}/#{year[-2..-1]}"
101
+ end
102
+
103
+ def creditcard_brand(brand)
104
+ case brand
105
+ when "visa" then "VISA"
106
+ when "master" then "MC"
107
+ when "discover" then "DSC"
108
+ when "american_express" then "AMX"
109
+ when "maestro" then "MAESTR"
110
+ else
111
+ raise "Unhandled credit card brand #{brand}"
112
+ end
113
+ end
114
+
115
+ def commit(action, parameters)
116
+ response = parse(ssl_post(url(action), post_data(action, parameters),
117
+ { 'Content-Type' => 'application/soap+xml; charset=utf-8'}))
118
+
119
+ Response.new(
120
+ success_from(response),
121
+ message_from(response),
122
+ response,
123
+ authorization: authorization_from(response),
124
+ test: test?
125
+ )
126
+ end
127
+
128
+ def url(action)
129
+ base_url = @options[:region] == 'uk' ? live_uk_url : live_na_url
130
+ "#{base_url}?op=#{action}"
131
+ end
132
+
133
+ def parse(body)
134
+ response = {}
135
+ hashify_xml!(body, response)
136
+ response
137
+ end
138
+
139
+ def dexmlize_param_name(name)
140
+ names = {
141
+ 'AUTHORIZATIONRESULT' => :authorization_result,
142
+ 'SETTLEMENTBATCHDATE' => :settlement_batch_date,
143
+ 'SETTLEMENTDATE' => :settlement_date,
144
+ 'TRANSACTIONID' => :transaction_id
145
+ }
146
+ names[name] || name.to_s.downcase.intern
147
+ end
148
+
149
+ def hashify_xml!(xml, response)
150
+ xml = REXML::Document.new(xml)
151
+
152
+ # Purchase, refund
153
+ xml.elements.each("//IATSRESPONSE/*") do |node|
154
+ recursively_parse_element(node, response)
155
+ end
156
+ end
157
+
158
+ # Flatten nested XML structures
159
+ def recursively_parse_element(node, response)
160
+ if(node.has_elements?)
161
+ node.elements.each { |n| recursively_parse_element(n, response) }
162
+ else
163
+ response[dexmlize_param_name(node.name)] = (node.text ? node.text.strip : nil)
164
+ end
165
+ end
166
+
167
+ def successful_result_message?(response)
168
+ response[:authorization_result].start_with?('OK')
169
+ end
170
+
171
+ def success_from(response)
172
+ response[:status] == "Success" && successful_result_message?(response)
173
+ end
174
+
175
+ def message_from(response)
176
+ if(!successful_result_message?(response))
177
+ return response[:authorization_result].strip
178
+ elsif(response[:status] == 'Failure')
179
+ return response[:errors]
180
+ else
181
+ response[:status]
182
+ end
183
+ end
184
+
185
+ def authorization_from(response)
186
+ response[:transaction_id]
187
+ end
188
+
189
+ ENVELOPE_NAMESPACES = {
190
+ "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
191
+ "xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
192
+ "xmlns:soap12" => "http://www.w3.org/2003/05/soap-envelope"
193
+ }
194
+
195
+ def post_data(action, parameters = {})
196
+ xml = Builder::XmlMarkup.new
197
+ xml.instruct!(:xml, :version => '1.0', :encoding => 'utf-8')
198
+ xml.tag! 'soap12:Envelope', ENVELOPE_NAMESPACES do
199
+ xml.tag! 'soap12:Body' do
200
+ xml.tag! action, { "xmlns" => "https://www.iatspayments.com/NetGate/" } do
201
+ xml.tag!('agentCode', @options[:agent_code])
202
+ xml.tag!('password', @options[:password])
203
+ parameters.each do |name, value|
204
+ xml.tag!(xmlize_param_name(name), value)
205
+ end
206
+ end
207
+ end
208
+ end
209
+ xml.target!
210
+ end
211
+
212
+ def xmlize_param_name(name)
213
+ names = { customer_ip_address: 'customerIPAddress' }
214
+ names[name] || name.to_s.camelcase(:lower)
32
215
  end
33
216
  end
34
217
  end