adyen 1.4.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -98,15 +98,16 @@ module Adyen
98
98
 
99
99
  def authorise_one_click_payment_request_body
100
100
  validate_parameters!(:recurring_detail_reference,
101
- :shopper => [:email, :reference],
102
- :card => [:cvc])
103
- content = ONE_CLICK_PAYMENT_BODY_PARTIAL % [@params[:recurring_detail_reference], @params[:card][:cvc]]
101
+ :shopper => [:email, :reference])
102
+ content = one_click_card_partial
103
+ content << ONE_CLICK_PAYMENT_BODY_PARTIAL % [@params[:recurring_detail_reference]]
104
104
  payment_request_body(content)
105
105
  end
106
106
 
107
107
  def payment_request_body(content)
108
108
  validate_parameters!(:merchant_account, :reference, :amount => [:currency, :value])
109
109
  content << amount_partial
110
+ content << installments_partial if @params[:installments]
110
111
  content << shopper_partial if @params[:shopper]
111
112
  content << fraud_offset_partial if @params[:fraud_offset]
112
113
  LAYOUT % [@params[:merchant_account], @params[:reference], content]
@@ -139,8 +140,18 @@ module Adyen
139
140
  AMOUNT_PARTIAL % @params[:amount].values_at(:currency, :value)
140
141
  end
141
142
 
143
+ def one_click_card_partial
144
+ if @params[:card] && @params[:card][:encrypted] && @params[:card][:encrypted][:json]
145
+ ENCRYPTED_CARD_PARTIAL % [@params[:card][:encrypted][:json]]
146
+ else
147
+ validate_parameters!(:card => [:cvc])
148
+ card = @params[:card].values_at(:cvc)
149
+ ONE_CLICK_CARD_PARTIAL % card
150
+ end
151
+ end
152
+
142
153
  def card_partial
143
- if @params[:card] and @params[:card][:encrypted] and @params[:card][:encrypted][:json]
154
+ if @params[:card] && @params[:card][:encrypted] && @params[:card][:encrypted][:json]
144
155
  ENCRYPTED_CARD_PARTIAL % [@params[:card][:encrypted][:json]]
145
156
  else
146
157
  validate_parameters!(:card => [:holder_name, :number, :cvc, :expiry_year, :expiry_month])
@@ -150,15 +161,21 @@ module Adyen
150
161
  end
151
162
  end
152
163
 
164
+ def installments_partial
165
+ if @params[:installments] && @params[:installments][:value]
166
+ INSTALLMENTS_PARTIAL % @params[:installments].values_at(:value)
167
+ end
168
+ end
169
+
153
170
  def shopper_partial
154
171
  @params[:shopper].map { |k, v| SHOPPER_PARTIALS[k] % v }.join("\n")
155
172
  end
156
-
173
+
157
174
  def fraud_offset_partial
158
175
  validate_parameters!(:fraud_offset)
159
176
  FRAUD_OFFSET_PARTIAL % @params[:fraud_offset]
160
177
  end
161
-
178
+
162
179
  class AuthorisationResponse < Response
163
180
  ERRORS = {
164
181
  "validation 101 Invalid card number" => [:number, 'is not a valid creditcard number'],
@@ -171,7 +188,8 @@ module Adyen
171
188
  AUTHORISED = 'Authorised'
172
189
  REFUSED = 'Refused'
173
190
 
174
- response_attrs :result_code, :auth_code, :refusal_reason, :psp_reference
191
+ response_attrs :result_code, :auth_code, :refusal_reason, :psp_reference,
192
+ :additional_data
175
193
 
176
194
  def success?
177
195
  super && params[:result_code] == AUTHORISED
@@ -217,10 +235,28 @@ module Adyen
217
235
  :psp_reference => result.text('./payment:pspReference'),
218
236
  :result_code => result.text('./payment:resultCode'),
219
237
  :auth_code => result.text('./payment:authCode'),
238
+ :additional_data => parse_additional_data(result.xpath('.//payment:additionalData')),
220
239
  :refusal_reason => (invalid_request? ? fault_message : result.text('./payment:refusalReason'))
221
240
  }
222
241
  end
223
242
  end
243
+
244
+ private
245
+ def parse_additional_data(xpath)
246
+ if xpath.empty?
247
+ {}
248
+ else
249
+ results = {}
250
+
251
+ xpath.map do |node|
252
+ key = node.text('./payment:entry/payment:key')
253
+ value = node.text('./payment:entry/payment:value')
254
+ results[key] = value unless key.empty?
255
+ end
256
+
257
+ results
258
+ end
259
+ end
224
260
  end
225
261
 
226
262
  class ModificationResponse < Response
@@ -8,14 +8,14 @@ module Adyen
8
8
  # The base class of the API classes that map to Adyen SOAP services.
9
9
  class SimpleSOAPClient
10
10
  # @private
11
- ENVELOPE = <<EOS
12
- <?xml version="1.0"?>
13
- <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
14
- <soap:Body>
15
- %s
16
- </soap:Body>
17
- </soap:Envelope>
18
- EOS
11
+ ENVELOPE = <<-EOXML.strip.freeze
12
+ <?xml version="1.0"?>
13
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
14
+ <soap:Body>
15
+ %s
16
+ </soap:Body>
17
+ </soap:Envelope>
18
+ EOXML
19
19
 
20
20
  # A CA file used to verify certificates when connecting to Adyen.
21
21
  #
@@ -5,24 +5,24 @@ module Adyen
5
5
  private
6
6
 
7
7
  def modification_request(method, body = nil)
8
- return <<EOS
9
- <payment:#{method} xmlns:payment="http://payment.services.adyen.com" xmlns:recurring="http://recurring.services.adyen.com" xmlns:common="http://common.services.adyen.com">
10
- <payment:modificationRequest>
11
- <payment:merchantAccount>%s</payment:merchantAccount>
12
- <payment:originalReference>%s</payment:originalReference>
13
- #{body}
14
- </payment:modificationRequest>
15
- </payment:#{method}>
16
- EOS
8
+ <<-EOXML
9
+ <payment:#{method} xmlns:payment="http://payment.services.adyen.com" xmlns:recurring="http://recurring.services.adyen.com" xmlns:common="http://common.services.adyen.com">
10
+ <payment:modificationRequest>
11
+ <payment:merchantAccount>%s</payment:merchantAccount>
12
+ <payment:originalReference>%s</payment:originalReference>
13
+ #{body}
14
+ </payment:modificationRequest>
15
+ </payment:#{method}>
16
+ EOXML
17
17
  end
18
18
 
19
19
  def modification_request_with_amount(method)
20
- modification_request(method, <<EOS)
21
- <payment:modificationAmount>
22
- <common:currency>%s</common:currency>
23
- <common:value>%s</common:value>
24
- </payment:modificationAmount>
25
- EOS
20
+ modification_request(method, <<-EOXML)
21
+ <payment:modificationAmount>
22
+ <common:currency>%s</common:currency>
23
+ <common:value>%s</common:value>
24
+ </payment:modificationAmount>
25
+ EOXML
26
26
  end
27
27
  end
28
28
 
@@ -36,26 +36,26 @@ EOS
36
36
  CANCEL_OR_REFUND_LAYOUT = modification_request(:cancelOrRefund)
37
37
 
38
38
  # @private
