adyen 0.3.0 → 0.3.2
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.
- data/adyen.gemspec +2 -2
- data/lib/adyen/form.rb +1 -1
- data/lib/adyen/notification.rb +1 -1
- data/lib/adyen/soap.rb +252 -65
- metadata +2 -2
data/adyen.gemspec
CHANGED
data/lib/adyen/form.rb
CHANGED
@@ -6,7 +6,7 @@ module Adyen
|
|
6
6
|
# to the Adyen payment system, using either a HTML form (see {Adyen::Form.hidden_fields})
|
7
7
|
# or a HTTP redirect (see {Adyen::Form.redirect_url}).
|
8
8
|
#
|
9
|
-
# Moreover, this module contains
|
9
|
+
# Moreover, this module contains the method {Adyen::Form.redirect_signature_check} to
|
10
10
|
# check the request that is made to your website after the visitor has made his payment
|
11
11
|
# on the Adyen system for genuinity.
|
12
12
|
#
|
data/lib/adyen/notification.rb
CHANGED
data/lib/adyen/soap.rb
CHANGED
@@ -1,73 +1,98 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require "handsoap"
|
3
|
+
rescue LoadError
|
4
|
+
$stderr.puts "The handsoap gem (>= 1.4.1) is required to use the SOAP clients:"
|
5
|
+
$stderr.puts "$ (sudo) gem install handsoap --source http://gemcutter.org"
|
6
|
+
end
|
2
7
|
|
3
8
|
module Adyen
|
4
9
|
|
5
|
-
# The SOAP module contains classes that interact with the Adyen
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# Adyen::SOAP::Base class.
|
10
|
+
# The SOAP module contains classes that interact with the Adyen SOAP
|
11
|
+
# services. The clients are based on the +handsoap+ library and requires at
|
12
|
+
# least version 1.4.1 of this gem.
|
9
13
|
#
|
10
|
-
# Note that you'll need an Adyen notification PSP reference for
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# be sent later on.
|
14
|
+
# Note that you'll need an Adyen notification PSP reference for most SOAP
|
15
|
+
# calls. Because of this, store all notifications that Adyen sends to you.
|
16
|
+
# (e.g. using the {Adyen::Notification} ActiveRecord class). Moreover, most
|
17
|
+
# SOAP calls do not respond that they were successful immediately, but a
|
18
|
+
# notifications to indicate that will be sent later on.
|
16
19
|
#
|
17
|
-
# You'll need to provide a username and password to interact
|
18
|
-
#
|
20
|
+
# You'll need to provide a username and password to interact with the Adyen
|
21
|
+
# SOAP services:
|
19
22
|
#
|
20
23
|
# Adyen::SOAP.username = 'ws@Company.MyAccount'
|
21
24
|
# Adyen::SOAP.password = 'very$ecret'
|
22
25
|
#
|
23
|
-
# You can setup default
|
26
|
+
# You can setup default parameters that will be used by every SOAP call by
|
27
|
+
# using {Adyen::SOAP.default_arguments}. You can override these default
|
28
|
+
# values by passing another value as parameter to the actual call.
|
29
|
+
#
|
30
|
+
# Adyen::SOAP.default_arguments[:merchant_account] = 'MyMerchant'
|
24
31
|
#
|
25
|
-
#
|
32
|
+
# All SOAP clients are based on the {Adyen::SOAP::Base} class, which sets up
|
33
|
+
# the Handsoap library to work with the Adyen SOAP services and implements
|
34
|
+
# shared functionality. Based on this class, the following services are available:
|
26
35
|
#
|
27
|
-
#
|
28
|
-
#
|
36
|
+
# * {Adyen::SOAP::RecurringService} - SOAP service for handling recurring payments.
|
37
|
+
# * {Adyen::SOAP::PaymentService} - SOAP service for modification to payments. Currently,
|
38
|
+
# this class is just a stub. Feel free to implement it as you need it.
|
29
39
|
module SOAP
|
30
40
|
|
31
41
|
class << self
|
32
|
-
|
33
|
-
# for
|
34
|
-
|
42
|
+
|
43
|
+
# Username for the HTTP Basic Authentication that Adyen uses. Your username
|
44
|
+
# should be something like +ws@Company.MyAccount+
|
45
|
+
# @return [String]
|
46
|
+
attr_accessor :username
|
47
|
+
|
48
|
+
# Password for the HTTP Basic Authentication that Adyen uses. You can choose
|
49
|
+
# your password yourself in the user management tool of the merchant area.
|
50
|
+
# @return [String]
|
51
|
+
attr_accessor :password
|
52
|
+
|
53
|
+
# Default arguments that will be used for every SOAP call.
|
54
|
+
# @return [Hash]
|
55
|
+
attr_accessor :default_arguments
|
35
56
|
end
|
36
57
|
|
37
|
-
|
38
|
-
self.default_arguments = {}
|
58
|
+
self.default_arguments = {} # Set default value
|
39
59
|
|
40
|
-
# The base class sets up XML namespaces and HTTP
|
41
|
-
# for all the Adyen SOAP services
|
60
|
+
# The base class sets up XML namespaces and the HTTP client
|
61
|
+
# for all the Adyen SOAP services.
|
42
62
|
class Base < Handsoap::Service
|
43
63
|
|
44
|
-
|
45
|
-
|
46
|
-
|
64
|
+
# Basic setup for the SOAP endpoint when creating a subclass.
|
65
|
+
#
|
66
|
+
# The version must be set to construct the request envelopes, the URI
|
67
|
+
# wil be set later using the correct {Adyen.environment} value. For now,
|
68
|
+
# use a bogus value so handsoap will not complain.
|
69
|
+
def self.inherited(klass) # :nodoc:
|
47
70
|
klass.endpoint :version => 1, :uri => 'bogus'
|
48
71
|
end
|
49
72
|
|
50
73
|
# Setup some CURL options to handle redirects correctly.
|
51
|
-
def on_after_create_http_client(http_client)
|
74
|
+
def on_after_create_http_client(http_client) # :nodoc:
|
52
75
|
http_client.follow_location = true
|
53
76
|
http_client.max_redirects = 1
|
54
77
|
end
|
55
78
|
|
56
|
-
# Setup basic authentication
|
57
|
-
|
79
|
+
# Setup basic authentication for SOAP requests
|
80
|
+
# @see Adyen::SOAP.username
|
81
|
+
# @see Adyen::SOAP.password
|
82
|
+
def on_after_create_http_request(http_request) # :nodoc:
|
58
83
|
debug { |logger| logger.puts "Authorization: #{Adyen::SOAP.username}:#{Adyen::SOAP.password}..." }
|
59
84
|
http_request.set_auth Adyen::SOAP.username, Adyen::SOAP.password
|
60
85
|
end
|
61
86
|
|
62
|
-
#
|
63
|
-
def on_create_document(doc)
|
87
|
+
# Sets up XML namespaces for composing the SOAP request body.
|
88
|
+
def on_create_document(doc) # :nodoc:
|
64
89
|
doc.alias 'payment', 'http://payment.services.adyen.com'
|
65
90
|
doc.alias 'recurring', 'http://recurring.services.adyen.com'
|
66
91
|
doc.alias 'common', 'http://common.services.adyen.com'
|
67
92
|
end
|
68
93
|
|
69
|
-
#
|
70
|
-
def on_response_document(doc)
|
94
|
+
# Sets up the XML namespaces for parsing the SOAP response.
|
95
|
+
def on_response_document(doc) # :nodoc:
|
71
96
|
doc.add_namespace 'payment', 'http://payment.services.adyen.com'
|
72
97
|
doc.add_namespace 'recurring', 'http://recurring.services.adyen.com'
|
73
98
|
doc.add_namespace 'common', 'http://common.services.adyen.com'
|
@@ -80,36 +105,155 @@ module Adyen
|
|
80
105
|
end
|
81
106
|
end
|
82
107
|
|
83
|
-
# SOAP client to interact with the payment modification service of Adyen.
|
84
|
-
#
|
108
|
+
# SOAP client to interact with the payment modification service of Adyen. This client
|
109
|
+
# implements the following calls:
|
110
|
+
#
|
111
|
+
# * +authorise+ to list recurring contracts for a shopper, using {Adyen::SOAP::PaymentService#authorise}.
|
112
|
+
# * +cancelOrRefund+ to cancel a payment (or refund if it has been captured), using
|
113
|
+
# {Adyen::SOAP::PaymentService#cancel_or_refund}.
|
114
|
+
#
|
115
|
+
# Before using this service, make sure to set the SOAP username and
|
116
|
+
# password (see {Adyen::SOAP.username} and {Adyen::SOAP.password}).
|
85
117
|
class PaymentService < Base
|
86
118
|
|
87
119
|
ENDPOINT_URI = 'https://pal-%s.adyen.com/pal/servlet/soap/Payment'
|
88
120
|
|
121
|
+
# Submits a recurring payment for authorisation.
|
122
|
+
#
|
123
|
+
# @example
|
124
|
+
# Adyen::SOAP::PaymentService.authorise(
|
125
|
+
# :merchant_account => 'MyAccount', :selected_recurring_detail_reference => 'LATEST',
|
126
|
+
# :shopper_reference => user.id, :shopper_email => user.email,
|
127
|
+
# :reference => invoice.id, :currency => invoice.currency, :value => invoice.amount)
|
128
|
+
#
|
129
|
+
# @param [Hash] args The paramaters to use for this call. These will be merged by any default
|
130
|
+
# parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
|
131
|
+
# is required by the Adyen SOAP service, so please provide a value for all options.
|
132
|
+
# @option args [String] :selected_recurring_detail_reference ('LATEST') This is the
|
133
|
+
# recurringDetailReference you want to use for this payment. You can use the
|
134
|
+
# value "LATEST" to select the most recently used recurring detail, which is the default.
|
135
|
+
# @option args [String] :merchant_account The merchant account you want to process this payment
|
136
|
+
# with.
|
137
|
+
# @option args [String] :currency The currency code (EUR, GBP, USD, etc).
|
138
|
+
# @option args [Integer] :value The value of the payment in cents.
|
139
|
+
# @option args [String] :reference Your reference for this payment. This (merchant) reference
|
140
|
+
# will be used in all communication to you about the status of the payment.
|
141
|
+
# Although it is a good idea to make sure it is unique, this is not a requirement.
|
142
|
+
# @option args [String] :shopper_email The email address of the shopper. This does not have to
|
143
|
+
# match the email address supplied with the initial payment, since it may have
|
144
|
+
# changed in the mean time.
|
145
|
+
# @option args [String] :shopper_reference The reference of the shopper. This should be
|
146
|
+
# the same as the reference that was used to create the recurring contract.
|
147
|
+
#
|
148
|
+
# @return [nil] This action returns nothing of interest. The result of the authorization
|
149
|
+
# will be communicated using a {Adyen::Notification notification}.
|
150
|
+
#
|
151
|
+
# @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=1
|
152
|
+
# The Adyen integration manual
|
153
|
+
# @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=7&nav=0,3
|
154
|
+
# The Adyen recurring payments manual.
|
155
|
+
def authorise(args = {})
|
156
|
+
invoke_args = Adyen::SOAP.default_arguments.merge(args)
|
157
|
+
invoke_args[:selected_recurring_detail_reference] ||= 'LATEST'
|
158
|
+
|
159
|
+
response = invoke('payment:authorise') do |message|
|
160
|
+
message.add('payment:paymentRequest') do |req|
|
161
|
+
req.add('payment:selectedRecurringDetailReference', invoke_args[:selected_recurring_detail_reference])
|
162
|
+
req.add('payment:recurring') do |recurring|
|
163
|
+
recurring.add('payment:contract', 'RECURRING')
|
164
|
+
end
|
165
|
+
req.add('payment:merchantAccount', invoke_args[:merchant_account])
|
166
|
+
req.add('payment:amount') do |amount|
|
167
|
+
amount.add('common:currency', invoke_args[:currency])
|
168
|
+
amount.add('common:value', invoke_args[:value])
|
169
|
+
end
|
170
|
+
req.add('payment:reference', invoke_args[:reference])
|
171
|
+
req.add('payment:shopperEmail', invoke_args[:shopper_email])
|
172
|
+
req.add('payment:shopperReference', invoke_args[:shopper_reference])
|
173
|
+
req.add('payment:shopperInteraction', 'ContAuth')
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Cancel or refund a payment.
|
179
|
+
#
|
180
|
+
# @param [Hash] args The paramaters to use for this call. These will be marged by any default
|
181
|
+
# parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
|
182
|
+
# is required by the Adyen SOAP service, so please provide a value for all options.
|
183
|
+
# @option args [String] :merchant_account The merchant account to file this payment under.
|
184
|
+
# @option args [String] :original_reference The psp_reference of the payment to cancel.
|
185
|
+
#
|
186
|
+
# @return [nil] This action returns nothing of interest. The result of the authorization
|
187
|
+
# will be communicated using a {Adyen::Notification notification}.
|
188
|
+
#
|
189
|
+
# @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=1
|
190
|
+
# The Adyen integration manual
|
191
|
+
def cancel_or_refund(args = {})
|
192
|
+
invoke_args = Adyen::SOAP.default_arguments.merge(args)
|
193
|
+
response = invoke('payment:cancelOrRefund') do |message|
|
194
|
+
message.add('payment:modificationRequest') do |req|
|
195
|
+
req.add('payment:merchantAccount', invoke_args[:merchant_account])
|
196
|
+
req.add('payment:originalReference', invoke_args[:original_reference])
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
89
200
|
end
|
90
201
|
|
91
|
-
# SOAP client to interact with the recurring payment service of Adyen.
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
202
|
+
# SOAP client to interact with the recurring payment service of Adyen. This clients
|
203
|
+
# implements the following calls:
|
204
|
+
#
|
205
|
+
# * +listRecurring+ to list recurring contracts for a shopper, using {Adyen::SOAP::RecurringService#list}.
|
206
|
+
# * +submitRecurring+ to submit a recurring payment for a shopper, using {Adyen::SOAP::RecurringService#submit}.
|
207
|
+
# * +deactivateRecurring+ to cancel a recurring contract, using {Adyen::SOAP::RecurringService#deactivate}.
|
95
208
|
#
|
96
|
-
#
|
209
|
+
# Before using this service, make sure to set the SOAP username and
|
210
|
+
# password (see {Adyen::SOAP.username} and {Adyen::SOAP.password}).
|
211
|
+
#
|
212
|
+
# The recurring service requires shoppers to have a recurring contract.
|
213
|
+
# Such a contract can be set up when creating the initial payment using
|
214
|
+
# the {Adyen::Form} methods. After the payment has been authorized, a
|
215
|
+
# {Adyen::Notification RECURRING_CONTRACT notification} will be sent. The
|
216
|
+
# PSP reference of this notification should be used as
|
217
|
+
# +:recurring_reference+ parameters in these calls.
|
218
|
+
#
|
219
|
+
# @see https://support.adyen.com/index.php?_m=downloads&_a=viewdownload&downloaditemid=7&nav=0,3
|
220
|
+
# The Adyen recurring payments manual.
|
97
221
|
class RecurringService < Base
|
98
222
|
|
223
|
+
# The endpoint URI for this SOAP service, in which test or live should be filled in as
|
224
|
+
# environment.
|
225
|
+
# @see Adyen.environment
|
99
226
|
ENDPOINT_URI = 'https://pal-%s.adyen.com/pal/servlet/soap/Recurring'
|
100
227
|
|
101
|
-
# Submits a recurring payment
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
228
|
+
# Submits a recurring payment for a recurring contract to Adyen.
|
229
|
+
#
|
230
|
+
# @example
|
231
|
+
# Adyen::SOAP::RecurringService.submit(
|
232
|
+
# :merchant_account => 'MyAccount',
|
233
|
+
# :shopper_reference => user.id, :shopper_email => user.email,
|
234
|
+
# :recurring_reference => user.contract_notification.psp_reference,
|
235
|
+
# :reference => invoice.id, :currency => invoice.currency, :value => invoice.amount)
|
236
|
+
#
|
237
|
+
# @param [Hash] args The paramaters to use for this call. These will be merged by any default
|
238
|
+
# parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
|
239
|
+
# is required by the Adyen SOAP service, so please provide a value for all options.
|
240
|
+
# @option args [String] :merchant_account The merchant account to file this payment under.
|
241
|
+
# @option args [String] :currency The currency code (EUR, GBP, USD, etc).
|
242
|
+
# @option args [Integer] :value The value of the payment in cents.
|
243
|
+
# @option args [Integer] :recurring_reference The psp_reference of the RECURRING_CONTRACT
|
244
|
+
# notification that was sent after the initial payment.
|
245
|
+
# @option args [String] :reference Your reference for this payment. This (merchant) reference
|
246
|
+
# will be used in all communication to you about the status of the payment.
|
247
|
+
# Although it is a good idea to make sure it is unique, this is not a requirement.
|
248
|
+
# @option args [String] :shopper_email The email address of the shopper. This does not have to
|
249
|
+
# match the email address supplied with the initial payment, since it may have
|
250
|
+
# changed in the mean time.
|
251
|
+
# @option args [String] :shopper_reference The reference of the shopper. This should be
|
252
|
+
# the same as the reference that was used to create the recurring contract.
|
253
|
+
#
|
254
|
+
# @return [nil] This method does not return anything. The result of the payment request will
|
255
|
+
# be communicated with an {Adyen::Notification}.
|
256
|
+
# @see Adyen::Notification#collect_payment_for_recurring_contract!
|
113
257
|
def submit(args = {})
|
114
258
|
invoke_args = Adyen::SOAP.default_arguments.merge(args)
|
115
259
|
response = invoke('recurring:submitRecurring') do |message|
|
@@ -127,12 +271,15 @@ module Adyen
|
|
127
271
|
end
|
128
272
|
end
|
129
273
|
|
130
|
-
# Retrieves the recurring contracts for a shopper.
|
274
|
+
# Retrieves the recurring contracts for a shopper.
|
275
|
+
#
|
276
|
+
# @param [Hash] args The paramaters to use for this call. These will be merged by any default
|
277
|
+
# parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
|
278
|
+
# is required by the Adyen SOAP service, so please provide a value for all options.
|
279
|
+
# @option args [String] :merchant_account The merchant account to file this payment under.
|
280
|
+
# @option args [String] :shopper_reference The refrence of the shopper. This should be
|
281
|
+
# the same as the reference that was used to create the recurring contract.
|
131
282
|
#
|
132
|
-
# * <tt>:merchent_account</tt> The merchant account under which to place
|
133
|
-
# this payment.
|
134
|
-
# * <tt>:shopper_reference</tt> The refrence of the shopper. This should be
|
135
|
-
# the same as the reference that was used to create the recurring contract.
|
136
283
|
def list(args = {})
|
137
284
|
invoke_args = Adyen::SOAP.default_arguments.merge(args)
|
138
285
|
response = invoke('recurring:listRecurringDetails') do |message|
|
@@ -143,15 +290,55 @@ module Adyen
|
|
143
290
|
end
|
144
291
|
end
|
145
292
|
|
293
|
+
# Disables a recurring payment contract. Requires the following arguments:
|
294
|
+
#
|
295
|
+
# @example
|
296
|
+
# Adyen::SOAP::RecurringService.disable(
|
297
|
+
# :merchant_account => 'MyAccount', :shopper_reference => user.id,
|
298
|
+
# :recurring_detail_reference => user.contract_notification.psp_reference)
|
299
|
+
#
|
300
|
+
# @param [Hash] args The paramaters to use for this call. These will be merged by any default
|
301
|
+
# parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
|
302
|
+
# is required by the Adyen SOAP service, so please provide a value for all options.
|
303
|
+
# @option args [String] :merchant_account Your merchant account.
|
304
|
+
# @option args [String] :shopper_reference The reference to the shopper. This shopperReference
|
305
|
+
# must be the same as the shopperReference used in the initial payment.
|
306
|
+
# @option args [String] :recurring_detail_reference The recurringDetailReference of the
|
307
|
+
# details you wish to disable. If you do not supply this field, all details for the shopper
|
308
|
+
# will be disabled, including the contract! This means that you can not add new details
|
309
|
+
# anymore.
|
310
|
+
def disable(args = {})
|
311
|
+
invoke_args = Adyen::SOAP.default_arguments.merge(args)
|
312
|
+
response = invoke('recurring:disable') do |message|
|
313
|
+
message.add('recurring:disableRequest') do |req|
|
314
|
+
req.add('recurring:merchantAccount', invoke_args[:merchant_account])
|
315
|
+
req.add('recurring:shopperReference', invoke_args[:shopper_reference])
|
316
|
+
req.add('recurring:recurringDetailReference', invoke_args[:recurring_detail_reference])
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
146
321
|
# Deactivates a recurring payment contract. Requires the following arguments:
|
147
322
|
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
154
|
-
#
|
323
|
+
# @example
|
324
|
+
# Adyen::SOAP::RecurringService.deactivate(
|
325
|
+
# :merchant_account => 'MyAccount', :shopper_reference => user.id,
|
326
|
+
# :recurring_reference => user.contract_notification.psp_reference,
|
327
|
+
# :reference => "Terminated account #{user.account.id}")
|
328
|
+
#
|
329
|
+
# @param [Hash] args The paramaters to use for this call. These will be merged by any default
|
330
|
+
# parameters set using {Adyen::SOAP.default_arguments}. Note that every option defined below
|
331
|
+
# is required by the Adyen SOAP service, so please provide a value for all options.
|
332
|
+
# @option args [String] :merchant_account The merchant account to file this payment under.
|
333
|
+
# @option args [String] :shopper_reference The refrence of the shopper. This should be
|
334
|
+
# the same as the reference that was used to create the recurring contract.
|
335
|
+
# @option args [Integer] :recurring_reference The psp_reference of the RECURRING_CONTRACT
|
336
|
+
# notification that was sent after the initial payment.
|
337
|
+
# @option args [String] :reference The (merchant) reference for this contract deactivation.
|
338
|
+
# Use any string you like that helps you identify this contract deactivation.
|
339
|
+
#
|
340
|
+
# @return [nil] This method does not return anything.
|
341
|
+
# @see Adyen::Notification#deactivate_recurring_contract!
|
155
342
|
def deactivate(args = {})
|
156
343
|
invoke_args = Adyen::SOAP.default_arguments.merge(args)
|
157
344
|
response = invoke('recurring:deactivateRecurring') do |message|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adyen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Willem van Bergen
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-12-
|
13
|
+
date: 2009-12-18 00:00:00 +01:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|