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/controllers/FrmStrpLiteEventsController.php +50 -36 6.26 → trunk View file →
@@ -83,9 +83,11 @@
83 83 }
84 84
85 85 FrmTransLiteAppHelper::add_note_to_payment( $payment_values, $note );
86 86
87 - $u = $frm_payment->update( $payment->id, $payment_values );
87 + // Read the status again right before the update, in case another request has already changed it.
88 + $payment_status_still_does_not_match = $this->payment_status_still_does_not_match( $payment->id );
89 + $updated = $frm_payment->update( $payment->id, $payment_values );
88 90
89 91 echo json_encode(
90 92 array(
91 93 'response' => 'Payment ' . $payment->id . ' was updated',
@@ -92,9 +94,9 @@
92 94 'success' => true,
93 95 )
94 96 );
95 97
96 - if ( ! $is_partial_refund ) {
98 + if ( ! $is_partial_refund && $payment_status_still_does_not_match && $updated ) {
97 99 $run_triggers = true;
98 100 }
99 101 }//end if
100 102
@@ -108,8 +110,26 @@
108 110 }
109 111 }
110 112
111 113 /**
114 + * Double check that the payment status has not changed.
115 + * This is to avoid running actions twice by mistake, since a Stripe Link
116 + * return URL and a webhook event can both process the same payment.
117 + *
118 + * @since 6.35
119 + *
120 + * @param int $payment_id The id of the payment to check.
121 + *
122 + * @return bool
123 + */
124 + private function payment_status_still_does_not_match( $payment_id ) {
125 + $frm_payment = new FrmTransLitePayment();
126 + $payment = $frm_payment->get_one( $payment_id );
127 +
128 + return $payment && $payment->status !== $this->status;
129 + }
130 +
131 + /**
112 132 * Skip updating the payment object for the first recurring payment.
113 133 * This is to prevent double notifications because the first recurring payment creates an invoice and that invoice triggers the payment events.
114 134 *
115 135 * @since 6.5, introduced in v2.07 of the Stripe add on.
@@ -170,9 +190,9 @@
170 190 private function reset_customer() {
171 191 global $wpdb;
172 192 $customer_id = $this->invoice->id;
173 193
174 - if ( empty( $customer_id ) ) {
194 + if ( ! $customer_id ) {
175 195 return;
176 196 }
177 197 $wpdb->query(
178 198 $wpdb->prepare(
@@ -186,8 +206,9 @@
186 206 /**
187 207 * @return void
188 208 */
189 209 private function maybe_subscription_canceled() {
210 + // phpcs:ignore Universal.Operators.StrictComparisons
190 211 if ( $this->invoice->cancel_at_period_end == true ) {
191 212 $this->subscription_canceled( 'future_cancel' );
192 213 }
193 214 }
@@ -263,11 +284,9 @@
263 284 }
264 285
265 286 $this->maybe_cancel_subscription( $sub );
266 287 $this->update_next_bill_date( $sub, $payment_values );
267 -
268 - $payment = $frm_payment->get_one( $payment_id );
269 - return $payment;
288 + return $frm_payment->get_one( $payment_id );
270 289 }
271 290
272 291 /**
273 292 * Check if a subscription has reached its payment limit.
@@ -310,10 +329,12 @@
310 329 return true;
311 330 };
312 331
313 332 add_filter( $hook, $filter, 99 );
314 - $cancelled = FrmStrpLiteApiHelper::cancel_subscription( $sub->sub_id );
315 333
334 + // There is no logged in user when a webhook event is processed, so the customer check has to be skipped here.
335 + $cancelled = FrmStrpLiteAppHelper::call_stripe_helper_class( 'cancel_subscription_without_customer_check', $sub->sub_id );
336 +
316 337 if ( $cancelled ) {
317 338 FrmTransLiteSubscriptionsController::change_subscription_status(
318 339 array(
319 340 'status' => 'future_cancel',
@@ -319,9 +340,12 @@
319 340 'status' => 'future_cancel',
320 341 'sub' => $sub,
321 342 )
322 343 );
344 + } else {
345 + FrmTransLiteLog::log_message( 'Stripe Webhook Message', 'Unable to cancel subscription ' . $sub->sub_id . ' after it reached its payment limit.' );
323 346 }
347 +
324 348 remove_filter( $hook, $filter, 99 );
325 349 }
326 350
327 351 /**
@@ -335,11 +359,10 @@
335 359 */
336 360 private function get_payments_count( $sub_id ) {
337 361 $frm_payment = new FrmTransLitePayment();
338 362 $all_payments = $frm_payment->get_all_by( $sub_id, 'sub_id' );
339 - $count = FrmTransLiteAppHelper::count_completed_payments( $all_payments );
340 363
341 - return $count;
364 + return FrmTransLiteAppHelper::count_completed_payments( $all_payments );
342 365 }
343 366
344 367 /**
345 368 * @since 6.5, introduced in v2.07 of the Stripe add on.
@@ -348,9 +371,9 @@
348 371 *
349 372 * @return bool
350 373 */
351 374 private function is_first_payment( $payment ) {
352 - return ! $payment->receipt_id || 0 === strpos( $payment->receipt_id, 'pi_' );
375 + return ! $payment->receipt_id || str_starts_with( $payment->receipt_id, 'pi_' );
353 376 }
354 377
355 378 /**
356 379 * @param string $sub_id
@@ -427,16 +450,15 @@
427 450 /**
428 451 * @return bool
429 452 */
430 453 private function is_partial_refund() {
431 - $partial = false;
454 + if ( $this->status !== 'refunded' ) {
455 + return false;
456 + }
432 457
433 - if ( $this->status === 'refunded' ) {
434 - $amount = $this->invoice->amount;
435 - $amount_refunded = $this->invoice->amount_refunded;
436 - $partial = $amount != $amount_refunded;
437 - }
438 - return $partial;
458 + $amount = $this->invoice->amount;
459 + $amount_refunded = $this->invoice->amount_refunded;
460 + return $amount !== $amount_refunded;
439 461 }
440 462
441 463 /**
442 464 * @param array $payment_values
@@ -458,8 +480,9 @@
458 480
459 481 if ( $unprocessed_event_ids ) {
460 482 $this->process_event_ids( $unprocessed_event_ids );
461 483 }
484 +
462 485 wp_send_json_success();
463 486 }
464 487
465 488 /**
@@ -478,15 +501,16 @@
478 501 set_transient( 'frm_last_process_' . $event_id, time(), 60 );
479 502
480 503 $this->event = FrmStrpLiteConnectHelper::get_event( $event_id );
481 504
482 - if ( is_object( $this->event ) ) {
483 - $this->handle_event();
484 - $this->track_handled_event( $event_id );
485 - FrmStrpLiteConnectHelper::process_event( $event_id );
486 - } else {
505 + if ( ! is_object( $this->event ) ) {
487 506 $this->count_failed_event( $event_id );
507 + continue;
488 508 }
509 +
510 + $this->handle_event();
511 + $this->track_handled_event( $event_id );
512 + FrmStrpLiteConnectHelper::process_event( $event_id );
489 513 }
490 514 }
491 515
492 516 /**
@@ -502,13 +526,9 @@
502 526 }
503 527
504 528 $option = get_option( self::$events_to_skip_option_name );
505 529
506 - if ( ! is_array( $option ) ) {
507 - return false;
508 - }
509 -
510 - return in_array( $event_id, $option, true );
530 + return is_array( $option ) && in_array( $event_id, $option, true );
511 531 }
512 532
513 533 /**
514 534 * @param string $event_id
@@ -527,17 +547,11 @@
527 547 *
528 548 * @return void
529 549 */
530 550 private function count_failed_event( $event_id ) {
531 - $transient_name = 'frm_failed_event_' . $event_id;
532 - $transient = get_transient( $transient_name );
533 -
534 - if ( is_int( $transient ) ) {
535 - $failed_count = $transient + 1;
536 - } else {
537 - $failed_count = 1;
538 - }
539 -
551 + $transient_name = 'frm_failed_event_' . $event_id;
552 + $transient = get_transient( $transient_name );
553 + $failed_count = is_int( $transient ) ? $transient + 1 : 1;
540 554 $maximum_retries = 3;
541 555
542 556 if ( $failed_count >= $maximum_retries ) {
543 557 $this->track_handled_event( $event_id );