1 | <?php |
||
2 | |||
3 | namespace Pronamic\WordPress\Pay\Gateways\IDealBasic; |
||
4 | |||
5 | use InvalidArgumentException; |
||
6 | use DateTime; |
||
7 | use DateTimeZone; |
||
8 | use Pronamic\WordPress\Money\Money; |
||
9 | use Pronamic\WordPress\Pay\Plugin; |
||
10 | |||
11 | /** |
||
12 | * Title: iDEAL Basic client |
||
13 | * Description: |
||
14 | * Copyright: 2005-2021 Pronamic |
||
15 | * Company: Pronamic |
||
16 | * |
||
17 | * @author Remco Tolsma |
||
18 | * @version 2.0.0 |
||
19 | * @since 1.0.0 |
||
20 | */ |
||
21 | class Client { |
||
22 | /** |
||
23 | * An payment type indicator for iDEAL |
||
24 | * |
||
25 | * @var string |
||
26 | */ |
||
27 | const PAYMENT_TYPE_IDEAL = 'ideal'; |
||
28 | |||
29 | /** |
||
30 | * The expire date format (yyyy-MMddTHH:mm:ss.SSSZ) |
||
31 | * The Z stands for the time zone (CET). |
||
32 | * |
||
33 | * @var string |
||
34 | */ |
||
35 | const DATE_EXPIRE_FORMAT = 'Y-m-d\TH:i:s.000\Z'; |
||
36 | |||
37 | /** |
||
38 | * The default expire date modifier |
||
39 | * |
||
40 | * @var string |
||
41 | */ |
||
42 | const EXPIRE_DATE_MODIFIER = '+30 minutes'; |
||
43 | |||
44 | /** |
||
45 | * Forbidden characters |
||
46 | * |
||
47 | * @doc Manual iDEAL Lite.pdf (4.2 Explanation of the hash code) |
||
48 | * @var string |
||
49 | */ |
||
50 | const FORBIDDEN_CHARACHTERS = "\t\n\r "; |
||
51 | |||
52 | /** |
||
53 | * The URL for testing |
||
54 | * |
||
55 | * @var string |
||
56 | */ |
||
57 | private $payment_server_url; |
||
58 | |||
59 | /** |
||
60 | * The merchant ID |
||
61 | * |
||
62 | * @var string |
||
63 | */ |
||
64 | private $merchant_id; |
||
65 | |||
66 | /** |
||
67 | * The sub ID |
||
68 | * |
||
69 | * @var string |
||
70 | */ |
||
71 | private $sub_id; |
||
72 | |||
73 | /** |
||
74 | * The hash key |
||
75 | * |
||
76 | * @var string |
||
77 | */ |
||
78 | private $hash_key; |
||
79 | |||
80 | /** |
||
81 | * The purchase ID |
||
82 | * |
||
83 | * @var string |
||
84 | */ |
||
85 | private $purchase_id; |
||
86 | |||
87 | /** |
||
88 | * The language |
||
89 | * |
||
90 | * @var string |
||
91 | */ |
||
92 | private $language; |
||
93 | |||
94 | /** |
||
95 | * Description |
||
96 | * |
||
97 | * @var string |
||
98 | */ |
||
99 | private $description; |
||
100 | |||
101 | /** |
||
102 | * The currency |
||
103 | * |
||
104 | * @var string |
||
105 | */ |
||
106 | private $currency; |
||
107 | |||
108 | /** |
||
109 | * Payment method |
||
110 | * |
||
111 | * @var string |
||
112 | */ |
||
113 | private $payment_type; |
||
114 | |||
115 | /** |
||
116 | * The expire date |
||
117 | * |
||
118 | * @var DateTime |
||
119 | */ |
||
120 | private $expire_date; |
||
121 | |||
122 | /** |
||
123 | * The expire date format |
||
124 | * |
||
125 | * @var string |
||
126 | */ |
||
127 | private $expire_date_format; |
||
128 | |||
129 | /** |
||
130 | * The expire date modifier |
||
131 | * |
||
132 | * @var string |
||
133 | */ |
||
134 | private $expire_date_modifier; |
||
135 | |||
136 | /** |
||
137 | * The forbidden characters |
||
138 | * |
||
139 | * @var string |
||
140 | */ |
||
141 | private $forbidden_characters; |
||
142 | |||
143 | /** |
||
144 | * The items |
||
145 | * |
||
146 | * @var Items |
||
147 | */ |
||
148 | private $items; |
||
149 | |||
150 | /** |
||
151 | * The consumer is automatically directed to this URL after a successful payment. |
||
152 | * |
||
153 | * @var string |
||
154 | */ |
||
155 | private $success_url; |
||
156 | |||
157 | /** |
||
158 | * The consumer is automatically directed to this URL after the transaction has been cancelled. |
||
159 | * |
||
160 | * @var string |
||
161 | */ |
||
162 | private $cancel_url; |
||
163 | |||
164 | /** |
||
165 | * The consumer is directed to this URL if an error has occurred. |
||
166 | * |
||
167 | * @var string |
||
168 | */ |
||
169 | private $error_url; |
||
170 | |||
171 | /** |
||
172 | * Constructs and initialize a iDEAL basic object |
||
173 | */ |
||
174 | 2 | public function __construct() { |
|
175 | 2 | $this->items = new Items(); |
|
176 | |||
177 | 2 | $this->forbidden_characters = array(); |
|
178 | |||
179 | 2 | $this->set_payment_type( self::PAYMENT_TYPE_IDEAL ); |
|
180 | 2 | $this->set_expire_date_format( self::DATE_EXPIRE_FORMAT ); |
|
181 | 2 | $this->set_expire_date_modifier( self::EXPIRE_DATE_MODIFIER ); |
|
182 | 2 | $this->set_forbidden_characters( self::FORBIDDEN_CHARACHTERS ); |
|
183 | 2 | } |
|
184 | |||
185 | /** |
||
186 | * Get the payment server URL |
||
187 | * |
||
188 | * @return string |
||
189 | */ |
||
190 | public function get_payment_server_url() { |
||
191 | return $this->payment_server_url; |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * Set the payment server URL |
||
196 | * |
||
197 | * @param string $url Payment server URL. |
||
198 | */ |
||
199 | public function set_payment_server_url( $url ) { |
||
200 | $this->payment_server_url = $url; |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * Get the merchant ID. |
||
205 | * |
||
206 | * @return string |
||
207 | */ |
||
208 | 1 | public function get_merchant_id() { |
|
209 | 1 | return $this->merchant_id; |
|
210 | } |
||
211 | |||
212 | /** |
||
213 | * Set the merchant ID. |
||
214 | * |
||
215 | * @param string $merchant_id Merchant ID. |
||
216 | */ |
||
217 | 1 | public function set_merchant_id( $merchant_id ) { |
|
218 | 1 | $this->merchant_id = $merchant_id; |
|
219 | 1 | } |
|
220 | |||
221 | /** |
||
222 | * Get the sub id |
||
223 | * |
||
224 | * @return string Sub id |
||
225 | */ |
||
226 | 1 | public function get_sub_id() { |
|
227 | 1 | return $this->sub_id; |
|
228 | } |
||
229 | |||
230 | /** |
||
231 | * Set the sub id |
||
232 | * |
||
233 | * @param string $sub_id Sub ID. |
||
234 | */ |
||
235 | 1 | public function set_sub_id( $sub_id ) { |
|
236 | 1 | $this->sub_id = $sub_id; |
|
237 | 1 | } |
|
238 | |||
239 | /** |
||
240 | * Get the hash key |
||
241 | * |
||
242 | * @return string Hash key |
||
243 | */ |
||
244 | 1 | public function get_hash_key() { |
|
245 | 1 | return $this->hash_key; |
|
246 | } |
||
247 | |||
248 | /** |
||
249 | * Set the hash key |
||
250 | * N..max50 |
||
251 | * |
||
252 | * @param string $hash_key Hash key. |
||
253 | */ |
||
254 | 1 | public function set_hash_key( $hash_key ) { |
|
255 | 1 | $this->hash_key = $hash_key; |
|
256 | 1 | } |
|
257 | |||
258 | /** |
||
259 | * Get the purchase id |
||
260 | * |
||
261 | * @return string Purchase id |
||
262 | */ |
||
263 | 1 | public function get_purchase_id() { |
|
264 | 1 | return $this->purchase_id; |
|
265 | } |
||
266 | |||
267 | /** |
||
268 | * Set the purchase id |
||
269 | * AN..max16 (AN = Alphanumeric, free text) |
||
270 | * |
||
271 | * @param string $purchase_id Purchase ID. |
||
272 | */ |
||
273 | 1 | public function set_purchase_id( $purchase_id ) { |
|
274 | 1 | $this->purchase_id = substr( $purchase_id, 0, 16 ); |
|
275 | 1 | } |
|
276 | |||
277 | /** |
||
278 | * Get the language |
||
279 | * |
||
280 | * @return string Language |
||
281 | */ |
||
282 | public function get_language() { |
||
283 | return $this->language; |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * Set the language |
||
288 | * |
||
289 | * @param string $language Language. |
||
290 | */ |
||
291 | 1 | public function set_language( $language ) { |
|
292 | 1 | $this->language = $language; |
|
293 | 1 | } |
|
294 | |||
295 | /** |
||
296 | * Get the description |
||
297 | * |
||
298 | * @return string Description |
||
299 | */ |
||
300 | 1 | public function get_description() { |
|
301 | 1 | return $this->description; |
|
302 | } |
||
303 | |||
304 | /** |
||
305 | * Set the description |
||
306 | * AN..max32 (AN = Alphanumeric, free text) |
||
307 | * |
||
308 | * @param string $description Description. |
||
309 | */ |
||
310 | 2 | public function set_description( $description ) { |
|
311 | 2 | $this->description = DataHelper::an32( $description ); |
|
312 | 2 | } |
|
313 | |||
314 | /** |
||
315 | * Get the currency |
||
316 | * |
||
317 | * @return string Currency |
||
318 | */ |
||
319 | public function get_currency() { |
||
320 | return $this->currency; |
||
321 | } |
||
322 | |||
323 | /** |
||
324 | * Set the currency |
||
325 | * |
||
326 | * @param string $currency Currency. |
||
327 | */ |
||
328 | 1 | public function set_currency( $currency ) { |
|
329 | 1 | $this->currency = $currency; |
|
330 | 1 | } |
|
331 | |||
332 | /** |
||
333 | * Get the payment type |
||
334 | * |
||
335 | * @return string Payment type |
||
336 | */ |
||
337 | 1 | public function get_payment_type() { |
|
338 | 1 | return $this->payment_type; |
|
339 | } |
||
340 | |||
341 | /** |
||
342 | * Set the payment type |
||
343 | * AN..max10 |
||
344 | * |
||
345 | * @param string $payment_type Payment type. |
||
346 | */ |
||
347 | 2 | public function set_payment_type( $payment_type ) { |
|
348 | 2 | $this->payment_type = $payment_type; |
|
349 | 2 | } |
|
350 | |||
351 | /** |
||
352 | * Get the expire date |
||
353 | * |
||
354 | * @param boolean $create_new Indicator for creating a new expire date. |
||
355 | * |
||
356 | * @return DateTime |
||
357 | */ |
||
358 | 1 | public function get_expire_date( $create_new = false ) { |
|
359 | 1 | if ( null === $this->expire_date || $create_new ) { |
|
360 | $this->expire_date = new DateTime( null, new DateTimeZone( Plugin::TIMEZONE ) ); |
||
361 | $this->expire_date->modify( $this->expire_date_modifier ); |
||
362 | } |
||
363 | |||
364 | 1 | return $this->expire_date; |
|
365 | } |
||
366 | |||
367 | /** |
||
368 | * Get the expire date format |
||
369 | * |
||
370 | * @return string the expire date format |
||
371 | */ |
||
372 | 1 | public function get_expire_date_format() { |
|
373 | 1 | return $this->expire_date_format; |
|
374 | } |
||
375 | |||
376 | /** |
||
377 | * Set the expire date format |
||
378 | * |
||
379 | * @param string $expire_date_format Expire date format. |
||
380 | */ |
||
381 | 2 | public function set_expire_date_format( $expire_date_format ) { |
|
382 | 2 | $this->expire_date_format = $expire_date_format; |
|
383 | 2 | } |
|
384 | |||
385 | /** |
||
386 | * Get the expire date modifier |
||
387 | * |
||
388 | * @return string Expire date modifier |
||
389 | */ |
||
390 | public function get_expire_date_modifier() { |
||
391 | return $this->expire_date_modifier; |
||
392 | } |
||
393 | |||
394 | /** |
||
395 | * Set the expire date modifier |
||
396 | * |
||
397 | * @param string $expire_date_modifier Expire date modifier. |
||
398 | */ |
||
399 | 2 | public function set_expire_date_modifier( $expire_date_modifier ) { |
|
400 | 2 | $this->expire_date_modifier = $expire_date_modifier; |
|
401 | 2 | } |
|
402 | |||
403 | /** |
||
404 | * Set the expire date |
||
405 | * |
||
406 | * @param DateTime $date Expire date. |
||
407 | */ |
||
408 | 1 | public function set_expire_date( DateTime $date ) { |
|
409 | 1 | $this->expire_date = $date; |
|
410 | 1 | } |
|
411 | |||
412 | /** |
||
413 | * Get the forbidden characters |
||
414 | * |
||
415 | * @return array |
||
416 | */ |
||
417 | 1 | public function get_forbidden_characters() { |
|
418 | 1 | return $this->forbidden_characters; |
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||
419 | } |
||
420 | |||
421 | /** |
||
422 | * Set the forbidden characters |
||
423 | * |
||
424 | * @param mixed $characters Array or string with forbidden characters. |
||
425 | * |
||
426 | * @throws InvalidArgumentException Passed characters is not an array or string. |
||
427 | */ |
||
428 | 2 | public function set_forbidden_characters( $characters ) { |
|
429 | 2 | if ( ! is_array( $characters ) && ! is_string( $characters ) ) { |
|
430 | throw new InvalidArgumentException( 'Invalid characters argument.' ); |
||
431 | } |
||
432 | |||
433 | 2 | if ( is_string( $characters ) ) { |
|
434 | 2 | $characters = str_split( $characters ); |
|
435 | } |
||
436 | |||
437 | 2 | $this->forbidden_characters = $characters; |
|
438 | 2 | } |
|
439 | |||
440 | /** |
||
441 | * Get the success URL |
||
442 | * |
||
443 | * @return string URL |
||
444 | */ |
||
445 | public function get_success_url() { |
||
446 | return $this->success_url; |
||
447 | } |
||
448 | |||
449 | /** |
||
450 | * Set the success URL |
||
451 | * |
||
452 | * @param string $url Success URL. |
||
453 | */ |
||
454 | 1 | public function set_success_url( $url ) { |
|
455 | 1 | $this->success_url = $url; |
|
456 | 1 | } |
|
457 | |||
458 | /** |
||
459 | * Get the cancel URL |
||
460 | * |
||
461 | * @return string Cancel URL |
||
462 | */ |
||
463 | public function get_cancel_url() { |
||
464 | return $this->cancel_url; |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * Set the cancel URL |
||
469 | * |
||
470 | * @param string $url Cancel URL. |
||
471 | */ |
||
472 | 1 | public function set_cancel_url( $url ) { |
|
473 | 1 | $this->cancel_url = $url; |
|
474 | 1 | } |
|
475 | |||
476 | /** |
||
477 | * Get the error URL |
||
478 | * |
||
479 | * @return string Error URL |
||
480 | */ |
||
481 | public function get_error_url() { |
||
482 | return $this->error_url; |
||
483 | } |
||
484 | |||
485 | /** |
||
486 | * Set the error URL |
||
487 | * |
||
488 | * @param string $url Error URL. |
||
489 | */ |
||
490 | 1 | public function set_error_url( $url ) { |
|
491 | 1 | $this->error_url = $url; |
|
492 | 1 | } |
|
493 | |||
494 | /** |
||
495 | * Get the items |
||
496 | * |
||
497 | * @return Items |
||
498 | */ |
||
499 | 1 | public function get_items() { |
|
500 | 1 | return $this->items; |
|
501 | } |
||
502 | |||
503 | /** |
||
504 | * Set the items |
||
505 | * |
||
506 | * @param Items $items Items. |
||
507 | */ |
||
508 | public function set_items( Items $items ) { |
||
509 | $this->items = $items; |
||
510 | } |
||
511 | |||
512 | /** |
||
513 | * Calculate the total amount of all items |
||
514 | * |
||
515 | * @return Money |
||
516 | */ |
||
517 | 1 | public function get_amount() { |
|
518 | 1 | return $this->items->get_amount(); |
|
519 | } |
||
520 | |||
521 | /** |
||
522 | * Create hash string |
||
523 | */ |
||
524 | 1 | public function create_hash_string() { |
|
525 | 1 | $string = array(); |
|
526 | |||
527 | // SHA1 hashcode, used only with the hashcode approach (Chapter 4). |
||
528 | 1 | $string[] = $this->get_hash_key(); |
|
529 | |||
530 | // Your AcceptorID is provided in the registration process, also known as merchant id. |
||
531 | 1 | $string[] = $this->get_merchant_id(); |
|
532 | |||
533 | // Provided in the registration process, value is normally '0' (zero). |
||
534 | 1 | $string[] = $this->get_sub_id(); |
|
535 | |||
536 | // Total amount of transaction. |
||
537 | 1 | $string[] = $this->get_amount()->get_minor_units()->format( 0, '', '' ); |
|
538 | |||
539 | // The online shop's unique order number, also known as purchase id. |
||
540 | 1 | $string[] = $this->get_purchase_id(); |
|
541 | |||
542 | // ?? Fixed value = ideal |
||
543 | 1 | $string[] = $this->get_payment_type(); |
|
544 | |||
545 | // yyyy-MMddTHH:mm:ss.SSS Z Time at which the transaction expires (maximum of 1 hour later). |
||
546 | // The consumer has time until then to pay with iDEAL. |
||
547 | 1 | $string[] = $this->get_expire_date()->format( $this->get_expire_date_format() ); |
|
548 | |||
549 | // Iterate through the items and concat. |
||
550 | 1 | foreach ( $this->get_items() as $item ) { |
|
551 | // Article number. <n> is 1 for the first product, 2 for the second, etc. |
||
552 | // N.B. Note that for every product type the parameters |
||
553 | // itemNumber<n>, itemDescription<n>, itemQuantity<n> and itemPrice<n> are mandatory. |
||
554 | 1 | $string[] = $item->get_number(); |
|
555 | |||
556 | // Description of article <n>. |
||
557 | 1 | $string[] = $item->get_description(); |
|
558 | |||
559 | // Number of items of article <n> that the consumer wants to buy. |
||
560 | 1 | $string[] = $item->get_quantity(); |
|
561 | |||
562 | // Price of article <n> in whole eurocents. |
||
563 | 1 | $string[] = $item->get_price()->get_minor_units()->format( 0, '', '' ); |
|
564 | } |
||
565 | |||
566 | 1 | $concat_string = implode( '', $string ); |
|
567 | |||
568 | // The characters "\t", "\n", "\r", " " (spaces) may not exist in the string. |
||
569 | 1 | $forbidden_characters = $this->get_forbidden_characters(); |
|
570 | 1 | $concat_string = str_replace( $forbidden_characters, '', $concat_string ); |
|
571 | |||
572 | // Delete special HTML entities. |
||
573 | 1 | $concat_string = html_entity_decode( $concat_string, ENT_COMPAT, 'UTF-8' ); |
|
574 | |||
575 | 1 | return $concat_string; |
|
576 | } |
||
577 | |||
578 | /** |
||
579 | * Create hash |
||
580 | * |
||
581 | * @return string Hash |
||
582 | */ |
||
583 | 1 | public function create_hash() { |
|
584 | 1 | return sha1( $this->create_hash_string() ); |
|
585 | } |
||
586 | |||
587 | /** |
||
588 | * Get the iDEAL HTML fields |
||
589 | * |
||
590 | * @since 1.1.1 |
||
591 | * @return array |
||
592 | */ |
||
593 | public function get_fields() { |
||
594 | $fields = array(); |
||
595 | |||
596 | $fields['merchantID'] = $this->get_merchant_id(); |
||
597 | $fields['subID'] = $this->get_sub_id(); |
||
598 | |||
599 | $fields['amount'] = $this->get_amount()->get_minor_units()->format( 0, '', '' ); |
||
600 | $fields['purchaseID'] = $this->get_purchase_id(); |
||
601 | $fields['language'] = $this->get_language(); |
||
602 | $fields['currency'] = $this->get_currency(); |
||
603 | $fields['description'] = $this->get_description(); |
||
604 | $fields['hash'] = $this->create_hash(); |
||
605 | $fields['paymentType'] = $this->get_payment_type(); |
||
606 | $fields['validUntil'] = $this->get_expire_date()->format( $this->get_expire_date_format() ); |
||
607 | |||
608 | $serial_number = 1; |
||
609 | foreach ( $this->get_items() as $item ) { |
||
610 | $fields[ 'itemNumber' . $serial_number ] = $item->get_number(); |
||
611 | $fields[ 'itemDescription' . $serial_number ] = $item->get_description(); |
||
612 | $fields[ 'itemQuantity' . $serial_number ] = $item->get_quantity(); |
||
613 | $fields[ 'itemPrice' . $serial_number ] = $item->get_price()->get_minor_units()->format( 0, '', '' ); |
||
614 | |||
615 | $serial_number ++; |
||
616 | } |
||
617 | |||
618 | $fields['urlCancel'] = $this->get_cancel_url(); |
||
619 | $fields['urlSuccess'] = $this->get_success_url(); |
||
620 | $fields['urlError'] = $this->get_error_url(); |
||
621 | |||
622 | return $fields; |
||
623 | } |
||
624 | } |
||
625 |