activemerchant 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +18 -0
  3. data/lib/active_merchant.rb +2 -0
  4. data/lib/active_merchant/billing/credit_card.rb +25 -113
  5. data/lib/active_merchant/billing/credit_card_formatting.rb +18 -0
  6. data/lib/active_merchant/billing/credit_card_methods.rb +83 -0
  7. data/lib/active_merchant/billing/gateway.rb +1 -0
  8. data/lib/active_merchant/billing/gateways.rb +2 -1
  9. data/lib/active_merchant/billing/gateways/data_cash.rb +475 -0
  10. data/lib/active_merchant/billing/gateways/linkpoint.rb +9 -24
  11. data/lib/active_merchant/billing/gateways/moneris.rb +1 -1
  12. data/lib/active_merchant/billing/gateways/payflow.rb +14 -0
  13. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -14
  14. data/lib/active_merchant/billing/gateways/paypal.rb +11 -3
  15. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +47 -22
  16. data/lib/active_merchant/billing/gateways/paypal_express.rb +3 -3
  17. data/lib/active_merchant/billing/gateways/psigate.rb +4 -19
  18. data/lib/active_merchant/billing/gateways/usa_epay.rb +5 -6
  19. data/lib/active_merchant/billing/integrations.rb +1 -0
  20. data/lib/active_merchant/billing/integrations/helper.rb +13 -6
  21. data/lib/active_merchant/billing/integrations/notification.rb +2 -1
  22. data/lib/active_merchant/billing/integrations/paypal/helper.rb +2 -0
  23. data/lib/active_merchant/billing/integrations/two_checkout.rb +18 -0
  24. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +59 -0
  25. data/lib/active_merchant/billing/integrations/two_checkout/notification.rb +114 -0
  26. data/lib/active_merchant/lib/posts_data.rb +18 -9
  27. data/lib/active_merchant/lib/validateable.rb +2 -2
  28. data/lib/certs/cacert.pem +7815 -0
  29. metadata +11 -2
  30. metadata.gz.sig +0 -0
@@ -127,6 +127,9 @@ module ActiveMerchant #:nodoc:
127
127
 
128
128
  TEST_URL = 'https://staging.linkpt.net:1129/'
129
129
  LIVE_URL = 'https://secure.linkpt.net:1129/'
130
+
131
+ # We don't have the certificate to verify LinkPoint
132
+ self.ssl_strict = false
130
133
 
131
134
  # @options = {
132
135
  # :store_number => options[:login],
@@ -136,12 +139,11 @@ module ActiveMerchant #:nodoc:
136
139
  requires!(options, :login)
137
140
 
138
141
  @options = {
139
- :result => 'LIVE'
142
+ :result => 'LIVE',
143
+ :pem => LinkpointGateway.pem_file
140
144
  }.update(options)
141
145
 
142
- @pem = @options[:pem] || LinkpointGateway.pem_file
143
-
144
- raise ArgumentError, "You need to pass in your pem file using the :pem parameter or set it globally using ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' ) or similar" if @pem.nil?
146
+ raise ArgumentError, "You need to pass in your pem file using the :pem parameter or set it globally using ActiveMerchant::Billing::LinkpointGateway.pem_file = File.read( File.dirname(__FILE__) + '/../mycert.pem' ) or similar" if @options[:pem].blank?
145
147
  end
146
148
 
147
149
  # Send a purchase request with periodic options
@@ -245,16 +247,16 @@ module ActiveMerchant #:nodoc:
245
247
  end
246
248
 
247
249
  private
248
-
249
250
  # Commit the transaction by posting the XML file to the LinkPoint server
250
251
  def commit(money, creditcard, options = {})
251
252
  parameters = parameters(money, creditcard, options)
253
+ url = test? ? TEST_URL : LIVE_URL
252
254
 
253
255
  if creditcard and result = test_result_from_cc_number(parameters[:creditcard][:cardnumber])
254
256
  return result
255
257
  end
256
258
 
257
- data = ssl_post post_data(parameters)
259
+ data = ssl_post(url, post_data(parameters))
258
260
 
259
261
  @response = parse(data)
260
262
 
