woocommerce/payment-gateway/Handlers/Abstract_Hosted_Payment_Handler.php
<?php
/**
* WooCommerce Payment Gateway Framework
*
* This source file is subject to the GNU General Public License v3.0
* that is bundled with this package in the file license.txt.
* It is also available through the world-wide-web at this URL:
* http://www.gnu.org/licenses/gpl-3.0.html
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@skyverge.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade the plugin to newer
* versions in the future. If you wish to customize the plugin for your
* needs please refer to http://www.skyverge.com
*
* @package SkyVerge/WooCommerce/Payment-Gateway/Admin
* @author SkyVerge
* @copyright Copyright (c) 2013-2024, SkyVerge, Inc.
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
*/
namespace SkyVerge\WooCommerce\PluginFramework\v5_12_4\Payment_Gateway\Handlers;
use SkyVerge\WooCommerce\PluginFramework\v5_12_4 as FrameworkBase;
if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v5_12_4\\Payment_Gateway\\Handlers\\Abstract_Hosted_Payment_Handler' ) ) :
/**
* The base hosted payment handler.
*
* Gateways can use this for common hosted response handling.
*
* @since 5.4.0
*/
#[\AllowDynamicProperties]
abstract class Abstract_Hosted_Payment_Handler extends Abstract_Payment_Handler {
/**
* Adds the action & filter hooks.
*
* @since 5.4.0
*/
protected function add_hooks() {
parent::add_hooks();
// renders the payment page
add_action( 'woocommerce_receipt_' . $this->get_gateway()->get_id(), array( $this, 'payment_page' ) );
// payment notification listener hook
if ( ! has_action( 'woocommerce_api_' . $this->get_gateway()->get_id() . '_process_payment', array( $this, 'handle_transaction_response_request' ) ) ) {
add_action( 'woocommerce_api_' . $this->get_gateway()->get_id() . '_process_payment', array( $this, 'handle_transaction_response_request' ) );
}
}
/**
* Processes a new order payment.
*
* This simply gets the URL for a redirect.
*
* @since 5.4.0
*
* @param \WC_Order $order order object
* @return array
* @throws FrameworkBase\SV_WC_Plugin_Exception
*/
public function process_order_payment( \WC_Order $order ) {
if ( $this->is_redirect() ) {
$payment_url = add_query_arg( $this->get_order_payment_params( $order ), $this->get_hosted_payment_url() );
} else {
$payment_url = $order->get_checkout_payment_url( true );
}
return array(
'result' => 'success',
'redirect' => $payment_url,
);
}
/**
* Renders the payment page.
*
* @since 5.4.0
*
* @param int $order_id order ID
*/
public function payment_page( $order_id ) {
// stub
}
/**
* Gets payment params for the given order object.
*
* @since 5.4.0
*
* @param \WC_Order $order order object
* @return array
*/
public function get_order_payment_params( \WC_Order $order ) {
return array();
}
/**
* Gets the URL for the hosted payment page or form.
*
* @since 5.4.0
*
* @return string
*/
abstract protected function get_hosted_payment_url();
/**
* Gets the response handler URL.
*
* @since 5.4.0
*
* @return string
*/
public function get_response_handler_url() {
return add_query_arg( 'wc-api', $this->get_gateway()->get_id() . '_process_payment', home_url( '/' ) );
}
/**
* Handles a transaction response request via the wc-api endpoint.
*
* @since 5.4.0
*/
public function handle_transaction_response_request() {
$order = null;
$response = null;
try {
// get the transaction response object for the current request
$response = $this->get_transaction_response( $_REQUEST );
// log the request
$this->log_transaction_response_request( $response->to_string_safe() );
// get the associated order, or die trying
$order = $this->get_order_from_response( $response );
// handle the order based on the response
$this->process_transaction_response( $response, $order );
$this->do_transaction_response_complete( $order, $response );
// catch general gateway exceptions, which indicate payment processing failures where the order should be retried
} catch ( FrameworkBase\SV_WC_Payment_Gateway_Exception $exception ) {
// try and get a user-friendly message if available
if ( ( $user_exception = $exception->getPrevious() ) && $user_exception->getMessage() ) {
$user_message = $user_exception->getMessage();
} else {
$user_message = '';
}
$this->do_transaction_response_failed( $order, $exception->getMessage(), $user_message, $response );
// catch other exceptions i.e. for malformed responses, where we don't want the customer to retry the order
} catch ( \Exception $exception ) {
if ( WC()->session ) {
WC()->session->held_order_received_text = __( 'There was a problem processing your order and it is being placed on hold for review. Please contact us to complete the transaction.', 'woocommerce-plugin-framework' );
}
// bail out and don't add a customer-facing notice to avoid them resubmitting
$this->do_transaction_response_invalid( $order, $exception->getMessage(), $response );
}
}
/**
* Handles the response when processing is complete.
*
* @since 5.4.0
*
* @param \WC_Order|null $order order object, if any
* @param FrameworkBase\SV_WC_Payment_Gateway_API_Response|null $response API response object, if any
*/
protected function do_transaction_response_complete( \WC_Order $order = null, FrameworkBase\SV_WC_Payment_Gateway_API_Response $response = null ) {
$this->do_transaction_request_response( $response, $this->get_gateway()->get_return_url( $order ) );
}
/**
* Handles the response when processing has failed.
*
* @since 5.4.0
*
* @param \WC_Order|null $order order object, if any
* @param string $message error message, for logging
* @param string $user_message user-facing message
* @param FrameworkBase\SV_WC_Payment_Gateway_API_Response|null $response API response object, if any
*/
protected function do_transaction_response_failed( \WC_Order $order = null, $message = '', $user_message = '', FrameworkBase\SV_WC_Payment_Gateway_API_Response $response = null ) {
$this->get_gateway()->add_debug_message( $message, 'error' );
if ( ! $user_message || ! is_string( $user_message ) ) {
$user_message = __( 'An error occurred, please try again or try an alternate form of payment.', 'woocommerce-plugin-framework' );
}
FrameworkBase\SV_WC_Helper::wc_add_notice( $user_message, 'error' );
$this->do_transaction_request_response( $response, $order ? $order->get_checkout_payment_url() : '' );
}
/**
* Handles the response when the response data is invalid.
*
* This will trigger when there is no way to salvage the payment, i.e. when the response data is invalid.
*
* @since 5.4.0
*
* @param \WC_Order|null $order order object, if any
* @param string $message error message, for logging
* @param FrameworkBase\SV_WC_Payment_Gateway_API_Response|null $response API response object, if any
*/
protected function do_transaction_response_invalid( \WC_Order $order = null, $message = '', FrameworkBase\SV_WC_Payment_Gateway_API_Response $response = null ) {
$this->get_gateway()->add_debug_message( $message, 'error' );
// if we have an order, mark it as held and add an order note
if ( $order ) {
if ( $order->is_paid() ) {
$order->add_order_note( $message );
} else {
$this->mark_order_as_held( $order, $message, $response );
}
}
$this->do_transaction_request_response( $response, $order ? $this->get_gateway()->get_return_url( $order ) : '' );
}
/**
* Handles the final payment request response.
*
* This is the final step after all payment verification and processing, and runs regardless of the transaction
* result.
*
* @since 5.4.0
*
* @param FrameworkBase\SV_WC_Payment_Gateway_API_Response|null $response
* @param string $url
*/
protected function do_transaction_request_response( FrameworkBase\SV_WC_Payment_Gateway_API_Response $response = null, $url = '' ) {
// if this is an IPN handler
if ( $this->is_ipn() ) {
status_header( 200 );
die;
}
wp_safe_redirect( $url ?: home_url() );
exit;
}
/**
* Logs a transaction response request.
*
* @since 5.4.0
*
* @param string $request data to log
* @param string $message prefix message, like Request: or Response:
*/
protected function log_transaction_response_request( $request, $message = '' ) {
// add log message to WC logger if log/both is enabled
if ( $this->get_gateway()->debug_log() ) {
// if a message wasn't provided, make our best effort
if ( ! $message ) {
$message = 'Request: %s';
}
$this->get_gateway()->get_plugin()->log( sprintf( $message, print_r( $request, true ) ), $this->get_gateway()->get_id() );
}
}
/**
* Gets an order object from an API response.
*
* @since 5.4.0
*
* @param FrameworkBase\SV_WC_Payment_Gateway_API_Payment_Notification_Response $response
* @return \WC_Order
* @throws \Exception
*/
protected function get_order_from_response( FrameworkBase\SV_WC_Payment_Gateway_API_Payment_Notification_Response $response ) {
$order = wc_get_order( $response->get_order_id() );
// if the order is invalid, bail
if ( ! $order ) {
throw new FrameworkBase\SV_WC_API_Exception( sprintf(
/* translators: Placeholders: %s - a WooCommerce order ID */
__( 'Could not find order %s', 'woocommerce-plugin-framework' ),
$response->get_order_id()
) );
}
$order = $this->get_gateway()->get_order( $order );
$order->payment->account_number = $response->get_account_number();
if ( $response instanceof FrameworkBase\SV_WC_Payment_Gateway_API_Payment_Notification_Credit_Card_Response ) {
$order->payment->exp_month = $response->get_exp_month();
$order->payment->exp_year = $response->get_exp_year();
$order->payment->card_type = $response->get_card_type();
} elseif ( $response instanceof FrameworkBase\SV_WC_Payment_Gateway_API_Payment_Notification_eCheck_Response ) {
$order->payment->account_type = $response->get_account_type();
$order->payment->check_number = $response->get_check_number();
}
return $order;
}
/**
* Gets an API response object for the given data.
*
* @since 5.4.0
*
* @param array $request_response_data the current request response data
* @return FrameworkBase\SV_WC_Payment_Gateway_API_Payment_Notification_Response API response object
* @throws FrameworkBase\SV_WC_API_Exception
*/
abstract protected function get_transaction_response( $request_response_data );
/** Conditional methods *******************************************************************************************/
/**
* Determines whether the payment response is IPN.
*
* @since 5.4.0
*
* @return bool
*/
public function is_ipn() {
return false;
}
/**
* Determines whether this is a redirect hosted form.
*
* @since 5.4.0
*
* @return bool
*/
public function is_redirect() {
return false;
}
}
endif;