PluginProbe
PayPlug for WooCommerce (Official) / 1.0.0
PayPlug for WooCommerce (Official) v1.0.0
3.0.0 2.18.0 1.0.17 1.0.18 1.0.19 1.0.20 1.0.21 1.0.22 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1.0 1.10.0 1.10.1 1.2.1 1.2.10 1.2.11 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 All 101 releases
payplug / src / Gateway / PayplugGateway.php

PayplugGateway.php in PayPlug for WooCommerce (Official) 1.0.0, at src/Gateway/PayplugGateway.php

1,308 lines 41.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Payplug\PayplugWoocommerce\Gateway;
4
5 // Exit if accessed directly
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit;
8 }
9
10 use Payplug\Authentication;
11 use Payplug\Exception\ConfigurationException;
12 use Payplug\Exception\HttpException;
13 use Payplug\Payplug;
14 use Payplug\PayplugWoocommerce\PayplugWoocommerceHelper;
15 use Payplug\Resource\Payment as PaymentResource;
16 use Payplug\Resource\Refund as RefundResource;
17 use WC_Payment_Gateway_CC;
18 use WC_Payment_Tokens;
19
20 /**
21 * PayPlug WooCommerce Gateway.
22 *
23 * @package Payplug\PayplugWoocommerce\Gateway
24 */
25 class PayplugGateway extends WC_Payment_Gateway_CC {
26
27 /**
28 * @var PayplugGatewayRequirements
29 */
30 private $requirements;
31
32 /**
33 * @var PayplugPermissions
34 */
35 private $permissions;
36
37 /**
38 * @var PayplugResponse
39 */
40 public $response;
41
42 /**
43 * @var PayplugApi
44 */
45 public $api;
46
47 /**
48 * @var \WC_Logger
49 */
50 protected static $log;
51
52 /**
53 * @var bool
54 */
55 protected static $log_enabled;
56
57 /**
58 * Logging method.
59 *
60 * @param string $message Log message.
61 * @param string $level Optional. Default 'info'.
62 * emergency|alert|critical|error|warning|notice|info|debug
63 */
64 public static function log( $message, $level = 'info' ) {
65 if ( self::$log_enabled ) {
66 if ( empty( self::$log ) ) {
67 self::$log = wc_get_logger();
68 }
69 self::$log->log( $level, $message, array( 'source' => 'payplug_gateway' ) );
70 }
71 }
72
73 public function __construct() {
74 $this->id = 'payplug';
75 $this->icon = '';
76 $this->has_fields = false;
77 $this->method_title = _x( 'PayPlug', 'Gateway method title', 'payplug' );
78 $this->method_description = __( 'Enable PayPlug for your customers.', 'payplug' );
79 $this->supports = array(
80 'products',
81 'refunds',
82 'tokenization',
83 );
84 $this->new_method_label = __( 'Pay with another credit card', 'payplug' );
85
86 $this->init_settings();
87 $this->requirements = new PayplugGatewayRequirements( $this );
88 if ( $this->user_logged_in() ) {
89 $this->init_payplug();
90 }
91 $this->init_form_fields();
92
93 $this->title = __( 'Credit card checkout', 'payplug' );
94 $this->description = ' ';
95 $this->mode = 'yes' === $this->get_option( 'mode', 'no' ) ? 'live' : 'test';
96 $this->debug = 'yes' === $this->get_option( 'debug', 'no' );
97 $this->email = $this->get_option( 'email' );
98 $this->payment_method = $this->get_option( 'payment_method' );
99 $this->oneclick = 'yes' === $this->get_option( 'oneclick', 'no' );
100
101 self::$log_enabled = $this->debug;
102
103 if ( 'test' === $this->mode ) {
104 $this->description = ( ! empty( $this->description ) ) ? " \n" : '';
105 $this->description .= __( 'You are in TEST MODE. In test mode you can use the card 4242424242424242 with any valid expiration date and CVC.', 'payplug' );
106 $this->description = trim( $this->description );
107 }
108
109 add_action( 'wp_enqueue_scripts', [ $this, 'scripts' ] );
110 add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, [ $this, 'process_admin_options' ] );
111 add_action( 'the_post', [ $this, 'validate_payment' ] );
112
113 add_filter( 'woocommerce_get_customer_payment_tokens', [ $this, 'filter_tokens' ], 10, 3 );
114 }
115
116 /**
117 * Validate order payment when the user is redirected to the success confirmation page.
118 *
119 * @throws \WC_Data_Exception
120 */
121 public function validate_payment() {
122 if ( ! is_wc_endpoint_url( 'order-received' ) || empty( $_GET['key'] ) ) {
123 return;
124 }
125
126 $order_id = wc_get_order_id_by_order_key( wc_clean( $_GET['key'] ) );
127 if ( empty( $order_id ) ) {
128 return;
129 }
130
131 $order = wc_get_order( $order_id );
132 if ( ! $order instanceof \WC_Order ) {
133 return;
134 }
135
136 $payment_method = PayplugWoocommerceHelper::is_pre_30() ? $order->payment_method : $order->get_payment_method();
137 if ( 'payplug' !== $payment_method ) {
138 return;
139 }
140
141 $transaction_id = PayplugWoocommerceHelper::is_pre_30() ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_transaction_id();
142 if ( empty( $transaction_id ) ) {
143 PayplugGateway::log( sprintf( 'Order #%s : Missing transaction id.', $order_id ), 'error' );
144
145 return;
146 }
147
148 try {
149 $payment = $this->api->payment_retrieve( $transaction_id );
150 } catch ( \Exception $e ) {
151 PayplugGateway::log(
152 sprintf( 'Order #%s : An error occurred while retrieving the payment data with the message : %s',
153 $order_id,
154 $e->getMessage()
155 )
156 );
157
158 return;
159 }
160
161 $this->response->process_payment( $payment );
162 }
163
164 /**
165 * Get payment icons.
166 *
167 * @return string
168 */
169 public function get_icon() {
170 $icons = apply_filters( 'payplug_payment_icons', [
171 'payplug' => '<img src="' . PAYPLUG_GATEWAY_PLUGIN_URL . '/assets/images/cards_icons.svg" alt="Visa & Mastercard" class="payplug-payment-icon" />',
172 ] );
173
174 $icons_str = '';
175 foreach ( $icons as $icon ) {
176 $icons_str .= $icon;
177 }
178
179 return $icons_str;
180 }
181
182 /**
183 * Check if this gateway is enabled
184 */
185 public function is_available() {
186 if ( 'yes' === $this->enabled ) {
187 return $this->requirements->satisfy_requirements() && ! empty( $this->get_api_key( $this->get_current_mode() ) );
188 }
189
190 return parent::is_available();
191 }
192
193 /**
194 * Load gateway settings.
195 */
196 public function init_settings() {
197 parent::init_settings();
198 $this->enabled = ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled'] ? 'yes' : 'no';
199 }
200
201 /**
202 * Register gateway settings.
203 */
204 public function init_form_fields() {
205 $fields = [
206 'enabled' => [
207 'title' => __( 'Enable/Disable', 'payplug' ),
208 'type' => 'checkbox',
209 'label' => __( 'Enable PayPlug', 'payplug' ),
210 'description' => __( 'Only Euro payments can be processed with PayPlug.', 'payplug' ),
211 'default' => 'no',
212 ],
213 'title_connexion' => [
214 'title' => __( 'Connection', 'payplug' ),
215 'type' => 'title',
216 ],
217 'email' => [
218 'type' => 'hidden',
219 'default' => '',
220 ],
221 'login' => [
222 'type' => 'login',
223 'default' => '',
224 ],
225 'payplug_test_key' => [
226 'type' => 'hidden',
227 'default' => '',
228 ],
229 'payplug_live_key' => [
230 'type' => 'hidden',
231 'default' => '',
232 ],
233 'payplug_merchant_id' => [
234 'type' => 'hidden',
235 'default' => '',
236 ],
237 'title_testmode' => [
238 'title' => __( 'Mode', 'payplug' ),
239 'type' => 'title',
240 ],
241 'mode' => [
242 'title' => '',
243 'label' => '',
244 'type' => 'yes_no',
245 'yes' => 'Live',
246 'no' => 'Test',
247 'description' => __( 'In TEST mode, all payments will be simulations and will not generate real transactions.', 'payplug' ),
248 'default' => 'no',
249 'hide_label' => true,
250 ],
251 'title_settings' => [
252 'title' => __( 'Settings', 'payplug' ),
253 'type' => 'title',
254 ],
255 'payment_method' => [
256 'title' => __( 'Payment page', 'payplug' ),
257 'type' => 'radio',
258 'description' => __( 'Customers will be redirected to a PayPlug payment page to finalize the transaction, or payments will be performed in an embeddable payment form on your website.', 'payplug' ),
259 'default' => 'redirect',
260 'desc_tip' => true,
261 'options' => array(
262 'redirect' => __( 'Redirect', 'payplug' ),
263 'embedded' => __( 'Integrated', 'payplug' ),
264 ),
265 ],
266 'debug' => [
267 'title' => __( 'Debug', 'payplug' ),
268 'type' => 'checkbox',
269 'description' => __( 'Debug mode saves additional information on your server for each operation done via the PayPlug plugin (Developer setting).', 'payplug' ),
270 'label' => __( 'Activate debug mode', 'payplug' ),
271 'default' => 'no',
272 'desc_tip' => true,
273 ],
274 'title_advanced_settings' => [
275 'title' => __( 'Advanced Settings', 'payplug' ),
276 'description' => __( 'This feature is available to PREMIUM accounts only. You can try it in TEST mode.',
277 'payplug' ),
278 'type' => 'title',
279 ],
280 'oneclick' => [
281 'title' => __( 'One Click Payment', 'payplug' ),
282 'type' => 'checkbox',
283 'label' => __( 'Activate', 'payplug' ),
284 'description' => __( 'Allow your customers to save their credit card information for later purchases.', 'payplug' ),
285 'default' => 'no',
286 'desc_tip' => true,
287 ],
288 ];
289
290 // Disable One-Click checkbox if the user doesn't have the permission to use it.
291 if ( $this->user_logged_in() && 'live' === $this->get_current_mode() ) {
292 $fields['oneclick']['disabled'] = ! $this->permissions->has_permissions( PayplugPermissions::SAVE_CARD );
293 }
294
295 /**
296 * Filter PayPlug gateway settings.
297 *
298 * @param array $fields
299 */
300 $fields = apply_filters( 'payplug_gateway_settings', $fields );
301 $this->form_fields = $fields;
302 }
303
304 /**
305 * Set global configuration for PayPlug instance.
306 */
307 public function init_payplug() {
308 $this->api = new PayplugApi( $this );
309 $this->api->init();
310
311 $this->permissions = new PayplugPermissions( $this );
312 $this->response = new PayplugResponse( $this );
313
314 // Register IPN handler
315 new PayplugIpnResponse( $this );
316 }
317
318 /**
319 * Embedded payment form scripts.
320 *
321 * Register scripts and additionnal data needed for the
322 * embedded payment form.
323 */
324 public function scripts() {
325 if ( ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) {
326 return;
327 }
328
329 // If PayPlug is not enabled bail.
330 if ( 'no' === $this->enabled ) {
331 return;
332 }
333
334 // If keys are not set bail.
335 if ( empty( $this->get_api_key( $this->mode ) ) ) {
336 PayplugGateway::log( 'Keys are not set correctly.' );
337
338 return;
339 }
340
341 // Register checkout styles.
342 wp_register_style( 'payplug-checkout', PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/css/payplug-checkout.css', [], PAYPLUG_GATEWAY_VERSION );
343 wp_enqueue_style( 'payplug-checkout' );
344
345
346 // Register scripts for embedded payment form.
347 if ( 'embedded' !== $this->payment_method ) {
348 return;
349 }
350
351 wp_register_script( 'payplug', 'https://api.payplug.com/js/1.2/form.js', [], '1.2', true );
352 wp_register_script( 'payplug-checkout', PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/js/payplug-checkout.js', [
353 'jquery',
354 'payplug'
355 ], PAYPLUG_GATEWAY_VERSION, true );
356 wp_localize_script( 'payplug-checkout', 'payplug_checkout_params', [
357 'ajax_url' => \WC_AJAX::get_endpoint( 'payplug_create_order' ),
358 'nonce' => [
359 'checkout' => wp_create_nonce( 'woocommerce-process_checkout' ),
360 ],
361 ] );
362 wp_enqueue_script( 'payplug-checkout' );
363 }
364
365 /**
366 * Filter saved tokens for the gateway.
367 *
368 * A token will be removed if :
369 * - it doesn't match the current merchant logged in,
370 * - or it doesn't match the current gateway mode,
371 * - or it is expired.
372 *
373 * @param array $tokens
374 * @param int $user_id
375 * @param string $gateway_id
376 *
377 * @return array
378 */
379 public function filter_tokens( $tokens, $user_id, $gateway_id ) {
380
381 if ( ! is_user_logged_in() || ! class_exists( 'WC_Payment_Gateway_CC' ) ) {
382 return $tokens;
383 }
384
385 /* @var \WC_Payment_Token_CC $token */
386 foreach ( $tokens as $k => $token ) {
387
388 if ( $this->id !== $token->get_gateway_id() ) {
389 continue;
390 }
391
392 // check if token is associated with a merchant id and if it match the current one
393 $token_merchant_id = $token->get_meta( 'payplug_account', true );
394 if ( empty( $token_merchant_id ) || $this->get_merchant_id() !== $token_merchant_id ) {
395 unset( $tokens[ $k ] );
396 continue;
397 }
398
399 // check if token is available for the current gateway mode
400 if ( $this->mode !== $token->get_meta( 'mode', true ) ) {
401 unset( $tokens[ $k ] );
402 continue;
403 }
404
405 // check if token is not expired
406 $current_month = \absint( date( 'n' ) );
407 $current_year = \absint( date( 'Y' ) );
408 if ( $current_year > (int) $token->get_expiry_year() ) {
409 unset( $tokens[ $k ] );
410 continue;
411 }
412
413 if ( $current_year === (int) $token->get_expiry_year() && $current_month >= (int) $token->get_expiry_month() ) {
414 unset( $tokens[ $k ] );
415 continue;
416 }
417 }
418
419 return $tokens;
420 }
421
422 public function payment_fields() {
423 $description = $this->get_description();
424 if ( ! empty( $description ) ) {
425 echo wpautop( wptexturize( $description ) );
426 }
427
428 if ( $this->oneclick_available() ) {
429 $this->tokenization_script();
430 $this->saved_payment_methods();
431 }
432 }
433
434 /**
435 * Handle admin display.
436 */
437 public function admin_options() {
438 wp_enqueue_style(
439 'payplug-gateway-style',
440 PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/css/app.css',
441 [],
442 PAYPLUG_GATEWAY_VERSION
443 );
444
445 $payplug_requirements = new PayplugGatewayRequirements( $this ); ?>
446
447 <h2 class="title--logo"><?php esc_html( $this->get_method_title() ) ?></h2>
448 <p><?php _e( sprintf( 'Version %s', PAYPLUG_GATEWAY_VERSION ) ); ?></p>
449 <div class="payplug-requirements">
450 <?php echo $payplug_requirements->curl_requirement(); ?>
451 <?php echo $payplug_requirements->php_requirement(); ?>
452 <?php echo $payplug_requirements->openssl_requirement(); ?>
453 <?php echo $payplug_requirements->account_requirement(); ?>
454 <?php echo $payplug_requirements->currency_requirement(); ?>
455 </div>
456 <?php echo wp_kses_post( wpautop( $this->get_method_description() ) ); ?>
457
458 <?php if ( $this->user_logged_in() ) : ?>
459 <table class="form-table">
460 <?php $this->generate_settings_html( $this->get_form_fields() ); ?>
461 </table>
462 <?php else:
463 $GLOBALS['hide_save_button'] = true; ?>
464 <h3 class="wc-settings-sub-title"><?php _e( 'Connection', 'payplug' ); ?></h3>
465 <table class="form-table">
466 <tbody>
467 <tr valign="top">
468 <th scope="row" class="titledesc">
469 <label for="payplug_email"><?php _e( 'Email', 'payplug' ); ?></label>
470 </th>
471 <td class="forminp">
472 <fieldset>
473 <legend class="screen-reader-text"><span><?php _e( 'Email', 'payplug' ); ?></span></legend>
474 <input class="input-text regular-input" type="text" name="payplug_email" id="payplug_email"
475 value="" placeholder="<?php _e( 'your@email.com', 'payplug' ); ?>"/>
476 </fieldset>
477 </td>
478 </tr>
479 <tr valign="top">
480 <th scope="row" class="titledesc">
481 <label for="payplug_password"><?php _e( 'Password', 'payplug' ); ?></label>
482 </th>
483 <td class="forminp">
484 <fieldset>
485 <legend class="screen-reader-text"><span><?php _e( 'Password', 'payplug' ); ?></span>
486 </legend>
487 <input class="input-text regular-input" type="password" name="payplug_password"
488 id="payplug_password" value=""/>
489 </fieldset>
490 </td>
491 </tr>
492 <tr valign="top">
493 <td class="forminp">
494 <input class="button" type="submit" value="<?php _e( 'Login', 'payplug' ); ?>">
495 <?php wp_nonce_field( 'payplug_user_login', '_loginaction' ); ?>
496 </td>
497 </tr>
498 </tbody>
499 </table>
500 <?php
501 endif;
502 }
503
504 /**
505 * Process admin options.
506 *
507 * @return bool
508 */
509 public function process_admin_options() {
510 $data = $this->get_post_data();
511
512 // Handle logout process
513 if (
514 isset( $data['submit_logout'] )
515 && false !== check_admin_referer( 'payplug_user_logout', '_logoutaction' )
516 ) {
517
518 $this->permissions->clear_permissions();
519
520 $data = get_option( $this->get_option_key() );
521 $data['payplug_test_key'] = '';
522 $data['payplug_live_key'] = '';
523 $data['payplug_merchant_id'] = '';
524 $data['enabled'] = 'no';
525 $data['mode'] = 'no';
526 update_option( $this->get_option_key(),
527 apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $data ) );
528 \WC_Admin_Settings::add_message( __( 'Successfully logged out.', 'payplug' ) );
529
530 return true;
531 }
532
533 // Handle login process
534 if (
535 isset( $data['payplug_email'] )
536 && false !== check_admin_referer( 'payplug_user_login', '_loginaction' )
537 ) {
538 $email = $data['payplug_email'];
539 $password = $data['payplug_password'];
540 $response = $this->retrieve_user_api_keys( $email, $password );
541 if ( is_wp_error( $response ) ) {
542 \WC_Admin_Settings::add_error( $response->get_error_message() );
543
544 return false;
545 }
546
547 // try to use the api keys to retrieve the merchant id
548 $merchant_id = isset( $response['test'] ) ? $this->retrieve_merchant_id( $response['test'] ) : '';
549
550 $this->init_form_fields();
551 $fields = $this->get_form_fields();
552 $data = [];
553
554 // Load existing values if the user is re-login.
555 foreach ( $fields as $key => $field ) {
556 if ( in_array( $field['type'], [ 'title', 'login' ] ) ) {
557 continue;
558 }
559
560 switch ( $key ) {
561 case 'enabled':
562 $val = 'yes';
563 break;
564 case 'mode':
565 $val = 'no';
566 break;
567 case 'payplug_test_key':
568 $val = esc_attr( $response['test'] );
569 break;
570 case 'payplug_live_key':
571 $val = esc_attr( $response['live'] );
572 break;
573 case 'payplug_merchant_id':
574 $val = esc_attr( $merchant_id );
575 break;
576 case 'email':
577 $val = esc_html( $email );
578 break;
579 default:
580 $val = $this->get_option( $key );
581 }
582
583 $data[ $key ] = $val;
584 }
585
586 $this->set_post_data( $data );
587 update_option( $this->get_option_key(),
588 apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $data ) );
589 \WC_Admin_Settings::add_message( __( 'Successfully logged in.', 'payplug' ) );
590
591 return true;
592 }
593
594 // Don't let user without live key leave TEST mode.
595 $mode_fieldkey = $this->get_field_key( 'mode' );
596 $live_key_fieldkey = $this->get_field_key( 'payplug_live_key' );
597 if ( isset( $data[ $mode_fieldkey ] ) && '1' === $data[ $mode_fieldkey ] && empty( $data[ $live_key_fieldkey ] ) ) {
598 $data[ $mode_fieldkey ] = '0';
599 $this->set_post_data( $data );
600 \WC_Admin_Settings::add_error( __( 'Your account does not support LIVE mode at the moment, it must be validated first. If your account has already been validated, please log out and log in again.', 'payplug' ) );
601 }
602
603 // Check user permissions before activating one-click feature.
604 $oneclick_fieldkey = $this->get_field_key( 'oneclick' );
605 if (
606 isset( $data[ $oneclick_fieldkey ] )
607 && '1' === $data[ $oneclick_fieldkey ]
608 && false === $this->permissions->has_permissions( PayplugPermissions::SAVE_CARD )
609 ) {
610 $data[ $oneclick_fieldkey ] = '0';
611 \WC_Admin_Settings::add_error( __( 'Only PREMIUM accounts can enable the One Click option in LIVE mode.', 'payplug' ) );
612 }
613
614 parent::process_admin_options();
615 }
616
617 /**
618 * Process payment.
619 *
620 * @param int $order_id
621 *
622 * @return array
623 * @throws \Exception
624 */
625 public function process_payment( $order_id ) {
626
627 PayplugGateway::log( sprintf( 'Processing payment for order #%s', $order_id ) );
628
629 $order = wc_get_order( $order_id );
630 $customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
631 $amount = (int) PayplugWoocommerceHelper::get_payplug_amount( $order->get_total() );
632 $amount = $this->validate_order_amount( $amount );
633 if ( is_wp_error( $amount ) ) {
634 PayplugGateway::log( sprintf( 'Invalid amount %s for the order.', $order->get_total() ), 'error' );
635 throw new \Exception( $amount->get_error_message() );
636 }
637
638 $payment_token_id = ( isset( $_POST[ 'wc-' . $this->id . '-payment-token' ] ) && 'new' !== $_POST[ 'wc-' . $this->id . '-payment-token' ] )
639 ? wc_clean( $_POST[ 'wc-' . $this->id . '-payment-token' ] )
640 : false;
641
642 if ( $payment_token_id && $this->oneclick_available() && (int) $customer_id > 0 ) {
643 PayplugGateway::log( sprintf( 'Payment token found.', $amount ) );
644
645 return $this->process_payment_with_token( $order, $amount, $customer_id, $payment_token_id );
646 }
647
648 return $this->process_standard_payment( $order, $amount, $customer_id );
649 }
650
651 /**
652 * @param \WC_Order $order
653 * @param int $amount
654 * @param int $customer_id
655 *
656 * @return array
657 * @throws \Exception
658 */
659 public function process_standard_payment( $order, $amount, $customer_id ) {
660
661 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
662 $customer_details = $this->prepare_customer_data( $order );
663
664 if (
665 ! empty( $customer_details['country'] )
666 && ! PayplugWoocommerceHelper::is_country_supported( $customer_details['country'] )
667 ) {
668 $customer_details['country'] = '';
669 }
670
671 try {
672 $payment_data = [
673 'amount' => $amount,
674 'currency' => get_woocommerce_currency(),
675 'allow_save_card' => $this->oneclick_available() && (int) $customer_id > 0,
676 'customer' => [
677 'first_name' => $this->limit_length( $customer_details['first_name'] ),
678 'last_name' => $this->limit_length( $customer_details['last_name'] ),
679 'email' => $this->limit_length( $customer_details['email'], 255 ),
680 'address1' => ( ! empty( $customer_details['address1'] ) ) ? $this->limit_length( $customer_details['address1'], 255 ) : null,
681 'postcode' => ( ! empty( $customer_details['postcode'] ) ) ? $this->limit_length( $customer_details['postcode'], 16 ) : null,
682 'city' => ( ! empty( $customer_details['city'] ) ) ? $this->limit_length( $customer_details['city'] ) : null,
683 'country' => ( ! empty( $customer_details['country'] ) ) ? $this->limit_length( $customer_details['country'], 2 ) : null,
684 ],
685 'hosted_payment' => [
686 'return_url' => esc_url_raw( $order->get_checkout_order_received_url() ),
687 'cancel_url' => esc_url_raw( $order->get_cancel_order_url_raw() ),
688 ],
689 'notification_url' => esc_url_raw( WC()->api_request_url( 'PayplugGateway' ) ),
690 'metadata' => [
691 'order_id' => $order_id,
692 'customer_id' => ( (int) $customer_id > 0 ) ? $customer_id : 'guest',
693 'domain' => $this->limit_length( esc_url_raw( home_url() ), 500 ),
694 ],
695 ];
696
697 /**
698 * Filter the payment data before it's used
699 *
700 * @param array $payment_data
701 * @param int $order_id
702 * @param array $customer_details
703 */
704 $payment_data = apply_filters( 'payplug_gateway_payment_data', $payment_data, $order_id, $customer_details );
705 $payment = $this->api->payment_create( $payment_data );
706
707 // Save transaction id for the order
708 PayplugWoocommerceHelper::is_pre_30()
709 ? update_post_meta( $order_id, '_transaction_id', $payment->id )
710 : $order->set_transaction_id( $payment->id );
711
712 if ( is_callable( [ $order, 'save' ] ) ) {
713 $order->save();
714 }
715
716 /**
717 * Fires once a payment has been created.
718 *
719 * @param int $order_id Order ID
720 * @param PaymentResource $payment Payment resource
721 */
722 \do_action( 'payplug_gateway_payment_created', $order_id, $payment );
723
724 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata( $payment );
725 PayplugWoocommerceHelper::save_transaction_metadata( $order, $metadata );
726
727 PayplugGateway::log( sprintf( 'Payment creation complete for order #%s', $order_id ) );
728
729 return [
730 'result' => 'success',
731 'redirect' => $payment->hosted_payment->payment_url,
732 'cancel' => $payment->hosted_payment->cancel_url,
733 ];
734 } catch ( HttpException $e ) {
735 PayplugGateway::log( sprintf( 'Error while processing order #%s : %s', $order_id, wc_print_r( $e->getErrorObject(), true ) ), 'error' );
736 throw new \Exception( __( 'Payment processing failed. Please retry.', 'payplug' ) );
737 } catch ( \Exception $e ) {
738 PayplugGateway::log( sprintf( 'Error while processing order #%s : %s', $order_id, $e->getMessage() ), 'error' );
739 throw new \Exception( __( 'Payment processing failed. Please retry.', 'payplug' ) );
740 }
741 }
742
743 /**
744 * @param \WC_Order $order
745 * @param int $amount
746 * @param int $customer_id
747 * @param string $token_id
748 *
749 * @return array
750 * @throws \Exception
751 */
752 public function process_payment_with_token( $order, $amount, $customer_id, $token_id ) {
753
754 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
755 $customer_details = $this->prepare_customer_data( $order );
756 $payment_token = WC_Payment_Tokens::get( $token_id );
757 if ( ! $payment_token || (int) $customer_id !== (int) $payment_token->get_user_id() ) {
758 PayplugGateway::log( 'Could not find the payment token or the payment doesn\'t belong to the current user.', 'error' );
759 throw new \Exception( __( 'Invalid payment method.', 'payplug' ) );
760 }
761
762 if (
763 ! empty( $customer_details['country'] )
764 && ! PayplugWoocommerceHelper::is_country_supported( $customer_details['country'] )
765 ) {
766 $customer_details['country'] = '';
767 }
768
769 try {
770 $payment_data = [
771 'amount' => $amount,
772 'currency' => get_woocommerce_currency(),
773 'payment_method' => $payment_token->get_token(),
774 'customer' => [
775 'first_name' => $this->limit_length( $customer_details['first_name'] ),
776 'last_name' => $this->limit_length( $customer_details['last_name'] ),
777 'email' => $this->limit_length( $customer_details['email'], 255 ),
778 'address1' => ( ! empty( $customer_details['address1'] ) ) ? $this->limit_length( $customer_details['address1'], 255 ) : null,
779 'postcode' => ( ! empty( $customer_details['postcode'] ) ) ? $this->limit_length( $customer_details['postcode'], 16 ) : null,
780 'city' => ( ! empty( $customer_details['city'] ) ) ? $this->limit_length( $customer_details['city'] ) : null,
781 'country' => ( ! empty( $customer_details['country'] ) ) ? $this->limit_length( $customer_details['country'], 2 ) : null,
782 ],
783 'notification_url' => esc_url_raw( WC()->api_request_url( 'PayplugGateway' ) ),
784 'metadata' => [
785 'order_id' => $order_id,
786 'customer_id' => ( (int) $customer_id > 0 ) ? $customer_id : 'guest',
787 'domain' => $this->limit_length( esc_url_raw( home_url() ), 500 ),
788 ],
789 ];
790
791 /** This filter is documented in src/Gateway/PayplugGateway */
792 $payment_data = apply_filters( 'payplug_gateway_payment_data', $payment_data, $order_id, $customer_details );
793 $payment = $this->api->payment_create( $payment_data );
794
795 /** This action is documented in src/Gateway/PayplugGateway */
796 \do_action( 'payplug_gateway_payment_created', $order_id, $payment );
797
798 $this->response->process_payment( $payment, true );
799
800 PayplugGateway::log( sprintf( 'Payment process complete for order #%s', $order_id ) );
801
802 return [
803 'result' => 'success',
804 'redirect' => $order->get_checkout_order_received_url(),
805 ];
806 } catch ( HttpException $e ) {
807 PayplugGateway::log( sprintf( 'Error while processing order #%s : %s', $order_id, wc_print_r( $e->getErrorObject(), true ) ), 'error' );
808 throw new \Exception( __( 'Payment processing failed. Please retry.', 'payplug' ) );
809 } catch ( \Exception $e ) {
810 PayplugGateway::log( sprintf( 'Error while processing order #%s : %s', $order_id, $e->getMessage() ), 'error' );
811 throw new \Exception( __( 'Payment processing failed. Please retry.', 'payplug' ) );
812 }
813 }
814
815 /**
816 * Process refund for an order paid with PayPlug gateway.
817 *
818 * @param int $order_id
819 * @param null $amount
820 * @param string $reason
821 *
822 * @return bool|\WP_Error
823 */
824 public function process_refund( $order_id, $amount = null, $reason = '' ) {
825
826 PayplugGateway::log( sprintf( 'Processing refund for order #%s', $order_id ) );
827
828 $order = wc_get_order( $order_id );
829 if ( ! $order instanceof \WC_Order ) {
830 PayplugGateway::log( sprintf( 'The order #%s does not exist.', $order_id ), 'error' );
831
832 return new \WP_Error( 'process_refund_error', sprintf( __( 'The order %s does not exist.', 'payplug' ), $order_id ) );
833 }
834
835 $transaction_id = PayplugWoocommerceHelper::is_pre_30() ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_transaction_id();
836 if ( empty( $transaction_id ) ) {
837 PayplugGateway::log( sprintf( 'The order does not have PayPlug transaction ID associated with it.', $order_id ), 'error' );
838
839 return new \WP_Error( 'process_refund_error', __( 'No PayPlug transaction was found for this order. The refund could not be processed.', 'payplug' ) );
840 }
841
842 $customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
843
844 $data = [
845 'metadata' => [
846 'order_id' => $order_id,
847 'customer_id' => ( (int) $customer_id > 0 ) ? $customer_id : 'guest',
848 ]
849 ];
850
851 if ( ! is_null( $amount ) ) {
852 $data['amount'] = PayplugWoocommerceHelper::get_payplug_amount( $amount );
853 }
854
855 if ( ! empty( $reason ) ) {
856 $data['metadata']['reason'] = $reason;
857 }
858
859 /**
860 * Filter the refund data before it's used.
861 *
862 * @param array $data
863 * @param int $order_id
864 * @param string $transaction_id
865 */
866 $data = apply_filters( 'payplug_gateway_refund_data', $data, $order_id, $transaction_id );
867
868 try {
869 $refund = $this->api->refund_create( $transaction_id, $data );
870
871 /**
872 * Fires once a refund has been created.
873 *
874 * @param int $order_id Order ID
875 * @param RefundResource $refund Refund resource
876 * @param string $transaction_id Transaction id
877 */
878 \do_action( 'payplug_gateway_refund_created', $order_id, $refund, $transaction_id );
879
880 $refund_meta_key = sprintf( '_pr_%s', wc_clean( $refund->id ) );
881 if ( PayplugWoocommerceHelper::is_pre_30() ) {
882 update_post_meta( $order_id, $refund_meta_key, $refund->id );
883 } else {
884 $order->add_meta_data( $refund_meta_key, $refund->id, true );
885 $order->save();
886 }
887
888 $note = sprintf( __( 'Refund %s : Refunded %s', 'payplug' ), wc_clean( $refund->id ), wc_price( ( (int) $refund->amount ) / 100 ) );
889 if ( ! empty( $refund->metadata['reason'] ) ) {
890 $note .= sprintf( ' (%s)', esc_html( $refund->metadata['reason'] ) );
891 }
892 $order->add_order_note( $note );
893
894 try {
895 $payment = $this->api->payment_retrieve( $transaction_id );
896 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata( $payment );
897 PayplugWoocommerceHelper::save_transaction_metadata( $order, $metadata );
898 } catch ( \Exception $e ) {
899 }
900
901 PayplugGateway::log( 'Refund process complete for the order.' );
902
903 return true;
904 } catch ( HttpException $e ) {
905 PayplugGateway::log( sprintf( 'Refund request error for the order %s from PayPlug API : %s', $order_id, wc_print_r( $e->getErrorObject(), true ) ), 'error' );
906
907 return new \WP_Error( 'process_refund_error', __( 'The transaction could not be refunded. Please try again.', 'payplug' ) );
908 } catch ( \Exception $e ) {
909 PayplugGateway::log( sprintf( 'Refund request error for the order %s : %s', $order_id, wc_clean( $e->getMessage() ) ), 'error' );
910
911 return new \WP_Error( 'process_refund_error', __( 'The transaction could not be refunded. Please try again.', 'payplug' ) );
912 }
913 }
914
915 /**
916 * @param \WC_Order $order
917 *
918 * @return array
919 */
920 public function prepare_customer_data( $order ) {
921 return [
922 'first_name' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_first_name : $order->get_billing_first_name(),
923 'last_name' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_last_name : $order->get_billing_last_name(),
924 'email' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_email : $order->get_billing_email(),
925 'address1' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_address_1 : $order->get_billing_address_1(),
926 'address2' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_address_2 : $order->get_billing_address_2(),
927 'postcode' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_postcode : $order->get_billing_postcode(),
928 'city' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_city : $order->get_billing_city(),
929 'country' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_country : $order->get_billing_country(),
930 ];
931 }
932
933 /**
934 * Check the order amount to ensure it's on the allowed range.
935 *
936 * @param int $amount
937 *
938 * @return int|\WP_Error
939 */
940 public function validate_order_amount( $amount ) {
941 if (
942 $amount < PayplugWoocommerceHelper::get_minimum_amount()
943 || $amount > PayplugWoocommerceHelper::get_maximum_amount()
944 ) {
945 return new \WP_Error(
946 'invalid order amount',
947 sprintf( __( 'Payments for this amount (%s) are not authorised with this payment gateway.', 'payplug' ), \wc_price( $amount / 100 ) )
948 );
949 }
950
951 return $amount;
952 }
953
954 /**
955 * Limit string length.
956 *
957 * @param string $value
958 * @param int $maxlength
959 *
960 * @return string
961 */
962 public function limit_length( $value, $maxlength = 100 ) {
963 return ( strlen( $value ) > $maxlength ) ? substr( $value, 0, $maxlength ) : $value;
964 }
965
966 /**
967 * Get user's keys.
968 *
969 * @param string $email
970 * @param string $password
971 *
972 * @return array|\WP_Error
973 */
974 public function retrieve_user_api_keys( $email, $password ) {
975 if ( empty( $email ) || empty( $password ) ) {
976 return new \WP_Error( 'missing_login_data', __( 'Please fill all login fields', 'payplug' ) );
977 }
978
979 try {
980 $response = Authentication::getKeysByLogin( $email, $password );
981 if ( empty( $response ) || ! isset( $response['httpResponse'] ) ) {
982 return new \WP_Error( 'invalid_credentials', __( 'Invalid credentials.', 'payplug' ) );
983 }
984
985 return $response['httpResponse']['secret_keys'];
986 } catch ( HttpException $e ) {
987 return new \WP_Error( 'invalid_credentials', __( 'Invalid credentials.', 'payplug' ) );
988 }
989 }
990
991 /**
992 * Get user merchant id.
993 *
994 * This method might be called during the login process before the global PayPlug
995 * configuration is set. In that case you can pass a valid token to make the request.
996 *
997 * @param string|null $key
998 *
999 * @return string
1000 */
1001 public function retrieve_merchant_id( $key = null ) {
1002 try {
1003 $response = ! is_null( $key ) ? Authentication::getAccount( new Payplug( $key ) ) : Authentication::getAccount();
1004 $merchant_id = isset( $response['httpResponse']['id'] ) ? $response['httpResponse']['id'] : '';
1005 } catch ( ConfigurationException $e ) {
1006 PayplugGateway::log( sprintf( 'Missing API key for PayPlug client : %s', wc_print_r( $e->getMessage(), true ) ), 'error' );
1007
1008 $merchant_id = '';
1009 } catch ( HttpException $e ) {
1010 PayplugGateway::log( sprintf( 'Account request error from PayPlug API : %s', wc_print_r( $e->getErrorObject(), true ) ), 'error' );
1011
1012 $merchant_id = '';
1013 } catch ( \Exception $e ) {
1014 PayplugGateway::log( sprintf( 'Account request error : %s', wc_clean( $e->getMessage() ) ), 'error' );
1015
1016 $merchant_id = '';
1017 }
1018
1019 return $merchant_id;
1020 }
1021
1022 /**
1023 * Generate Hidden HTML.
1024 *
1025 * @param string $key
1026 * @param array $data
1027 *
1028 * @return string
1029 */
1030 public function generate_hidden_html( $key, $data ) {
1031 $field_key = $this->get_field_key( $key );
1032 $defaults = array(
1033 'title' => '',
1034 'disabled' => false,
1035 'class' => '',
1036 'css' => '',
1037 'placeholder' => '',
1038 'type' => 'text',
1039 'desc_tip' => false,
1040 'description' => '',
1041 'custom_attributes' => array(),
1042 );
1043
1044 $data = wp_parse_args( $data, $defaults );
1045
1046 ob_start();
1047 ?>
1048 <input
1049 type="<?php echo esc_attr( $data['type'] ); ?>" name="<?php echo esc_attr( $field_key ); ?>"
1050 id="<?php echo esc_attr( $field_key ); ?>"
1051 value="<?php echo esc_attr( $this->get_option( $key ) ); ?>"/>
1052 <?php
1053
1054 return ob_get_clean();
1055 }
1056
1057 /**
1058 * Generate Yes/No Input HTML.
1059 *
1060 * @param string $key
1061 * @param array $data
1062 *
1063 * @return string
1064 */
1065 public function generate_yes_no_html( $key, $data ) {
1066 $field_key = $this->get_field_key( $key );
1067 $defaults = array(
1068 'title' => '',
1069 'no' => 'No',
1070 'yes' => 'Yes',
1071 'disabled' => false,
1072 'class' => '',
1073 'css' => '',
1074 'placeholder' => '',
1075 'type' => 'text',
1076 'desc_tip' => false,
1077 'description' => '',
1078 'custom_attributes' => [],
1079 'hide_label' => false,
1080 );
1081
1082 $data = wp_parse_args( $data, $defaults );
1083 $checked = 'yes' === $this->get_option( $key ) ? '1' : '0';
1084
1085 ob_start();
1086 ?>
1087 <tr valign="top">
1088 <?php if ( ! $data['hide_label'] ) : ?>
1089 <th scope="row" class="titledesc">
1090 <label for="<?php echo esc_attr( $field_key ); ?>">
1091 <?php echo wp_kses_post( $data['title'] ); ?>
1092 <?php echo $this->get_tooltip_html( $data ); ?>
1093 </label>
1094 </th>
1095 <?php endif; ?>
1096 <td class="forminp">
1097 <fieldset>
1098 <legend class="screen-reader-text"><span><?php echo wp_kses_post( $data['title'] ); ?></span>
1099 </legend>
1100 <div class="radio--custom">
1101 <input class="radio radio-yes <?php echo esc_attr( $data['class'] ); ?>"
1102 type="radio"
1103 name="<?php echo esc_attr( $field_key ); ?>"
1104 id="<?php echo esc_attr( $field_key ); ?>-yes"
1105 value="1"
1106 <?php checked( '1', $checked ); ?>
1107 <?php disabled( $data['disabled'], true ); ?>
1108 <?php echo $this->get_custom_attribute_html( $data ); ?>>
1109 <label for="<?php echo esc_attr( $field_key ); ?>-yes"><?php echo esc_html( $data['yes'] ); ?></label>
1110 </div>
1111 <div class="radio--custom">
1112 <input class="radio radio-no <?php echo esc_attr( $data['class'] ); ?>"
1113 type="radio"
1114 name="<?php echo esc_attr( $field_key ); ?>"
1115 id="<?php echo esc_attr( $field_key ); ?>-no"
1116 value="0"
1117 <?php checked( '0', $checked ); ?>
1118 <?php disabled( $data['disabled'], true ); ?>
1119 <?php echo $this->get_custom_attribute_html( $data ); ?>>
1120 <label for="<?php echo esc_attr( $field_key ); ?>-no"><?php echo esc_html( $data['no'] ); ?></label>
1121 </div>
1122 <?php echo $this->get_description_html( $data ); ?>
1123 </fieldset>
1124 </td>
1125 </tr>
1126 <?php
1127
1128 return ob_get_clean();
1129 }
1130
1131 /**
1132 * Generate Radio Input HTML.
1133 *
1134 * @param string $key
1135 * @param array $data
1136 *
1137 * @return string
1138 */
1139 public function generate_radio_html( $key, $data ) {
1140 $field_key = $this->get_field_key( $key );
1141 $defaults = array(
1142 'title' => '',
1143 'disabled' => false,
1144 'class' => '',
1145 'css' => '',
1146 'placeholder' => '',
1147 'type' => 'text',
1148 'desc_tip' => false,
1149 'description' => '',
1150 'custom_attributes' => [],
1151 'options' => [],
1152 );
1153
1154 $data = wp_parse_args( $data, $defaults );
1155
1156 ob_start();
1157 ?>
1158 <tr valign="top">
1159 <th scope="row" class="titledesc">
1160 <label for="<?php echo esc_attr( $field_key ); ?>">
1161 <?php echo wp_kses_post( $data['title'] ); ?>
1162 <?php echo $this->get_tooltip_html( $data ); ?>
1163 </label>
1164 </th>
1165 <td class="forminp">
1166 <fieldset>
1167 <legend class="screen-reader-text"><span><?php echo wp_kses_post( $data['title'] ); ?></span>
1168 </legend>
1169 <?php foreach ( $data['options'] as $option_key => $option_value ) : ?>
1170 <input class="radio <?php echo esc_attr( $data['class'] ); ?>"
1171 type="radio"
1172 name="<?php echo esc_attr( $field_key ); ?>"
1173 id="<?php echo esc_attr( $field_key ); ?>-<?php echo esc_attr( $option_key ); ?>"
1174 value="<?php echo esc_attr( $option_key ); ?>"
1175 <?php checked( $option_key, $this->get_option( $key ) ); ?>
1176 <?php disabled( $data['disabled'], true ); ?>
1177 <?php echo $this->get_custom_attribute_html( $data ); ?>>
1178 <label for="<?php echo esc_attr( $field_key ); ?>-<?php echo esc_attr( $option_key ); ?>"><?php echo esc_html( $option_value ); ?></label>
1179 <?php endforeach; ?>
1180 </fieldset>
1181 </td>
1182 </tr>
1183 <?php
1184
1185 return ob_get_clean();
1186 }
1187
1188 /**
1189 * Generate Login HTML.
1190 *
1191 * @param string $key
1192 * @param array $data
1193 *
1194 * @return string
1195 */
1196 public function generate_login_html( $key, $data ) {
1197 $field_key = $this->get_field_key( $key );
1198 $defaults = [];
1199
1200 $data = wp_parse_args( $data, $defaults );
1201
1202 ob_start();
1203 ?>
1204 <tr valign="top">
1205 <td class="forminp">
1206 <p><?php echo $this->get_option( 'email' ); ?></p>
1207 <p>
1208 <input type="submit" name="submit_logout" value="<?php _e( 'Logout', 'payplug' ); ?>">
1209 <?php wp_nonce_field( 'payplug_user_logout', '_logoutaction' ); ?>
1210 |
1211 <a href="https://portal.payplug.com"
1212 target="_blank"><?php _e( 'Go to your PayPlug Portal', 'payplug' ); ?></a>
1213 </p>
1214 </td>
1215 </tr>
1216 <?php
1217
1218 return ob_get_clean();
1219 }
1220
1221 /**
1222 * Validate Radio Field.
1223 *
1224 * Make sure the data is escaped correctly, etc.
1225 *
1226 * @param string $key
1227 * @param string|null $value Posted Value
1228 *
1229 * @return string
1230 */
1231 public function validate_radio_field( $key, $value ) {
1232 $value = is_null( $value ) ? '' : $value;
1233
1234 return wc_clean( stripslashes( $value ) );
1235 }
1236
1237 /**
1238 * Validate Yes/No Field.
1239 *
1240 * @param string $key
1241 * @param string $value Posted Value
1242 *
1243 * @return string
1244 */
1245 public function validate_yes_no_field( $key, $value ) {
1246 return ( '1' === (string) $value ) ? 'yes' : 'no';
1247 }
1248
1249 /**
1250 * Get PayPlug gateway mode.
1251 *
1252 * @return string
1253 */
1254 public function get_current_mode() {
1255 return ( 'yes' === $this->get_option( 'mode' ) ) ? 'live' : 'test';
1256 }
1257
1258 /**
1259 * Get user API key.
1260 *
1261 * @param string $mode
1262 *
1263 * @return string
1264 */
1265 public function get_api_key( $mode = 'test' ) {
1266
1267 switch ( $mode ) {
1268 case 'test':
1269 $key = $this->get_option( 'payplug_test_key' );
1270 break;
1271 case 'live':
1272 $key = $this->get_option( 'payplug_live_key' );
1273 break;
1274 default:
1275 $key = '';
1276 break;
1277 }
1278
1279 return $key;
1280 }
1281
1282 /**
1283 * Get current merchant id.
1284 *
1285 * @return string
1286 */
1287 public function get_merchant_id() {
1288 return $this->get_option( 'payplug_merchant_id', '' );
1289 }
1290
1291 /**
1292 * Check if user is logged in and we have an API key for TEST mode.
1293 *
1294 * @return bool
1295 */
1296 public function user_logged_in() {
1297 return ! empty( $this->get_option( 'payplug_test_key' ) );
1298 }
1299
1300 /**
1301 * Check if oneclick payment is activated and merchant can use it.
1302 *
1303 * @return bool
1304 */
1305 public function oneclick_available() {
1306 return $this->oneclick && $this->permissions->has_permissions( PayplugPermissions::SAVE_CARD );
1307 }
1308 }