@@ -288,7 +290,7 @@ module ActiveMerchant #:nodoc:
288
290
 
289
291
  return xml.to_s
290
292
  end
291
-
293
+
292
294
  # Set up the parameters hash just once so we don't have to do it
293
295
  # for every action.
294
296
  def parameters(money, creditcard, options = {})
@@ -408,23 +410,6 @@ module ActiveMerchant #:nodoc:
408
410
 
409
411
  response
410
412
  end
411
-
412
- # Redefine ssl_post to use our PEM file
413
- def ssl_post(data)
414
-
415
- raise "PEM file invalid or missing!" unless @pem =~ %r{RSA.*CERTIFICATE}m
416
-
417
- uri = URI.parse(test? ? TEST_URL : LIVE_URL)
418
-
419
- http = Net::HTTP.new(uri.host, uri.port)
420
-
421
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @ssl_strict
422
- http.use_ssl = true
423
- http.cert = OpenSSL::X509::Certificate.new(@pem)
424
- http.key = OpenSSL::PKey::RSA.new(@pem)
425
-
426
- http.post(uri.path, data).body
427
- end
428
413
 
429
414
  # Make a ruby type out of the response string
430
415
  def normalize(field)
@@ -59,7 +59,7 @@ module ActiveMerchant #:nodoc:
59
59
 
60
60
  # Moneris requires both the order_id and the transaction number of
61
61
  # the original authorization. To maintain the same interface as the other
62
- # gateways the two numbers are concatenated together with an _ separator as
62
+ # gateways the two numbers are concatenated together with a ; separator as
63
63
  # the authorization number returned by authorization
64
64
  def capture(money, authorization, options = {})
65
65
  txn_number, order_id = authorization.split(';')
@@ -48,6 +48,8 @@ module ActiveMerchant #:nodoc:
48
48
  add_address(xml, 'ShipTo', shipping_address, options)
49
49
 
50
50
  xml.tag! 'TotalAmt', amount(money), 'Currency' => currency(money)
51
+
52
+
51
53
  end
52
54
 
53
55
  xml.tag! 'Tender' do
@@ -65,6 +67,11 @@ module ActiveMerchant #:nodoc:
65
67
  xml.tag! 'ExpDate', expdate(credit_card)
66
68
  xml.tag! 'NameOnCard', credit_card.name
67
69
  xml.tag! 'CVNum', credit_card.verification_value if credit_card.verification_value?
70
+
71
+ if [ 'switch', 'solo' ].include?(credit_card.type.to_s)
72
+ xml.tag!('ExtData', 'Name' => 'CardStart', 'Value' => startdate(credit_card)) unless credit_card.start_month.blank? || credit_card.start_year.blank?
73
+ xml.tag!('ExtData', 'Name' => 'CardIssue', 'Value' => credit_card.issue_number) unless credit_card.issue_number.blank?
74
+ end
68
75
  end
69
76
  end
70
77
 
@@ -75,6 +82,13 @@ module ActiveMerchant #:nodoc:
75
82
  "#{year}#{month}"
76
83
  end
77
84
 
85
+ def startdate(creditcard)
86
+ year = format(creditcard.start_year, :two_digits)
87
+ month = format(creditcard.start_month, :two_digits)
88
+
89
+ "#{month}#{year}"
90
+ end
91
+
78
92
  def build_response(success, message, response, options = {})
79
93
  Response.new(success, message, response, options)
80
94
  end
@@ -111,20 +111,7 @@ module ActiveMerchant #:nodoc:
111
111
  end
112
112
  end
113
113
  end
114
-
115
- def ssl_post(url, data, headers)
116
- uri = URI.parse(url)
117
-
118
- http = Net::HTTP.new(uri.host, uri.port)
119
-
120
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
121
- http.verify_depth = 5
122
- http.use_ssl = true
123
- http.ca_file = File.dirname(__FILE__) + '/f73e89fd.0'
124
-
125
- http.post(uri.path, data, headers).body
126
- end
127
-
114
+
128
115
  def parse(data)
129
116
  response = {}
130
117
  xml = REXML::Document.new(data)
@@ -40,7 +40,7 @@ module ActiveMerchant #:nodoc:
40
40
  xml = Builder::XmlMarkup.new :indent => 2