39
- LAYOUT = <<EOS
40
- <payment:authorise xmlns:payment="http://payment.services.adyen.com" xmlns:recurring="http://recurring.services.adyen.com" xmlns:common="http://common.services.adyen.com">
41
- <payment:paymentRequest>
42
- <payment:merchantAccount>%s</payment:merchantAccount>
43
- <payment:reference>%s</payment:reference>
44
- %s
45
- </payment:paymentRequest>
46
- </payment:authorise>
47
- EOS
39
+ LAYOUT = <<-EOXML
40
+ <payment:authorise xmlns:payment="http://payment.services.adyen.com" xmlns:recurring="http://recurring.services.adyen.com" xmlns:common="http://common.services.adyen.com">
41
+ <payment:paymentRequest>
42
+ <payment:merchantAccount>%s</payment:merchantAccount>
43
+ <payment:reference>%s</payment:reference>
44
+ %s
45
+ </payment:paymentRequest>
46
+ </payment:authorise>
47
+ EOXML
48
48
 
49
49
  # @private
50
- AMOUNT_PARTIAL = <<EOS
50
+ AMOUNT_PARTIAL = <<-EOXML
51
51
  <payment:amount>
52
52
  <common:currency>%s</common:currency>
53
53
  <common:value>%s</common:value>
54
54
  </payment:amount>
55
- EOS
55
+ EOXML
56
56
 
57
57
  # @private
58
- CARD_PARTIAL = <<EOS
58
+ CARD_PARTIAL = <<-EOXML
59
59
  <payment:card>
60
60
  <payment:holderName>%s</payment:holderName>
61
61
  <payment:number>%s</payment:number>
@@ -63,10 +63,24 @@ EOS
63
63
  <payment:expiryYear>%s</payment:expiryYear>
64
64
  <payment:expiryMonth>%02d</payment:expiryMonth>
65
65
  </payment:card>
66
- EOS
66
+ EOXML
67
+
68
+ # @private
69
+ ONE_CLICK_CARD_PARTIAL = <<-EOXML
70
+ <payment:card>
71
+ <payment:cvc>%s</payment:cvc>
72
+ </payment:card>
73
+ EOXML
67
74
 
68
75
  # @private
69
- ENCRYPTED_CARD_PARTIAL = <<EOS
76
+ INSTALLMENTS_PARTIAL = <<-EOXML
77
+ <payment:installments>
78
+ <common:value>%s</common:value>
79
+ </payment:installments>
80
+ EOXML
81
+
82
+ # @private
83
+ ENCRYPTED_CARD_PARTIAL = <<-EOXML
70
84
  <additionalAmount xmlns="http://payment.services.adyen.com" xsi:nil="true" />
71
85
  <additionalData xmlns="http://payment.services.adyen.com">
72
86
  <entry>
@@ -74,34 +88,31 @@ EOS
74
88
  <value xsi:type="xsd:string">%s</value>
75
89
  </entry>
76
90
  </additionalData>
77
- EOS
91
+ EOXML
78
92
 
79
93
  # @private
80
- ENABLE_RECURRING_CONTRACTS_PARTIAL = <<EOS
94
+ ENABLE_RECURRING_CONTRACTS_PARTIAL = <<-EOXML
81
95
  <payment:recurring>
82
96
  <payment:contract>RECURRING,ONECLICK</payment:contract>
83
97
  </payment:recurring>
84
- EOS
98
+ EOXML
85
99
 
86
100
  # @private
87
- RECURRING_PAYMENT_BODY_PARTIAL = <<EOS
101
+ RECURRING_PAYMENT_BODY_PARTIAL = <<-EOXML
88
102
  <payment:recurring>
89
103
  <payment:contract>RECURRING</payment:contract>
90
104
  </payment:recurring>
91
105
  <payment:selectedRecurringDetailReference>%s</payment:selectedRecurringDetailReference>
92
106
  <payment:shopperInteraction>ContAuth</payment:shopperInteraction>
93
- EOS
107
+ EOXML
94
108
 
95
109
  # @private
96
- ONE_CLICK_PAYMENT_BODY_PARTIAL = <<EOS
110
+ ONE_CLICK_PAYMENT_BODY_PARTIAL = <<-EOXML
97
111
  <payment:recurring>
