PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / trunk
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More vtrunk
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
← All changes | stripe/models/FrmStrpLiteAuth.php +276 -83 6.5.2 → trunk View file →
@@ -18,12 +18,14 @@
18 18 *
19 19 * @since 6.5, introduced in v2.0 of the Stripe add on.
20 20 *
21 21 * @param string $html Form HTML that gets filtered through frm_filter_final_form.
22 + *
22 23 * @return string
23 24 */
24 25 public static function maybe_show_message( $html ) {
25 26 $link_error = FrmAppHelper::simple_get( 'frm_link_error' );
27 +
26 28 if ( $link_error ) {
27 29 $message = '<div class="frm_error_style">' . self::get_message_for_stripe_link_code( $link_error ) . '</div>';
28 30 self::insert_error_message( $message, $html );
29 31 return $html;
@@ -29,25 +31,26 @@
29 31 return $html;
30 32 }
31 33
32 34 $form_id = self::check_html_for_form_id_match( $html );
35 +
33 36 if ( false === $form_id ) {
34 37 return $html;
35 38 }
36 39
37 40 $details = FrmStrpLiteUrlParamHelper::get_details_for_form( $form_id );
41 +
38 42 if ( ! is_array( $details ) ) {
39 43 return $html;
40 44 }
41 45
42 - $atts = array(
46 + $atts = array(
43 47 'fields' => FrmFieldsHelper::get_form_fields( $form_id ),
44 48 'entry' => $details['entry'],
45 49 );
46 50 self::prepare_success_atts( $atts );
47 51
48 - $intent = $details['intent'];
49 - $payment = $details['payment'];
52 + $intent = $details['intent'];
50 53
51 54 if ( self::intent_has_failed_status( $intent ) ) {
52 55 $message = '<div class="frm_error_style">' . $intent->last_payment_error->message . '</div>';
53 56 self::insert_error_message( $message, $html );
@@ -54,25 +57,24 @@
54 57 return $html;
55 58 }
56 59
57 60 $intent_is_processing = 'processing' === $intent->status;
61 +
58 62 if ( $intent_is_processing ) {
59 63 // Append an additional processing message to the end of the success message.
60 - $filter = function( $message ) {
64 + $filter = function ( $message ) {
61 65 $stripe_settings = FrmStrpLiteAppHelper::get_settings();
62 - $message .= '<p>' . esc_html( $stripe_settings->settings->processing_message ) . '</p>';
63 - return $message;
66 + return $message . ( '<p>' . esc_html( $stripe_settings->settings->processing_message ) . '</p>' );
64 67 };
65 68 add_filter( 'frm_content', $filter );
66 69 }
67 70
68 71 ob_start();
69 - FrmFormsController::run_success_action( $atts );
70 - $message = ob_get_contents();
71 - ob_end_clean();
72 + FrmFormsController::run_on_submit_actions( $atts );
73 + $message = ob_get_clean();
72 74
73 75 // Clean up the filter we added above so no other success messages get altered if there are multiple forms.
74 - if ( $intent_is_processing && isset( $filter ) ) {
76 + if ( $intent_is_processing ) {
75 77 remove_filter( 'frm_content', $filter );
76 78 }
77 79
78 80 return $message;
@@ -78,9 +80,10 @@
78 80 return $message;
79 81 }
80 82
81 83 /**
82 - * @param string|int $form_id
84 + * @param int|string $form_id
85 + *
83 86 * @return array|false
84 87 */
85 88 private static function check_request_params( $form_id ) {
86 89 if ( ! FrmStrpLiteAppHelper::stripe_is_configured() ) {
@@ -87,8 +90,9 @@
87 90 return false;
88 91 }
89 92
90 93 $details = FrmStrpLiteUrlParamHelper::get_details_for_form( $form_id );
94 +
91 95 if ( ! is_array( $details ) ) {
92 96 return false;
93 97 }
94 98
@@ -104,14 +108,16 @@
104 108 *
105 109 * @since 6.5
106 110 *
107 111 * @param string $html
108 - * @return int|false Matching form id or false if there is no match.
112 + *
113 + * @return false|int Matching form id or false if there is no match.
109 114 */
110 115 private static function check_html_for_form_id_match( $html ) {
111 116 foreach ( self::$form_ids as $form_id ) {
112 117 $substring = '<input type="hidden" name="form_id" value="' . $form_id . '"';
113 - if ( strpos( $html, $substring ) ) {
118 +
119 + if ( str_contains( $html, $substring ) ) {
114 120 return $form_id;
115 121 }
116 122 }
117 123
@@ -124,8 +130,9 @@
124 130 *
125 131 * @since 6.5, introduced in v3.0 of the Stripe add on.
126 132 *
127 133 * @param string $code
134 + *
128 135 * @return string
129 136 */
130 137 private static function get_message_for_stripe_link_code( $code ) {
131 138 switch ( $code ) {
@@ -144,8 +151,10 @@
144 151 case 'create_subscription_failed':
145 152 return __( 'Something went wrong when trying to create a subscription.', 'formidable' );
146 153 case 'payment_failed':
147 154 return __( 'Payment was not successfully processed.', 'formidable' );
155 + case 'amount_mismatch':
156 + return __( 'The payment amount does not match the expected amount.', 'formidable' );
148 157 }
149 158 return '';
150 159 }
151 160
@@ -154,8 +163,9 @@
154 163 *
155 164 * @since 6.5, introduced in v2.0 of the Stripe add on.
156 165 *
157 166 * @param array $atts
167 + *
158 168 * @return void
159 169 */
160 170 private static function prepare_success_atts( &$atts ) {
161 171 $atts['form'] = FrmForm::getOne( $atts['entry']->form_id );
@@ -161,8 +171,20 @@
161 171 $atts['form'] = FrmForm::getOne( $atts['entry']->form_id );
162 172 $atts['entry_id'] = $atts['entry']->id;
163 173 $opt = 'success_action';
164 174 $atts['conf_method'] = ! empty( $atts['form']->options[ $opt ] ) ? $atts['form']->options[ $opt ] : 'message';
175 +
176 + $actions = FrmFormsController::get_met_on_submit_actions( $atts, 'create' );
177 +
178 + if ( ! $actions ) {
179 + return;
180 + }
181 +
182 + $action = reset( $actions );
183 +
184 + if ( ! empty( $action->post_content['success_action'] ) && 'message' === $action->post_content['success_action'] ) {
185 + $atts['conf_method'] = $action->post_content['success_action'];
186 + }
165 187 }
166 188
167 189 /**
168 190 * Insert a message/error where the form styling will be applied.
@@ -167,12 +189,18 @@
167 189 /**
168 190 * Insert a message/error where the form styling will be applied.
169 191 *
170 192 * @since 6.5, introduced in v2.0 of the Stripe add on.
193 + *
194 + * @param string $message Message.
195 + * @param string $form Form.
196 + *
197 + * @return void
171 198 */
172 199 private static function insert_error_message( $message, &$form ) {
173 200 $add_after = '<fieldset>';
174 - $pos = strpos( $form, $add_after );
201 + $pos = strpos( $form, $add_after );
202 +
175 203 if ( $pos !== false ) {
176 204 $form = substr_replace( $form, $add_after . $message, $pos, strlen( $add_after ) );
177 205 }
178 206 }
@@ -180,13 +208,15 @@
180 208 /**
181 209 * Include the token if going between pages.
182 210 *
183 211 * @param object $form The form being submitted.
212 + *
184 213 * @return void
185 214 */
186 215 public static function add_hidden_token_field( $form ) {
187 216 $posted_form = FrmAppHelper::get_param( 'form_id', 0, 'post', 'absint' );
188 - if ( $posted_form != $form->id || FrmFormsController::just_created_entry( $form->id ) ) {
217 +
218 + if ( $posted_form !== (int) $form->id || FrmFormsController::just_created_entry( $form->id ) ) {
189 219 // Check to make sure the correct form was submitted.
190 220 // Was an entry already created and the form should be loaded fresh?
191 221
192 222 $intents = self::maybe_create_intents( $form->id );
@@ -195,10 +225,11 @@
195 225 return;
196 226 }
197 227
198 228 $intents = self::get_payment_intents( 'frmintent' . $form->id );
199 - if ( ! empty( $intents ) ) {
200 - self::update_intent_pricing( $form->id, $intents );
229 +
230 + if ( $intents ) {
231 + self::update_intent_pricing( $form->id, $intents, $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
201 232 } else {
202 233 $intents = self::maybe_create_intents( $form->id );
203 234 }
204 235
@@ -211,8 +242,9 @@
211 242 * @since 6.5, introduced in v2.02 of the Stripe add on.
212 243 *
213 244 * @param array $intents
214 245 * @param stdClass $form
246 + *
215 247 * @return void
216 248 */
217 249 private static function include_intents_in_form( $intents, $form ) {
218 250 foreach ( $intents as $intent ) {
@@ -233,8 +265,9 @@
233 265 *
234 266 * @since 6.5, introduced in v2.0 of the Stripe add on.
235 267 *
236 268 * @param string $name
269 + *
237 270 * @return mixed
238 271 */
239 272 public static function get_payment_intents( $name ) {
240 273 // phpcs:ignore WordPress.Security.NonceVerification.Missing
@@ -240,9 +273,11 @@
240 273 // phpcs:ignore WordPress.Security.NonceVerification.Missing
241 274 if ( ! isset( $_POST[ $name ] ) ) {
242 275 return array();
243 276 }
244 - $intents = $_POST[ $name ]; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
277 +
278 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing
279 + $intents = $_POST[ $name ];
245 280 FrmAppHelper::sanitize_value( 'sanitize_text_field', $intents );
246 281 return $intents;
247 282 }
248 283
@@ -259,9 +294,11 @@
259 294 if ( empty( $_POST['form'] ) ) {
260 295 wp_die();
261 296 }
262 297
263 - $form = json_decode( stripslashes( $_POST['form'] ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
298 + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
299 + $form = json_decode( stripslashes( $_POST['form'] ), true );
300 +
264 301 if ( ! is_array( $form ) ) {
265 302 wp_die();
266 303 }
267 304
@@ -267,26 +304,25 @@
267 304
268 305 self::format_form_data( $form );
269 306
270 307 $form_id = absint( $form['form_id'] );
271 - $intents = isset( $form[ 'frmintent' . $form_id ] ) ? $form[ 'frmintent' . $form_id ] : array();
308 + $intents = $form[ 'frmintent' . $form_id ] ?? array();
272 309
273 - if ( empty( $intents ) ) {
310 + if ( ! $intents ) {
274 311 wp_die();
275 312 }
276 313
277 - if ( ! is_array( $intents ) ) {
278 - $intents = array( $intents );
279 - } else {
314 + if ( is_array( $intents ) ) {
280 315 foreach ( $intents as $k => $intent ) {
281 316 if ( is_array( $intent ) && isset( $intent[ $k ] ) ) {
282 317 $intents[ $k ] = $intent[ $k ];
283 318 }
284 319 }
320 + } else {
321 + $intents = array( $intents );
285 322 }
286 323
287 - $_POST = $form;
288 - self::update_intent_pricing( $form_id, $intents );
324 + self::update_intent_pricing( $form_id, $intents, $form );
289 325
290 326 wp_die();
291 327 }
292 328
@@ -293,20 +329,23 @@
293 329 /**
294 330 * Update pricing on page turn and non-ajax validation.
295 331 *
296 332 * @since 6.5, introduced in v2.0 of the Stripe add on.
297 - * @param int $form_id
298 - * @param array $intents
333 + *
334 + * @param int|string $form_id
335 + * @param array $intents
336 + * @param array $form_data
337 + *
299 338 * @return void
300 339 */
301 - private static function update_intent_pricing( $form_id, &$intents ) {
302 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
303 - if ( ! isset( $_POST['form_id'] ) || absint( $_POST['form_id'] ) != $form_id ) {
340 + private static function update_intent_pricing( $form_id, &$intents, $form_data ) {
341 + if ( ! isset( $form_data['form_id'] ) || absint( $form_data['form_id'] ) !== (int) $form_id ) {
304 342 return;
305 343 }
306 344
307 345 $actions = FrmStrpLiteActionsController::get_actions_before_submit( $form_id );
308 - if ( empty( $actions ) || empty( $intents ) ) {
346 +
347 + if ( ! $actions || ! $intents ) {
309 348 return;
310 349 }
311 350
312 351 $form = FrmForm::getOne( $form_id );
@@ -321,18 +360,26 @@
321 360 }
322 361
323 362 foreach ( $intents as $k => $intent ) {
324 363 $intent_id = explode( '_secret_', $intent )[0];
325 - $is_setup_intent = 0 === strpos( $intent_id, 'seti_' );
364 + $is_setup_intent = str_starts_with( $intent_id, 'seti_' );
365 +
326 366 if ( $is_setup_intent ) {
327 367 continue;
328 368 }
329 369
330 - $saved = FrmStrpLiteAppHelper::call_stripe_helper_class( 'get_intent', $intent_id );
370 + $saved = FrmStrpLiteAppHelper::call_stripe_helper_class( 'get_intent', $intent_id );
371 +
372 + if ( empty( $saved->metadata->action ) ) {
373 + continue;
374 + }
375 +
331 376 foreach ( $actions as $action ) {
377 + // phpcs:ignore Universal.Operators.StrictComparisons
332 378 if ( $saved->metadata->action != $action->ID ) {
333 379 continue;
334 380 }
381 +
335 382 $intents[ $k ] = array(
336 383 'id' => $intent,
337 384 'action' => $action->ID,
338 385 );
@@ -337,23 +384,26 @@
337 384 'action' => $action->ID,
338 385 );
339 386
340 387 $amount = $action->post_content['amount'];
341 - if ( strpos( $amount, '[' ) === false ) {
388 +
389 + if ( ! str_contains( $amount, '[' ) ) {
342 390 // The amount is static, so it doesn't need an update.
343 391 continue;
344 392 }
345 393
346 394 // Update amount based on field shortcodes.
347 - $entry = self::generate_false_entry();
395 + $entry = self::generate_false_entry( $form_data );
348 396 $amount = FrmStrpLiteActionsController::prepare_amount( $amount, compact( 'form', 'entry', 'action' ) );
397 +
398 + // phpcs:ignore Universal.Operators.StrictComparisons
349 399 if ( $saved->amount == $amount || $amount == '000' ) {
350 400 continue;
351 401 }
352 402
353 403 FrmStrpLiteAppHelper::call_stripe_helper_class( 'update_intent', $intent_id, array( 'amount' => $amount ) );
354 - }
355 - }
404 + }//end foreach
405 + }//end foreach
356 406 }
357 407
358 408 /**
359 409 * Create an entry object with posted values.
@@ -358,30 +408,34 @@
358 408 /**
359 409 * Create an entry object with posted values.
360 410 *
361 411 * @since 6.5, introduced in v2.0 of the Stripe add on.
412 + *
413 + * @param array $form_data
414 + *
362 415 * @return stdClass
363 416 */
364 - private static function generate_false_entry() {
365 - $entry = new stdClass();
366 - $entry->post_id = 0;
367 - $entry->id = 0;
368 - $entry->metas = array();
417 + private static function generate_false_entry( $form_data ) {
418 + $entry = new stdClass();
419 + $entry->post_id = 0;
420 + $entry->id = 0;
421 + $entry->item_key = '';
422 + $entry->metas = array();
369 423
370 - // phpcs:ignore WordPress.Security.NonceVerification.Missing
371 - foreach ( $_POST as $k => $v ) {
424 + foreach ( $form_data as $k => $v ) {
372 425 $k = sanitize_text_field( stripslashes( $k ) );
373 426 $v = wp_unslash( $v );
374 427
375 - if ( $k === 'item_meta' ) {
376 - foreach ( $v as $f => $value ) {
377 - FrmAppHelper::sanitize_value( 'wp_kses_post', $value );
378 - $entry->metas[ absint( $f ) ] = $value;
379 - }
380 - } else {
428 + if ( $k !== 'item_meta' ) {
381 429 FrmAppHelper::sanitize_value( 'wp_kses_post', $v );
382 430 $entry->{$k} = $v;
431 + continue;
383 432 }
433 +
434 + foreach ( $v as $f => $value ) {
435 + FrmAppHelper::sanitize_value( 'wp_kses_post', $value );
436 + $entry->metas[ absint( $f ) ] = $value;
437 + }
384 438 }
385 439
386 440 return $entry;
387 441 }
@@ -391,8 +445,9 @@
391 445 *
392 446 * @since 6.5, introduced in v2.0 of the Stripe add on.
393 447 *
394 448 * @param array $form
449 + *
395 450 * @return void
396 451 */
397 452 private static function format_form_data( &$form ) {
398 453 $formatted = array();
@@ -398,16 +453,18 @@
398 453 $formatted = array();
399 454
400 455 foreach ( $form as $input ) {
401 456 $key = $input['name'];
402 - if ( isset( $formatted[ $key ] ) ) {
403 - if ( is_array( $formatted[ $key ] ) ) {
404 - $formatted[ $key ][] = $input['value'];
405 - } else {
406 - $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
407 - }
457 +
458 + if ( ! isset( $formatted[ $key ] ) ) {
459 + $formatted[ $key ] = $input['value'];
460 + continue;
461 + }
462 +
463 + if ( is_array( $formatted[ $key ] ) ) {
464 + $formatted[ $key ][] = $input['value'];
408 465 } else {
409 - $formatted[ $key ] = $input['value'];
466 + $formatted[ $key ] = array( $formatted[ $key ], $input['value'] );
410 467 }
411 468 }
412 469
413 470 parse_str( http_build_query( $formatted ), $form );
@@ -418,19 +475,26 @@
418 475 * This only happens in two cases: For stripe link, and when processing a one-time payment before the entry is created.
419 476 *
420 477 * @since 6.5, introduced in v2.0 of the Stripe add on.
421 478 *
422 - * @param string|int $form_id
479 + * @param int|string $form_id
480 + *
423 481 * @return array
424 482 */
425 483 private static function maybe_create_intents( $form_id ) {
426 484 $intents = array();
485 + $details = self::check_request_params( $form_id );
427 486
428 - $details = self::check_request_params( $form_id );
429 - if ( is_array( $details ) && ! self::intent_has_failed_status( $details['intent'] ) ) {
487 + if ( is_array( $details ) ) {
488 + $payment = $details['payment'];
489 + $intent = $details['intent'];
490 + $payment_failed = self::payment_failed( $payment, $intent );
491 +
430 492 // Exit early if the request params are set.
431 493 // This way an extra payment intent isn't created for Stripe Link.
432 - return $intents;
494 + if ( ! $payment_failed ) {
495 + return $intents;
496 + }
433 497 }
434 498
435 499 if ( ! FrmStrpLiteAppHelper::call_stripe_helper_class( 'initialize_api' ) ) {
436 500 // Stripe is not configured, so don't create intents.
@@ -449,8 +513,9 @@
449 513 continue;
450 514 }
451 515
452 516 $intent = self::create_intent( $action );
517 +
453 518 if ( ! is_object( $intent ) ) {
454 519 // A non-object is a string error message.
455 520 // The error gets logged to results.log so we can just skip it.
456 521 // Reasons it could fail is because a payment method type was specified that will not work.
@@ -464,9 +529,9 @@
464 529 $intents[] = array(
465 530 'id' => $intent->client_secret,
466 531 'action' => $action->ID,
467 532 );
468 - }
533 + }//end foreach
469 534
470 535 return $intents;
471 536 }
472 537
@@ -475,14 +540,19 @@
475 540 *
476 541 * @since 3.0 This code was moved out of self::maybe_create_intents into a new function.
477 542 *
478 543 * @param WP_Post $action
544 + *
479 545 * @return mixed
480 546 */
481 547 private static function create_intent( $action ) {
482 - $amount = $action->post_content['amount'];
548 + $amount = $action->post_content['amount'];
549 + $currency = $action->post_content['currency'];
550 +
551 + // phpcs:ignore Universal.Operators.StrictComparisons
483 552 if ( $amount == '000' ) {
484 - $amount = 100; // Create the intent when the form loads.
553 + // Create the intent when the form loads.
554 + $amount = in_array( strtolower( $currency ), array( 'aud', 'cad', 'eur', 'gbp', 'usd' ), true ) ? 100 : 1000;
485 555 }
486 556
487 557 if ( 'recurring' === $action->post_content['type'] ) {
488 558 $payment_method_types = FrmStrpLitePaymentTypeHandler::get_payment_method_types( $action );
@@ -490,12 +560,14 @@
490 560 }
491 561
492 562 $new_charge = array(
493 563 'amount' => $amount,
494 - 'currency' => $action->post_content['currency'],
564 + 'currency' => $currency,
495 565 'metadata' => array( 'action' => $action->ID ),
496 566 );
497 567
568 + $new_charge = self::maybe_add_statement_descriptor( $new_charge );
569 +
498 570 if ( FrmStrpLitePaymentTypeHandler::should_use_automatic_payment_methods( $action ) ) {
499 571 $new_charge['automatic_payment_methods'] = array( 'enabled' => true );
500 572 } else {
501 573 $payment_method_types = FrmStrpLitePaymentTypeHandler::get_payment_method_types( $action );
@@ -505,14 +577,104 @@
505 577 return FrmStrpLiteAppHelper::call_stripe_helper_class( 'create_intent', $new_charge );
506 578 }
507 579
508 580 /**
581 + * Add the statement descriptor to the intent data, if it is valid.
582 + *
583 + * @param array $intent_data
584 + *
585 + * @return array
586 + */
587 + private static function maybe_add_statement_descriptor( $intent_data ) {
588 + $statement_descriptor = self::get_statement_descriptor();
589 +
590 + if ( false !== $statement_descriptor ) {
591 + $intent_data['statement_descriptor'] = $statement_descriptor;
592 + }
593 +
594 + return $intent_data;
595 + }
596 +
597 + /**
598 + * Get the statement descriptor for a payment intent.
599 + *
600 + * @since 6.23
601 + *
602 + * @return false|string False if the statement descriptor is not valid.
603 + */
604 + private static function get_statement_descriptor() {
605 + $name = get_bloginfo( 'name' );
606 +
607 + /**
608 + * Filters the statement descriptor for a payment intent.
609 + * This way a site can use the name they want on their statements.
610 + *
611 + * @since 6.23
612 + *
613 + * @param string $name The name of the site.
614 + */
615 + $name = apply_filters( 'frm_stripe_statement_descriptor', $name );
616 +
617 + if ( ! is_string( $name ) ) {
618 + return false;
619 + }
620 +
621 + $name = self::strip_special_characters_from_statement_descriptor( $name );
622 +
623 + return self::statement_descriptor_is_valid( $name ) ? $name : false;
624 + }
625 +
626 + /**
627 + * Remove the special characters that Stripe doesn't allow in statement descriptors, in case any exist.
628 + *
629 + * @since 6.23
630 + *
631 + * @param string $name The name of the site.
632 + *
633 + * @return string The name with special characters removed.
634 + */
635 + private static function strip_special_characters_from_statement_descriptor( $name ) {
636 + $special_characters = array(
637 + '<',
638 + '>',
639 + '\\',
640 + "'",
641 + '"',
642 + '*',
643 + );
644 + return str_replace( $special_characters, '', $name );
645 + }
646 +
647 + /**
648 + * Stripe includes requirements at https://docs.stripe.com/get-started/account/statement-descriptors
649 + * We need to make sure that the descriptor contains only Latin characters, and that it is between 5 and 22 characters long.
650 + *
651 + * @since 6.23
652 + *
653 + * @param string $name Passed by reference, as this is updated if it is too long.
654 + *
655 + * @return bool
656 + */
657 + private static function statement_descriptor_is_valid( &$name ) {
658 + if ( strlen( $name ) < 5 ) {
659 + return false;
660 + }
661 +
662 + if ( strlen( $name ) > 22 ) {
663 + $name = substr( $name, 0, 22 );
664 + }
665 +
666 + return (bool) preg_match( '/^[a-zA-Z0-9\s\p{P}]+$/', $name );
667 + }
668 +
669 + /**
509 670 * Create a customer and an associated setup intent for a recurring Stripe link payment.
510 671 *
511 672 * @since 6.5, introduced in v3.0 of the Stripe add on.
512 673 *
513 674 * @param array $payment_method_types
514 - * @return object|false
675 + *
676 + * @return false|object
515 677 */
516 678 private static function create_setup_intent( $payment_method_types ) {
517 679 $payment_info = array(
518 680 'user_id' => FrmTransLiteAppHelper::get_user_id_for_current_payment(),
@@ -519,8 +681,9 @@
519 681 );
520 682
521 683 // We need to add a customer to support subscriptions with link.
522 684 $customer = FrmStrpLiteAppHelper::call_stripe_helper_class( 'get_customer', $payment_info );
685 +
523 686 if ( ! is_object( $customer ) ) {
524 687 return false;
525 688 }
526 689
@@ -529,20 +692,22 @@
529 692
530 693 /**
531 694 * @since 6.5, introduced in v2.0 of the Stripe add on.
532 695 *
533 - * @param string|int $form_id
696 + * @param int|string $form_id
534 697 * @param array $actions
698 + *
535 699 * @return void
536 700 */
537 701 private static function add_amount_to_actions( $form_id, &$actions ) {
538 - if ( empty( $actions ) ) {
702 + if ( ! $actions ) {
539 703 return;
540 704 }
705 +
541 706 $form = FrmForm::getOne( $form_id );
542 707
543 708 foreach ( $actions as $k => $action ) {
544 - $amount = self::get_amount_before_submit( compact( 'action', 'form' ) );
709 + $amount = self::get_amount_before_submit( compact( 'action', 'form' ) );
545 710 $actions[ $k ]->post_content['amount'] = $amount;
546 711 }
547 712 }
548 713
@@ -549,12 +714,12 @@
549 714 /**
550 715 * @since 6.5, introduced in v2.0 of the Stripe add on.
551 716 *
552 717 * @param array $atts
718 + *
553 719 * @return string
554 720 */
555 721 private static function get_amount_before_submit( $atts ) {
556 - $amount = $atts['action']->post_content['amount'];
557 722 return FrmStrpLiteActionsController::prepare_amount( $atts['action']->post_content['amount'], $atts );
558 723 }
559 724
560 725 /**
@@ -564,8 +729,9 @@
564 729 *
565 730 * @since 6.5, introduced in v2.0 of the Stripe add on.
566 731 *
567 732 * @param array $atts
733 + *
568 734 * @return string
569 735 */
570 736 public static function return_url( $atts ) {
571 737 $atts = array(
@@ -572,15 +738,9 @@
572 738 'entry' => $atts['entry'],
573 739 );
574 740 self::prepare_success_atts( $atts );
575 741
576 - if ( $atts['conf_method'] === 'redirect' ) {
577 - $redirect = self::get_redirect_url( $atts );
578 - } else {
579 - $redirect = self::get_message_url( $atts );
580 - }
581 -
582 - return $redirect;
742 + return $atts['conf_method'] === 'redirect' ? self::get_redirect_url( $atts ) : self::get_message_url( $atts );
583 743 }
584 744
585 745 /**
586 746 * If the form should redirect, get the url to redirect to.
@@ -587,15 +747,19 @@
587 747 *
588 748 * @since 6.5, introduced in v2.0 of the Stripe add on.
589 749 *
590 750 * @param array $atts {
751 + * The form and entry details.
752 + *
591 753 * @type stdClass $form
592 754 * @type stdClass $entry
593 755 * }
756 + *
594 757 * @return string
595 758 */
596 759 private static function get_redirect_url( $atts ) {
597 760 $actions = FrmFormsController::get_met_on_submit_actions( $atts );
761 +
598 762 if ( $actions ) {
599 763 $success_url = reset( $actions )->post_content['success_url'];
600 764 }
601 765
@@ -602,9 +766,9 @@
602 766 if ( empty( $success_url ) ) {
603 767 $success_url = $atts['form']->options['success_url'];
604 768 }
605 769
606 - $success_url = trim( $atts['form']->options['success_url'] );
770 + $success_url = trim( $success_url );
607 771 $success_url = apply_filters( 'frm_content', $success_url, $atts['form'], $atts['entry'] );
608 772 $success_url = do_shortcode( $success_url );
609 773 $atts['id'] = $atts['entry']->id;
610 774
@@ -612,19 +776,23 @@
612 776 return apply_filters( 'frm_redirect_url', $success_url, $atts['form'], $atts );
613 777 }
614 778
615 779 /**
616 - * If the form should should a message, apend it to the success url.
780 + * If the form should should a message, append it to the success url.
617 781 *
618 782 * @since 6.5, introduced in v2.0 of the Stripe add on.
619 783 *
620 784 * @param array $atts
785 + *
786 + * @return string
621 787 */
622 788 private static function get_message_url( $atts ) {
623 789 $url = self::get_referer_url( $atts['entry_id'], false );
790 +
624 791 if ( false === $url ) {
625 792 $url = FrmAppHelper::get_server_value( 'HTTP_REFERER' );
626 793 }
794 +
627 795 return add_query_arg( array( 'frmstrp' => $atts['entry_id'] ), $url );
628 796 }
629 797
630 798 /**
@@ -629,11 +797,12 @@
629 797
630 798 /**
631 799 * @since 6.5
632 800 *
633 - * @param string|int $entry_id
801 + * @param int|string $entry_id
634 802 * @param bool $delete_meta
635 - * @return string|false
803 + *
804 + * @return false|string
636 805 */
637 806 public static function get_referer_url( $entry_id, $delete_meta = true ) {
638 807 $row = FrmDb::get_row(
639 808 'frm_item_metas',
@@ -643,8 +812,9 @@
643 812 'meta_value LIKE' => '{"referer":',
644 813 ),
645 814 'id, meta_value'
646 815 );
816 +
647 817 if ( ! $row ) {
648 818 return false;
649 819 }
650 820
@@ -654,9 +824,12 @@
654 824 if ( ! is_array( $meta ) || empty( $meta['referer'] ) ) {
655 825 return false;
656 826 }
657 827
658 - self::delete_temporary_referer_meta( (int) $row->id );
828 + if ( $delete_meta ) {
829 + self::delete_temporary_referer_meta( (int) $row->id );
830 + }
831 +
659 832 return $meta['referer'];
660 833 }
661 834
662 835 /**
@@ -662,8 +835,9 @@
662 835 /**
663 836 * Delete the referer meta as we'll no longer need it.
664 837 *
665 838 * @param int $row_id
839 + *
666 840 * @return void
667 841 */
668 842 private static function delete_temporary_referer_meta( $row_id ) {
669 843 global $wpdb;
@@ -675,10 +849,29 @@
675 849 *
676 850 * @since 6.5.1
677 851 *
678 852 * @param object $intent
853 + *
679 854 * @return bool
680 855 */
681 856 private static function intent_has_failed_status( $intent ) {
682 857 return in_array( $intent->status, array( 'requires_source', 'requires_payment_method', 'canceled' ), true );
858 + }
859 +
860 + /**
861 + * Check if a payment failed.
862 + *
863 + * @since 6.8
864 + *
865 + * @param object $payment
866 + * @param object $intent
867 + *
868 + * @return bool
869 + */
870 + public static function payment_failed( $payment, $intent ) {
871 + if ( self::intent_has_failed_status( $intent ) ) {
872 + return true;
873 + }
874 + // The $intent will be "succeeded" with a failed payment when testing with the 4000000000000341 credit card.
875 + return 'payment_failed' === FrmAppHelper::simple_get( 'frm_link_error' ) && 'failed' === $payment->status;
683 876 }
684 877 }