PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.32.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.32.1
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / stripe / controllers / FrmTransLitePaymentsController.php

FrmTransLitePaymentsController.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.32.1, at stripe/controllers/FrmTransLitePaymentsController.php

363 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmTransLitePaymentsController extends FrmTransLiteCRUDController {
7
8 /**
9 * @return void
10 */
11 public static function menu() {
12 $frm_settings = FrmAppHelper::get_settings();
13
14 // Remove the PayPal submenu (PayPal payments will just appear in the regular Payments page).
15 remove_action( 'admin_menu', 'FrmPaymentsController::menu', 26 );
16
17 if ( in_array( FrmAppHelper::simple_get( 'action' ), array( 'edit', 'new', 'bulk_delete' ), true ) && is_callable( 'FrmPaymentsController::route' ) ) {
18 // Use the PayPal addon for add new and edit routing if it is active.
19 // This is required to support the "edit" link when using the Stripe Lite table view.
20 // It is also required for the "Add New" button to work on the payments table page.
21 $menu_route = 'FrmPaymentsController::route';
22 } else {
23 $menu_route = 'FrmTransLitePaymentsController::route';
24 }
25
26 $payments_string = __( 'Payments', 'formidable' );
27 add_submenu_page(
28 'formidable',
29 $frm_settings->menu . ' | ' . $payments_string,
30 self::payments_menu_title( $payments_string ),
31 'frm_view_entries',
32 'formidable-payments',
33 $menu_route
34 );
35 }
36
37 /**
38 * @since 6.11.1
39 *
40 * @param string $payments_string
41 *
42 * @return string
43 */
44 private static function payments_menu_title( $payments_string ) {
45 ob_start();
46 echo esc_html( $payments_string );
47 FrmAppHelper::show_pill_text();
48 return ob_get_clean();
49 }
50
51 /**
52 * @return void
53 */
54 public static function route() {
55 $action = isset( $_REQUEST['frm_action'] ) ? 'frm_action' : 'action';
56 $action = FrmAppHelper::get_param( $action, '', 'get', 'sanitize_title' );
57 $type = FrmAppHelper::get_param( 'type', '', 'get', 'sanitize_title' );
58 $class_name = $type === 'subscriptions' ? 'FrmTransLiteSubscriptionsController' : 'FrmTransLitePaymentsController';
59
60 if ( method_exists( $class_name, $action ) ) {
61 $class_name::$action();
62 return;
63 }
64
65 FrmTransLiteListsController::route( $action );
66 }
67
68 /**
69 * @param object $payment
70 *
71 * @return void
72 */
73 public static function load_sidebar_actions( $payment ) {
74 FrmTransLiteActionsController::actions_js();
75
76 $date_format = __( 'M j, Y @ G:i', 'formidable' );
77 $created_at = FrmAppHelper::get_localized_date( $date_format, $payment->created_at );
78 include FrmTransLiteAppHelper::plugin_path() . '/views/payments/sidebar_actions.php';
79 }
80
81 /**
82 * Echo a receipt link.
83 *
84 * @param object $payment
85 *
86 * @return void
87 */
88 public static function show_receipt_link( $payment ) {
89 $link = esc_html( $payment->receipt_id );
90 $paysys = $payment->paysys;
91
92 if ( $payment->receipt_id !== 'None' && self::should_filter_receipt_link( $paysys ) ) {
93 /**
94 * Filter a receipt link for a specific gateway.
95 * For example, Stripe uses frm_pay_stripe_receipt.
96 *
97 * @param string $link
98 */
99 $link = apply_filters( 'frm_pay_' . $paysys . '_receipt', $link );
100 }
101
102 FrmAppHelper::kses_echo( $link, array( 'a' ) );
103 }
104
105 /**
106 * @param string $paysys
107 *
108 * @return bool
109 */
110 private static function should_filter_receipt_link( $paysys ) {
111 $allowed_types = array( 'stripe', 'authnet_aim' );
112 return in_array( $paysys, $allowed_types, true );
113 }
114
115 /**
116 * Echo a refund link.
117 *
118 * @param object $payment
119 *
120 * @return void
121 */
122 public static function show_refund_link( $payment ) {
123 $link = self::refund_link( $payment );
124 FrmTransLiteAppHelper::echo_confirmation_link( $link );
125 }
126
127 /**
128 * Show a link to a payment entry (unless it is deleted).
129 *
130 * @param object $payment
131 *
132 * @return void
133 */
134 public static function show_entry_link( $payment ) {
135 $entry = FrmDb::get_col( 'frm_items', array( 'id' => $payment->item_id ) );
136
137 if ( ! $entry ) {
138 // translators: %d: Entry ID.
139 echo esc_html( sprintf( __( '%d (Deleted)', 'formidable' ), $payment->item_id ) );
140 return;
141 }
142
143 // phpcs:disable Generic.WhiteSpace.ScopeIndent
144 ?>
145 <a href="?page=formidable-entries&amp;action=show&amp;frm_action=show&amp;id=<?php echo absint( $payment->item_id ); ?>">
146 <?php echo absint( $payment->item_id ); ?>
147 </a>
148 <?php
149 // phpcs:enable Generic.WhiteSpace.ScopeIndent
150 }
151
152 /**
153 * Get a refund link.
154 *
155 * @param object $payment
156 *
157 * @return string
158 */
159 public static function refund_link( $payment ) {
160 if ( $payment->status === 'refunded' ) {
161 $link = esc_html__( 'Refunded', 'formidable' );
162 } else {
163 $confirm = __( 'Are you sure you want to refund that payment?', 'formidable' );
164 $link = admin_url( 'admin-ajax.php?action=frm_trans_refund&payment_id=' . $payment->id . '&nonce=' . wp_create_nonce( 'frm_trans_ajax' ) );
165 $link = '<a href="' . esc_url( $link ) . '" class="frm_trans_ajax_link" data-frmverify="' . esc_attr( $confirm ) . '">';
166 $link .= esc_html__( 'Refund', 'formidable' );
167 $link .= '</a>';
168 }
169
170 $paysys = $payment->paysys;
171
172 if ( self::should_filter_refund_link( $paysys ) ) {
173 /**
174 * Filter the refund link for a specific gateway.
175 * For example, Stripe uses frm_pay_stripe_refund_link.
176 *
177 * @param string $link
178 * @param object $payment
179 */
180 return apply_filters( 'frm_pay_' . $paysys . '_refund_link', $link, $payment );
181 }
182
183 return $link;
184 }
185
186 /**
187 * @param string $paysys
188 *
189 * @return bool
190 */
191 private static function should_filter_refund_link( $paysys ) {
192 $allowed_types = array( 'stripe', 'authnet_aim' );
193 return in_array( $paysys, $allowed_types, true );
194 }
195
196 /**
197 * Process the ajax request to refund a payment.
198 *
199 * @return void
200 */
201 public static function refund_payment() {
202 FrmAppHelper::permission_check( 'frm_edit_entries' );
203 check_ajax_referer( 'frm_trans_ajax', 'nonce' );
204
205 $payment_id = FrmAppHelper::get_param( 'payment_id', '', 'get', 'absint' );
206
207 if ( ! $payment_id ) {
208 wp_die( esc_html__( 'Oops! No payment was selected for refund.', 'formidable' ) );
209 }
210
211 $payment = ( new FrmTransLitePayment() )->get_one( $payment_id );
212
213 if ( ! $payment ) {
214 wp_die( esc_html__( 'Oops! That payment does not exist.', 'formidable' ) );
215 }
216
217 $refunded = false;
218 $reason = '';
219 $debug_id = '';
220 $paysys = $payment->paysys;
221
222 switch ( $paysys ) {
223 case 'stripe':
224 $refunded = FrmStrpLiteAppHelper::call_stripe_helper_class( 'refund_payment', $payment->receipt_id );
225 break;
226 case 'square':
227 $refunded = FrmSquareLiteConnectHelper::refund_payment( $payment->receipt_id );
228 break;
229 case 'paypal':
230 $response = FrmPayPalLiteConnectHelper::refund_payment( $payment->receipt_id );
231
232 // Check for structured error response with message and debug_id
233 if ( is_object( $response ) && isset( $response->message ) && isset( $response->debug_id ) ) {
234 $refunded = false;
235 $reason = $response->message;
236 $debug_id = $response->debug_id;
237 } elseif ( false === $response ) {
238 $refunded = false;
239 $reason = self::get_paypal_refund_reason();
240 $debug_id = FrmPayPalLiteConnectHelper::get_latest_debug_id_from_paypal_api();
241 } elseif ( is_object( $response ) && isset( $response->refund_error ) ) {
242 // Handle mock error responses from PayPal API
243 $refunded = false;
244 $reason = $response->message ?? '';
245 $debug_id = $response->debug_id ?? '';
246 } else {
247 $refunded = true;
248 }
249
250 break;
251 default:
252 $refunded = false;
253 break;
254 }//end switch
255
256 if ( $refunded ) {
257 self::change_payment_status( $payment, 'refunded' );
258 $message = __( 'Refunded', 'formidable' );
259 // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found
260 } else {
261 // If the reason is already a complete error message, use it directly
262 // instead of wrapping it redundantly in "Refund Failed (...)"
263 if ( $reason && ! preg_match( '/^[A-Z_]+$/', $reason ) ) {
264 $message = $reason;
265 } else {
266 $message = __( 'Refund Failed', 'formidable' );
267
268 if ( $reason ) {
269 $message .= ' (' . $reason . ')';
270 }
271 }
272 }
273
274 if ( $debug_id ) {
275 $message .= '<br><br>Debug ID: ' . esc_html( $debug_id );
276 }
277
278 wp_die(
279 sprintf(
280 '<div class="%1$s">%2$s</div>',
281 $refunded ? 'frm_updated_message' : 'frm_error_style',
282 wp_kses_post( $message )
283 )
284 );
285 }
286
287 /**
288 * Get a human-friendly reason from the latest PayPal refund error.
289 *
290 * Handles both uppercase issue codes (e.g. REFUND_FAILED_INSUFFICIENT_FUNDS)
291 * and human-friendly description strings from the PayPal API.
292 * Strips the {{debug_id:...}} token if present.
293 *
294 * @since 6.31
295 *
296 * @return string
297 */
298 private static function get_paypal_refund_reason() {
299 $error = FrmPayPalLiteConnectHelper::get_latest_error_from_paypal_api();
300
301 if ( ! $error ) {
302 return '';
303 }
304
305 $error = preg_replace( '/\{\{debug_id:[^}]+\}\}/', '', $error );
306 $error = trim( $error );
307
308 if ( preg_match( '/^[A-Z_]+$/', $error ) ) {
309 return self::convert_uppercase_underscores_to_ucwords( $error, array( 'REFUND_FAILED_', 'REFUND_' ) );
310 }
311
312 return $error;
313 }
314
315 /**
316 * @param string $error The uppercase underscored string to convert.
317 * @param array $prefixes_to_strip Prefixes to remove before converting.
318 *
319 * @return string
320 */
321 private static function convert_uppercase_underscores_to_ucwords( $error, $prefixes_to_strip = array() ) {
322 if ( ! preg_match( '/^[A-Z_]+$/', $error ) ) {
323 return '';
324 }
325
326 $reason = str_replace( $prefixes_to_strip, '', $error );
327 return ucwords( strtolower( str_replace( '_', ' ', $reason ) ) );
328 }
329
330 /**
331 * Update the status of a payment.
332 *
333 * @param object $payment
334 * @param string $status
335 *
336 * @return void
337 */
338 public static function change_payment_status( $payment, $status ) {
339 if ( $status === $payment->status ) {
340 // The payment status has not actually changed.
341 return;
342 }
343
344 $frm_payment = new FrmTransLitePayment();
345 $frm_payment->update( $payment->id, array( 'status' => $status ) );
346 FrmTransLiteActionsController::trigger_payment_status_change( compact( 'status', 'payment' ) );
347 }
348
349 /**
350 * @since 6.31
351 *
352 * @param array|string $expected_gateways
353 * @param array|string $selected_gateways
354 *
355 * @return void
356 */
357 public static function maybe_hide_payment_setting( $expected_gateways, $selected_gateways ) {
358 if ( ! array_intersect( (array) $expected_gateways, (array) $selected_gateways ) ) {
359 echo ' frm_hidden';
360 }
361 }
362 }
363