98
112
  <payment:contract>ONECLICK</payment:contract>
99
113
  </payment:recurring>
100
114
  <payment:selectedRecurringDetailReference>%s</payment:selectedRecurringDetailReference>
101
- <payment:card>
102
- <payment:cvc>%s</payment:cvc>
103
- </payment:card>
104
- EOS
115
+ EOXML
105
116
 
106
117
  # @private
107
118
  SHOPPER_PARTIALS = {
@@ -110,7 +121,7 @@ EOS
110
121
  :ip => ' <payment:shopperIP>%s</payment:shopperIP>',
111
122
  :statement => ' <payment:shopperStatement>%s</payment:shopperStatement>',
112
123
  }
113
-
124
+
114
125
  # @private
115
126
  FRAUD_OFFSET_PARTIAL = '<payment:fraudOffset>%s</payment:fraudOffset>'
116
127
  end
@@ -8,48 +8,48 @@ module Adyen
8
8
  #
9
9
  # This module extends the {PaymentService} class and thus these methods are callable on it.
10
10
  module TestHelpers
11
- AUTHORISE_RESPONSE = SimpleSOAPClient::ENVELOPE % <<EOS
12
- <ns1:authoriseResponse xmlns:ns1="http://payment.services.adyen.com">
13
- <ns1:paymentResult>
14
- <additionalData xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
15
- <authCode xmlns="http://payment.services.adyen.com">1234</authCode>
16
- <dccAmount xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
17
- <dccSignature xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
18
- <fraudResult xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
19
- <issuerUrl xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
20
- <md xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
21
- <paRequest xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
22
- <pspReference xmlns="http://payment.services.adyen.com">9876543210987654</pspReference>
23
- <refusalReason xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
24
- <resultCode xmlns="http://payment.services.adyen.com">Authorised</resultCode>
25
- </ns1:paymentResult>
26
- </ns1:authoriseResponse>
27
- EOS
11
+ AUTHORISE_RESPONSE = SimpleSOAPClient::ENVELOPE % <<-EOXML
12
+ <ns1:authoriseResponse xmlns:ns1="http://payment.services.adyen.com">
13
+ <ns1:paymentResult>
14
+ <additionalData xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
15
+ <authCode xmlns="http://payment.services.adyen.com">1234</authCode>
16
+ <dccAmount xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
17
+ <dccSignature xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
18
+ <fraudResult xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
19
+ <issuerUrl xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
20
+ <md xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
21
+ <paRequest xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
22
+ <pspReference xmlns="http://payment.services.adyen.com">9876543210987654</pspReference>
23
+ <refusalReason xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
24
+ <resultCode xmlns="http://payment.services.adyen.com">Authorised</resultCode>
25
+ </ns1:paymentResult>
26
+ </ns1:authoriseResponse>
27
+ EOXML
28
28
 
29
- AUTHORISATION_REFUSED_RESPONSE = SimpleSOAPClient::ENVELOPE % <<EOS
30
- <ns1:authoriseResponse xmlns:ns1="http://payment.services.adyen.com">
31
- <ns1:paymentResult>
32
- <additionalData xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
33
- <authCode xmlns="http://payment.services.adyen.com">1234</authCode>
34
- <dccAmount xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
35
- <dccSignature xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
36
- <fraudResult xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
37
- <issuerUrl xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
38
- <md xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
39
- <paRequest xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
40
- <pspReference xmlns="http://payment.services.adyen.com">9876543210987654</pspReference>
41
- <refusalReason xmlns="http://payment.services.adyen.com">You need to actually own money.</refusalReason>
42
- <resultCode xmlns="http://payment.services.adyen.com">Refused</resultCode>
43
- </ns1:paymentResult>
44
- </ns1:authoriseResponse>
45
- EOS
29
+ AUTHORISATION_REFUSED_RESPONSE = SimpleSOAPClient::ENVELOPE % <<-EOXML
30
+ <ns1:authoriseResponse xmlns:ns1="http://payment.services.adyen.com">
31
+ <ns1:paymentResult>
32
+ <additionalData xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
33
+ <authCode xmlns="http://payment.services.adyen.com">1234</authCode>
34
+ <dccAmount xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
35
+ <dccSignature xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
36
+ <fraudResult xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
37
+ <issuerUrl xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
38
+ <md xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
39
+ <paRequest xmlns="http://payment.services.adyen.com" xsi:nil="true"/>
40
+ <pspReference xmlns="http://payment.services.adyen.com">9876543210987654</pspReference>
41
+ <refusalReason xmlns="http://payment.services.adyen.com">You need to actually own money.</refusalReason>
42
+ <resultCode xmlns="http://payment.services.adyen.com">Refused</resultCode>
43
+ </ns1:paymentResult>
44
+ </ns1:authoriseResponse>
45
+ EOXML
46
46
 