41
41
  xml.tag! 'DoDirectPaymentReq', 'xmlns' => PAYPAL_NAMESPACE do
42
42
  xml.tag! 'DoDirectPaymentRequest', 'xmlns:n2' => EBAY_NAMESPACE do
43
- xml.tag! 'n2:Version', '2.0'
43
+ xml.tag! 'n2:Version', API_VERSION
44
44
  xml.tag! 'n2:DoDirectPaymentRequestDetails' do
45
45
  xml.tag! 'n2:PaymentAction', action
46
46
  xml.tag! 'n2:PaymentDetails' do
@@ -64,10 +64,16 @@ module ActiveMerchant #:nodoc:
64
64
  xml.tag! 'n2:CreditCard' do
65
65
  xml.tag! 'n2:CreditCardType', credit_card_type(credit_card.type)
66
66
  xml.tag! 'n2:CreditCardNumber', credit_card.number
67
- xml.tag! 'n2:ExpMonth', sprintf("%.2i", credit_card.month)
68
- xml.tag! 'n2:ExpYear', sprintf("%.4i", credit_card.year)
67
+ xml.tag! 'n2:ExpMonth', format(credit_card.month, :two_digits)
68
+ xml.tag! 'n2:ExpYear', format(credit_card.year, :four_digits)
69
69
  xml.tag! 'n2:CVV2', credit_card.verification_value
70
70
 
71
+ if [ 'switch', 'solo' ].include?(credit_card.type.to_s)
72
+ xml.tag! 'n2:StartMonth', format(credit_card.start_month, :two_digits) unless credit_card.start_month.blank?
73
+ xml.tag! 'n2:StartYear', format(credit_card.start_year, :four_digits) unless credit_card.start_year.blank?
74
+ xml.tag! 'n2:IssueNumber', format(credit_card.issue_number, :two_digits) unless credit_card.issue_number.blank?
75
+ end
76
+
71
77
  xml.tag! 'n2:CardOwner' do
72
78
  xml.tag! 'n2:PayerName' do
73
79
  xml.tag! 'n2:FirstName', credit_card.first_name
@@ -86,6 +92,8 @@ module ActiveMerchant #:nodoc:
86
92
  when 'master' then 'MasterCard'
87
93
  when 'discover' then 'Discover'
88
94
  when 'american_express' then 'Amex'
95
+ when 'switch' then 'Switch'
96
+ when 'solo' then 'Solo'
89
97
  end
90
98
  end
91
99
 
@@ -7,6 +7,7 @@ module ActiveMerchant #:nodoc:
7
7
  base.cattr_accessor :pem_file
8
8
  end
9
9
 
10
+ API_VERSION = '2.0'
10
11
  TEST_URL = 'https://api.sandbox.paypal.com/2.0/'
11
12
  LIVE_URL = 'https://api-aa.paypal.com/2.0/'
12
13
 
@@ -21,6 +22,17 @@ module ActiveMerchant #:nodoc:
21
22
  'xmlns:n1' => EBAY_NAMESPACE,
22
23
  'env:mustUnderstand' => '0'
23
24
  }
25
+
26
+ AUSTRALIAN_STATES = {
27
+ 'ACT' => 'Australian Capital Territory',
28
+ 'NSW' => 'New South Wales',
29
+ 'NT' => 'Northern Territory',
30
+ 'QLD' => 'Queensland',
31
+ 'SA' => 'South Australia',
32
+ 'TAS' => 'Tasmania',
33
+ 'VIC' => 'Victoria',
34
+ 'WA' => 'Western Australia'
35
+ }
24
36
 
25
37
  # <tt>:pem</tt> The text of your PayPal PEM file. Note
26
38
  # this is not the path to file, but its
@@ -42,6 +54,10 @@ module ActiveMerchant #:nodoc:
42
54
  @options[:test] || Base.gateway_mode == :test
43
55
  end
44
56
 
57
+ def reauthorize(money, authorization, options = {})
58
+ commit 'DoReauthorization', build_reauthorize_request(money, authorization, options)
59
+ end
60
+
45
61
  def capture(money, authorization, options = {})
