# payplug/3.1.0/src/PayplugWoocommerceRequest.php

PayPlug for WooCommerce (Official), version 3.1.0. 673 lines.

- Page: https://pluginprobe.com/plugins/payplug/3.1.0/code/src/PayplugWoocommerceRequest.php
- Raw: https://pluginprobe.com/plugins/payplug/3.1.0/raw/src/PayplugWoocommerceRequest.php
- Modified: 2026-09-25T08:27:34+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/payplug/3.1.0/code/src/PayplugWoocommerceRequest.php#L10-L20`.

```php
<?php

namespace Payplug\PayplugWoocommerce;

// Exit if accessed directly
use Automattic\WooCommerce\Utilities\OrderUtil;
use Payplug\Exception\HttpException;
use Payplug\PayplugWoocommerce\Gateway\PayplugAddressData;
use Payplug\PayplugWoocommerce\Gateway\PayplugGateway;
use Payplug\PayplugWoocommerce\Traits\ServiceGetter;

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Class PayplugWoocommerceRequest
 */
class PayplugWoocommerceRequest
{
    use ServiceGetter;

    /**
     * Gateway settings.
     *
     * @var array
     */
    protected $settings;

    /**
     * @var PayplugGateway
     */
    protected $gateway;

    /**
     * PayplugWoocommerceRequest constructor.
     */
    public function __construct()
    {
        $this->settings = $this->get_configuration()->get_options();

        if (empty($this->settings) || !isset($this->settings['enabled']) || !(bool) $this->settings['enabled']) {
            return;
        }

        // Don't load for change payment method page.
        if (isset($_GET['change_payment_method'])) {
            return;
        }

        add_action('template_redirect', [$this, 'set_session']);
        add_action('wc_ajax_payplug_create_order', [$this, 'ajax_create_order']);
        add_action('wc_ajax_applepay_update_payment', [$this, 'applepay_update_payment']);
        add_action('wc_ajax_applepay_get_order_totals', [$this, 'applepay_get_order_totals']);
        add_action('wc_ajax_payplug_order_review_url', [$this, 'ajax_create_payment']);
        add_action('wc_ajax_payplug_apple_pay_create_order_pay', [$this, 'ajax_apple_pay_create_order_pay']);
        add_action('wc_ajax_payplug_check_payment', [$this, 'check_payment']);
        add_action('wc_ajax_payplug_create_intent', [$this, 'create_payment_intent']);
    }

    /**
     * Sets the WC customer session if one is not set.
     * This is needed so nonces can be verified by AJAX Request.
     */
    public function set_session(): void
    {
        if (!is_product() || (isset(WC()->session) && WC()->session->has_session())) {
            return;
        }

        $session_class = apply_filters('woocommerce_session_handler', 'WC_Session_Handler');
        $wc_session = new $session_class();

        if (version_compare(WC_VERSION, '3.3', '>=')) {
            $wc_session->init();
        }

        $wc_session->set_customer_session_cookie(true);
    }

    /**
     * Create the woocommerce order in the BO
     */
    public function ajax_create_order(): void
    {
        if (WC()->cart->is_empty()) {
            wp_send_json_error(__('Empty cart', 'payplug'));
        }

        if (!defined('WOOCOMMERCE_CHECKOUT')) {
            define('WOOCOMMERCE_CHECKOUT', true);
        }

        WC()->checkout()->process_checkout();

        die(0);
    }

    /**
     * Process Apple Pay payment for an existing order on the order-pay page.
     */
    public function ajax_apple_pay_create_order_pay(): void
    {
        if (!check_ajax_referer('woocommerce-process_checkout', 'woocommerce-process-checkout-nonce', false)) {
            wp_send_json([
                'result' => 'failure',
                'messages' => '<ul class="woocommerce-error"><li>' . __('Invalid order.', 'payplug') . '</li></ul>',
            ]);

            return;
        }

        $order_id = isset($_POST['order_id']) ? absint(wp_unslash($_POST['order_id'])) : 0;
        $order_key = isset($_POST['order_key']) ? wc_clean(wp_unslash($_POST['order_key'])) : '';

        $order = $order_id ? wc_get_order($order_id) : null;
        if (!$order || !hash_equals($order->get_order_key(), $order_key)) {
            wp_send_json([
                'result' => 'failure',
                'messages' => '<ul class="woocommerce-error"><li>' . __('Invalid order.', 'payplug') . '</li></ul>',
            ]);

            return;
        }

        // This AJAX request's own URL never carries the order-pay query var (only the page
        // that triggered it does), but WC_Payment_Gateway::get_order_total() - used by this
        // plugin's own check_gateway() filter on woocommerce_available_payment_gateways to
        // enforce per-method amount permissions - reads that query var to know whether to use
        // the order's total or the (here empty, on order-pay) cart's. Left unset, it falls
        // back to a cart total of 0, which the amount-permission check then rejects, making
        // Apple Pay appear unavailable below. Setting it restores the normal, fully validated
        // availability check (API key, requirements, amount permissions, etc.).
        global $wp_query;
        $wp_query->set('order-pay', $order_id);

        $available_gateways = WC()->payment_gateways->get_available_payment_gateways();
        if (!isset($available_gateways['apple_pay'])) {
            wp_send_json([
                'result' => 'failure',
                'messages' => '<ul class="woocommerce-error"><li>' . __('Apple Pay not available.', 'payplug') . '</li></ul>',
            ]);

            return;
        }

        try {
            $result = $available_gateways['apple_pay']->process_payment($order_id);
            wp_send_json($result);
        } catch (\Exception $e) {
            wp_send_json([
                'result' => 'failure',
                'messages' => '<ul class="woocommerce-error"><li>' . esc_html($e->getMessage()) . '</li></ul>',
            ]);
        }
    }

    /**
     * Create the woocommerce order in the BO
     */
    public function ajax_create_payment(): void
    {
        global $wp;

        $https_referer = wc_clean(wp_unslash($_POST['_wp_http_referer'] ?? ''));
        $path = wp_parse_url($https_referer) ?: [];
        $output = [];
        if (!empty($path['query'])) {
            wp_parse_str($path['query'], $output);
        }

        if (isset($output['order-pay'])) {
            $order_id = absint($output['order-pay']);
        } else {
            preg_match('/(?<=order-pay\/)\d*/', $path['path'] ?? '', $matches);
            $order_id = !empty($matches[0]) ? absint($matches[0]) : 0;
        }

        // The referer is client-supplied and can be spoofed: only trust it as an order-pay
        // request once the order it names is confirmed real and the key matches, exactly
        // like the order-pay AJAX flows below already require (create_payment_intent,
        // ajax_apple_pay_create_order_pay). Otherwise fall through as a regular checkout.
        $order = $order_id ? wc_get_order($order_id) : false;
        if (!$order instanceof \WC_Order || !hash_equals($order->get_order_key(), wc_clean(wp_unslash($output['key'] ?? '')))) {
            $order_id = 0;
        }

        // Order-pay repays an existing order, whose line items live on the order itself,
        // not the session cart - which is legitimately empty here (the customer already
        // completed checkout for it), so only require a non-empty cart on a fresh checkout.
        if (empty($order_id) && WC()->cart->is_empty()) {
            wp_send_json_error(__('Empty cart', 'payplug'));
        }

        if (!defined('WOOCOMMERCE_CHECKOUT')) {
            define('WOOCOMMERCE_CHECKOUT', true);
        }

        $payment_method = $_POST['payment_method'];

        if ($payment_method === 'payplug' || $payment_method === 'american_express') {
            $method = $this->get_configuration()->get_option('payment_methods.configuration.payplug.embedded_mode');
            if ('integrated' != $method && 'popup' != $method) {
                $this->ajax_create_order();
            }
        } else {
            $this->ajax_create_order();
        }

        $this->process_order_payment($order_id, $payment_method);
    }

    /**
     * wordpress class-wc-checkout.php
     * We don't need all the other verifications, at this point customer already had the checkout and it's all valid, the order already exists
     * We can+t use WP method because it's protected
     * Process an order that does require payment.
     *
     * @since 3.0.0
     *
     * @param int $order_id Order ID.
     * @param string $payment_method Payment method.
     */
    protected function process_order_payment($order_id, $payment_method): void
    {
        // This AJAX request's own URL never carries the order-pay query var (only the page
        // that triggered it does), but WC_Payment_Gateway::get_order_total() - used by this
        // plugin's own check_gateway() filter on woocommerce_available_payment_gateways to
        // enforce per-method amount permissions - reads that query var to know whether to use
        // the order's total or the (here empty, on order-pay) cart's. Left unset, it falls
        // back to a cart total of 0, which the amount-permission check then rejects, making
        // every gateway appear unavailable below. Setting it restores the normal, fully
        // validated availability check (API key, requirements, amount permissions, etc.).
        global $wp_query;
        $wp_query->set('order-pay', $order_id);

        $available_gateways = WC()->payment_gateways->get_available_payment_gateways();

        if (!isset($available_gateways[$payment_method])) {
            return;
        }

        // Store Order ID in session so it can be re-used after payment failure.
        WC()->session->set('order_awaiting_payment', $order_id);

        // Process Payment.
        $result = $available_gateways[$payment_method]->process_payment($order_id);

        // Redirect to success/confirmation/payment page.
        if (isset($result['result']) && 'success' === $result['result']) {
            $result['order_id'] = $order_id;

            $result = apply_filters('woocommerce_payment_successful_result', $result, $order_id);

            if (!wp_doing_ajax()) {
                // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
                wp_redirect($result['redirect']);
                exit;
            }

            wp_send_json($result);
        }
    }

    /**
     * Update Payplug API Payment for Apple Pay
     */
    public function applepay_update_payment(): void
    {
        $payment_id = $_POST['payment_id'];
        $apiService = new \Payplug\PayplugWoocommerce\Service\Api();
        $mode = PayplugWoocommerceHelper::check_mode() ? 'live' : 'test';
        $bearer_token = $apiService->get_bearer_token($mode);
        try {
            \Payplug\Payplug::init([
                'secretKey' => $bearer_token,
                'apiVersion' => '2019-08-06',
            ]);
        } catch (\Exception $e) {
            $is_401 = (method_exists($e, 'getCode') && $e->getCode() == 401)
                || strpos($e->getMessage(), '401') !== false;
            if ($is_401 && $this->try_refresh_jwt($apiService, $mode)) {
                try {
                    \Payplug\Payplug::init([
                        'secretKey' => $apiService->get_bearer_token($mode),
                        'apiVersion' => '2019-08-06',
                    ]);
                } catch (\Exception $e) {
                    PayplugWoocommerceHelper::payplug_logout();
                    wp_send_json_error(__('You have been disconnected from the Payplug module (error 401)', 'payplug'));

                    return;
                }
            } else {
                if ($is_401) {
                    PayplugWoocommerceHelper::payplug_logout();
                    wp_send_json_error(__('You have been disconnected from the Payplug module (error 401)', 'payplug'));
                } else {
                    wp_send_json_error($e->getMessage());
                }

                return;
            }
        }
        $apple_pay = [];
        $apple_pay['payment_token'] = $_POST['payment_token'];
        $payment = \Payplug\Payment::retrieve($payment_id);
        $data = ['apple_pay' => $apple_pay];
        $update = $payment->update($data);
        wp_send_json_success(['result' => $update->is_paid]);
    }

    public function applepay_get_order_totals(): void
    {
        try {
            wp_send_json_success(WC()->cart->total);
        } catch (\Exception $e) {
            PayplugGateway::log($e->getMessage());
            wp_send_json_error($e->getMessage());
        }
    }

    /**
     * Empty cart for Apple Pay on product page
     */
    public function applepay_empty_cart(): void
    {
        try {
            WC()->cart->empty_cart();
            wp_send_json_success();
        } catch (\Exception $e) {
            wp_send_json_error([
                'message' => __('Your order was cancelled.', 'woocommerce'),
            ]);
        }
    }

    /**
     * Add the product on the current page to the cart for Apple Pay on product page
     */
    public function applepay_add_to_cart(): void
    {
        try {
            if (!empty($_POST['product_id'])) {
                $product_id = $_POST['product_id'];
            } else {
                $product_id = $_POST['product_variation_id'];
            }

            $product_quantity = !empty($_POST['product_quantity']) ? $_POST['product_quantity'] : 1;

            WC()->cart->add_to_cart($product_id, $product_quantity);
            wp_send_json_success([
                'total' => WC()->cart->total - WC()->cart->shipping_total,
            ]);
        } catch (\Exception $e) {
            wp_send_json_error([
                'message' => __('Your order was cancelled.', 'woocommerce'),
            ]);
        }
    }

    /**
     * Limit string length.
     *
     * @param string $value
     * @param int $maxlength
     *
     * @return string
     */
    public function limit_length($value, $maxlength = 100)
    {
        return (strlen($value) > $maxlength) ? substr($value, 0, $maxlength) : $value;
    }

    public function check_payment(): void
    {
        global $wpdb;
        $payment_id = $_POST['payment_id'];
        if (empty($payment_id)) {
            wp_send_json_error(__('Invalid request.', 'payplug'));
        }
        $apiService = new \Payplug\PayplugWoocommerce\Service\Api();
        $mode = PayplugWoocommerceHelper::check_mode() ? 'live' : 'test';
        $bearer_token = $apiService->get_bearer_token($mode);
        try {
            \Payplug\Payplug::init([
                'secretKey' => $bearer_token,
                'apiVersion' => '2019-08-06',
            ]);
            $payment = \Payplug\Payment::retrieve($payment_id);
        } catch (\Exception $e) {
            $is_401 = (method_exists($e, 'getCode') && $e->getCode() == 401)
                || strpos($e->getMessage(), '401') !== false;
            if ($is_401) {
                if ($this->try_refresh_jwt($apiService, $mode)) {
                    try {
                        \Payplug\Payplug::init([
                            'secretKey' => $apiService->get_bearer_token($mode),
                            'apiVersion' => '2019-08-06',
                        ]);
                        $payment = \Payplug\Payment::retrieve($payment_id);
                    } catch (\Exception $e) {
                        PayplugWoocommerceHelper::payplug_logout();
                        wp_send_json_error(__('You have been disconnected from the Payplug module (error 401)', 'payplug'));

                        return;
                    }
                } else {
                    PayplugWoocommerceHelper::payplug_logout();
                    wp_send_json_error(__('You have been disconnected from the Payplug module (error 401)', 'payplug'));

                    return;
                }
            } else {
                $order_id = $this->getOrderFromPaymentId($payment_id);
                $order = wc_get_order($order_id);
                PayplugGateway::log(
                    sprintf(
                        'Order #%s : An error occurred while retrieving the payment data with the message : %s',
                        $order_id,
                        $e->getMessage()
                    )
                );
                wp_send_json_error($e->getMessage());
            }
        }
        $order_id = $this->getOrderFromPaymentId($payment_id);
        $order = wc_get_order($order_id);
        $return_url = esc_url_raw($order->get_checkout_order_received_url());
        if ((isset($payment->failure)) && (!empty($payment->failure)) || ($payment->is_paid === false && is_null($payment->paid_at))) {
            $order->update_status('failed', __('Order cancelled by customer.', 'woocommerce'));
            wp_send_json_error(
                [
                    'code' => isset($payment->failure->code) ? $payment->failure->code : 500,
                    'message' => !empty($payment->failure->message) ? $payment->failure->message : __('payplug_integrated_payment_error', 'payplug'),
                    'cancel_url' => esc_url_raw($order->get_cancel_order_url_raw()),
                ]
            );
        }

        wp_send_json_success([
            'payment_id' => $payment->id,
            'result' => 'success',
            'redirect' => !empty($payment->hosted_payment->payment_url) ? $payment->hosted_payment->payment_url : $return_url,
            'cancel' => !empty($payment->hosted_payment->cancel_url) ? $payment->hosted_payment->cancel_url : null,
        ]);
    }

    /**
     * Attempt to regenerate the JWT for the given mode and persist the new token.
     *
     * @param \Payplug\PayplugWoocommerce\Service\Api $apiService
     * @param string $mode
     *
     * @return bool True if a new JWT was successfully obtained and stored.
     */
    private function try_refresh_jwt($apiService, $mode)
    {
        $configuration = $this->get_configuration();
        $options = $configuration->get_options();
        $oauth_client_data = isset($options['oauth_client_data']) ? json_decode($options['oauth_client_data'], true) : [];
        $client_data = isset($oauth_client_data[$mode]) ? $oauth_client_data[$mode] : [];
        if (empty($client_data)) {
            return false;
        }
        $new_jwt = $apiService->generate_jwt(
            isset($client_data['client_id']) ? $client_data['client_id'] : '',
            isset($client_data['client_secret']) ? $client_data['client_secret'] : ''
        );
        if (empty($new_jwt) || !isset($new_jwt['access_token'])) {
            return false;
        }
        $jwt = isset($options['jwt']) ? json_decode($options['jwt'], true) : [];
        $jwt[$mode] = $new_jwt;
        $api_keys = isset($options['api_key']) ? json_decode($options['api_key'], true) : [];
        $api_keys[$mode] = $new_jwt['access_token'];
        $configuration->update_option('jwt', json_encode($jwt));
        $configuration->update_option('api_key', json_encode($api_keys));

        return true;
    }

    /**
     * @param $payment_id
     *
     * @return int|string|null
     */
    private function getOrderFromPaymentId($payment_id)
    {
        global $wpdb;

        if (OrderUtil::custom_orders_table_usage_is_enabled()) {
            $orders = wc_get_orders(
                [
                    'field_query' => [
                        [
                            'key' => 'transaction_id',
                            'comparison' => $payment_id,
                        ],
                    ],
                ]
            );
            $order = $orders[0];
            $order_id = $order->get_id();
        } else {
            $sql = 'SELECT post_id
					FROM $wpdb->postmeta
					WHERE meta_key = "_transaction_id" AND meta_value = %s';
            $order_id = $wpdb->get_var(
                $wpdb->prepare(
                    $sql,
                    $payment_id
                )
            );
        }

        return $order_id;
    }

    public function create_payment_intent(): void
    {
        if (!check_ajax_referer('woocommerce-process_checkout', 'woocommerce-process-checkout-nonce', false)) {
            wp_send_json_error(__('Invalid order.', 'payplug'), 403);

            return;
        }

        $order_id = isset($_POST['order_id']) ? absint(wp_unslash($_POST['order_id'])) : 0;
        $this->gateway = $this->get_payplug_gateway(isset($_POST['gateway']) ? wc_clean(wp_unslash($_POST['gateway'])) : '');
        $order = wc_get_order($order_id);

        if (!$order instanceof \WC_Order || !$this->gateway) {
            wp_send_json_error(__('Invalid order.', 'payplug'));

            return;
        }

        // On order-pay, closing the payment sheet should keep the customer on the order-pay
        // page, not cancel the order and send them to the cart like a fresh checkout attempt
        // would.
        $is_order_pay = is_wc_endpoint_url('order-pay') || !empty($_POST['order_pay_key']);

        if (!empty($_POST['order_pay_key'])) {
            $order_pay_key = wc_clean(wp_unslash($_POST['order_pay_key']));
            if (!hash_equals($order->get_order_key(), $order_pay_key)) {
                wp_send_json_error(__('Invalid order.', 'payplug'));

                return;
            }
        }

        $cancel_url = $is_order_pay
            ? esc_url_raw($order->get_checkout_payment_url())
            : esc_url_raw($order->get_cancel_order_url_raw());

        $customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
        $return_url = esc_url_raw($order->get_checkout_order_received_url());
        $address_data = PayplugAddressData::from_order($order);
        $amount = (int) PayplugWoocommerceHelper::get_payplug_amount($order->get_total());
        $amount = $this->gateway->validate_order_amount($amount);

        $payment_data = [
            'amount' => $amount,
            'currency' => get_woocommerce_currency(),
            'allow_save_card' => $this->gateway->save_card_available() && (int) $customer_id > 0,
            'billing' => $address_data->get_billing(),
            'shipping' => $address_data->get_shipping(),
            'hosted_payment' => [
                'return_url' => $return_url,
            ],
            'notification_url' => esc_url_raw(WC()->api_request_url('PayplugGateway')),
            'metadata' => [
                'order_id' => $order_id,
                'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest',
                'domain' => $this->limit_length(esc_url_raw(home_url()), 500),
            ],
        ];

        if (PayplugWoocommerceHelper::is_checkout_block() && is_checkout()) {
            $payment_data['metadata']['woocommerce_block'] = 'CHECKOUT';
        } elseif (PayplugWoocommerceHelper::is_cart_block() && is_cart()) {
            $payment_data['metadata']['woocommerce_block'] = 'CART';
        }

        if ($this->gateway->id === 'apple_pay') {
            unset($payment_data['allow_save_card']);

            $payment_data['payment_method'] = $this->gateway->id;
            $payment_data['payment_context'] = [
                'apple_pay' => [
                    'domain_name' => $this->gateway->domain_name,
                    'application_data' => base64_encode(json_encode([
                        'apple_pay_domain' => $this->gateway->domain_name,
                    ])),
                ],
            ];
            $payment_data['hosted_payment']['cancel_url'] = $cancel_url;
            $payment_data['metadata']['applepay_workflow'] = 'checkout';
        }

        $method = $this->get_configuration()->get_option('payment_methods.configuration.payplug.embedded_mode');

        if ('integrated' == $method && $this->gateway->id === 'payplug') {
            $payment_data['initiator'] = 'PAYER';
            $payment_data['integration'] = 'INTEGRATED_PAYMENT';
            unset($payment_data['hosted_payment']['cancel_url']);
        }

        if ('popup' == $method && $this->gateway->id === 'american_express') {
            $payment_data['payment_method'] = $this->gateway->id;
        }

        /**
         * Filter the payment data before it's used
         *
         * @param array $payment_data
         * @param int $order_id
         * @param array $customer_details
         * @param PayplugAddressData $address_data
         */
        $payment_data = apply_filters('payplug_gateway_payment_data', $payment_data, $order_id, [], $address_data);

        try {
            $payment = $this->gateway->payplug_api->payment_create($payment_data);
        } catch (HttpException $e) {
            PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
            wp_send_json_error(__('Payment processing failed. Please retry.', 'payplug'));

            return;
        } catch (\Exception $e) {
            PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
            wp_send_json_error(__('Payment processing failed. Please retry.', 'payplug'));

            return;
        }

        // Save transaction id on the order
        PayplugWoocommerceHelper::is_pre_30() ? update_post_meta($order_id, '_transaction_id', $payment->id) : $order->set_transaction_id($payment->id);

        if (is_callable([$order, 'save'])) {
            $order->save();
        }

        $metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
        PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);

        wp_send_json_success([
            'payment_id' => $payment->id,
            'merchant_session' => isset($payment->payment_method['merchant_session']) ? $payment->payment_method['merchant_session'] : null,
            'redirect' => !empty($payment->hosted_payment->payment_url) ? $payment->hosted_payment->payment_url : $return_url,
            'cancel' => $cancel_url,
        ]);
    }

    /**
     * Returns an instantiated gateway.
     *
     * @return PayplugGateway
     */
    protected function get_payplug_gateway($id)
    {
        if (!isset($this->gateway)) {
            $gateways = WC()->payment_gateways()->payment_gateways();
            foreach ($gateways as $gateway) {
                if ($gateway->id === $id) {
                    return $gateway;
                }
            }
        }
    }
}

```
