JezK
Edit File: class-cartflows-pro-gateway-cppw-paypal.php
<?php /** * Checkout Plugins - Payment Gateway ( Smart Payment ). * * @package cartflows */ if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * Class Cartflows_Pro_Gateway_Cppw_Paypal. */ class Cartflows_Pro_Gateway_Cppw_Paypal { /** * Member Variable * * @var instance */ private static $instance; /** * Key name variable * * @var key */ public $key = 'cppw_paypal'; /** * Refund supported variable * * @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_filter( 'cppw_should_save_billing_agreement', array( $this, 'tokenize_if_required' ) ); add_filter( 'cppw_should_enable_billing_token_on_checkout', array( $this, 'tokenize_if_required' ) ); add_action( 'wp_ajax_wcf_cppw_create_paypal_order', array( $this, 'create_payment_order' ) ); add_action( 'wp_ajax_nopriv_wcf_cppw_create_paypal_order', array( $this, 'create_payment_order' ) ); add_action( 'cartflows_offer_subscription_created', array( $this, 'add_subscription_payment_meta' ), 10, 3 ); add_action( 'cartflows_offer_child_order_created_' . $this->key, array( $this, 'add_required_meta_to_child_order' ), 10, 3 ); } /** * Handle payment type of checkout plugin paypal. * * @param string $paypal_order_id paypal order id. * @param array $payment_source_body payment source body. * * @return object */ public function handle_payment_type_request( $paypal_order_id, $payment_source_body ) { wcf()->logger->log( 'Started: ' . __CLASS__ . '::' . __FUNCTION__ ); $gateway = $this->get_wc_gateway(); $paypal_type = $gateway->paypal_type; wcf()->logger->log( 'PayPal Type: ' . $paypal_type ); if ( 'capture' === $paypal_type ) { return $gateway->capture_order_request( $paypal_order_id, $payment_source_body ); } if ( 'authorize' === $paypal_type ) { return $gateway->authorize_order_request( $paypal_order_id, $payment_source_body ); } wcf()->logger->log( 'Started: ' . __CLASS__ . '::' . __FUNCTION__ ); } /** * After payment process. * * @param object $order order data. * @param array $product product data. * @return bool */ public function process_offer_payment( $order, $product ) { wcf()->logger->log( 'Started: ' . __CLASS__ . '::' . __FUNCTION__ ); $is_successful = false; $nonce = isset( $_POST['_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, $product['action'] ) ) { wcf()->logger->log( 'Nonce verification failed. ' . __CLASS__ . '::' . __FUNCTION__ ); return $is_successful; } // Confirm if same transaction id then only create Woo Offer order. $offer_order_tr_id = $order->get_meta( 'cartflows_offer_txn_resp_' . $product['step_id'] ); $received_tr_id = isset( $_POST['transaction_id'] ) ? sanitize_text_field( wp_unslash( $_POST['transaction_id'] ) ) : ''; if ( $offer_order_tr_id === $received_tr_id ) { $is_successful = true; } wcf()->logger->log( 'Payment Status: ' . $is_successful ); wcf()->logger->log( 'Ended: ' . __CLASS__ . '::' . __FUNCTION__ ); return $is_successful; } /** * Verify 3DS and create intent accordingly. * * @return void */ public function create_payment_order() { wcf()->logger->log( 'Started: ' . __CLASS__ . '::' . __FUNCTION__ ); $nonce = isset( $_POST['_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_nonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, 'wcf_cppw_create_paypal_order' ) ) { wp_send_json( array( 'result' => 'fail', 'message' => 'Nonce verification failed.', ) ); } $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'] ); } $step_id = isset( $_POST['step_id'] ) ? intval( $_POST['step_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'] ) ) : ''; $order = wc_get_order( $order_id ); // Verify order ownership via order_key. if ( ! $order instanceof WC_Order || ! $order->key_is_valid( $order_key ) ) { wp_send_json_error( array( 'message' => __( 'Invalid order', 'cartflows-pro' ) ) ); return; } $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'] ) ) ) { wcf()->logger->log( 'Zero value product selected' ); wp_send_json( array( 'result' => 'fail', 'message' => '0 value product', ) ); } else { wcf()->logger->log( 'Creating paypal order.' ); $gateway = $this->get_wc_gateway(); // Create paypal order from paypal end. $paypal_order = $gateway->create_paypal_order_of_wc( $order, number_format( (float) $offer_product['total'], 2, '.', '' ) ); if ( empty( $paypal_order['id'] ) ) { wcf()->logger->log( 'No paypal order created.' ); } wcf()->logger->log( 'Paypal Order Response: ' . wcf_print_r( $paypal_order, true ) ); // Capture newly created paypal order based on billing agreement id. $billing_agreement_id = $order->get_meta( CPPW_SUB_AGREEMENT_ID ); wcf()->logger->log( 'Billing Agreement ID: ' . $billing_agreement_id ); $payment_source_body = $gateway->generate_payment_source_body( $billing_agreement_id ); wcf()->logger->log( 'Payment Source Body: ' . wcf_print_r( $payment_source_body, true ) ); // Handle payment request created above. $capture_payment = $this->handle_payment_type_request( $paypal_order['id'], $payment_source_body ); // Retrive transaction id of offer prder and save it. $transaction_id = $gateway->get_transaction_id( $capture_payment ); wcf()->logger->log( 'Captured Paypal Order: ' . wcf_print_r( $capture_payment, true ) ); if ( isset( $capture_payment['status'] ) && 'COMPLETED' === $capture_payment['status'] ) { if ( 'capture' === $gateway->paypal_type ) { $price_breakdown = ! empty( $capture_payment['purchase_units'][0]['payments']['captures'][0]['seller_receivable_breakdown'] ) ? $capture_payment['purchase_units'][0]['payments']['captures'][0]['seller_receivable_breakdown'] : false; // Save offer order paypal fee & other data in main order meta. Will update it later in clid order. $this->add_fee_net_amount( $order, $price_breakdown, $step_id ); } $order->update_meta_data( 'cartflows_offer_txn_resp_' . $step_id, $transaction_id ); $order->update_meta_data( 'cartflows_offer' . CPPW_SUB_AGREEMENT_ID . '_' . $step_id, $billing_agreement_id ); $order->save(); wcf()->logger->log( 'Transaction ID: ' . $transaction_id ); wp_send_json( array( 'status' => 'succeeded', 'transaction_id' => $transaction_id, ) ); } else { wcf()->logger->log( 'Creating a PayPal order failed due to the order not captured.' ); wp_send_json( array( 'status' => 'fail', 'message' => 'Payment Failed', ) ); } } wcf()->logger->log( 'Ended: ' . __CLASS__ . '::' . __FUNCTION__ ); } /** * Add paypal fee * * @param object $order Add order paypal fee. * @param mixed $price_breakdown Fee amount's breakdown. * @param int $step_id step id. * * @return void */ public static function add_fee_net_amount( $order, $price_breakdown, $step_id ) { wcf()->logger->log( 'Started: ' . __CLASS__ . '::' . __FUNCTION__ ); if ( ! empty( $price_breakdown['paypal_fee']['value'] ) ) { $order->update_meta_data( 'cartflows_offer' . CPPW_PAYPAL_FEE . '_' . $step_id, $price_breakdown['paypal_fee']['value'] ); } if ( ! empty( $price_breakdown['net_amount']['value'] ) ) { $order->update_meta_data( 'cartflows_offer' . CPPW_PAYPAL_NET . '_' . $step_id, $price_breakdown['net_amount']['value'] ); } wcf()->logger->log( 'Paypal Fee ' . $price_breakdown['paypal_fee']['value'] ); wcf()->logger->log( 'Paypal Net Amount ' . $price_breakdown['net_amount']['value'] ); wcf()->logger->log( 'Ended: ' . __CLASS__ . '::' . __FUNCTION__ ); } /** * Tokenize to save source of payment if required * * @param bool $save_source force save source. * * @return bool */ public function tokenize_if_required( $save_source ) { wcf()->logger->log( 'Started: ' . __CLASS__ . '::' . __FUNCTION__ ); $checkout_id = _get_wcf_checkout_id(); $flow_id = wcf()->utils->get_flow_id_from_step_id( $checkout_id ); if ( ! $checkout_id ) { $checkout_id = wcf()->utils->get_checkout_id_from_post_data(); $flow_id = wcf()->utils->get_flow_id_from_post_data(); } if ( $checkout_id && $flow_id && wcf_pro()->flow->is_upsell_exist_in_flow( $flow_id, $checkout_id ) ) { $save_source = true; wcf()->logger->log( 'Force save source enabled' ); } wcf()->logger->log( 'Ended: ' . __CLASS__ . '::' . __FUNCTION__ ); return $save_source; } /** * Get WooCommerce payment geteway. * * @return object */ public function get_wc_gateway() { $gateways = wc()->payment_gateways->payment_gateways(); return $gateways[ $this->key ]; } /** * Allow gateways to declare whether they support offer refund * * @return bool */ public function is_api_refund() { return $this->is_api_refund; } /** * Process offer refund * * @param object $order Order Object. * @param array $offer_data offer data. * * @return bool. */ public function process_offer_refund( $order, $offer_data ) { wcf()->logger->log( 'Started: ' . __CLASS__ . '::' . __FUNCTION__ ); $response = false; $refund_amount = strval( $offer_data['refund_amount'] ); $refund_reason = $offer_data['refund_reason']; $gateway = $this->get_wc_gateway(); $is_refund_success = $gateway->process_refund( $order->get_id(), $refund_amount, $refund_reason ); if ( $is_refund_success ) { $response = true; } wcf()->logger->log( 'Ended: ' . __CLASS__ . '::' . __FUNCTION__ ); return $response; } /** * Setup the Payment data for Stripe's Automatic Subscription. * * @param WC_Subscription|WC_Order $subscription An instance of a subscription object. * @param object $order Object of order. * @param array $offer_product array of offer product. * * @return void */ public function add_subscription_payment_meta( $subscription, $order, $offer_product ) { if ( 'cppw_paypal' === $order->get_payment_method() ) { $subscription->update_meta_data( CPPW_SUB_AGREEMENT_ID, $order->get_meta( CPPW_SUB_AGREEMENT_ID ) ); $subscription->save(); } } /** * Save the parent payment meta to child order. * * @param object $parent_order Object of order. * @param object $child_order Object of order. * @param int $transaction_id transaction id. * * @return void */ public function add_required_meta_to_child_order( $parent_order, $child_order, $transaction_id ) { wcf()->logger->log( 'Started: ' . __CLASS__ . '::' . __FUNCTION__ ); $offer_step_id = $child_order->get_meta( '_cartflows_offer_step_id' ); $child_order->update_meta_data( CPPW_PAYPAL_TRANSACTION_ID, $parent_order->get_meta( 'cartflows_offer_txn_resp_' . $offer_step_id ) ); $child_order->update_meta_data( CPPW_SUB_AGREEMENT_ID, $parent_order->get_meta( 'cartflows_offer' . CPPW_SUB_AGREEMENT_ID . '_' . $offer_step_id ) ); $child_order->update_meta_data( CPPW_PAYPAL_FEE, $parent_order->get_meta( 'cartflows_offer' . CPPW_PAYPAL_FEE . '_' . $offer_step_id ) ); $child_order->update_meta_data( CPPW_PAYPAL_NET, $parent_order->get_meta( 'cartflows_offer' . CPPW_PAYPAL_NET . '_' . $offer_step_id ) ); $child_order->save(); wcf()->logger->log( 'Started: ' . __CLASS__ . '::' . __FUNCTION__ ); } } /** * Prepare if class 'Cartflows_Pro_Gateway_Cppw_Paypal' exist. * Kicking this off by calling 'get_instance()' method */ Cartflows_Pro_Gateway_Cppw_Paypal::get_instance();