46
62
  commit 'DoCapture', build_capture_request(money, authorization, options)
47
63
  end
@@ -69,13 +85,26 @@ module ActiveMerchant #:nodoc:
69
85
  end
70
86
 
71
87
  private
88
+ def build_reauthorize_request(money, authorization, options)
89
+ xml = Builder::XmlMarkup.new :indent => 2
90
+
91
+ xml.tag! 'DoReauthorizationReq', 'xmlns' => PAYPAL_NAMESPACE do
92
+ xml.tag! 'DoReauthorizationRequest', 'xmlns:n2' => EBAY_NAMESPACE do
93
+ xml.tag! 'n2:Version', API_VERSION
94
+ xml.tag! 'AuthorizationID', authorization
95
+ xml.tag! 'Amount', amount(money), 'currencyID' => currency(money)
96
+ end
97
+ end
98
+
99
+ xml.target!
100
+ end
72
101
 
73
102
  def build_capture_request(money, authorization, options)
74
103
  xml = Builder::XmlMarkup.new :indent => 2
75
104
 
76
105
  xml.tag! 'DoCaptureReq', 'xmlns' => PAYPAL_NAMESPACE do
77
106
  xml.tag! 'DoCaptureRequest', 'xmlns:n2' => EBAY_NAMESPACE do
78
- xml.tag! 'n2:Version', '2.0'
107
+ xml.tag! 'n2:Version', API_VERSION
79
108
  xml.tag! 'AuthorizationID', authorization
80
109
  xml.tag! 'Amount', amount(money), 'currencyID' => currency(money)
81
110
  xml.tag! 'CompleteType', 'Complete'
@@ -91,7 +120,7 @@ module ActiveMerchant #:nodoc:
91
120
 
92
121
  xml.tag! 'RefundTransactionReq', 'xmlns' => PAYPAL_NAMESPACE do
93
122
  xml.tag! 'RefundTransactionRequest', 'xmlns:n2' => EBAY_NAMESPACE do
94
- xml.tag! 'n2:Version', '2.0'
123
+ xml.tag! 'n2:Version', API_VERSION
95
124
  xml.tag! 'TransactionID', identification
96
125
  xml.tag! 'Amount', amount(money), 'currencyID' => currency(money)
97
126
  xml.tag! 'RefundType', 'Partial'
@@ -107,7 +136,7 @@ module ActiveMerchant #:nodoc:
107
136
 
108
137
  xml.tag! 'DoVoidReq', 'xmlns' => PAYPAL_NAMESPACE do
109
138
  xml.tag! 'DoVoidRequest', 'xmlns:n2' => EBAY_NAMESPACE do
110
- xml.tag! 'n2:Version', '2.0'
139
+ xml.tag! 'n2:Version', API_VERSION
111
140
  xml.tag! 'AuthorizationID', authorization
112
141
  xml.tag! 'Note', options[:description]
113
142
  end
@@ -124,7 +153,7 @@ module ActiveMerchant #:nodoc:
124
153
 
125
154
  xml.tag! 'MassPayReq', 'xmlns' => PAYPAL_NAMESPACE do
126
155
  xml.tag! 'MassPayRequest', 'xmlns:n2' => EBAY_NAMESPACE do
127
- xml.tag! 'n2:Version', '2.0'
156
+ xml.tag! 'n2:Version', API_VERSION
128
157
  xml.tag! 'EmailSubject', default_options[:subject] if default_options[:subject]
129
158
  recipients.each do |money, recipient, options|
130
159
  options ||= default_options
@@ -132,6 +161,7 @@ module ActiveMerchant #:nodoc:
132
161
  xml.tag! 'ReceiverEmail', recipient
133
162
  xml.tag! 'Amount', amount(money), 'currencyID' => currency(money)
134
163
  xml.tag! 'Note', options[:note] if options[:note]
164
+ xml.tag! 'UniqueId', options[:unique_id] if options[:unique_id]
135
165
  end
136
166
  end
137
167
  end
@@ -139,21 +169,7 @@ module ActiveMerchant #:nodoc:
139
169
 
140
170
  xml.target!
141
171
  end