47
- AUTHORISATION_REQUEST_INVALID_RESPONSE = SimpleSOAPClient::ENVELOPE % <<EOS
48
- <soap:Fault>
49
- <faultcode>soap:Server</faultcode>
50
- <faultstring>validation 101 Invalid card number</faultstring>
51
- </soap:Fault>
52
- EOS
47
+ AUTHORISATION_REQUEST_INVALID_RESPONSE = SimpleSOAPClient::ENVELOPE % <<-EOXML
48
+ <soap:Fault>
49
+ <faultcode>soap:Server</faultcode>
50
+ <faultstring>validation 101 Invalid card number</faultstring>
51
+ </soap:Fault>
52
+ EOXML
53
53
 
54
54
  # @return [AuthorisationResponse] A authorisation succeeded response instance.
55
55
  def success_stub
@@ -104,15 +104,15 @@ EOS
104
104
  #
105
105
  # This module extends the {RecurringService} class and thus these methods are callable on it.
106
106
  module TestHelpers
107
- DISABLE_RESPONSE = SimpleSOAPClient::ENVELOPE % <<EOS
108
- <ns1:disableResponse xmlns:ns1="http://recurring.services.adyen.com">
109
- <ns1:result>
110
- <response xmlns="http://recurring.services.adyen.com">
111
- %s
112
- </response>
113
- </ns1:result>
114
- </ns1:disableResponse>
115
- EOS
107
+ DISABLE_RESPONSE = SimpleSOAPClient::ENVELOPE % <<-EOXML
108
+ <ns1:disableResponse xmlns:ns1="http://recurring.services.adyen.com">
109
+ <ns1:result>
110
+ <response xmlns="http://recurring.services.adyen.com">
111
+ %s
112
+ </response>
113
+ </ns1:result>
114
+ </ns1:disableResponse>
115
+ EOXML
116
116
 
117
117
  # @return [DisableResponse] A ‘disable succeeded’ response instance.
118
118
  def disabled_stub
@@ -31,6 +31,10 @@ module Adyen
31
31
  def perform_xpath(query, root_node)
32
32
  root_node.xpath(query, NS)
33
33
  end
34
+
35
+ def stringify_nodeset(nodeset)
36
+ nodeset.to_xml(encoding: 'UTF-8')
37
+ end
34
38
  end
35
39
 
36
40
  class REXMLBackend
@@ -48,7 +52,11 @@ module Adyen
48
52
 
49
53
  def perform_xpath(query, root_node)
50
54
  REXML::XPath.match(root_node, query, NS)
51
- end
55
+ end
56
+
57
+ def stringify_nodeset(nodeset)
58
+ nodeset.map { |n| n.to_s }.join("")
59
+ end
52
60
  end
53
61
 
54
62
  # @return A backend to handle XML parsing.
@@ -116,7 +124,7 @@ module Adyen
116
124
 
117
125
  # @return [String] A string representation of this node.
