PluginProbe
PayPlug for WooCommerce (Official) / 1.0.4
PayPlug for WooCommerce (Official) v1.0.4
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.4, at src/Gateway/PayplugGateway.php

1,307 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->saved_payment_methods();
430 }
431 }
432
433 /**
434 * Handle admin display.
435 */
436 public function admin_options() {
437 wp_enqueue_style(
438 'payplug-gateway-style',
439 PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/css/app.css',
440 [],
441 PAYPLUG_GATEWAY_VERSION
442 );
443
444 $payplug_requirements = new PayplugGatewayRequirements( $this ); ?>
445
446 <h2 class="title--logo"><?php esc_html( $this->get_method_title() ) ?></h2>
447 <p><?php _e( sprintf( 'Version %s', PAYPLUG_GATEWAY_VERSION ) ); ?></p>
448 <div class="payplug-requirements">
449 <?php echo $payplug_requirements->curl_requirement(); ?>
450 <?php echo $payplug_requirements->php_requirement(); ?>
451 <?php echo $payplug_requirements->openssl_requirement(); ?>
452 <?php echo $payplug_requirements->account_requirement(); ?>
453 <?php echo $payplug_requirements->currency_requirement(); ?>
454 </div>
455 <?php echo wp_kses_post( wpautop( $this->get_method_description() ) ); ?>
456
457 <?php if ( $this->user_logged_in() ) : ?>
458 <table class="form-table">
459 <?php $this->generate_settings_html( $this->get_form_fields() ); ?>
460 </table>
461 <?php else:
462 $GLOBALS['hide_save_button'] = true; ?>
463 <h3 class="wc-settings-sub-title"><?php _e( 'Connection', 'payplug' ); ?></h3>
464 <table class="form-table">
465 <tbody>
466 <tr valign="top">
467 <th scope="row" class="titledesc">
468 <label for="payplug_email"><?php _e( 'Email', 'payplug' ); ?></label>
469 </th>
470 <td class="forminp">
471 <fieldset>
472 <legend class="screen-reader-text"><span><?php _e( 'Email', 'payplug' ); ?></span></legend>
473 <input class="input-text regular-input" type="text" name="payplug_email" id="payplug_email"
474 value="" placeholder="<?php _e( 'your@email.com', 'payplug' ); ?>"/>
475 </fieldset>
476 </td>
477 </tr>
478 <tr valign="top">
479 <th scope="row" class="titledesc">
480 <label for="payplug_password"><?php _e( 'Password', 'payplug' ); ?></label>
481 </th>
482 <td class="forminp">
483 <fieldset>
484 <legend class="screen-reader-text"><span><?php _e( 'Password', 'payplug' ); ?></span>
485 </legend>
486 <input class="input-text regular-input" type="password" name="payplug_password"
487 id="payplug_password" value=""/>
488 </fieldset>
489 </td>
490 </tr>
491 <tr valign="top">
492 <td class="forminp">
493 <input class="button" type="submit" value="<?php _e( 'Login', 'payplug' ); ?>">
494 <?php wp_nonce_field( 'payplug_user_login', '_loginaction' ); ?>
495 </td>
496 </tr>
497 </tbody>
498 </table>
499 <?php
500 endif;
501 }
502
503 /**
504 * Process admin options.
505 *
506 * @return bool
507 */
508 public function process_admin_options() {
509 $data = $this->get_post_data();
510
511 // Handle logout process
512 if (
513 isset( $data['submit_logout'] )
514 && false !== check_admin_referer( 'payplug_user_logout', '_logoutaction' )
515 ) {
516
517 $this->permissions->clear_permissions();
518
519 $data = get_option( $this->get_option_key() );
520 $data['payplug_test_key'] = '';
521 $data['payplug_live_key'] = '';
522 $data['payplug_merchant_id'] = '';
523 $data['enabled'] = 'no';
524 $data['mode'] = 'no';
525 update_option( $this->get_option_key(),
526 apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $data ) );
527 \WC_Admin_Settings::add_message( __( 'Successfully logged out.', 'payplug' ) );
528
529 return true;
530 }
531
532 // Handle login process
533 if (
534 isset( $data['payplug_email'] )
535 && false !== check_admin_referer( 'payplug_user_login', '_loginaction' )
536 ) {
537 $email = $data['payplug_email'];
538 $password = $data['payplug_password'];
539 $response = $this->retrieve_user_api_keys( $email, $password );
540 if ( is_wp_error( $response ) ) {
541 \WC_Admin_Settings::add_error( $response->get_error_message() );
542
543 return false;
544 }
545
546 // try to use the api keys to retrieve the merchant id
547 $merchant_id = isset( $response['test'] ) ? $this->retrieve_merchant_id( $response['test'] ) : '';
548
549 $this->init_form_fields();
550 $fields = $this->get_form_fields();
551 $data = [];
552
553 // Load existing values if the user is re-login.
554 foreach ( $fields as $key => $field ) {
555 if ( in_array( $field['type'], [ 'title', 'login' ] ) ) {
556 continue;
557 }
558
559 switch ( $key ) {
560 case 'enabled':
561 $val = 'yes';
562 break;
563 case 'mode':
564 $val = 'no';
565 break;
566 case 'payplug_test_key':
567 $val = esc_attr( $response['test'] );
568 break;
569 case 'payplug_live_key':
570 $val = esc_attr( $response['live'] );
571 break;
572 case 'payplug_merchant_id':
573 $val = esc_attr( $merchant_id );
574 break;
575 case 'email':
576 $val = esc_html( $email );
577 break;
578 default:
579 $val = $this->get_option( $key );
580 }
581
582 $data[ $key ] = $val;
583 }
584
585 $this->set_post_data( $data );
586 update_option( $this->get_option_key(),
587 apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $data ) );
588 \WC_Admin_Settings::add_message( __( 'Successfully logged in.', 'payplug' ) );
589
590 return true;
591 }
592
593 // Don't let user without live key leave TEST mode.
594 $mode_fieldkey = $this->get_field_key( 'mode' );
595 $live_key_fieldkey = $this->get_field_key( 'payplug_live_key' );
596 if ( isset( $data[ $mode_fieldkey ] ) && '1' === $data[ $mode_fieldkey ] && empty( $data[ $live_key_fieldkey ] ) ) {
597 $data[ $mode_fieldkey ] = '0';
598 $this->set_post_data( $data );
599 \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' ) );
600 }
601
602 // Check user permissions before activating one-click feature.
603 $oneclick_fieldkey = $this->get_field_key( 'oneclick' );
604 if (
605 isset( $data[ $oneclick_fieldkey ] )
606 && '1' === $data[ $oneclick_fieldkey ]
607 && false === $this->permissions->has_permissions( PayplugPermissions::SAVE_CARD )
608 ) {
609 $data[ $oneclick_fieldkey ] = '0';
610 \WC_Admin_Settings::add_error( __( 'Only PREMIUM accounts can enable the One Click option in LIVE mode.', 'payplug' ) );
611 }
612
613 parent::process_admin_options();
614 }
615
616 /**
617 * Process payment.
618 *
619 * @param int $order_id
620 *
621 * @return array
622 * @throws \Exception
623 */
624 public function process_payment( $order_id ) {
625
626 PayplugGateway::log( sprintf( 'Processing payment for order #%s', $order_id ) );
627
628 $order = wc_get_order( $order_id );
629 $customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
630 $amount = (int) PayplugWoocommerceHelper::get_payplug_amount( $order->get_total() );
631 $amount = $this->validate_order_amount( $amount );
632 if ( is_wp_error( $amount ) ) {
633 PayplugGateway::log( sprintf( 'Invalid amount %s for the order.', $order->get_total() ), 'error' );
634 throw new \Exception( $amount->get_error_message() );
635 }
636
637 $payment_token_id = ( isset( $_POST[ 'wc-' . $this->id . '-payment-token' ] ) && 'new' !== $_POST[ 'wc-' . $this->id . '-payment-token' ] )
638 ? wc_clean( $_POST[ 'wc-' . $this->id . '-payment-token' ] )
639 : false;
640
641 if ( $payment_token_id && $this->oneclick_available() && (int) $customer_id > 0 ) {
642 PayplugGateway::log( sprintf( 'Payment token found.', $amount ) );
643
644 return $this->process_payment_with_token( $order, $amount, $customer_id, $payment_token_id );
645 }
646
647 return $this->process_standard_payment( $order, $amount, $customer_id );
648 }
649
650 /**
651 * @param \WC_Order $order
652 * @param int $amount
653 * @param int $customer_id
654 *
655 * @return array
656 * @throws \Exception
657 */
658 public function process_standard_payment( $order, $amount, $customer_id ) {
659
660 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
661 $customer_details = $this->prepare_customer_data( $order );
662
663 if (
664 ! empty( $customer_details['country'] )
665 && ! PayplugWoocommerceHelper::is_country_supported( $customer_details['country'] )
666 ) {
667 $customer_details['country'] = '';
668 }
669
670 try {
671 $payment_data = [
672 'amount' => $amount,
673 'currency' => get_woocommerce_currency(),
674 'allow_save_card' => $this->oneclick_available() && (int) $customer_id > 0,
675 'customer' => [
676 'first_name' => $this->limit_length( $customer_details['first_name'] ),
677 'last_name' => $this->limit_length( $customer_details['last_name'] ),
678 'email' => $this->limit_length( $customer_details['email'], 255 ),
679 'address1' => ( ! empty( $customer_details['address1'] ) ) ? $this->limit_length( $customer_details['address1'], 255 ) : null,
680 'postcode' => ( ! empty( $customer_details['postcode'] ) ) ? $this->limit_length( $customer_details['postcode'], 16 ) : null,
681 'city' => ( ! empty( $customer_details['city'] ) ) ? $this->limit_length( $customer_details['city'] ) : null,
682 'country' => ( ! empty( $customer_details['country'] ) ) ? $this->limit_length( $customer_details['country'], 2 ) : null,
683 ],
684 'hosted_payment' => [
685 'return_url' => esc_url_raw( $order->get_checkout_order_received_url() ),
686 'cancel_url' => esc_url_raw( $order->get_cancel_order_url_raw() ),
687 ],
688 'notification_url' => esc_url_raw( WC()->api_request_url( 'PayplugGateway' ) ),
689 'metadata' => [
690 'order_id' => $order_id,
691 'customer_id' => ( (int) $customer_id > 0 ) ? $customer_id : 'guest',
692 'domain' => $this->limit_length( esc_url_raw( home_url() ), 500 ),
693 ],
694 ];
695
696 /**
697 * Filter the payment data before it's used
698 *
699 * @param array $payment_data
700 * @param int $order_id
701 * @param array $customer_details
702 */
703 $payment_data = apply_filters( 'payplug_gateway_payment_data', $payment_data, $order_id, $customer_details );
704 $payment = $this->api->payment_create( $payment_data );
705
706 // Save transaction id for the order
707 PayplugWoocommerceHelper::is_pre_30()
708 ? update_post_meta( $order_id, '_transaction_id', $payment->id )
709 : $order->set_transaction_id( $payment->id );
710
711 if ( is_callable( [ $order, 'save' ] ) ) {
712 $order->save();
713 }
714
715 /**
716 * Fires once a payment has been created.
717 *
718 * @param int $order_id Order ID
719 * @param PaymentResource $payment Payment resource
720 */
721 \do_action( 'payplug_gateway_payment_created', $order_id, $payment );
722
723 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata( $payment );
724 PayplugWoocommerceHelper::save_transaction_metadata( $order, $metadata );
725
726 PayplugGateway::log( sprintf( 'Payment creation complete for order #%s', $order_id ) );
727
728 return [
729 'result' => 'success',
730 'redirect' => $payment->hosted_payment->payment_url,
731 'cancel' => $payment->hosted_payment->cancel_url,
732 ];
733 } catch ( HttpException $e ) {
734 PayplugGateway::log( sprintf( 'Error while processing order #%s : %s', $order_id, wc_print_r( $e->getErrorObject(), true ) ), 'error' );
735 throw new \Exception( __( 'Payment processing failed. Please retry.', 'payplug' ) );
736 } catch ( \Exception $e ) {
737 PayplugGateway::log( sprintf( 'Error while processing order #%s : %s', $order_id, $e->getMessage() ), 'error' );
738 throw new \Exception( __( 'Payment processing failed. Please retry.', 'payplug' ) );
739 }
740 }
741
742 /**
743 * @param \WC_Order $order
744 * @param int $amount
745 * @param int $customer_id
746 * @param string $token_id
747 *
748 * @return array
749 * @throws \Exception
750 */
751 public function process_payment_with_token( $order, $amount, $customer_id, $token_id ) {
752
753 $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
754 $customer_details = $this->prepare_customer_data( $order );
755 $payment_token = WC_Payment_Tokens::get( $token_id );
756 if ( ! $payment_token || (int) $customer_id !== (int) $payment_token->get_user_id() ) {
757 PayplugGateway::log( 'Could not find the payment token or the payment doesn\'t belong to the current user.', 'error' );
758 throw new \Exception( __( 'Invalid payment method.', 'payplug' ) );
759 }
760
761 if (
762 ! empty( $customer_details['country'] )
763 && ! PayplugWoocommerceHelper::is_country_supported( $customer_details['country'] )
764 ) {
765 $customer_details['country'] = '';
766 }
767
768 try {
769 $payment_data = [
770 'amount' => $amount,
771 'currency' => get_woocommerce_currency(),
772 'payment_method' => $payment_token->get_token(),
773 'customer' => [
774 'first_name' => $this->limit_length( $customer_details['first_name'] ),
775 'last_name' => $this->limit_length( $customer_details['last_name'] ),
776 'email' => $this->limit_length( $customer_details['email'], 255 ),
777 'address1' => ( ! empty( $customer_details['address1'] ) ) ? $this->limit_length( $customer_details['address1'], 255 ) : null,
778 'postcode' => ( ! empty( $customer_details['postcode'] ) ) ? $this->limit_length( $customer_details['postcode'], 16 ) : null,
779 'city' => ( ! empty( $customer_details['city'] ) ) ? $this->limit_length( $customer_details['city'] ) : null,
780 'country' => ( ! empty( $customer_details['country'] ) ) ? $this->limit_length( $customer_details['country'], 2 ) : null,
781 ],
782 'notification_url' => esc_url_raw( WC()->api_request_url( 'PayplugGateway' ) ),
783 'metadata' => [
784 'order_id' => $order_id,
785 'customer_id' => ( (int) $customer_id > 0 ) ? $customer_id : 'guest',
786 'domain' => $this->limit_length( esc_url_raw( home_url() ), 500 ),
787 ],
788 ];
789
790 /** This filter is documented in src/Gateway/PayplugGateway */
791 $payment_data = apply_filters( 'payplug_gateway_payment_data', $payment_data, $order_id, $customer_details );
792 $payment = $this->api->payment_create( $payment_data );
793
794 /** This action is documented in src/Gateway/PayplugGateway */
795 \do_action( 'payplug_gateway_payment_created', $order_id, $payment );
796
797 $this->response->process_payment( $payment, true );
798
799 PayplugGateway::log( sprintf( 'Payment process complete for order #%s', $order_id ) );
800
801 return [
802 'result' => 'success',
803 'redirect' => $order->get_checkout_order_received_url(),
804 ];
805 } catch ( HttpException $e ) {
806 PayplugGateway::log( sprintf( 'Error while processing order #%s : %s', $order_id, wc_print_r( $e->getErrorObject(), true ) ), 'error' );
807 throw new \Exception( __( 'Payment processing failed. Please retry.', 'payplug' ) );
808 } catch ( \Exception $e ) {
809 PayplugGateway::log( sprintf( 'Error while processing order #%s : %s', $order_id, $e->getMessage() ), 'error' );
810 throw new \Exception( __( 'Payment processing failed. Please retry.', 'payplug' ) );
811 }
812 }
813
814 /**
815 * Process refund for an order paid with PayPlug gateway.
816 *
817 * @param int $order_id
818 * @param null $amount
819 * @param string $reason
820 *
821 * @return bool|\WP_Error
822 */
823 public function process_refund( $order_id, $amount = null, $reason = '' ) {
824
825 PayplugGateway::log( sprintf( 'Processing refund for order #%s', $order_id ) );
826
827 $order = wc_get_order( $order_id );
828 if ( ! $order instanceof \WC_Order ) {
829 PayplugGateway::log( sprintf( 'The order #%s does not exist.', $order_id ), 'error' );
830
831 return new \WP_Error( 'process_refund_error', sprintf( __( 'The order %s does not exist.', 'payplug' ), $order_id ) );
832 }
833
834 $transaction_id = PayplugWoocommerceHelper::is_pre_30() ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_transaction_id();
835 if ( empty( $transaction_id ) ) {
836 PayplugGateway::log( sprintf( 'The order does not have PayPlug transaction ID associated with it.', $order_id ), 'error' );
837
838 return new \WP_Error( 'process_refund_error', __( 'No PayPlug transaction was found for this order. The refund could not be processed.', 'payplug' ) );
839 }
840
841 $customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
842
843 $data = [
844 'metadata' => [
845 'order_id' => $order_id,
846 'customer_id' => ( (int) $customer_id > 0 ) ? $customer_id : 'guest',
847 ]
848 ];
849
850 if ( ! is_null( $amount ) ) {
851 $data['amount'] = PayplugWoocommerceHelper::get_payplug_amount( $amount );
852 }
853
854 if ( ! empty( $reason ) ) {
855 $data['metadata']['reason'] = $reason;
856 }
857
858 /**
859 * Filter the refund data before it's used.
860 *
861 * @param array $data
862 * @param int $order_id
863 * @param string $transaction_id
864 */
865 $data = apply_filters( 'payplug_gateway_refund_data', $data, $order_id, $transaction_id );
866
867 try {
868 $refund = $this->api->refund_create( $transaction_id, $data );
869
870 /**
871 * Fires once a refund has been created.
872 *
873 * @param int $order_id Order ID
874 * @param RefundResource $refund Refund resource
875 * @param string $transaction_id Transaction id
876 */
877 \do_action( 'payplug_gateway_refund_created', $order_id, $refund, $transaction_id );
878
879 $refund_meta_key = sprintf( '_pr_%s', wc_clean( $refund->id ) );
880 if ( PayplugWoocommerceHelper::is_pre_30() ) {
881 update_post_meta( $order_id, $refund_meta_key, $refund->id );
882 } else {
883 $order->add_meta_data( $refund_meta_key, $refund->id, true );
884 $order->save();
885 }
886
887 $note = sprintf( __( 'Refund %s : Refunded %s', 'payplug' ), wc_clean( $refund->id ), wc_price( ( (int) $refund->amount ) / 100 ) );
888 if ( ! empty( $refund->metadata['reason'] ) ) {
889 $note .= sprintf( ' (%s)', esc_html( $refund->metadata['reason'] ) );
890 }
891 $order->add_order_note( $note );
892
893 try {
894 $payment = $this->api->payment_retrieve( $transaction_id );
895 $metadata = PayplugWoocommerceHelper::extract_transaction_metadata( $payment );
896 PayplugWoocommerceHelper::save_transaction_metadata( $order, $metadata );
897 } catch ( \Exception $e ) {
898 }
899
900 PayplugGateway::log( 'Refund process complete for the order.' );
901
902 return true;
903 } catch ( HttpException $e ) {
904 PayplugGateway::log( sprintf( 'Refund request error for the order %s from PayPlug API : %s', $order_id, wc_print_r( $e->getErrorObject(), true ) ), 'error' );
905
906 return new \WP_Error( 'process_refund_error', __( 'The transaction could not be refunded. Please try again.', 'payplug' ) );
907 } catch ( \Exception $e ) {
908 PayplugGateway::log( sprintf( 'Refund request error for the order %s : %s', $order_id, wc_clean( $e->getMessage() ) ), 'error' );
909
910 return new \WP_Error( 'process_refund_error', __( 'The transaction could not be refunded. Please try again.', 'payplug' ) );
911 }
912 }
913
914 /**
915 * @param \WC_Order $order
916 *
917 * @return array
918 */
919 public function prepare_customer_data( $order ) {
920 return [
921 'first_name' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_first_name : $order->get_billing_first_name(),
922 'last_name' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_last_name : $order->get_billing_last_name(),
923 'email' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_email : $order->get_billing_email(),
924 'address1' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_address_1 : $order->get_billing_address_1(),
925 'address2' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_address_2 : $order->get_billing_address_2(),
926 'postcode' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_postcode : $order->get_billing_postcode(),
927 'city' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_city : $order->get_billing_city(),
928 'country' => PayplugWoocommerceHelper::is_pre_30() ? $order->billing_country : $order->get_billing_country(),
929 ];
930 }
931
932 /**
933 * Check the order amount to ensure it's on the allowed range.
934 *
935 * @param int $amount
936 *
937 * @return int|\WP_Error
938 */
939 public function validate_order_amount( $amount ) {
940 if (
941 $amount < PayplugWoocommerceHelper::get_minimum_amount()
942 || $amount > PayplugWoocommerceHelper::get_maximum_amount()
943 ) {
944 return new \WP_Error(
945 'invalid order amount',
946 sprintf( __( 'Payments for this amount (%s) are not authorised with this payment gateway.', 'payplug' ), \wc_price( $amount / 100 ) )
947 );
948 }
949
950 return $amount;
951 }
952
953 /**
954 * Limit string length.
955 *
956 * @param string $value
957 * @param int $maxlength
958 *
959 * @return string
960 */
961 public function limit_length( $value, $maxlength = 100 ) {
962 return ( strlen( $value ) > $maxlength ) ? substr( $value, 0, $maxlength ) : $value;
963 }
964
965 /**
966 * Get user's keys.
967 *
968 * @param string $email
969 * @param string $password
970 *
971 * @return array|\WP_Error
972 */
973 public function retrieve_user_api_keys( $email, $password ) {
974 if ( empty( $email ) || empty( $password ) ) {
975 return new \WP_Error( 'missing_login_data', __( 'Please fill all login fields', 'payplug' ) );
976 }
977
978 try {
979 $response = Authentication::getKeysByLogin( $email, $password );
980 if ( empty( $response ) || ! isset( $response['httpResponse'] ) ) {
981 return new \WP_Error( 'invalid_credentials', __( 'Invalid credentials.', 'payplug' ) );
982 }
983
984 return $response['httpResponse']['secret_keys'];
985 } catch ( HttpException $e ) {
986 return new \WP_Error( 'invalid_credentials', __( 'Invalid credentials.', 'payplug' ) );
987 }
988 }
989
990 /**
991 * Get user merchant id.
992 *
993 * This method might be called during the login process before the global PayPlug
994 * configuration is set. In that case you can pass a valid token to make the request.
995 *
996 * @param string|null $key
997 *
998 * @return string
999 */
1000 public function retrieve_merchant_id( $key = null ) {
1001 try {
1002 $response = ! is_null( $key ) ? Authentication::getAccount( new Payplug( $key ) ) : Authentication::getAccount();
1003 $merchant_id = isset( $response['httpResponse']['id'] ) ? $response['httpResponse']['id'] : '';
1004 } catch ( ConfigurationException $e ) {
1005 PayplugGateway::log( sprintf( 'Missing API key for PayPlug client : %s', wc_print_r( $e->getMessage(), true ) ), 'error' );
1006
1007 $merchant_id = '';
1008 } catch ( HttpException $e ) {
1009 PayplugGateway::log( sprintf( 'Account request error from PayPlug API : %s', wc_print_r( $e->getErrorObject(), true ) ), 'error' );
1010
1011 $merchant_id = '';
1012 } catch ( \Exception $e ) {
1013 PayplugGateway::log( sprintf( 'Account request error : %s', wc_clean( $e->getMessage() ) ), 'error' );
1014
1015 $merchant_id = '';
1016 }
1017
1018 return $merchant_id;
1019 }
1020
1021 /**
1022 * Generate Hidden HTML.
1023 *
1024 * @param string $key
1025 * @param array $data
1026 *
1027 * @return string
1028 */
1029 public function generate_hidden_html( $key, $data ) {
1030 $field_key = $this->get_field_key( $key );
1031 $defaults = array(
1032 'title' => '',
1033 'disabled' => false,
1034 'class' => '',
1035 'css' => '',
1036 'placeholder' => '',
1037 'type' => 'text',
1038 'desc_tip' => false,
1039 'description' => '',
1040 'custom_attributes' => array(),
1041 );
1042
1043 $data = wp_parse_args( $data, $defaults );
1044
1045 ob_start();
1046 ?>
1047 <input
1048 type="<?php echo esc_attr( $data['type'] ); ?>" name="<?php echo esc_attr( $field_key ); ?>"
1049 id="<?php echo esc_attr( $field_key ); ?>"
1050 value="<?php echo esc_attr( $this->get_option( $key ) ); ?>"/>
1051 <?php
1052
1053 return ob_get_clean();
1054 }
1055
1056 /**
1057 * Generate Yes/No Input HTML.
1058 *
1059 * @param string $key
1060 * @param array $data
1061 *
1062 * @return string
1063 */
1064 public function generate_yes_no_html( $key, $data ) {
1065 $field_key = $this->get_field_key( $key );
1066 $defaults = array(
1067 'title' => '',
1068 'no' => 'No',
1069 'yes' => 'Yes',
1070 'disabled' => false,
1071 'class' => '',
1072 'css' => '',
1073 'placeholder' => '',
1074 'type' => 'text',
1075 'desc_tip' => false,
1076 'description' => '',
1077 'custom_attributes' => [],
1078 'hide_label' => false,
1079 );
1080
1081 $data = wp_parse_args( $data, $defaults );
1082 $checked = 'yes' === $this->get_option( $key ) ? '1' : '0';
1083
1084 ob_start();
1085 ?>
1086 <tr valign="top">
1087 <?php if ( ! $data['hide_label'] ) : ?>
1088 <th scope="row" class="titledesc">
1089 <label for="<?php echo esc_attr( $field_key ); ?>">
1090 <?php echo wp_kses_post( $data['title'] ); ?>
1091 <?php echo $this->get_tooltip_html( $data ); ?>
1092 </label>
1093 </th>
1094 <?php endif; ?>
1095 <td class="forminp">
1096 <fieldset>
1097 <legend class="screen-reader-text"><span><?php echo wp_kses_post( $data['title'] ); ?></span>
1098 </legend>
1099 <div class="radio--custom">
1100 <input class="radio radio-yes <?php echo esc_attr( $data['class'] ); ?>"
1101 type="radio"
1102 name="<?php echo esc_attr( $field_key ); ?>"
1103 id="<?php echo esc_attr( $field_key ); ?>-yes"
1104 value="1"
1105 <?php checked( '1', $checked ); ?>
1106 <?php disabled( $data['disabled'], true ); ?>
1107 <?php echo $this->get_custom_attribute_html( $data ); ?>>
1108 <label for="<?php echo esc_attr( $field_key ); ?>-yes"><?php echo esc_html( $data['yes'] ); ?></label>
1109 </div>
1110 <div class="radio--custom">
1111 <input class="radio radio-no <?php echo esc_attr( $data['class'] ); ?>"
1112 type="radio"
1113 name="<?php echo esc_attr( $field_key ); ?>"
1114 id="<?php echo esc_attr( $field_key ); ?>-no"
1115 value="0"
1116 <?php checked( '0', $checked ); ?>
1117 <?php disabled( $data['disabled'], true ); ?>
1118 <?php echo $this->get_custom_attribute_html( $data ); ?>>
1119 <label for="<?php echo esc_attr( $field_key ); ?>-no"><?php echo esc_html( $data['no'] ); ?></label>
1120 </div>
1121 <?php echo $this->get_description_html( $data ); ?>
1122 </fieldset>
1123 </td>
1124 </tr>
1125 <?php
1126
1127 return ob_get_clean();
1128 }
1129
1130 /**
1131 * Generate Radio Input HTML.
1132 *
1133 * @param string $key
1134 * @param array $data
1135 *
1136 * @return string
1137 */
1138 public function generate_radio_html( $key, $data ) {
1139 $field_key = $this->get_field_key( $key );
1140 $defaults = array(
1141 'title' => '',
1142 'disabled' => false,
1143 'class' => '',
1144 'css' => '',
1145 'placeholder' => '',
1146 'type' => 'text',
1147 'desc_tip' => false,
1148 'description' => '',
1149 'custom_attributes' => [],
1150 'options' => [],
1151 );
1152
1153 $data = wp_parse_args( $data, $defaults );
1154
1155 ob_start();
1156 ?>
1157 <tr valign="top">
1158 <th scope="row" class="titledesc">
1159 <label for="<?php echo esc_attr( $field_key ); ?>">
1160 <?php echo wp_kses_post( $data['title'] ); ?>
1161 <?php echo $this->get_tooltip_html( $data ); ?>
1162 </label>
1163 </th>
1164 <td class="forminp">
1165 <fieldset>
1166 <legend class="screen-reader-text"><span><?php echo wp_kses_post( $data['title'] ); ?></span>
1167 </legend>
1168 <?php foreach ( $data['options'] as $option_key => $option_value ) : ?>
1169 <input class="radio <?php echo esc_attr( $data['class'] ); ?>"
1170 type="radio"
1171 name="<?php echo esc_attr( $field_key ); ?>"
1172 id="<?php echo esc_attr( $field_key ); ?>-<?php echo esc_attr( $option_key ); ?>"
1173 value="<?php echo esc_attr( $option_key ); ?>"
1174 <?php checked( $option_key, $this->get_option( $key ) ); ?>
1175 <?php disabled( $data['disabled'], true ); ?>
1176 <?php echo $this->get_custom_attribute_html( $data ); ?>>
1177 <label for="<?php echo esc_attr( $field_key ); ?>-<?php echo esc_attr( $option_key ); ?>"><?php echo esc_html( $option_value ); ?></label>
1178 <?php endforeach; ?>
1179 </fieldset>
1180 </td>
1181 </tr>
1182 <?php
1183
1184 return ob_get_clean();
1185 }
1186
1187 /**
1188 * Generate Login HTML.
1189 *
1190 * @param string $key
1191 * @param array $data
1192 *
1193 * @return string
1194 */
1195 public function generate_login_html( $key, $data ) {
1196 $field_key = $this->get_field_key( $key );
1197 $defaults = [];
1198
1199 $data = wp_parse_args( $data, $defaults );
1200
1201 ob_start();
1202 ?>
1203 <tr valign="top">
1204 <td class="forminp">
1205 <p><?php echo $this->get_option( 'email' ); ?></p>
1206 <p>
1207 <input type="submit" name="submit_logout" value="<?php _e( 'Logout', 'payplug' ); ?>">
1208 <?php wp_nonce_field( 'payplug_user_logout', '_logoutaction' ); ?>
1209 |
1210 <a href="https://portal.payplug.com"
1211 target="_blank"><?php _e( 'Go to your PayPlug Portal', 'payplug' ); ?></a>
1212 </p>
1213 </td>
1214 </tr>
1215 <?php
1216
1217 return ob_get_clean();
1218 }
1219
1220 /**
1221 * Validate Radio Field.
1222 *
1223 * Make sure the data is escaped correctly, etc.
1224 *
1225 * @param string $key
1226 * @param string|null $value Posted Value
1227 *
1228 * @return string
1229 */
1230 public function validate_radio_field( $key, $value ) {
1231 $value = is_null( $value ) ? '' : $value;
1232
1233 return wc_clean( stripslashes( $value ) );
1234 }
1235
1236 /**
1237 * Validate Yes/No Field.
1238 *
1239 * @param string $key
1240 * @param string $value Posted Value
1241 *
1242 * @return string
1243 */
1244 public function validate_yes_no_field( $key, $value ) {
1245 return ( '1' === (string) $value ) ? 'yes' : 'no';
1246 }
1247
1248 /**
1249 * Get PayPlug gateway mode.
1250 *
1251 * @return string
1252 */
1253 public function get_current_mode() {
1254 return ( 'yes' === $this->get_option( 'mode' ) ) ? 'live' : 'test';
1255 }
1256
1257 /**
1258 * Get user API key.
1259 *
1260 * @param string $mode
1261 *
1262 * @return string
1263 */
1264 public function get_api_key( $mode = 'test' ) {
1265
1266 switch ( $mode ) {
1267 case 'test':
1268 $key = $this->get_option( 'payplug_test_key' );
1269 break;
1270 case 'live':
1271 $key = $this->get_option( 'payplug_live_key' );
1272 break;
1273 default:
1274 $key = '';
1275 break;
1276 }
1277
1278 return $key;
1279 }
1280
1281 /**
1282 * Get current merchant id.
1283 *
1284 * @return string
1285 */
1286 public function get_merchant_id() {
1287 return $this->get_option( 'payplug_merchant_id', '' );
1288 }
1289
1290 /**
1291 * Check if user is logged in and we have an API key for TEST mode.
1292 *
1293 * @return bool
1294 */
1295 public function user_logged_in() {
1296 return ! empty( $this->get_option( 'payplug_test_key' ) );
1297 }
1298
1299 /**
1300 * Check if oneclick payment is activated and merchant can use it.
1301 *
1302 * @return bool
1303 */
1304 public function oneclick_available() {
1305 return $this->oneclick && $this->permissions->has_permissions( PayplugPermissions::SAVE_CARD );
1306 }
1307 }