menu . ' | ' . $payments_string,
self::payments_menu_title( $payments_string ),
'frm_view_entries',
'formidable-payments',
$menu_route
);
}
/**
* @since 6.11.1
*
* @param string $payments_string
*
* @return string
*/
private static function payments_menu_title( $payments_string ) {
ob_start();
echo esc_html( $payments_string );
FrmAppHelper::show_pill_text();
return ob_get_clean();
}
/**
* @return void
*/
public static function route() {
$action = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action';
$action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
$type = FrmAppHelper::get_param( 'type', '', 'get', 'sanitize_title' );
$class_name = $type === 'subscriptions' ? 'FrmTransLiteSubscriptionsController' : 'FrmTransLitePaymentsController';
if ( method_exists( $class_name, $action ) ) {
$class_name::$action();
return;
}
FrmTransLiteListsController::route( $action );
}
/**
* @param object $payment
*
* @return void
*/
public static function load_sidebar_actions( $payment ) {
FrmTransLiteActionsController::actions_js();
$date_format = __( 'M j, Y @ G:i', 'formidable' );
$created_at = FrmAppHelper::get_localized_date( $date_format, $payment->created_at );
include FrmTransLiteAppHelper::plugin_path() . '/views/payments/sidebar_actions.php';
}
/**
* Echo a receipt link.
*
* @param object $payment
*
* @return void
*/
public static function show_receipt_link( $payment ) {
$link = esc_html( $payment->receipt_id );
$paysys = $payment->paysys;
if ( $payment->receipt_id !== 'None' && self::should_filter_receipt_link( $paysys ) ) {
/**
* Filter a receipt link for a specific gateway.
* For example, Stripe uses frm_pay_stripe_receipt.
*
* @param string $link
*/
$link = apply_filters( 'frm_pay_' . $paysys . '_receipt', $link );
}
FrmAppHelper::kses_echo( $link, array( 'a' ) );
}
/**
* @param string $paysys
*
* @return bool
*/
private static function should_filter_receipt_link( $paysys ) {
$allowed_types = array( 'stripe', 'authnet_aim' );
return in_array( $paysys, $allowed_types, true );
}
/**
* Echo a refund link.
*
* @param object $payment
*
* @return void
*/
public static function show_refund_link( $payment ) {
$link = self::refund_link( $payment );
FrmTransLiteAppHelper::echo_confirmation_link( $link );
}
/**
* Show a link to a payment entry (unless it is deleted).
*
* @param object $payment
*
* @return void
*/
public static function show_entry_link( $payment ) {
$entry = FrmDb::get_col( 'frm_items', array( 'id' => $payment->item_id ) );
if ( ! $entry ) {
// translators: %d: Entry ID.
echo esc_html( sprintf( __( '%d (Deleted)', 'formidable' ), $payment->item_id ) );
return;
}
// phpcs:disable Generic.WhiteSpace.ScopeIndent
?>
item_id ); ?>
status === 'refunded' ) {
$link = esc_html__( 'Refunded', 'formidable' );
} else {
$confirm = __( 'Are you sure you want to refund that payment?', 'formidable' );
$link = admin_url( 'admin-ajax.php?action=frm_trans_refund&payment_id=' . $payment->id . '&nonce=' . wp_create_nonce( 'frm_trans_ajax' ) );
$link = '';
$link .= esc_html__( 'Refund', 'formidable' );
$link .= '';
}
$paysys = $payment->paysys;
if ( self::should_filter_refund_link( $paysys ) ) {
/**
* Filter the refund link for a specific gateway.
* For example, Stripe uses frm_pay_stripe_refund_link.
*
* @param string $link
* @param object $payment
*/
return apply_filters( 'frm_pay_' . $paysys . '_refund_link', $link, $payment );
}
return $link;
}
/**
* @param string $paysys
*
* @return bool
*/
private static function should_filter_refund_link( $paysys ) {
$allowed_types = array( 'stripe', 'authnet_aim' );
return in_array( $paysys, $allowed_types, true );
}
/**
* Process the ajax request to refund a payment.
*
* @return void
*/
public static function refund_payment() {
FrmAppHelper::permission_check( 'frm_edit_entries' );
check_ajax_referer( 'frm_trans_ajax', 'nonce' );
$payment_id = FrmAppHelper::get_param( 'payment_id', '', 'get', 'absint' );
if ( ! $payment_id ) {
wp_die( esc_html__( 'Oops! No payment was selected for refund.', 'formidable' ) );
}
$payment = ( new FrmTransLitePayment() )->get_one( $payment_id );
if ( ! $payment ) {
wp_die( esc_html__( 'Oops! That payment does not exist.', 'formidable' ) );
}
$refunded = false;
$reason = '';
$debug_id = '';
$paysys = $payment->paysys;
switch ( $paysys ) {
case 'stripe':
$refunded = FrmStrpLiteAppHelper::call_stripe_helper_class( 'refund_payment', $payment->receipt_id );
break;
case 'square':
$refunded = FrmSquareLiteConnectHelper::refund_payment( $payment->receipt_id );
break;
case 'paypal':
$response = FrmPayPalLiteConnectHelper::refund_payment( $payment->receipt_id );
// Check for structured error response with message and debug_id
if ( is_object( $response ) && isset( $response->message ) && isset( $response->debug_id ) ) {
$refunded = false;
$reason = $response->message;
$debug_id = $response->debug_id;
} elseif ( false === $response ) {
$refunded = false;
$reason = self::get_paypal_refund_reason();
$debug_id = FrmPayPalLiteConnectHelper::get_latest_debug_id_from_paypal_api();
} elseif ( is_object( $response ) && isset( $response->refund_error ) ) {
// Handle mock error responses from PayPal API
$refunded = false;
$reason = $response->message ?? '';
$debug_id = $response->debug_id ?? '';
} else {
$refunded = true;
}
break;
default:
$refunded = false;
break;
}//end switch
if ( $refunded ) {
self::change_payment_status( $payment, 'refunded' );
$message = __( 'Refunded', 'formidable' );
// phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
} else {
// If the reason is already a complete error message, use it directly
// instead of wrapping it redundantly in "Refund Failed (...)"
if ( $reason && ! preg_match( '/^[A-Z_]+$/', $reason ) ) {
$message = $reason;
} else {
$message = __( 'Refund Failed', 'formidable' );
if ( $reason ) {
$message .= ' (' . $reason . ')';
}
}
}
if ( $debug_id ) {
$message .= '
Debug ID: ' . esc_html( $debug_id );
}
wp_die(
sprintf(
'