1 | <?php |
||||||
2 | |||||||
3 | namespace Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3; |
||||||
4 | |||||||
5 | use DOMDocument; |
||||||
6 | use Exception; |
||||||
7 | use Pronamic\WordPress\Pay\Core\Util as Core_Util; |
||||||
8 | use Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3\XML\AcquirerErrorResMessage; |
||||||
9 | use Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3\XML\AcquirerStatusReqMessage; |
||||||
10 | use Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3\XML\AcquirerStatusResMessage; |
||||||
11 | use Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3\XML\DirectoryRequestMessage; |
||||||
12 | use Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3\XML\DirectoryResponseMessage; |
||||||
13 | use Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3\XML\Message; |
||||||
14 | use Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3\XML\RequestMessage; |
||||||
15 | use Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3\XML\ResponseMessage; |
||||||
16 | use Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3\XML\TransactionRequestMessage; |
||||||
17 | use Pronamic\WordPress\Pay\Gateways\IDealAdvancedV3\XML\TransactionResponseMessage; |
||||||
18 | use SimpleXMLElement; |
||||||
19 | use WP_Error; |
||||||
0 ignored issues
–
show
|
|||||||
20 | use XMLSecurityDSig; |
||||||
21 | use XMLSecurityKey; |
||||||
22 | |||||||
23 | /** |
||||||
24 | * Title: iDEAL client |
||||||
25 | * Description: |
||||||
26 | * Copyright: 2005-2019 Pronamic |
||||||
27 | * Company: Pronamic |
||||||
28 | * |
||||||
29 | * @author Remco Tolsma |
||||||
30 | * @version 2.0.0 |
||||||
31 | * @since 1.0.0 |
||||||
32 | */ |
||||||
33 | class Client { |
||||||
34 | /** |
||||||
35 | * Acquirer URL |
||||||
36 | * |
||||||
37 | * @var string |
||||||
38 | */ |
||||||
39 | public $acquirer_url; |
||||||
40 | |||||||
41 | /** |
||||||
42 | * Directory request URL |
||||||
43 | * |
||||||
44 | * @var string |
||||||
45 | */ |
||||||
46 | public $directory_request_url; |
||||||
47 | |||||||
48 | /** |
||||||
49 | * Transaction request URL |
||||||
50 | * |
||||||
51 | * @var string |
||||||
52 | */ |
||||||
53 | public $transaction_request_url; |
||||||
54 | |||||||
55 | /** |
||||||
56 | * Status request URL |
||||||
57 | * |
||||||
58 | * @var string |
||||||
59 | */ |
||||||
60 | public $status_request_url; |
||||||
61 | |||||||
62 | /** |
||||||
63 | * Merchant ID |
||||||
64 | * |
||||||
65 | * @var string |
||||||
66 | */ |
||||||
67 | public $merchant_id; |
||||||
68 | |||||||
69 | /** |
||||||
70 | * Sub ID |
||||||
71 | * |
||||||
72 | * @var string |
||||||
73 | */ |
||||||
74 | public $sub_id; |
||||||
75 | |||||||
76 | /** |
||||||
77 | * Private certificate |
||||||
78 | * |
||||||
79 | * @var string |
||||||
80 | */ |
||||||
81 | public $private_certificate; |
||||||
82 | |||||||
83 | /** |
||||||
84 | * Private key |
||||||
85 | * |
||||||
86 | * @var string |
||||||
87 | */ |
||||||
88 | public $private_key; |
||||||
89 | |||||||
90 | /** |
||||||
91 | * Private key password |
||||||
92 | * |
||||||
93 | * @var string |
||||||
94 | */ |
||||||
95 | public $private_key_password; |
||||||
96 | |||||||
97 | /** |
||||||
98 | * Error |
||||||
99 | * |
||||||
100 | * @var WP_Error |
||||||
101 | */ |
||||||
102 | private $error; |
||||||
103 | |||||||
104 | /** |
||||||
105 | * Constructs and initialzies an iDEAL Advanced v3 client object |
||||||
106 | */ |
||||||
107 | public function __construct() { |
||||||
108 | |||||||
109 | } |
||||||
110 | |||||||
111 | /** |
||||||
112 | * Get the latest error |
||||||
113 | * |
||||||
114 | * @return WP_Error or null |
||||||
115 | */ |
||||||
116 | public function get_error() { |
||||||
117 | return $this->error; |
||||||
118 | } |
||||||
119 | |||||||
120 | /** |
||||||
121 | * Set the acquirer URL |
||||||
122 | * |
||||||
123 | * @param string $url |
||||||
124 | */ |
||||||
125 | public function set_acquirer_url( $url ) { |
||||||
126 | $this->acquirer_url = $url; |
||||||
127 | |||||||
128 | $this->directory_request_url = $url; |
||||||
129 | $this->transaction_request_url = $url; |
||||||
130 | $this->status_request_url = $url; |
||||||
131 | } |
||||||
132 | |||||||
133 | /** |
||||||
134 | * Send an specific request message to an specific URL |
||||||
135 | * |
||||||
136 | * @param string $url |
||||||
137 | * @param RequestMessage $message |
||||||
138 | * |
||||||
139 | * @return ResponseMessage |
||||||
140 | */ |
||||||
141 | private function send_message( $url, RequestMessage $message ) { |
||||||
142 | $result = false; |
||||||
143 | |||||||
144 | // Sign |
||||||
145 | $document = $message->get_document(); |
||||||
146 | $document = $this->sign_document( $document ); |
||||||
147 | |||||||
148 | if ( false !== $document ) { |
||||||
149 | // Stringify |
||||||
150 | $data = $document->saveXML(); |
||||||
151 | |||||||
152 | /* |
||||||
153 | * Fix for a incorrect implementation at https://www.ideal-checkout.nl/simulator/. |
||||||
154 | * |
||||||
155 | * @since 1.1.11 |
||||||
156 | */ |
||||||
157 | if ( 'https://www.ideal-checkout.nl/simulator/' === $url ) { |
||||||
158 | $data = $document->C14N( true, false ); |
||||||
159 | } |
||||||
160 | |||||||
161 | // Remote post |
||||||
162 | $response = wp_remote_post( $url, array( |
||||||
0 ignored issues
–
show
The function
wp_remote_post was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
163 | 'method' => 'POST', |
||||||
164 | 'headers' => array( |
||||||
165 | 'Content-Type' => 'text/xml; charset=' . Message::XML_ENCODING, |
||||||
166 | ), |
||||||
167 | 'body' => $data, |
||||||
168 | ) ); |
||||||
169 | |||||||
170 | // Handle response |
||||||
171 | if ( ! is_wp_error( $response ) ) { |
||||||
0 ignored issues
–
show
The function
is_wp_error was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
172 | if ( 200 === wp_remote_retrieve_response_code( $response ) ) { |
||||||
0 ignored issues
–
show
The function
wp_remote_retrieve_response_code was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
173 | $body = wp_remote_retrieve_body( $response ); |
||||||
0 ignored issues
–
show
The function
wp_remote_retrieve_body was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
174 | |||||||
175 | $xml = Core_Util::simplexml_load_string( $body ); |
||||||
176 | |||||||
177 | if ( is_wp_error( $xml ) ) { |
||||||
178 | $this->error = $xml; |
||||||
0 ignored issues
–
show
It seems like
$xml of type SimpleXMLElement is incompatible with the declared type WP_Error of property $error .
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property. Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.. ![]() |
|||||||
179 | } else { |
||||||
180 | $document = self::parse_document( $xml ); |
||||||
0 ignored issues
–
show
The method
Pronamic\WordPress\Pay\G...lient::parse_document() is not static, but was called statically.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
181 | |||||||
182 | if ( is_wp_error( $document ) ) { |
||||||
183 | $this->error = $document; |
||||||
184 | } else { |
||||||
185 | $result = $document; |
||||||
186 | } |
||||||
187 | } |
||||||
188 | } else { |
||||||
189 | $this->error = new WP_Error( |
||||||
190 | 'wrong_response_code', |
||||||
191 | sprintf( |
||||||
192 | /* translators: %s: response code */ |
||||||
193 | __( 'The response code (<code>%s<code>) from the iDEAL provider was incorrect.', 'pronamic_ideal' ), |
||||||
0 ignored issues
–
show
The function
__ was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
194 | wp_remote_retrieve_response_code( $response ) |
||||||
195 | ) |
||||||
196 | ); |
||||||
197 | } |
||||||
198 | } else { |
||||||
199 | $this->error = $response; |
||||||
200 | } |
||||||
201 | } |
||||||
202 | |||||||
203 | return $result; |
||||||
0 ignored issues
–
show
The expression
return $result could also return false which is incompatible with the documented return type Pronamic\WordPress\Pay\G...dV3\XML\ResponseMessage . Did you maybe forget to handle an error condition?
If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled. ![]() |
|||||||
204 | } |
||||||
205 | |||||||
206 | /** |
||||||
207 | * Parse the specified document and return parsed result |
||||||
208 | * |
||||||
209 | * @param SimpleXMLElement $document |
||||||
210 | * |
||||||
211 | * @return ResponseMessage|WP_Error |
||||||
212 | */ |
||||||
213 | private function parse_document( SimpleXMLElement $document ) { |
||||||
214 | $this->error = null; |
||||||
215 | |||||||
216 | $name = $document->getName(); |
||||||
217 | |||||||
218 | switch ( $name ) { |
||||||
219 | case AcquirerErrorResMessage::NAME: |
||||||
220 | $message = AcquirerErrorResMessage::parse( $document ); |
||||||
221 | |||||||
222 | $this->error = new WP_Error( |
||||||
223 | 'IDealAdvancedV3_error', |
||||||
224 | sprintf( '%s. %s', $message->error->get_message(), $message->error->get_detail() ), |
||||||
225 | $message->error |
||||||
226 | ); |
||||||
227 | |||||||
228 | return $message; |
||||||
229 | case DirectoryResponseMessage::NAME: |
||||||
230 | return DirectoryResponseMessage::parse( $document ); |
||||||
231 | case TransactionResponseMessage::NAME: |
||||||
232 | return TransactionResponseMessage::parse( $document ); |
||||||
233 | case AcquirerStatusResMessage::NAME: |
||||||
234 | return AcquirerStatusResMessage::parse( $document ); |
||||||
235 | default: |
||||||
236 | return new WP_Error( |
||||||
237 | 'IDealAdvancedV3_error', |
||||||
238 | /* translators: %s: XML document element name */ |
||||||
239 | sprintf( __( 'Unknwon iDEAL message (%s)', 'pronamic_ideal' ), $name ) |
||||||
0 ignored issues
–
show
The function
__ was not found. Maybe you did not declare it correctly or list all dependencies?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
240 | ); |
||||||
241 | } |
||||||
242 | } |
||||||
243 | |||||||
244 | /** |
||||||
245 | * Get directory of issuers |
||||||
246 | * |
||||||
247 | * @return Directory |
||||||
248 | */ |
||||||
249 | public function get_directory() { |
||||||
250 | $directory = false; |
||||||
251 | |||||||
252 | $request_dir_message = new DirectoryRequestMessage(); |
||||||
253 | |||||||
254 | $merchant = $request_dir_message->get_merchant(); |
||||||
255 | $merchant->set_id( $this->merchant_id ); |
||||||
256 | $merchant->set_sub_id( $this->sub_id ); |
||||||
257 | |||||||
258 | $response_dir_message = $this->send_message( $this->directory_request_url, $request_dir_message ); |
||||||
259 | |||||||
260 | if ( $response_dir_message instanceof DirectoryResponseMessage ) { |
||||||
261 | $directory = $response_dir_message->get_directory(); |
||||||
262 | } |
||||||
263 | |||||||
264 | return $directory; |
||||||
0 ignored issues
–
show
|
|||||||
265 | } |
||||||
266 | |||||||
267 | /** |
||||||
268 | * Create transaction |
||||||
269 | * |
||||||
270 | * @param Transaction $transaction |
||||||
271 | * @param string $issuer_id |
||||||
272 | * |
||||||
273 | * @return TransactionResponseMessage |
||||||
274 | */ |
||||||
275 | public function create_transaction( Transaction $transaction, $return_url, $issuer_id ) { |
||||||
276 | $message = new TransactionRequestMessage(); |
||||||
277 | |||||||
278 | $merchant = $message->get_merchant(); |
||||||
279 | $merchant->set_id( $this->merchant_id ); |
||||||
280 | $merchant->set_sub_id( $this->sub_id ); |
||||||
281 | $merchant->set_return_url( $return_url ); |
||||||
282 | |||||||
283 | $message->issuer = new Issuer(); |
||||||
284 | $message->issuer->set_id( $issuer_id ); |
||||||
285 | |||||||
286 | $message->transaction = $transaction; |
||||||
287 | |||||||
288 | return $this->send_message( $this->transaction_request_url, $message ); |
||||||
0 ignored issues
–
show
|
|||||||
289 | } |
||||||
290 | |||||||
291 | /** |
||||||
292 | * Get the status of the specified transaction ID |
||||||
293 | * |
||||||
294 | * @param string $transaction_id |
||||||
295 | * |
||||||
296 | * @return TransactionResponseMessage |
||||||
297 | */ |
||||||
298 | public function get_status( $transaction_id ) { |
||||||
299 | $message = new AcquirerStatusReqMessage(); |
||||||
300 | |||||||
301 | $merchant = $message->get_merchant(); |
||||||
302 | $merchant->set_id( $this->merchant_id ); |
||||||
303 | $merchant->set_sub_id( $this->sub_id ); |
||||||
304 | |||||||
305 | $message->transaction = new Transaction(); |
||||||
306 | $message->transaction->set_id( $transaction_id ); |
||||||
307 | |||||||
308 | return $this->send_message( $this->status_request_url, $message ); |
||||||
0 ignored issues
–
show
|
|||||||
309 | } |
||||||
310 | |||||||
311 | /** |
||||||
312 | * Sign the specified DOMDocument |
||||||
313 | * |
||||||
314 | * @link https://github.com/Maks3w/xmlseclibs/blob/v1.3.0/tests/xml-sign.phpt |
||||||
315 | * |
||||||
316 | * @param DOMDocument $document |
||||||
317 | * |
||||||
318 | * @return DOMDocument |
||||||
319 | */ |
||||||
320 | private function sign_document( DOMDocument $document ) { |
||||||
321 | $result = false; |
||||||
322 | |||||||
323 | try { |
||||||
324 | $dsig = new XMLSecurityDSig(); |
||||||
325 | |||||||
326 | // For canonicalization purposes the exclusive (9) algorithm must be used. |
||||||
327 | // @link http://pronamic.nl/wp-content/uploads/2012/12/iDEAL-Merchant-Integration-Guide-ENG-v3.3.1.pdf #page 30 |
||||||
328 | $dsig->setCanonicalMethod( XMLSecurityDSig::EXC_C14N ); |
||||||
329 | |||||||
330 | // For hashing purposes the SHA-256 (11) algorithm must be used. |
||||||
331 | // @link http://pronamic.nl/wp-content/uploads/2012/12/iDEAL-Merchant-Integration-Guide-ENG-v3.3.1.pdf #page 30 |
||||||
332 | $dsig->addReference( |
||||||
333 | $document, |
||||||
334 | XMLSecurityDSig::SHA256, |
||||||
335 | array( 'http://www.w3.org/2000/09/xmldsig#enveloped-signature' ), |
||||||
336 | array( |
||||||
337 | 'force_uri' => true, |
||||||
338 | ) |
||||||
339 | ); |
||||||
340 | |||||||
341 | // For signature purposes the RSAWithSHA 256 (12) algorithm must be used. |
||||||
342 | // @link http://pronamic.nl/wp-content/uploads/2012/12/iDEAL-Merchant-Integration-Guide-ENG-v3.3.1.pdf #page 31 |
||||||
343 | $key = new XMLSecurityKey( XMLSecurityKey::RSA_SHA256, array( |
||||||
344 | 'type' => 'private', |
||||||
345 | ) ); |
||||||
346 | |||||||
347 | $key->passphrase = $this->private_key_password; |
||||||
348 | |||||||
349 | $key->loadKey( $this->private_key ); |
||||||
350 | |||||||
351 | // Test if we can get an private key object, to prefent the following errors: |
||||||
352 | // Warning: openssl_sign() [function.openssl-sign]: supplied key param cannot be coerced into a private key |
||||||
353 | $result = openssl_get_privatekey( $this->private_key, $this->private_key_password ); |
||||||
354 | |||||||
355 | if ( false !== $result ) { |
||||||
356 | // Sign |
||||||
357 | $dsig->sign( $key ); |
||||||
358 | |||||||
359 | // The public key must be referenced using a fingerprint of an X.509 |
||||||
360 | // certificate. The fingerprint must be calculated according |
||||||
361 | // to the following formula HEX(SHA-1(DER certificate)) (13) |
||||||
362 | // @link http://pronamic.nl/wp-content/uploads/2012/12/iDEAL-Merchant-Integration-Guide-ENG-v3.3.1.pdf #page 31 |
||||||
363 | $fingerprint = Security::get_sha_fingerprint( $this->private_certificate ); |
||||||
364 | |||||||
365 | $dsig->addKeyInfoAndName( $fingerprint ); |
||||||
366 | |||||||
367 | // Add the signature |
||||||
368 | $dsig->appendSignature( $document->documentElement ); |
||||||
369 | |||||||
370 | $result = $document; |
||||||
371 | } else { |
||||||
372 | throw new Exception( 'Can not load private key' ); |
||||||
373 | } |
||||||
374 | } catch ( Exception $e ) { |
||||||
375 | $this->error = new WP_Error( 'xml_security', $e->getMessage(), $e ); |
||||||
376 | } |
||||||
377 | |||||||
378 | return $result; |
||||||
0 ignored issues
–
show
|
|||||||
379 | } |
||||||
380 | } |
||||||
381 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths