JezK
Edit File: class-cartflows-pro-gateway-mollie-ideal.php
<?php /** * Mollie Gateway. Ideal method. * * @package cartflows */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Class Cartflows_Pro_Gateway_Mollie_ideal. */ class Cartflows_Pro_Gateway_Mollie_Ideal extends Cartflows_Pro_Mollie_Gateway_Helper { /** * Member Variable * * @var instance */ private static $instance; /** * Key name variable * * @var key */ public $key = 'mollie_wc_gateway_ideal'; /** * Refund Supported * * @var is_api_refund */ public $is_api_refund = true; /** * Initiator */ public static function get_instance() { if ( ! isset( self::$instance ) ) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ public function __construct() { add_action( $this->plugin_id . '_customer_return_payment_success', array( $this, 'maybe_setup_upsell' ), 5 ); /* Create mollie customer id for non logged-in user - Ideal */ add_filter( 'woocommerce_mollie_wc_gateway_ideal_args', array( $this, 'maybe_create_mollie_customer_id' ), 10, 2 ); add_action( 'wp_ajax_wcf_mollie_ideal_process', array( $this, 'process_ideal_payment' ) ); add_action( 'wp_ajax_nopriv_wcf_mollie_ideal_process', array( $this, 'process_ideal_payment' ) ); /** * Mollie ideal webhook while creating payment */ add_action( 'woocommerce_api_cartflows_mollie_ideal_webhook', array( $this, 'maybe_handle_mollie_ideal_webhook' ) ); add_action( 'cartflows_offer_child_order_created_' . $this->key, array( $this, 'store_mollie_meta_keys_for_refund' ), 10, 3 ); add_action( 'cartflows_offer_subscription_created', array( $this, 'add_subscription_payment_meta_for_mollie' ), 10, 3 ); } /** * May be setup upsell. * * @param object $order Order object. * * @return void */ public function maybe_setup_upsell( $order ) { Cartflows_Pro_Frontend::get_instance()->start_the_upsell_flow( $order ); } /** * Get webhook url. * * @param int $step_id step id. * @param int $order_id order id. * @param string $order_key order key. * * @return string */ public function get_webhook_url( $step_id, $order_id, $order_key ) { $url = WC()->api_request_url( 'cartflows_mollie_ideal_webhook' ); $args = array( 'step_id' => $step_id, 'order_id' => $order_id, 'order_key' => $order_key, ); return add_query_arg( $args, $url ); } /** * After payment process. * * @param object $order order data. * @param array $product product data. * @return array */ public function process_offer_payment( $order, $product ) { $is_successful = false; $tr_id = $order->get_meta( '_' . $product['step_id'] . '_tr_id', true ); $response_data = array( 'id' => $tr_id, ); $this->store_offer_transaction( $order, $response_data, $product ); if ( '' !== $tr_id ) { $is_successful = true; } return $is_successful; } /** * Store Offer Trxn Charge. * * @param WC_Order $order The order that is being paid for. * @param Object $response The response that is send from the payment gateway. * @param array $product The product data. */ public function store_offer_transaction( $order, $response, $product ) { wcf()->logger->log( 'Mollie Ideal : Offer Transaction :: Transaction ID = ' . $response['id'] . ' Captured' ); $order->update_meta_data( 'cartflows_offer_txn_resp_' . $product['step_id'], $response['id'] ); $order->save(); } /** * Process credit card upsell payment. * * @return bool */ public function process_ideal_payment() { $nonce = isset( $_POST['_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'wcf_mollie_ideal_process' ) ) { return; } $step_id = isset( $_POST['step_id'] ) ? intval( $_POST['step_id'] ) : 0; $flow_id = isset( $_POST['flow_id'] ) ? intval( $_POST['flow_id'] ) : 0; $order_id = isset( $_POST['order_id'] ) ? sanitize_text_field( wp_unslash( $_POST['order_id'] ) ) : 0; $order_key = isset( $_POST['order_key'] ) ? sanitize_text_field( wp_unslash( $_POST['order_key'] ) ) : ''; $session_key = isset( $_COOKIE[ CARTFLOWS_SESSION_COOKIE . $flow_id ] ) ? sanitize_text_field( wp_unslash( $_COOKIE[ CARTFLOWS_SESSION_COOKIE . $flow_id ] ) ) : ''; //phpcs:ignore WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___COOKIE $order = wc_get_order( $order_id ); $variation_id = ''; $input_qty = ''; if ( isset( $_POST['variation_id'] ) && ! empty( $_POST['variation_id'] ) ) { $variation_id = intval( $_POST['variation_id'] ); } if ( isset( $_POST['input_qty'] ) && ! empty( $_POST['input_qty'] ) ) { $input_qty = intval( $_POST['input_qty'] ); } // May need to update the data if variation and quantity changes using offer shortcodes ). $product_data = array( 'variation_id' => $variation_id, 'input_qty' => $input_qty, ); $order->update_meta_data( 'wcf_offer_product_data_' . $step_id, $product_data ); $order->save(); $offer_product = wcf_pro()->utils->get_offer_data( $step_id, $variation_id, $input_qty, $order_id ); if ( isset( $offer_product['price'] ) && ( floatval( 0 ) === floatval( $offer_product['price'] ) || '' === trim( $offer_product['price'] ) ) ) { wp_send_json( array( 'result' => 'fail', 'message' => __( '0 value product', 'cartflows-pro' ), ) ); } else { $api_key = $this->get_mollie_api_key(); $customer_id = $this->get_user_mollie_customer_id( $order ); if ( $customer_id ) { $data = array( 'amount' => array( 'currency' => $order->get_currency(), 'value' => $this->format_currency_value( $offer_product['price'], $order->get_currency() ), ), 'description' => "One-click payment {$order_id}_{$step_id}", 'redirectUrl' => $this->get_return_url( $step_id, $order_id, $order_key, $session_key ), 'webhookUrl' => $this->get_webhook_url( $step_id, $order_id, $order_key ), 'method' => 'ideal', 'metadata' => array( 'order_id' => $order_id, ), 'customerId' => $customer_id, ); $arguments = array( 'method' => 'POST', 'headers' => array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $api_key, ), 'body' => wp_json_encode( $data ), ); $payment_object = $this->get_mollie_api_response_body( $this->create_payment_api, $arguments ); if ( $payment_object ) { wp_send_json( array( 'result' => 'success', 'redirect' => $this->get_process_payment_redirect( $payment_object ), ) ); } } wp_send_json( array( 'result' => 'fail', 'message' => __( 'Customer id not found. Payment failed', 'cartflows-pro' ), ) ); } } /** * Handle mollie payment webhook. * * @return void */ public function maybe_handle_mollie_ideal_webhook() { wcf()->logger->log( 'Start-' . __CLASS__ . ':' . __METHOD__ . ': Entering' ); //phpcs:disable WordPress.Security.NonceVerification // Webhook test by Mollie. if ( isset( $_GET['testByMollie'] ) ) { wcf()->logger->log( __METHOD__ . ': Webhook tested by Mollie.' ); return; } if ( empty( $_GET['order_id'] ) || empty( $_GET['order_key'] ) || empty( $_GET['step_id'] ) ) { $this->set_http_response_code( 400 ); wcf()->logger->log( __METHOD__ . ': No order ID or order key provided.' ); return; } $step_id = sanitize_text_field( wp_unslash( $_GET['step_id'] ) ); $order_id = sanitize_text_field( wp_unslash( $_GET['order_id'] ) ); $order_key = sanitize_text_field( wp_unslash( $_GET['order_key'] ) ); $order = wc_get_order( $order_id ); if ( ! $order ) { $this->set_http_response_code( 404 ); wcf()->logger->log( __METHOD__ . ": Could not find order $order_id." ); return; } if ( ! $order->key_is_valid( $order_key ) ) { $this->set_http_response_code( 401 ); wcf()->logger->log( __METHOD__ . ": Invalid key key for order $order_id." ); return; } // No Mollie payment id provided. if ( empty( $_POST['id'] ) ) { $this->set_http_response_code( 400 ); wcf()->logger->log( __METHOD__ . ': No payment object ID provided.' ); return; } $payment_object_id = sanitize_text_field( wp_unslash( $_POST['id'] ) ); // Load the payment from Mollie, do not use cache. $get_payment = $this->get_payment_api . $payment_object_id; $api_key = $this->get_mollie_api_key(); $arguments = array( 'method' => 'GET', 'headers' => array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $api_key, ), ); try { $payment = $this->get_mollie_api_response_body( $get_payment, $arguments ); } catch ( Exception $e ) { $this->set_http_response_code( 400 ); return; } wcf()->logger->log( 'Processed payment : ' . wcf_print_r( $payment, true ) ); // Payment not found. if ( ! $payment ) { $this->set_http_response_code( 404 ); wcf()->logger->log( __METHOD__ . ": payment object $payment_object_id not found." ); return; } if ( $order_id != $payment->metadata->order_id ) { $this->set_http_response_code( 400 ); wcf()->logger->log( __METHOD__ . ": Order ID does not match order_id in payment metadata. Payment ID {$payment->id}, order ID $order_id" ); return; } // List of order status which needs to be used to confirm the payment is processed successfully or not. $order_statuses = array( 'failed', 'canceled', 'expired' ); // Log the error and return if the order status of the payment is not success or open. if ( in_array( $payment->status, $order_statuses ) ) { $this->set_http_response_code( 204 ); wcf()->logger->log( __METHOD__ . ": Order is not complete for order ID {$order_id}. Received order status: {$payment->status}" ); wcf()->logger->log( 'End-' . __CLASS__ . ':' . __METHOD__ . ': Exiting as order is not complete.' ); return; } $order->update_meta_data( '_' . $step_id . '_tr_id', $payment_object_id ); $order->save(); $order->add_order_note( __( 'Mollie ideal payment processed.', 'cartflows-pro' ) ); //phpcs:enable WordPress.Security.NonceVerification wcf()->logger->log( 'End-' . __CLASS__ . ':' . __METHOD__ . ': Exiting' ); } /** * Is gateway support offer refund? * * @return bool */ public function is_api_refund() { return $this->is_api_refund; } } /** * Prepare if class 'Cartflows_Pro_Gateway_Mollie_Credit_Card' exist. * Kicking this off by calling 'get_instance()' method */ Cartflows_Pro_Gateway_Mollie_Ideal::get_instance();