142
-
143
- def ssl_post(data)
144
- uri = URI.parse(test? ? TEST_URL : LIVE_URL)
145
-
146
- http = Net::HTTP.new(uri.host, uri.port)
147
-
148
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
149
- http.use_ssl = true
150
- http.cert = OpenSSL::X509::Certificate.new(@options[:pem])
151
- http.key = OpenSSL::PKey::RSA.new(@options[:pem])
152
- http.ca_file = File.dirname(__FILE__) + '/api_cert_chain.crt'
153
-
154
- http.post(uri.path, data).body
155
- end
156
-
172
+
157
173
  def parse(action, xml)
158
174
  response = {}
159
175
  xml = REXML::Document.new(xml)
@@ -229,19 +245,28 @@ module ActiveMerchant #:nodoc:
229
245
  xml.tag! 'n2:Street1', address[:address1]
230
246
  xml.tag! 'n2:Street2', address[:address2]
231
247
  xml.tag! 'n2:CityName', address[:city]
232
- xml.tag! 'n2:StateOrProvince', address[:state]
248
+ xml.tag! 'n2:StateOrProvince', lookup_state(address)
233
249
  xml.tag! 'n2:Country', address[:country]
234
250
  xml.tag! 'n2:PostalCode', address[:zip]
235
251
  xml.tag! 'n2:Phone', address[:phone]
236
252
  end
237
253
  end
254
+
255
+ def lookup_state(address)
256
+ state = if address[:country].to_s.upcase == 'AU' || address[:country] == 'Australia'
257
+ AUSTRALIAN_STATES[address[:state]] || address[:state]
258
+ else
259
+ address[:state]
260
+ end
261
+ end
238
262
 
239
263
  def currency(money)
240
264
  money.respond_to?(:currency) ? money.currency : 'USD'
241
265
  end
242
266
 
243
267
  def commit(action, request)
244
- data = ssl_post build_request(request)
268
+ url = test? ? TEST_URL : LIVE_URL
269
+ data = ssl_post(url, build_request(request))
245
270
 
246
271
  @response = parse(action, data)
247
272
 
@@ -250,7 +275,7 @@ module ActiveMerchant #:nodoc:
250
275
 
251
276
  build_response(success, message, @response,
252
277
  :test => test?,
253
- :authorization => @response[:transaction_id]
278
+ :authorization => @response[:transaction_id] || @response[:authorization_id] # latter one is from reauthorization
254
279
  )
255
280
  end
256
281
  end
@@ -50,7 +50,7 @@ module ActiveMerchant #:nodoc:
50
50
  xml = Builder::XmlMarkup.new :indent => 2
51
51
  xml.tag! 'GetExpressCheckoutDetailsReq', 'xmlns' => PAYPAL_NAMESPACE do
52
52
  xml.tag! 'GetExpressCheckoutDetailsRequest', 'xmlns:n2' => EBAY_NAMESPACE do
53
- xml.tag! 'n2:Version', '2.0'
53
+ xml.tag! 'n2:Version', API_VERSION
54
54
  xml.tag! 'Token', token
55
55
  end
56
56
  end
@@ -62,7 +62,7 @@ module ActiveMerchant #:nodoc:
62
62
  xml = Builder::XmlMarkup.new :indent => 2
63
63
  xml.tag! 'DoExpressCheckoutPaymentReq', 'xmlns' => PAYPAL_NAMESPACE do
64
64
  xml.tag! 'DoExpressCheckoutPaymentRequest', 'xmlns:n2' => EBAY_NAMESPACE do
65
- xml.tag! 'n2:Version', '2.0'
65
+ xml.tag! 'n2:Version', API_VERSION
66
66
  xml.tag! 'n2:DoExpressCheckoutPaymentRequestDetails' do
67
67
  xml.tag! 'n2:PaymentAction', action
68
68
  xml.tag! 'n2:Token', options[:token]
@@ -82,7 +82,7 @@ module ActiveMerchant #:nodoc:
82
82
  xml = Builder::XmlMarkup.new :indent => 2
83
83
  xml.tag! 'SetExpressCheckoutReq', 'xmlns' => PAYPAL_NAMESPACE do
