PluginProbe
PayPlug for WooCommerce (Official) / 3.1.0
PayPlug for WooCommerce (Official) v3.1.0
3.1.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 All 102 releases
← All changes | src/Gateway/PayplugGateway.php +1192 -1128 1.0.8 → 3.1.0 View file →
@@ -2,10 +2,10 @@
2 2
3 3 namespace Payplug\PayplugWoocommerce\Gateway;
4 4
5 5 // Exit if accessed directly
6 -if ( ! defined( 'ABSPATH' ) ) {
7 - exit;
6 +if (!defined('ABSPATH')) {
7 + exit;
8 8 }
9 9
10 10 use Payplug\Authentication;
11 11 use Payplug\Exception\ConfigurationException;
@@ -10,9 +10,11 @@
10 10 use Payplug\Authentication;
11 11 use Payplug\Exception\ConfigurationException;
12 12 use Payplug\Exception\HttpException;
13 13 use Payplug\Payplug;
14 +use Payplug\PayplugWoocommerce\Helper\Lock;
14 15 use Payplug\PayplugWoocommerce\PayplugWoocommerceHelper;
16 +use Payplug\PayplugWoocommerce\Traits\ServiceGetter;
15 17 use Payplug\Resource\Payment as PaymentResource;
16 18 use Payplug\Resource\Refund as RefundResource;
17 19 use WC_Payment_Gateway_CC;
18 20 use WC_Payment_Tokens;
@@ -18,1291 +20,1353 @@
18 20 use WC_Payment_Tokens;
19 21
20 22 /**
21 23 * PayPlug WooCommerce Gateway.
22 - *
23 - * @package Payplug\PayplugWoocommerce\Gateway
24 24 */
25 -class PayplugGateway extends WC_Payment_Gateway_CC {
25 +class PayplugGateway extends WC_Payment_Gateway_CC
26 +{
27 + use ServiceGetter;
26 28
27 - /**
28 - * @var PayplugGatewayRequirements
29 - */
30 - private $requirements;
29 + public const OPTION_NAME = 'payplug_config';
31 30
32 - /**
33 - * @var PayplugPermissions
34 - */
35 - private $permissions;
31 + /**
32 + * @var string
33 + */
34 + public $mode;
35 + /**
36 + * @var bool
37 + */
38 + public $debug;
39 + /**
40 + * @var string
41 + */
42 + public $email;
43 + /**
44 + * @var string
45 + */
46 + public $embedded_mode;
47 + /**
48 + * @var bool
49 + */
50 + public $save_card;
36 51
37 - /**
38 - * @var PayplugResponse
39 - */
40 - public $response;
52 + /**
53 + * @var string
54 + */
55 + public $oney_type;
41 56
42 - /**
43 - * @var PayplugApi
44 - */
45 - public $api;
57 + /**
58 + * @var string
59 + */
60 + public $oney_product_animation;
46 61
47 - /**
48 - * @var \WC_Logger
49 - */
50 - protected static $log;
62 + /**
63 + * @var PayplugGatewayRequirements
64 + */
65 + private $requirements;
51 66
52 - /**
53 - * @var bool
54 - */
55 - protected static $log_enabled;
67 + /**
68 + * @var PayplugPermissions
69 + */
70 + private $permissions;
56 71
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 + * @var PayplugResponse
74 + */
75 + public $response;
72 76
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' );
77 + /**
78 + * @var PayplugApi
79 + */
80 + public $payplug_api;
85 81
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();
82 + /**
83 + * @var \WC_Logger
84 + */
85 + protected static $log;
92 86
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' );
87 + /**
88 + * @var bool
89 + */
90 + protected static $log_enabled;
100 91
101 - self::$log_enabled = $this->debug;
92 + /**
93 + * @var float
94 + */
95 + public const MIN_AMOUNT = 0.99;
102 96
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 - }
97 + /**
98 + * @var float
99 + */
100 + public const MAX_AMOUNT = 20000;
108 101
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' ] );
102 + /**
103 + * @var string
104 + */
105 + private $payplug_merchant_country = 'FR';
112 106
113 - add_filter( 'woocommerce_get_customer_payment_tokens', [ $this, 'filter_tokens' ], 10, 3 );
114 - }
107 + protected $oney_response;
108 + public $min_oney_price;
109 + public $oney_thresholds_min;
110 + public $max_oney_price;
111 + public $oney_thresholds_max;
115 112
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 - }
113 + public const ENABLE_ON_TEST_MODE = true;
125 114
126 - $order_id = wc_get_order_id_by_order_key( wc_clean( $_GET['key'] ) );
127 - if ( empty( $order_id ) ) {
128 - return;
129 - }
115 + /**
116 + * Logging method.
117 + *
118 + * @param string $message Log message.
119 + * @param string $level Optional. Default 'info'.
120 + * emergency|alert|critical|error|warning|notice|info|debug
121 + */
122 + public static function log($message, $level = 'info'): void
123 + {
124 + if (!self::$log_enabled) {
125 + return;
126 + }
130 127
131 - $order = wc_get_order( $order_id );
132 - if ( ! $order instanceof \WC_Order ) {
133 - return;
134 - }
128 + if (empty(self::$log)) {
129 + self::$log = PayplugWoocommerceHelper::is_pre_30() ? new \WC_Logger() : wc_get_logger();
130 + }
135 131
136 - $payment_method = PayplugWoocommerceHelper::is_pre_30() ? $order->payment_method : $order->get_payment_method();
137 - if ( 'payplug' !== $payment_method ) {
138 - return;
139 - }
132 + PayplugWoocommerceHelper::is_pre_30()
133 + ? self::$log->add('payplug_gateway', $message)
134 + : self::$log->log($level, $message, ['source' => 'payplug_gateway']);
135 + }
140 136
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' );
137 + /**
138 + * Construct method
139 + *
140 + * @return void
141 + */
142 + public function __construct()
143 + {
144 + //required plugin id
145 + $this->id = 'payplug';
146 + $this->supports = [
147 + 'products',
148 + 'refunds',
149 + 'tokenization',
150 + ];
144 151
145 - return;
146 - }
152 + $payplug_gateways = ['payplug', 'american_express', 'apple_pay', 'bancontact', 'oney_x3_with_fees', 'oney_x3_without_fees', 'oney_x4_with_fees', 'oney_x4_without_fees', 'satispay', 'ideal', 'mybank', 'wero', 'bizum', 'scalapay'];
147 153
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 - );
154 + //save buttom admin
155 + if ((!empty($_GET['section'])) && (in_array($_GET['section'], $payplug_gateways))) {
156 + $GLOBALS['hide_save_button'] = true;
157 + }
157 158
158 - return;
159 - }
159 + $this->init_settings();
160 + $this->requirements = new PayplugGatewayRequirements($this);
161 + if ($this->user_logged_in()) {
162 + $this->init_payplug();
163 + }
160 164
161 - $this->response->process_payment( $payment );
162 - }
165 + $this->mode = (bool) $this->get_configuration()->get_option('mode') ? 'live' : 'test';
166 + $this->debug = (bool) $this->get_configuration()->get_option('debug');
167 + $this->email = (string) $this->get_configuration()->get_option('email');
163 168
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 - ] );
169 + //admin form
170 + $this->init_form_fields();
173 171
174 - $icons_str = '';
175 - foreach ( $icons as $icon ) {
176 - $icons_str .= $icon;
177 - }
172 + add_filter('woocommerce_get_customer_payment_tokens', [$this, 'filter_tokens'], 10, 3);
178 173
179 - return $icons_str;
180 - }
174 + self::$log_enabled = $this->debug;
181 175
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 - }
176 + add_filter('woocommerce_get_order_item_totals', [$this, 'customize_gateway_title'], 10, 2);
177 + add_action('woocommerce_thankyou', [$this, 'validate_payment']);
178 + add_action('the_post', [$this, 'validate_payment']);
179 + add_action('woocommerce_available_payment_gateways', [$this, 'check_gateway']);
180 + }
189 181
190 - return parent::is_available();
191 - }
182 + /**
183 + * @param $option
184 + * @param $value
185 + *
186 + * @return bool|void
187 + */
188 + public function update_option($option, $value = '')
189 + {
190 + if ($this->needs_setup()) {
191 + wp_send_json_error('needs_setup');
192 + wp_die();
193 + }
192 194
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 - }
195 + parent::update_option($option, $value);
196 + }
200 197
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 - ];
198 + /**
199 + * this payment gateway cannot be updated on the wooco payment settings
200 + *
201 + * @return bool
202 + */
203 + public function needs_setup()
204 + {
205 + return true;
206 + }
289 207
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 - }
208 + /**
209 + * Customize gateway title in emails.
210 + *
211 + * @param array $total_rows
212 + * @param \WC_Order $order
213 + *
214 + * @return array
215 + *
216 + * @author Clément Boirie
217 + */
218 + public function customize_gateway_title($total_rows, $order)
219 + {
220 + $get_payment_method = $this->id;
221 + if (method_exists($order, 'get_payment_method')) {
222 + $get_payment_method = $order->get_payment_method();
223 + }
294 224
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 - }
225 + $payment_method = PayplugWoocommerceHelper::is_pre_30() ? $order->payment_method : $get_payment_method;
226 + if (
227 + $this->id !== $payment_method
228 + || !isset($total_rows['payment_method'])
229 + ) {
230 + return $total_rows;
231 + }
303 232
304 - /**
305 - * Set global configuration for PayPlug instance.
306 - */
307 - public function init_payplug() {
308 - $this->api = new PayplugApi( $this );
309 - $this->api->init();
233 + $total_rows['payment_method']['value'] = __('Credit card', 'payplug');
310 234
311 - $this->permissions = new PayplugPermissions( $this );
312 - $this->response = new PayplugResponse( $this );
235 + return $total_rows;
236 + }
313 237
314 - // Register IPN handler
315 - new PayplugIpnResponse( $this );
316 - }
238 + /**
239 + * Validate order payment when the user is redirected to the success confirmation page.
240 + *
241 + * @throws \WC_Data_Exception
242 + */
243 + public function validate_payment($id = null, $save_request = true, $ipn = false): void
244 + {
245 + global $wp;
317 246
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 - }
247 + if (!$ipn) {
248 + if (!is_wc_endpoint_url('order-received') || (empty($_GET['key']) && empty($id))) {
249 + return;
250 + }
251 + }
328 252
329 - // If PayPlug is not enabled bail.
330 - if ( 'no' === $this->enabled ) {
331 - return;
332 - }
253 + if (!empty($_GET['order-received'])) {
254 + $order_id = (int) ($_GET['order-received']);
255 + } elseif (!empty($id) && !is_object($id)) {
256 + $order_id = (int) $id;
257 + }
333 258
334 - // If keys are not set bail.
335 - if ( empty( $this->get_api_key( $this->mode ) ) ) {
336 - PayplugGateway::log( 'Keys are not set correctly.' );
259 + if (empty($order_id) && (isset($wp->query_vars['order-received']))) {
260 + $order_id = apply_filters(
261 + 'woocommerce_thankyou_order_id',
262 + absint($wp->query_vars['order-received'])
263 + );
264 + }
337 265
338 - return;
339 - }
266 + if (empty($order_id)) {
267 + if (empty($_GET['key']) && empty($id) && !is_object($id)) {
268 + return;
269 + }
270 + $order_id = wc_get_order_id_by_order_key(wc_clean((!empty($_GET['key']) ? $_GET['key'] : (int) $id)));
271 + }
340 272
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' );
273 + if (empty($order_id)) {
274 + return;
275 + }
344 276
277 + $order = wc_get_order($order_id);
278 + if (!$order instanceof \WC_Order) {
279 + return;
280 + }
345 281
346 - // Register scripts for embedded payment form.
347 - if ( 'embedded' !== $this->payment_method ) {
348 - return;
349 - }
282 + $payment_method = PayplugWoocommerceHelper::is_pre_30() ? $order->payment_method : $order->get_payment_method();
283 + if (!in_array($payment_method, ['payplug', 'oney_x3_with_fees', 'oney_x4_with_fees', 'oney_x3_without_fees', 'oney_x4_without_fees', 'bancontact', 'apple_pay', 'american_express', 'satispay', 'mybank', 'ideal', 'wero', 'bizum', 'scalapay'])) {
284 + return;
285 + }
350 286
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 - }
287 + if ($payment_method === $this->id) {
288 + $transaction_id = PayplugWoocommerceHelper::is_pre_30() ? get_post_meta($order_id, '_transaction_id', true) : $order->get_transaction_id();
289 + if (empty($transaction_id)) {
290 + self::log(sprintf('Order #%s : Missing transaction id.', $order_id), 'error');
364 291
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 ) {
292 + return;
293 + }
380 294
381 - if ( ! is_user_logged_in() || ! class_exists( 'WC_Payment_Gateway_CC' ) ) {
382 - return $tokens;
383 - }
295 + $lock_id = Lock::handle_insert($save_request, $transaction_id);
296 + if (!$lock_id) {
297 + return;
298 + }
384 299
385 - /* @var \WC_Payment_Token_CC $token */
386 - foreach ( $tokens as $k => $token ) {
300 + try {
301 + $payment = $this->payplug_api->payment_retrieve($transaction_id);
302 + } catch (\Exception $e) {
303 + self::log(
304 + sprintf(
305 + 'Order #%s : An error occurred while retrieving the payment data with the message : %s',
306 + $order_id,
307 + $e->getMessage()
308 + )
309 + );
387 310
388 - if ( $this->id !== $token->get_gateway_id() ) {
389 - continue;
390 - }
311 + return;
312 + }
391 313
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 - }
314 + $this->response->process_payment($payment);
398 315
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 - }
316 + \Payplug\PayplugWoocommerce\Model\Lock::delete_lock($lock_id);
317 + $waiting_requests = \Payplug\PayplugWoocommerce\Model\Lock::get_lock_by_payment_id($transaction_id);
404 318
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 - }
319 + if ($waiting_requests) {
320 + \Payplug\PayplugWoocommerce\Model\Lock::delete_lock_by_payment_id($transaction_id);
321 + $this->validate_payment($order_id, false);
322 + }
323 + }
324 + }
412 325
413 - if ( $current_year === (int) $token->get_expiry_year() && $current_month >= (int) $token->get_expiry_month() ) {
414 - unset( $tokens[ $k ] );
415 - continue;
416 - }
417 - }
326 + /**
327 + * Check if this gateway is enabled
328 + */
329 + public function is_available()
330 + {
331 + if ('yes' == $this->enabled) {
332 + $available = $this->requirements->satisfy_requirements() && !empty($this->get_api_key($this->get_current_mode()));
418 333
419 - return $tokens;
420 - }
334 + // $this->enabled only reflects the country/context check as it was at gateway construction time,
335 + // so re-run it here to catch context that only becomes known later in the request (e.g. order-pay).
336 + if ($available && method_exists($this, 'checkGateway')) {
337 + $available = $this->checkGateway();
338 + }
421 339
422 - public function payment_fields() {
423 - $description = $this->get_description();
424 - if ( ! empty( $description ) ) {
425 - echo wpautop( wptexturize( $description ) );
426 - }
340 + return $available;
341 + }
427 342
428 - if ( $this->oneclick_available() ) {
429 - $this->tokenization_script();
430 - $this->saved_payment_methods();
431 - }
432 - }
343 + return parent::is_available();
344 + }
433 345
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 - );
346 + /**
347 + * Load gateway settings.
348 + */
349 + public function init_settings(): void
350 + {
351 + parent::init_settings();
352 + $enabled = !empty($this->settings['enabled']) && (bool) $this->settings['enabled'];
353 + $this->enabled = $enabled ? 'yes' : 'no';
354 + }
444 355
445 - $payplug_requirements = new PayplugGatewayRequirements( $this ); ?>
356 + /**
357 + * Register gateway settings.
358 + */
359 + public function init_form_fields(): void
360 + {
361 + $anchor = esc_html_x(__('More informations', 'payplug'), 'modal', 'payplug');
362 + $domain = __('support.payplug.com/hc/fr/articles/4408142346002', 'payplug');
363 + $link = sprintf(' <a href="https://%s" target="_blank">%s</a>', $domain, $anchor);
446 364
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() ) ); ?>
365 + $anchor_bancontact = esc_html_x(__('payplug_bancontact_activation_request', 'payplug'), 'modal', 'payplug');
366 + $domain_bancontact = __('payplug_bancontact_activation_url', 'payplug');
367 + $bancontact_call_to_action = sprintf(' <a id="bancontact_call_to_action" href="https://%s" target="_blank">%s</a>', $domain_bancontact, $anchor_bancontact);
457 368
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( '[email protected]', '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 - }
369 + $oney_cfg = $this->get_configuration()->get_option('payment_methods.configuration.oney');
370 + if (!empty($oney_cfg)) {
371 + $oney_amount = json_decode($oney_cfg['custom_amounts'], true);
372 + $oney_amount['min'] = (float) $oney_amount['min'] / 100;
373 + $oney_amount['max'] = (float) $oney_amount['max'] / 100;
374 + } else {
375 + $oney_amount = [
376 + 'min' => 100,
377 + 'max' => 3000,
378 + ];
379 + }
380 + $fields = [
381 + 'enabled' => [
382 + 'title' => __('Enable/Disable', 'payplug'),
383 + 'type' => 'checkbox',
384 + 'label' => __('Enable PayPlug', 'payplug'),
385 + 'description' => __('Only Euro payments can be processed with PayPlug.', 'payplug'),
386 + 'default' => false,
387 + ],
388 + 'title' => [
389 + 'title' => __('Title', 'payplug'),
390 + 'type' => 'text',
391 + 'description' => __('The payment solution title displayed to your customers during checkout', 'payplug'),
392 + 'default' => _x('Credit card checkout', 'Default gateway title', 'payplug'),
393 + 'desc_tip' => false,
394 + ],
395 + 'description' => [
396 + 'title' => __('Description', 'payplug'),
397 + 'type' => 'text',
398 + 'description' => __('The payment solution description displayed to your customers during checkout', 'payplug'),
399 + 'default' => '',
400 + 'desc_tip' => false,
401 + ],
402 + 'title_connexion' => [
403 + 'title' => __('Connection', 'payplug'),
404 + 'type' => 'title',
405 + ],
406 + 'email' => [
407 + 'type' => 'hidden',
408 + 'default' => '',
409 + ],
410 + 'login' => [
411 + 'type' => 'login',
412 + 'default' => '',
413 + ],
414 + 'title_testmode' => [
415 + 'title' => __('Mode', 'payplug'),
416 + 'type' => 'title',
417 + ],
418 + 'mode' => [
419 + 'title' => '',
420 + 'label' => '',
421 + 'type' => 'yes_no',
422 + 'yes' => 'Live',
423 + 'no' => 'Test',
424 + 'description' => __('In TEST mode, all payments will be simulations and will not generate real transactions.', 'payplug'),
425 + 'default' => false,
426 + 'hide_label' => true,
427 + ],
428 + 'title_settings' => [
429 + 'title' => __('Settings', 'payplug'),
430 + 'type' => 'title',
431 + ],
432 + 'payment_method' => [
433 + 'title' => __('Payment page', 'payplug'),
434 + 'type' => 'radio',
435 + 'options' => [
436 + 'redirect' => __('Redirect', 'payplug'),
437 + 'embedded' => __('Integrated', 'payplug'),
438 + ],
439 + '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'),
440 + 'default' => 'redirect',
441 + 'desc_tip' => false,
442 + ],
443 + 'debug' => [
444 + 'title' => __('Debug', 'payplug'),
445 + 'type' => 'checkbox',
446 + 'description' => __('Debug mode saves additional information on your server for each operation done via the PayPlug plugin (Developer setting).', 'payplug'),
447 + 'label' => __('Activate debug mode', 'payplug'),
448 + 'default' => true,
449 + 'desc_tip' => false,
450 + ],
451 + 'title_advanced_settings' => [
452 + 'title' => __('payplug_advanced_settings', 'payplug'),
453 + 'description' => __(
454 + 'Your current offer does not allow this option. Try it on TEST mode. More information <a href="https://www.payplug.com/pricing" target="_blank">here.</a>',
455 + 'payplug'
456 + ),
457 + 'type' => 'title',
458 + ],
459 + 'save_card' => [
460 + 'title' => __('One Click Payment', 'payplug'),
461 + 'type' => 'checkbox',
462 + 'label' => __('Activate', 'payplug'),
463 + 'description' => __('Allow your customers to save their credit card information for later purchases.', 'payplug'),
464 + 'default' => false,
465 + 'desc_tip' => false,
466 + ],
467 + 'bancontact' => [
468 + 'title' => __('payplug_bancontact_activate_title', 'payplug'),
469 + 'type' => 'checkbox',
470 + 'label' => __('Activate', 'payplug'),
471 + 'description' => '<p class="description" id="bancontact_test_mode_description"> ' . __('payplug_bancontact_testmode_description', 'payplug') . ' </p>' .
472 + '<p class="description" id="bancontact_live_mode_description_disabled"> ' . __('payplug_bancontact_livemode_description_disabled', 'payplug') . ' </p>' .
473 + $bancontact_call_to_action,
474 + 'default' => false,
475 + ],
476 + 'apple_pay' => [
477 + 'title' => __('payplug_apple_pay_activate_title', 'payplug'),
478 + 'type' => 'checkbox',
479 + 'label' => __('Activate', 'payplug'),
480 + 'description' => '<p class="description" id="apple_pay_test_mode_description"> ' . __('payplug_apple_pay_testmode_description', 'payplug') . ' </p>' .
481 + '<p class="description" id="apple_pay_live_mode_description"> ' . __('payplug_apple_pay_livemode_description', 'payplug') . ' </p>',
482 + 'default' => false,
483 + ],
484 + 'american_express' => [
485 + 'title' => __('payplug_amex_title', 'payplug'),
486 + 'type' => 'checkbox',
487 + 'label' => __('payplug_amex_activate', 'payplug'),
488 + 'description' => '<p class="description" id="amex_test_mode_description"> ' . __('payplug_amex_testmode_description', 'payplug') . ' </p>' .
489 + '<p class="description" id="amex_live_mode_description"> ' . __('payplug_amex_livemode_description', 'payplug') . ' </p>',
490 + 'default' => false,
491 + ],
492 + 'oney' => [
493 + 'title' => __('3x 4x Oney payments', 'payplug'),
494 + 'type' => 'checkbox',
495 + 'label' => __('Activate', 'payplug'),
496 + // TRAD
497 + 'description' => sprintf(__('Allow your customers to split payments into 3 or 4 installments, for orders between %s€ and %s€', 'payplug'), $this->min_oney_price, $this->max_oney_price) . $link,
498 + 'default' => false,
499 + 'desc_tip' => false,
500 + ],
501 + 'oney_type' => [
502 + 'title' => '',
503 + 'type' => 'oney_type',
504 + 'options' => [
505 + 'with_fees' => __('Oney with fees', 'payplug'),
506 + 'without_fees' => __('Oney without fees', 'payplug'),
507 + ],
508 + 'descriptions' => [
509 + 'with_fees' => __('The fees are split between you and your customers', 'payplug'),
510 + 'without_fees' => __('You pay the fees', 'payplug'),
511 + ],
512 + 'description' => '',
513 + 'default' => 'with_fees',
514 + 'desc_tip' => false,
515 + ],
516 + 'oney_thresholds' => [
517 + 'title' => '',
518 + 'type' => 'oney_thresholds',
519 + 'description' => sprintf(
520 + __('I would like to offer guaranteed payment in installments for amounts between %s€ and %s€.', 'payplug'),
521 + '<b class="min">' . $oney_amount['min'] . '</b>',
522 + '<b class="max">' . $oney_amount['max'] . '</b>'
523 + ),
524 + 'desc_tip' => false,
525 + ],
526 + 'oney_product_animation' => [
527 + 'title' => __('oney_installments_pop_up', 'payplug'),
528 + 'description' => __('display_the_oney_installments_pop_up_on_the_product_page', 'payplug'),
529 + 'label' => __('Activate', 'payplug'),
530 + 'default' => false,
531 + 'desc_tip' => false,
532 + 'type' => 'oney_product_animation',
533 + ],
534 + ];
503 535
504 - /**
505 - * Process admin options.
506 - *
507 - * @return bool
508 - */
509 - public function process_admin_options() {
510 - $data = $this->get_post_data();
536 + if ($this->user_logged_in()) {
537 + if ($this->permissions->has_permissions(PayplugPermissions::SAVE_CARD)) {
538 + unset($fields['title_advanced_settings']);
539 + } elseif ('live' === $this->get_current_mode()) {
540 + $fields['save_card']['disabled'] = true;
541 + }
542 + }
511 543
512 - // Handle logout process
513 - if (
514 - isset( $data['submit_logout'] )
515 - && false !== check_admin_referer( 'payplug_user_logout', '_logoutaction' )
516 - ) {
544 + /**
545 + * Filter PayPlug gateway settings.
546 + *
547 + * @param array $fields
548 + */
549 + $fields = apply_filters('payplug_gateway_settings', $fields);
550 + $this->form_fields = $fields;
551 + }
517 552
518 - $this->permissions->clear_permissions();
553 + /**
554 + * Set global configuration for PayPlug instance.
555 + */
556 + public function init_payplug(): void
557 + {
558 + $this->payplug_api = new PayplugApi($this);
559 + $this->payplug_api->init();
519 560
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' ) );
561 + // init() just resolved this for the same mode - reuse it instead of asking
562 + // Service\Api::get_bearer_token() to do so again right after.
563 + $this->permissions = new PayplugPermissions($this, $this->payplug_api->get_current_bearer_token());
564 + $this->response = new PayplugResponse($this);
529 565
530 - return true;
531 - }
566 + // Register IPN handler
567 + new PayplugIpnResponse($this);
568 + }
532 569
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() );
570 + /**
571 + * Filter saved tokens for the gateway.
572 + *
573 + * A token will be removed if :
574 + * - it doesn't match the current merchant logged in,
575 + * - or it doesn't match the current gateway mode,
576 + * - or it is expired.
577 + *
578 + * @param array $tokens
579 + * @param int $user_id
580 + * @param string $gateway_id
581 + *
582 + * @return array
583 + */
584 + public function filter_tokens($tokens, $user_id, $gateway_id)
585 + {
586 + if (!is_user_logged_in() || !class_exists('WC_Payment_Gateway_CC')) {
587 + return $tokens;
588 + }
543 589
544 - return false;
545 - }
590 + $saved_card = $this->get_configuration()->get_option('payment_methods.configuration.payplug.save_card')
591 + && is_user_logged_in();
546 592
547 - // try to use the api keys to retrieve the merchant id
548 - $merchant_id = isset( $response['test'] ) ? $this->retrieve_merchant_id( $response['test'] ) : '';
593 + if (!$saved_card) {
594 + foreach ($tokens as $token_id => $token) {
595 + if ('payplug' == (string) $token->get_gateway_id()) {
596 + unset($tokens[$token_id]);
597 + }
598 + }
549 599
550 - $this->init_form_fields();
551 - $fields = $this->get_form_fields();
552 - $data = [];
600 + return $tokens;
601 + }
553 602
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 - }
603 + /* @var \WC_Payment_Token_CC $token */
604 + foreach ($tokens as $k => $token) {
605 + if ($this->id !== $token->get_gateway_id()) {
606 + continue;
607 + }
559 608
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 - }
609 + // check if token is associated with a merchant id and if it match the current one
610 + $token_merchant_id = $token->get_meta('payplug_account', true);
611 + if (empty($token_merchant_id) || $this->get_merchant_id() !== $token_merchant_id) {
612 + unset($tokens[$k]);
613 + continue;
614 + }
582 615
583 - $data[ $key ] = $val;
584 - }
616 + // check if token is available for the current gateway mode
617 + if ($this->mode != $token->get_meta('mode', true)) {
618 + unset($tokens[$k]);
619 + continue;
620 + }
585 621
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' ) );
622 + // check if token is not expired
623 + $current_month = \absint(date('n'));
624 + $current_year = \absint(date('Y'));
625 + if ($current_year > (int) $token->get_expiry_year()) {
626 + unset($tokens[$k]);
627 + continue;
628 + }
590 629
591 - return true;
592 - }
630 + if ($current_year === (int) $token->get_expiry_year() && $current_month > (int) $token->get_expiry_month()) {
631 + unset($tokens[$k]);
632 + continue;
633 + }
634 + }
593 635
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 - }
636 + return $tokens;
637 + }
602 638
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 - }
639 + /**
640 + * extra payment fields
641 + */
642 + public function payment_fields(): void
643 + {
644 + $description = $this->get_description();
613 645
614 - parent::process_admin_options();
615 - }
646 + if (!empty($description)) {
647 + echo wpautop(wptexturize($description));
648 + }
616 649
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 ) {
650 + if ($this->save_card_available()) {
651 + $this->tokenization_script();
652 + $this->saved_payment_methods();
653 + }
654 + }
626 655
627 - PayplugGateway::log( sprintf( 'Processing payment for order #%s', $order_id ) );
656 + /**
657 + * Handle admin display.
658 + */
659 + public function admin_options(): void
660 + {
661 + /************ VUE Code *************/
662 + wp_enqueue_script('chunk-vendors.js', PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/dist/js/chunk-vendors-' . PAYPLUG_GATEWAY_VERSION . '.js', [], PAYPLUG_GATEWAY_VERSION);
663 + wp_enqueue_script('app.js', PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/dist/js/app-' . PAYPLUG_GATEWAY_VERSION . '.js', [], PAYPLUG_GATEWAY_VERSION);
664 + wp_enqueue_style('app.css', PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/dist/css/app-' . PAYPLUG_GATEWAY_VERSION . '.css', [], PAYPLUG_GATEWAY_VERSION);
665 + wp_localize_script(
666 + 'app.js',
667 + 'payplug_admin_config',
668 + [
669 + 'img_path' => esc_url(PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/dist/'),
670 + 'ajax_url' => get_home_url(),
671 + 'rest_url' => get_home_url() . '/?rest_route=/payplug_api/',
672 + ]
673 + ); ?>
674 + <script>window.get_data_url = "<?php echo rest_url('payplug/data'); ?>"</script>
675 + <script>window.set_data_url = "<?php echo rest_url('payplug/save_data'); ?>"</script>
676 + <div id="payplug_admin"></div>
628 677
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 - }
678 + <?php
637 679
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;
680 + /*********** End VUE Code ***********/
681 + }
641 682
642 - if ( $payment_token_id && $this->oneclick_available() && (int) $customer_id > 0 ) {
643 - PayplugGateway::log( sprintf( 'Payment token found.', $amount ) );
683 + /**
684 + * Process payment.
685 + *
686 + * @param int $order_id
687 + *
688 + * @throws \Exception
689 + *
690 + * @return array
691 + */
692 + public function process_payment($order_id)
693 + {
694 + self::log(sprintf('Processing payment for order #%s', $order_id));
644 695
645 - return $this->process_payment_with_token( $order, $amount, $customer_id, $payment_token_id );
646 - }
696 + $order = wc_get_order($order_id);
697 + if (!$order instanceof \WC_Order) {
698 + self::log(sprintf('Order #%s not found.', $order_id), 'error');
699 + throw new \Exception(__('Order not found.', 'payplug'));
700 + }
701 + $customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
702 + $amount = (int) PayplugWoocommerceHelper::get_payplug_amount($order->get_total());
703 + $amount = $this->validate_order_amount($amount);
647 704
648 - return $this->process_standard_payment( $order, $amount, $customer_id );
649 - }
705 + if (is_wp_error($amount)) {
706 + self::log(sprintf('Invalid amount %s for the order.', $order->get_total()), 'error');
707 + throw new \Exception($amount->get_error_message());
708 + }
650 709
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 ) {
710 + $payment_token_id = (isset($_POST['wc-' . $this->id . '-payment-token']) && 'new' !== $_POST['wc-' . $this->id . '-payment-token'])
711 + ? wc_clean($_POST['wc-' . $this->id . '-payment-token'])
712 + : false;
660 713
661 - $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
662 - $customer_details = $this->prepare_customer_data( $order );
714 + if ($payment_token_id && (int) $customer_id > 0) {
715 + self::log(sprintf('Payment token found.', $amount));
663 716
664 - if (
665 - ! empty( $customer_details['country'] )
666 - && ! PayplugWoocommerceHelper::is_country_supported( $customer_details['country'] )
667 - ) {
668 - $customer_details['country'] = '';
669 - }
717 + return $this->process_payment_with_token($order, $amount, $customer_id, $payment_token_id);
718 + }
670 719
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 - ];
720 + return $this->process_standard_payment($order, $amount, $customer_id);
721 + }
696 722
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 );
723 + /**
724 + * Whether the current request is repaying an existing order (order-pay page, or one of
725 + * the order-pay AJAX flows). The order_key/order_pay_key posted by those AJAX flows must
726 + * match the order's own key: their mere presence isn't proof of anything, since any
727 + * request can set them.
728 + *
729 + * @param \WC_Order|null $order
730 + *
731 + * @return bool
732 + */
733 + protected function is_order_pay_request($order): bool
734 + {
735 + if (is_wc_endpoint_url('order-pay')) {
736 + return true;
737 + }
706 738
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 );
739 + $posted_key = $_POST['order_pay_key'] ?? $_POST['order_key'] ?? '';
740 + if (empty($posted_key)) {
741 + return false;
742 + }
711 743
712 - if ( is_callable( [ $order, 'save' ] ) ) {
713 - $order->save();
714 - }
744 + return $order instanceof \WC_Order && hash_equals($order->get_order_key(), wc_clean(wp_unslash($posted_key)));
745 + }
715 746
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 );
747 + /**
748 + * if payment was generated by an intend, we shouldn't generate another one and try to pay it, this would generate duplications
749 + *
750 + * @param $order
751 + *
752 + * @throws \Exception
753 + *
754 + * @return array|null
755 + */
756 + private function process_standard_intent_payment($order)
757 + {
758 + // This can run from AJAX endpoints whose own request URL never carries the order-pay
759 + // query var, so is_wc_endpoint_url() alone can't detect that context here: fall back
760 + // to the order_key/order_pay_key sent by the order-pay AJAX flows.
761 + $is_order_pay = $this->is_order_pay_request($order);
723 762
724 - $metadata = PayplugWoocommerceHelper::extract_transaction_metadata( $payment );
725 - PayplugWoocommerceHelper::save_transaction_metadata( $order, $metadata );
763 + //no order-pay page, no ajax_on_order_review_page
764 + if (!$is_order_pay &&
765 + PayplugWoocommerceHelper::is_checkout_block() &&
766 + (
767 + ('payplug' == $this->id && in_array($this->embedded_mode, ['integrated', 'popup'])) ||
768 + ('american_express' == $this->id && 'popup' == $this->embedded_mode)
769 + ) &&
770 + ($_GET['wc-ajax'] ?? '') !== 'payplug_order_review_url' &&
771 + !empty($order->get_transaction_id())
772 + ) {
773 + $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
726 774
727 - PayplugGateway::log( sprintf( 'Payment creation complete for order #%s', $order_id ) );
775 + try {
776 + $payment = $this->payplug_api->payment_retrieve($order->get_transaction_id());
777 + if (ob_get_length() > 0) {
778 + ob_clean();
779 + }
728 780
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 - }
781 + // Save transaction id for the order
782 + PayplugWoocommerceHelper::is_pre_30()
783 + ? update_post_meta($order_id, '_transaction_id', $payment->id)
784 + : $order->set_transaction_id($payment->id);
742 785
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 ) {
786 + if ($payment->is_paid) {
787 + $finished_status = wc_get_is_paid_statuses();
788 + $order->set_status($finished_status[0]);
789 + }
753 790
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 - }
791 + if (is_callable([$order, 'save'])) {
792 + $order->save();
793 + }
761 794
762 - if (
763 - ! empty( $customer_details['country'] )
764 - && ! PayplugWoocommerceHelper::is_country_supported( $customer_details['country'] )
765 - ) {
766 - $customer_details['country'] = '';
767 - }
795 + /**
796 + * Fires once a payment has been created.
797 + *
798 + * @param int $order_id Order ID
799 + * @param PaymentResource $payment Payment resource
800 + */
801 + \do_action('payplug_gateway_payment_created', $order_id, $payment);
768 802
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 - ];
803 + $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
804 + PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);
790 805
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 );
806 + self::log(sprintf('Payment intent created for order #%s', $order_id));
794 807
795 - /** This action is documented in src/Gateway/PayplugGateway */
796 - \do_action( 'payplug_gateway_payment_created', $order_id, $payment );
808 + $return_url = esc_url_raw($order->get_checkout_order_received_url());
797 809
798 - $this->response->process_payment( $payment, true );
810 + $result = [
811 + 'payment_id' => $payment->id,
812 + 'result' => 'success',
813 + 'redirect' => !empty($payment->hosted_payment->payment_url) ? $payment->hosted_payment->payment_url : $return_url,
814 + 'cancel' => !empty($payment->hosted_payment->cancel_url) ? $payment->hosted_payment->cancel_url : null,
815 + ];
799 816
800 - PayplugGateway::log( sprintf( 'Payment process complete for order #%s', $order_id ) );
817 + // wp_send_json_success() calls die(), which is only safe for the classic
818 + // wc-ajax request this was written for: the Store API checkout flow (used by
819 + // the checkout block) calls process_payment() through the REST framework,
820 + // and killing the process mid-request there produces a broken response.
821 + if (wp_doing_ajax()) {
822 + wp_send_json_success($result);
823 + }
801 824
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 - }
825 + return $result;
826 + } catch (HttpException $e) {
827 + self::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
828 + throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
829 + } catch (\Exception $e) {
830 + self::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
831 + throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
832 + }
833 + }
814 834
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 = '' ) {
835 + return null;
836 + }
825 837
826 - PayplugGateway::log( sprintf( 'Processing refund for order #%s', $order_id ) );
838 + /**
839 + * @param \WC_Order $order
840 + * @param int $amount
841 + * @param int $customer_id
842 + *
843 + * @throws \Exception
844 + *
845 + * @return array
846 + */
847 + public function process_standard_payment($order, $amount, $customer_id)
848 + {
849 + $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
827 850
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' );
851 + $intent = $this->process_standard_intent_payment($order);
852 + if (!empty($intent)) {
853 + return $intent;
854 + }
831 855
832 - return new \WP_Error( 'process_refund_error', sprintf( __( 'The order %s does not exist.', 'payplug' ), $order_id ) );
833 - }
856 + try {
857 + $address_data = PayplugAddressData::from_order($order);
834 858
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' );
859 + $return_url = esc_url_raw($order->get_checkout_order_received_url());
838 860
839 - return new \WP_Error( 'process_refund_error', __( 'No PayPlug transaction was found for this order. The refund could not be processed.', 'payplug' ) );
840 - }
861 + if (!(substr($return_url, 0, 4) === 'http')) {
862 + $return_url = get_site_url() . $return_url;
863 + }
841 864
842 - $customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
865 + $payment_data = [
866 + 'amount' => $amount,
867 + 'currency' => get_woocommerce_currency(),
868 + 'allow_save_card' => $this->save_card_available() && (int) $customer_id > 0,
869 + 'billing' => $address_data->get_billing(),
870 + 'shipping' => $address_data->get_shipping(),
871 + 'hosted_payment' => [
872 + 'return_url' => $return_url,
873 + 'cancel_url' => esc_url_raw($order->get_cancel_order_url_raw()),
874 + ],
875 + 'notification_url' => esc_url_raw(WC()->api_request_url('PayplugGateway')),
876 + 'metadata' => [
877 + 'order_id' => $order_id,
878 + 'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest',
879 + 'domain' => $this->limit_length(esc_url_raw(home_url()), 500),
880 + ],
881 + ];
843 882
844 - $data = [
845 - 'metadata' => [
846 - 'order_id' => $order_id,
847 - 'customer_id' => ( (int) $customer_id > 0 ) ? $customer_id : 'guest',
848 - ]
849 - ];
883 + if (PayplugWoocommerceHelper::is_checkout_block() && is_checkout()) {
884 + $payment_data['metadata']['woocommerce_block'] = 'CHECKOUT';
885 + } elseif (PayplugWoocommerceHelper::is_cart_block() && is_cart()) {
886 + $payment_data['metadata']['woocommerce_block'] = 'CART';
887 + }
850 888
851 - if ( ! is_null( $amount ) ) {
852 - $data['amount'] = PayplugWoocommerceHelper::get_payplug_amount( $amount );
853 - }
889 + //IP request required variables
890 + if ('integrated' == $this->embedded_mode) {
891 + $payment_data['initiator'] = 'PAYER';
892 + $payment_data['integration'] = 'INTEGRATED_PAYMENT';
893 + unset($payment_data['hosted_payment']['cancel_url']);
894 + }
854 895
855 - if ( ! empty( $reason ) ) {
856 - $data['metadata']['reason'] = $reason;
857 - }
896 + //for subscriptions the card needs to be saved
897 + $is_subscription = PayplugWoocommerceHelper::is_subscription();
898 + if (!empty($is_subscription) && $is_subscription === true) {
899 + $payment_data['allow_save_card'] = false;
900 + $payment_data['save_card'] = true;
901 + $payment_data['force_3ds'] = true;
902 + $payment_data['metadata']['subscription'] = 'subscription';
903 + }
858 904
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 );
905 + /**
906 + * Filter the payment data before it's used
907 + *
908 + * @param array $payment_data
909 + * @param int $order_id
910 + * @param array $customer_details
911 + * @param PayplugAddressData $address_data
912 + */
913 + $payment_data = apply_filters('payplug_gateway_payment_data', $payment_data, $order_id, [], $address_data);
914 + $payment = $this->payplug_api->payment_create($payment_data);
867 915
868 - try {
869 - $refund = $this->api->refund_create( $transaction_id, $data );
916 + // Save transaction id for the order
917 + PayplugWoocommerceHelper::is_pre_30()
918 + ? update_post_meta($order_id, '_transaction_id', $payment->id)
919 + : $order->set_transaction_id($payment->id);
870 920
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 );
921 + $order->set_payment_method($this->id);
922 + $order->set_payment_method_title($this->method_title);
879 923
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 - }
924 + if (is_callable([$order, 'save'])) {
925 + $order->save();
926 + }
887 927
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 );
928 + /**
929 + * Fires once a payment has been created.
930 + *
931 + * @param int $order_id Order ID
932 + * @param PaymentResource $payment Payment resource
933 + */
934 + \do_action('payplug_gateway_payment_created', $order_id, $payment);
893 935
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 - }
936 + $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
937 + PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);
900 938
901 - PayplugGateway::log( 'Refund process complete for the order.' );
939 + self::log(sprintf('Payment creation complete for order #%s', $order_id));
902 940
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' );
941 + if (ob_get_length() > 0) {
942 + ob_clean();
943 + }
906 944
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' );
945 + return [
946 + 'payment_id' => $payment->id,
947 + 'result' => 'success',
948 + 'redirect' => !empty($payment->hosted_payment->payment_url) ? $payment->hosted_payment->payment_url : $return_url,
949 + 'cancel' => !empty($payment->hosted_payment->cancel_url) ? $payment->hosted_payment->cancel_url : null,
950 + ];
951 + } catch (HttpException $e) {
952 + self::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
953 + throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
954 + } catch (\Exception $e) {
955 + self::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
956 + throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
957 + }
958 + }
910 959
911 - return new \WP_Error( 'process_refund_error', __( 'The transaction could not be refunded. Please try again.', 'payplug' ) );
912 - }
913 - }
960 + /**
961 + * @param \WC_Order $order
962 + * @param int $amount
963 + * @param int $customer_id
964 + * @param string $token_id
965 + *
966 + * @throws \Exception
967 + *
968 + * @return array
969 + */
970 + public function process_payment_with_token($order, $amount, $customer_id, $token_id)
971 + {
972 + $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
973 + $payment_token = WC_Payment_Tokens::get($token_id);
974 + if (!$payment_token || (int) $customer_id !== (int) $payment_token->get_user_id()) {
975 + self::log('Could not find the payment token or the payment doesn\'t belong to the current user.', 'error');
976 + throw new \Exception(__('Invalid payment method.', 'payplug'));
977 + }
914 978
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 - }
979 + try {
980 + $address_data = PayplugAddressData::from_order($order);
932 981
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 - }
982 + $return_url = esc_url_raw($order->get_checkout_order_received_url());
950 983
951 - return $amount;
952 - }
984 + if (!(substr($return_url, 0, 4) === 'http')) {
985 + $return_url = get_site_url() . $return_url;
986 + }
953 987
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 - }
988 + $payment_data = [
989 + 'amount' => $amount,
990 + 'currency' => get_woocommerce_currency(),
991 + 'payment_method' => $payment_token->get_token(),
992 + 'allow_save_card' => false,
993 + 'billing' => $address_data->get_billing(),
994 + 'shipping' => $address_data->get_shipping(),
995 + 'initiator' => 'PAYER',
996 + 'hosted_payment' => [
997 + 'return_url' => $return_url,
998 + 'cancel_url' => esc_url_raw($order->get_cancel_order_url_raw()),
999 + ],
1000 + 'notification_url' => esc_url_raw(WC()->api_request_url('PayplugGateway')),
1001 + 'metadata' => [
1002 + 'order_id' => $order_id,
1003 + 'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest',
1004 + 'domain' => $this->limit_length(esc_url_raw(home_url()), 500),
1005 + 'woocommerce_block' => \WC_Blocks_Utils::has_block_in_page(wc_get_page_id('checkout'), 'woocommerce/checkout'),
1006 + ],
1007 + ];
965 1008
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 - }
1009 + $is_subscription = PayplugWoocommerceHelper::is_subscription();
1010 + if (!empty($is_subscription) && $is_subscription === true) {
1011 + $payment_data['metadata']['subscription'] = 'subscription';
1012 + }
978 1013
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 - }
1014 + /** This filter is documented in src/Gateway/PayplugGateway */
1015 + $payment_data = apply_filters('payplug_gateway_payment_data', $payment_data, $order_id, [], $address_data);
1016 + $payment = $this->payplug_api->payment_create($payment_data);
984 1017
985 - return $response['httpResponse']['secret_keys'];
986 - } catch ( HttpException $e ) {
987 - return new \WP_Error( 'invalid_credentials', __( 'Invalid credentials.', 'payplug' ) );
988 - }
989 - }
1018 + // Save transaction id for the order
1019 + PayplugWoocommerceHelper::is_pre_30()
1020 + ? update_post_meta($order_id, '_transaction_id', $payment->id)
1021 + : $order->set_transaction_id($payment->id);
990 1022
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' );
1023 + if (is_callable([$order, 'save'])) {
1024 + $order->save();
1025 + }
1007 1026
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' );
1027 + /** This action is documented in src/Gateway/PayplugGateway */
1028 + \do_action('payplug_gateway_payment_created', $order_id, $payment);
1011 1029
1012 - $merchant_id = '';
1013 - } catch ( \Exception $e ) {
1014 - PayplugGateway::log( sprintf( 'Account request error : %s', wc_clean( $e->getMessage() ) ), 'error' );
1030 + $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
1031 + PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);
1015 1032
1016 - $merchant_id = '';
1017 - }
1033 + $this->response->process_payment($payment, true);
1034 + if (($payment->__get('is_paid'))) {
1035 + $redirect = $order->get_checkout_order_received_url();
1036 + } elseif (isset($payment->__get('hosted_payment')->payment_url)) {
1037 + $redirect = $payment->__get('hosted_payment')->payment_url;
1038 + } else {
1039 + $redirect = $return_url;
1040 + }
1018 1041
1019 - return $merchant_id;
1020 - }
1042 + return [
1043 + 'payment_id' => $payment->id,
1044 + 'result' => 'success',
1045 + 'is_paid' => $payment->__get('is_paid'), // Use for path redirect before DSP2
1046 + 'redirect' => $redirect,
1047 + ];
1048 + } catch (HttpException $e) {
1049 + self::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
1050 + throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
1051 + } catch (\Exception $e) {
1052 + self::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
1053 + throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
1054 + }
1055 + }
1021 1056
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 - );
1057 + /**
1058 + * Process refund for an order paid with PayPlug gateway.
1059 + *
1060 + * @param int $order_id
1061 + * @param null $amount
1062 + * @param string $reason
1063 + *
1064 + * @return bool|\WP_Error
1065 + */
1066 + public function process_refund($order_id, $amount = null, $reason = '')
1067 + {
1068 + self::log(sprintf('Processing refund for order #%s', $order_id));
1043 1069
1044 - $data = wp_parse_args( $data, $defaults );
1070 + if (!$this->user_logged_in()) {
1071 + self::log(__('You must be logged in with your PayPlug account.', 'payplug'), 'error');
1045 1072
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
1073 + return new \WP_Error('process_refund_error', __('You must be logged in with your PayPlug account.', 'payplug'));
1074 + }
1053 1075
1054 - return ob_get_clean();
1055 - }
1076 + $order = wc_get_order($order_id);
1077 + if (!$order instanceof \WC_Order) {
1078 + self::log(sprintf('The order #%s does not exist.', $order_id), 'error');
1056 1079
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 - );
1080 + return new \WP_Error('process_refund_error', sprintf(__('The order %s does not exist.', 'payplug'), $order_id));
1081 + }
1081 1082
1082 - $data = wp_parse_args( $data, $defaults );
1083 - $checked = 'yes' === $this->get_option( $key ) ? '1' : '0';
1083 + if ($order->get_status() === 'cancelled') {
1084 + self::log(sprintf('The order #%s cannot be refund.', $order_id), 'error');
1084 1085
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
1086 + return new \WP_Error('process_refund_error', sprintf(__('The order %s cannot be refund.', 'payplug'), $order_id));
1087 + }
1127 1088
1128 - return ob_get_clean();
1129 - }
1089 + $transaction_id = PayplugWoocommerceHelper::is_pre_30() ? get_post_meta($order_id, '_transaction_id', true) : $order->get_transaction_id();
1090 + if (empty($transaction_id)) {
1091 + self::log(sprintf('The order #%s does not have PayPlug transaction ID associated with it.', $order_id), 'error');
1130 1092
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 - );
1093 + return new \WP_Error('process_refund_error', __('No PayPlug transaction was found for this order. The refund could not be processed.', 'payplug'));
1094 + }
1153 1095
1154 - $data = wp_parse_args( $data, $defaults );
1096 + $customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
1155 1097
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
1098 + $data = [
1099 + 'metadata' => [
1100 + 'order_id' => $order_id,
1101 + 'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest',
1102 + 'refund_from' => 'woocommerce',
1103 + ],
1104 + ];
1184 1105
1185 - return ob_get_clean();
1186 - }
1106 + if (!is_null($amount)) {
1107 + $data['amount'] = PayplugWoocommerceHelper::get_payplug_amount($amount);
1108 + }
1187 1109
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 = [];
1110 + if (!empty($reason)) {
1111 + $data['metadata']['reason'] = $reason;
1112 + }
1199 1113
1200 - $data = wp_parse_args( $data, $defaults );
1114 + /**
1115 + * Filter the refund data before it's used.
1116 + *
1117 + * @param array $data
1118 + * @param int $order_id
1119 + * @param string $transaction_id
1120 + */
1121 + $data = apply_filters('payplug_gateway_refund_data', $data, $order_id, $transaction_id);
1201 1122
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
1123 + try {
1124 + $refund = $this->payplug_api->refund_create($transaction_id, $data);
1217 1125
1218 - return ob_get_clean();
1219 - }
1126 + /**
1127 + * Fires once a refund has been created.
1128 + *
1129 + * @param int $order_id Order ID
1130 + * @param RefundResource $refund Refund resource
1131 + * @param string $transaction_id Transaction id
1132 + */
1133 + \do_action('payplug_gateway_refund_created', $order_id, $refund, $transaction_id);
1220 1134
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;
1135 + $refund_meta_key = sprintf('_pr_%s', wc_clean($refund->id));
1136 + if (PayplugWoocommerceHelper::is_pre_30()) {
1137 + update_post_meta($order_id, $refund_meta_key, $refund->id);
1138 + } else {
1139 + $order->add_meta_data($refund_meta_key, $refund->id, true);
1140 + $order->save();
1141 + }
1233 1142
1234 - return wc_clean( stripslashes( $value ) );
1235 - }
1143 + $note = sprintf(__('Refund %s : Refunded %s', 'payplug'), wc_clean($refund->id), wc_price(((int) $refund->amount) / 100));
1144 + if (!empty($refund->metadata['reason'])) {
1145 + $note .= sprintf(' (%s)', esc_html($refund->metadata['reason']));
1146 + }
1147 + $order->add_order_note($note);
1236 1148
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 - }
1149 + try {
1150 + $payment = $this->payplug_api->payment_retrieve($transaction_id);
1151 + $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
1152 + PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);
1153 + } catch (\Exception $e) {
1154 + }
1248 1155
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 - }
1156 + self::log('Refund process complete for the order.');
1257 1157
1258 - /**
1259 - * Get user API key.
1260 - *
1261 - * @param string $mode
1262 - *
1263 - * @return string
1264 - */
1265 - public function get_api_key( $mode = 'test' ) {
1158 + return true;
1159 + } catch (HttpException $e) {
1160 + self::log(sprintf('Refund request error for the order %s from PayPlug API : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
1266 1161
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 - }
1162 + return new \WP_Error('process_refund_error', __('The transaction could not be refunded. Please try again.', 'payplug'));
1163 + } catch (\Exception $e) {
1164 + self::log(sprintf('Refund request error for the order %s : %s', $order_id, wc_clean($e->getMessage())), 'error');
1278 1165
1279 - return $key;
1280 - }
1166 + return new \WP_Error('process_refund_error', __('The transaction could not be refunded. Please try again.', 'payplug'));
1167 + }
1168 + }
1281 1169
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 - }
1170 + /**
1171 + * Check the order amount to ensure it's on the allowed range.
1172 + *
1173 + * @param int $amount
1174 + *
1175 + * @return int|\WP_Error
1176 + */
1177 + public function validate_order_amount($amount)
1178 + {
1179 + if (
1180 + $amount < PayplugWoocommerceHelper::get_minimum_amount()
1181 + || $amount > PayplugWoocommerceHelper::get_maximum_amount()
1182 + ) {
1183 + return new \WP_Error(
1184 + 'invalid order amount',
1185 + sprintf(__('Payments for this amount (%s) are not authorised with this payment gateway.', 'payplug'), \wc_price($amount / 100))
1186 + );
1187 + }
1290 1188
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 - }
1189 + return $amount;
1190 + }
1299 1191
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 -}
1192 + /**
1193 + * Limit string length.
1194 + *
1195 + * @param string $value
1196 + * @param int $maxlength
1197 + *
1198 + * @return string
1199 + */
1200 + public function limit_length($value, $maxlength = 100)
1201 + {
1202 + return (strlen($value) > $maxlength) ? substr($value, 0, $maxlength) : $value;
1203 + }
1204 +
1205 + /**
1206 + * Get user's keys.
1207 + *
1208 + * @param string $email
1209 + * @param string $password
1210 + *
1211 + * @return array|\WP_Error
1212 + */
1213 + public function retrieve_user_api_keys($email, $password)
1214 + {
1215 + if (empty($email) || empty($password)) {
1216 + return new \WP_Error('missing_login_data', __('Please fill all login fields', 'payplug'));
1217 + }
1218 +
1219 + try {
1220 + $response = Authentication::getKeysByLogin($email, $password);
1221 + if (empty($response) || !isset($response['httpResponse']) && 'payplug' === $this->id) {
1222 + return new \WP_Error('invalid_credentials', __('Invalid credentials.', 'payplug'));
1223 + }
1224 +
1225 + return $response['httpResponse']['secret_keys'];
1226 + } catch (HttpException $e) {
1227 + if ('payplug' === $this->id) {
1228 + return new \WP_Error('invalid_credentials', __('Invalid credentials.', 'payplug'));
1229 + }
1230 + }
1231 + }
1232 +
1233 + /**
1234 + * Get user merchant id.
1235 + *
1236 + * This method might be called during the login process before the global PayPlug
1237 + * configuration is set. In that case you can pass a valid token to make the request.
1238 + *
1239 + * @param string|null $key
1240 + *
1241 + * @return string
1242 + */
1243 + public function retrieve_merchant_id($key = null)
1244 + {
1245 + $merchant_id = '';
1246 + try {
1247 + $response = !is_null($key) && !empty($key) ? Authentication::getAccount(new Payplug($key)) : Authentication::getAccount();
1248 + PayplugWoocommerceHelper::set_transient_data($response);
1249 + $merchant_id = isset($response['httpResponse']['id']) ? $response['httpResponse']['id'] : '';
1250 + } catch (ConfigurationException $e) {
1251 + self::log(sprintf('Missing API key for PayPlug client : %s', wc_print_r($e->getMessage(), true)), 'error');
1252 + } catch (HttpException $e) {
1253 + self::log(sprintf('Account request error from PayPlug API : %s', wc_print_r($e->getErrorObject(), true)), 'error');
1254 + PayplugWoocommerceHelper::exception_handler_400_logout($e->getCode(), '', sprintf('Account request error from PayPlug API : %s', wc_print_r($e->getMessage(), true)));
1255 + } catch (\Exception $e) {
1256 + self::log(sprintf('Account request error : %s', wc_clean($e->getMessage())), 'error');
1257 + }
1258 +
1259 + return $merchant_id;
1260 + }
1261 +
1262 + /**
1263 + * Get PayPlug gateway mode.
1264 + *
1265 + * @return string
1266 + */
1267 + public function get_current_mode()
1268 + {
1269 + return $this->get_configuration()->get_option('mode');
1270 + }
1271 +
1272 + /**
1273 + * Get user API key.
1274 + *
1275 + * @param string $mode
1276 + *
1277 + * @return string
1278 + */
1279 + public function get_api_key($mode = 'test')
1280 + {
1281 + return $this->get_api()->get_bearer_token($mode);
1282 + }
1283 +
1284 + /**
1285 + * Get current merchant id.
1286 + *
1287 + * @return string
1288 + */
1289 + public function get_merchant_id()
1290 + {
1291 + return $this->get_configuration()->get_option('company_id');
1292 + }
1293 +
1294 + /**
1295 + * Check if user is logged in and we have an API key for TEST mode.
1296 + *
1297 + * @return bool
1298 + */
1299 + public function user_logged_in()
1300 + {
1301 + $options = $this->get_configuration()->get_options();
1302 + if (empty($options) || !isset($options['api_key']) || !isset($options['jwt'])) {
1303 + return false;
1304 + }
1305 +
1306 + $jwt = json_decode($options['jwt'], true);
1307 + if (!empty($jwt) && isset($jwt['test']) && isset($jwt['test']['access_token'])) {
1308 + return true;
1309 + }
1310 +
1311 + $api_key = json_decode($options['api_key'], true);
1312 +
1313 + return !empty($api_key) && isset($api_key['test']) && !empty($api_key['test']);
1314 + }
1315 +
1316 + /**
1317 + * Check if oneclick payment is activated and merchant can use it.
1318 + *
1319 + * @return bool
1320 + */
1321 + public function save_card_available()
1322 + {
1323 + return 'payplug' == $this->id && $this->user_logged_in()
1324 + && $this->save_card
1325 + && $this->permissions->has_permissions(PayplugPermissions::SAVE_CARD);
1326 + }
1327 +
1328 + /**
1329 + * Check if the gatteway is allowed for the order amount
1330 + *
1331 + * @param array
1332 + *
1333 + * @return array
1334 + */
1335 + public function check_gateway($gateways)
1336 + {
1337 + if (!empty(WC()->cart) && isset($gateways[$this->id]) && $gateways[$this->id]->id == $this->id) {
1338 + $order_amount = $this->get_order_total();
1339 + foreach ($gateways[$this->id]->settings['payment_methods']['permissions'] as $key => &$permission) {
1340 + $method_amounts = json_decode($permission['amounts'], true);
1341 + if ($order_amount < $method_amounts['min']['EUR'] / 100 || $order_amount > $method_amounts['max']['EUR'] / 100) {
1342 + unset($gateways[$key]);
1343 + }
1344 + }
1345 + }
1346 +
1347 + if ((bool) $this->get_configuration()->get_option('payment_methods.configuration.oney.with_fees')) {
1348 + unset($gateways['oney_x3_without_fees']);
1349 + unset($gateways['oney_x4_without_fees']);
1350 + } else {
1351 + unset($gateways['oney_x3_with_fees']);
1352 + unset($gateways['oney_x4_with_fees']);
1353 + }
1354 +
1355 + return $gateways;
1356 + }
1357 +
1358 + /**
1359 + * Can the order be refunded via this gateway?
1360 + *
1361 + *
1362 + * @param WC_Order $order Order object.
1363 + *
1364 + * @return bool If false, the automatic refund button is hidden in the UI.
1365 + */
1366 + public function can_refund_order($order)
1367 + {
1368 + $status = $order->get_status();
1369 +
1370 + return $order && $this->supports('refunds') && $status !== 'cancelled' && $status !== 'failed';
1371 + }
1372 +}