PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.15
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.15
6.2.15 6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 All 197 releases
← All changes | app/Modules/Payments/PaymentHandler.php +63 -11 6.2.2 → 6.2.15 View file →
@@ -27,8 +27,30 @@
27 27 use FluentForm\App\Modules\Payments\PaymentMethods\Stripe\StripeSettings;
28 28
29 29 class PaymentHandler
30 30 {
31 + /**
32 + * A reversed order (refund/partial-refund/cancel) must not fire the deferred
33 + * submission action pipeline (notifications, integrations, user registration)
34 + * when it is later reached via a double-opt-in / admin-approval confirmation or a
35 + * subscription webhook. The normal paid flow is unaffected (latch already set),
36 + * as are non-payment forms and unsettled-but-not-reversed states (pending/failed).
37 + */
38 + public function skipActionsForReversedPayment($shouldProcess, $submission, $form)
39 + {
40 + if (!$shouldProcess || empty($form->has_payment)) {
41 + return $shouldProcess;
42 + }
43 +
44 + $paymentStatus = isset($submission->payment_status) ? $submission->payment_status : null;
45 +
46 + if (PaymentHelper::isReversedPaymentStatus($paymentStatus)) {
47 + return false;
48 + }
49 +
50 + return $shouldProcess;
51 + }
52 +
31 53 public function init()
32 54 {
33 55
34 56 add_filter('fluentform/global_settings_components', [$this, 'pushGlobalSettings'], 1, 1);
@@ -35,9 +57,11 @@
35 57
36 58 add_filter('fluentform/global_settings_component_settings_data', [$this, 'getGlobalSettingsPaymentVars']);
37 59
38 60 add_action('wp_ajax_fluentform_handle_payment_ajax_endpoint', [$this, 'handleAjaxEndpoints']);
39 -
61 +
62 + add_filter('fluentform/should_process_submission_actions', [$this, 'skipActionsForReversedPayment'], 10, 3);
63 +
40 64 if (!$this->isEnabled()) {
41 65 return;
42 66 }
43 67
@@ -299,9 +323,12 @@
299 323 [$this, 'validatePaymentInputs'],
300 324 10,
301 325 3
302 326 );
303 -
327 +
328 + add_filter('fluentform/validate_input_item_custom_payment_component', [$this, 'validatePaymentNumber'], 10, 3);
329 + add_filter('fluentform/validate_input_item_item_quantity_component', [$this, 'validatePaymentNumber'], 10, 3);
330 +
304 331 add_filter(
305 332 'fluentform/validate_input_item_payment_method',
306 333 [$this, 'validatePaymentMethod'],
307 334 10,
@@ -395,16 +422,20 @@
395 422 public function handleAjaxEndpoints()
396 423 {
397 424 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce verified by Acl::verify()
398 425 $route = isset($_REQUEST['route']) ? sanitize_text_field(wp_unslash($_REQUEST['route'])) : '';
399 - $formScopedRoutes = [
400 - 'get_form_settings',
401 - 'save_form_settings',
426 + $paymentMutationRoutes = [
402 427 'update_transaction',
403 428 'cancel_subscription'
404 429 ];
430 + $formSettingRoutes = [
431 + 'get_form_settings',
432 + 'save_form_settings'
433 + ];
405 434
406 - if (in_array($route, $formScopedRoutes, true)) {
435 + if (in_array($route, $paymentMutationRoutes, true)) {
436 + Acl::verify('fluentform_manage_payments', $this->resolveRouteFormId($route));
437 + } elseif (in_array($route, $formSettingRoutes, true)) {
407 438 Acl::verify('fluentform_forms_manager', $this->resolveRouteFormId($route));
408 439 } else {
409 440 Acl::verify('fluentform_settings_manager');
410 441 }
@@ -462,10 +493,11 @@
462 493 return;
463 494 }
464 495
465 496 $paymentAction = new PaymentAction($form, $insertData, $data);
466 -
497 +
467 498 if (!$paymentAction->getSubscriptionItems() && !$paymentAction->getCalculatedAmount()) {
499 + $paymentAction->flagZeroTotalOrder();
468 500 return;
469 501 }
470 502
471 503 /*
@@ -612,9 +644,14 @@
612 644
613 645 $submissionIds = array_unique($submissionIds);
614 646 $transactionIds = array_unique($transactionIds);
615 647
648 + // Claim only unowned submissions; payer_email is unverified and may match a registered owner.
616 649 \FluentForm\App\Models\Submission::whereIn('id', $submissionIds)
650 + ->where(function ($query) {
651 + $query->whereNull('user_id')
652 + ->orWhere('user_id', '');
653 + })
617 654 ->update([
618 655 'user_id' => $userId,
619 656 'updated_at' => current_time('mysql')
620 657 ]);
@@ -681,9 +718,9 @@
681 718 if ('yes' === ArrayHelper::get($selectedPlan, 'user_input')) {
682 719 $userGivenValue = ArrayHelper::get($formData, "{$field['name']}_custom_$selectedPlanIndex");
683 720 $userGivenValue = $userGivenValue ?: 0;
684 721 $planMinValue = ArrayHelper::get($selectedPlan, 'user_input_min_value');
685 - if (!is_numeric($userGivenValue) || ($planMinValue && $userGivenValue < $planMinValue)) {
722 + if (!is_numeric($userGivenValue) || $userGivenValue < 0 || ($planMinValue && $userGivenValue < $planMinValue)) {
686 723 $error = __('This subscription plan value is invalid', 'fluentform');
687 724 }
688 725 }
689 726 }
@@ -692,9 +729,10 @@
692 729 }
693 730
694 731 public function validatePaymentInputs($error, $field, $formData)
695 732 {
696 - if (ArrayHelper::get($formData, $field['name'])) {
733 + // A submitted "0" is still a value and must match an offered option.
734 + if (!in_array(ArrayHelper::get($formData, $field['name']), [null, '', []], true)) {
697 735 $fieldType = ArrayHelper::get($field, 'raw.attributes.type');
698 736
699 737 if (in_array($fieldType, ['radio', 'select', 'checkbox'])) {
700 738 $pricingOptions = array_column(
@@ -706,9 +744,9 @@
706 744
707 745 if (in_array($fieldType, ['radio', 'select'])) {
708 746 $acceptedPaymentPlan = in_array($formData[$field['name']], $pricingOptions);
709 747 } else {
710 - $acceptedPaymentPlan = array_diff($formData[$field['name']], $pricingOptions);
748 + $acceptedPaymentPlan = array_diff((array) $formData[$field['name']], $pricingOptions);
711 749
712 750 $acceptedPaymentPlan = empty($acceptedPaymentPlan);
713 751 }
714 752
@@ -719,9 +757,23 @@
719 757 }
720 758
721 759 return $error;
722 760 }
723 -
761 +
762 + // The order builder silently drops an amount or quantity it cannot price, zeroing the order.
763 + public function validatePaymentNumber($error, $field, $formData)
764 + {
765 + $value = ArrayHelper::get($formData, $field['name']);
766 + if ($error || in_array($value, [null, ''], true) || (is_numeric($value) && $value > 0)) {
767 + return $error;
768 + }
769 +
770 + $isOptionalZero = is_numeric($value) && 0 == $value
771 + && !ArrayHelper::get($field, 'raw.settings.validation_rules.required.value');
772 +
773 + return $isOptionalZero ? $error : __('This payment item is invalid', 'fluentform');
774 + }
775 +
724 776 public function validatePaymentMethod($error, $field, $formData, $fields, $form)
725 777 {
726 778 if ($selectedMethod = ArrayHelper::get($formData, $field['name'])) {
727 779 $activeMethods = array_keys(PaymentHelper::getFormPaymentMethods($form->id));