84
84
  xml.tag! 'SetExpressCheckoutRequest', 'xmlns:n2' => EBAY_NAMESPACE do
85
- xml.tag! 'n2:Version', '2.0'
85
+ xml.tag! 'n2:Version', API_VERSION
86
86
  xml.tag! 'n2:SetExpressCheckoutRequestDetails' do
87
87
  xml.tag! 'n2:PaymentAction', action
88
88
  xml.tag! 'n2:OrderTotal', amount(money), 'currencyID' => currency(money)
@@ -100,7 +100,9 @@ module ActiveMerchant #:nodoc:
100
100
  return result
101
101
  end
102
102
 
103
- data = ssl_post post_data(parameters)
103
+ url = test? ? TEST_URL : LIVE_URL
104
+
105
+ data = ssl_post(url, post_data(parameters))
104
106
  @response = parse(data)
105
107
  success = (@response[:approved] == "APPROVED")
106
108
  message = message_form(@response)
@@ -159,12 +161,9 @@ module ActiveMerchant #:nodoc:
159
161
  xml.to_s
160
162
  end
161
163
 
162
-
163
164
  # Set up the parameters hash just once so we don't have to do it
164
165
  # for every action.
165
- def parameters(money, creditcard, options = {})
166
-
167
-
166
+ def parameters(money, creditcard, options = {})
168
167
  params = {
169
168
  # General order paramters
170
169
  :StoreID => @options[:store_id],
@@ -227,20 +226,6 @@ module ActiveMerchant #:nodoc:
227
226
  return params
228
227
  end
229
228
 
230
- # Redefine ssl_post to use correct url depending on test mode
231
- def ssl_post(data)
232
- # In my case I only want the LIVE_URL to be used when in prodcution mode
233
- # because Psigate charges for all transactions
234
- uri = URI.parse(test? ? TEST_URL : LIVE_URL )
235
- http = Net::HTTP.new(uri.host, uri.port)
236
-
237
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @ssl_strict
238
- http.use_ssl = true
239
-
240
- http.post(uri.path, data).body
241
- end
242
-
243
-
244
229
  def message_form(response)
245
230
  if response[:approved] == "APPROVED"
246
231
  return 'Success'
@@ -3,6 +3,7 @@ module ActiveMerchant #:nodoc:
3
3
 
4
4
  class UsaEpayGateway < Gateway
5
5
  GATEWAY_URL = 'https://www.usaepay.com/gate.php'
6
+ POST_HEADERS = { 'Content-Type' => 'application/x-www-form-urlencoded' }
6
7
 
7
8
  attr_reader :url
8
9
  attr_reader :response
@@ -46,10 +47,10 @@ module ActiveMerchant #:nodoc:
46
47
  private
47
48
 
48
49
  def expdate(creditcard)
49
- year = sprintf("%.4i", creditcard.year)
50
- month = sprintf("%.2i", creditcard.month)
50
+ year = format(creditcard.year, :two_digits)
51
+ month = format(creditcard.month, :two_digits)
51
52
 
52
- "#{year[-2..-1]}#{month}"
53
+ "#{month}#{year}"
53
54
  end
54
55
 
55
56
  def add_customer_data(post, options)
@@ -151,9 +152,7 @@ module ActiveMerchant #:nodoc:
151
152
  return result
152
153
  end
153
154
 
154
- data = ssl_post GATEWAY_URL,
155
- post_data(action, parameters),
156
- { 'Content-Type' => 'application/x-www-form-urlencoded' }
155
+ data = ssl_post(GATEWAY_URL, post_data(action, parameters), POST_HEADERS)
157
156
 
158
157
  @response = parse(data)
159
158
  success = @response[:status] == 'Approved'
@@ -5,6 +5,7 @@ require 'active_merchant/billing/integrations/chronopay'
5
5
  require 'active_merchant/billing/integrations/paypal'
6
6
  require 'active_merchant/billing/integrations/nochex'
7
7
  require 'active_merchant/billing/integrations/gestpay'
8
+ require 'active_merchant/billing/integrations/two_checkout'
8
9
 
9
10
  # make the bogus gateway be classified correctly by the inflector
10
11
  Inflector.inflections do |inflect|