118
126
  def to_s
119
- Array === @node ? @node.join("") : @node.to_s
127
+ backend.stringify_nodeset(@node)
120
128
  end
121
129
 
122
130
  # @yield [XMLQuerier] A member of this node set, ready to be queried.
@@ -7,7 +7,7 @@ module Adyen
7
7
  module Encoding
8
8
  def self.hmac_base64(hmac_key, message)
9
9
  digest = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), hmac_key, message)
10
- Base64.encode64(digest).strip
10
+ Base64.strict_encode64(digest).strip
11
11
  end
12
12
 
13
13
  def self.gzip_base64(message)
@@ -15,7 +15,7 @@ module Adyen
15
15
  gz = Zlib::GzipWriter.new(sio)
16
16
  gz.write(message)
17
17
  gz.close
18
- Base64.encode64(sio.string).gsub("\n", "")
18
+ Base64.strict_encode64(sio.string)
19
19
  end
20
20
  end
21
21
  end
@@ -119,6 +119,10 @@ module Adyen
119
119
  parameters[:billing_address_sig] = calculate_billing_address_signature(parameters, shared_secret)
120
120
  end
121
121
 
122
+ if parameters[:shopper]
123
+ parameters[:shopper_sig] = calculate_shopper_signature(parameters, shared_secret)
124
+ end
125
+
122
126
  return parameters
123
127
  end
124
128
 
@@ -165,6 +169,20 @@ module Adyen
165
169
  }.join('&')
166
170
  end
167
171
 
172
+ # @see Adyen::Form.redirect_url
173
+ #
174
+ # Returns an absolute URL very similar to the one returned by Adyen::Form.redirect_url
175
+ # except that it uses the directory.shtml call which returns a list of all available
176
+ # payment methods
177
+ #
178
+ # @param [Hash] parameters The payment parameters to include in the payment request.
179
+ # @return [String] An absolute URL to redirect to the Adyen payment system.
180
+ def payment_methods_url(parameters = {})
181
+ url(nil, :directory) + '?' + flat_payment_parameters(parameters).map { |(k, v)|
182
+ "#{k}=#{CGI.escape(v)}"
183
+ }.join('&')
184
+ end
185
+
168
186
  # Returns a HTML snippet of hidden INPUT tags with the provided payment parameters.
169
187
  # The snippet can be included in a payment form that POSTs to the Adyen payment system.
170
188
  #
@@ -266,6 +284,20 @@ module Adyen
266
284
  Adyen::Encoding.hmac_base64(shared_secret, calculate_billing_address_signature_string(parameters[:billing_address]))
267
285
  end
268
286
 
287
+ # shopperSig: shopper.firstName + shopper.infix + shopper.lastName + shopper.gender + shopper.dateOfBirthDayOfMonth + shopper.dateOfBirthMonth + shopper.dateOfBirthYear + shopper.telephoneNumber
288
+ # (Note that you can send only shopper.firstName and shopper.lastName if you like. Do NOT include shopperSocialSecurityNumber in the shopperSig!)
289
+ def calculate_shopper_signature_string(parameters)
290
+ %w(first_name infix last_name gender date_of_birth_day_of_month date_of_birth_month date_of_birth_year telephone_number).map do |key|
291
+ parameters[key.to_sym]
292
+ end.join
293
+ end
294
+
295
+ def calculate_shopper_signature(parameters, shared_secret = nil)
296
+ shared_secret ||= parameters.delete(:shared_secret)
297
+ raise ArgumentError, "Cannot calculate shopper request signature with empty shared_secret" if shared_secret.to_s.empty?
298
+ Adyen::Encoding.hmac_base64(shared_secret, calculate_shopper_signature_string(parameters[:shopper]))
299
+ end
300
+
269
301
  ######################################################
270
302
  # REDIRECT SIGNATURE CHECKING
271
303
  ######################################################