diff --git a/CRM/Core/Payment/StripeIPN.php b/CRM/Core/Payment/StripeIPN.php
index b7c499413791f95486a02b0ade81beac69cbc019..635783a2efe5ebea7e9194f5d770dba91adb1fdc 100644
--- a/CRM/Core/Payment/StripeIPN.php
+++ b/CRM/Core/Payment/StripeIPN.php
@@ -171,7 +171,12 @@ class CRM_Core_Payment_StripeIPN {
       return TRUE;
     }
 
-    $pendingStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
+    $pendingContributionStatusID = (int) CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
+    $failedContributionStatusID = (int) CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Failed');
+    $statusesAllowedToComplete = [
+      $pendingContributionStatusID,
+      $failedContributionStatusID
+    ];
 
     // NOTE: If you add an event here make sure you add it to the webhook or it will never be received!
     switch($this->event_type) {
@@ -223,7 +228,8 @@ class CRM_Core_Payment_StripeIPN {
           return TRUE;
         }
 
-        if ($this->contribution['contribution_status_id'] == $pendingStatusId) {
+        // If contribution is in Pending or Failed state record payment and transition to Completed
+        if (in_array($this->contribution['contribution_status_id'], $statusesAllowedToComplete)) {
           $params = [
             'contribution_id' => $this->contribution['id'],
             'trxn_date' => $this->receive_date,
@@ -245,7 +251,7 @@ class CRM_Core_Payment_StripeIPN {
           return TRUE;
         }
 
-        if ($this->contribution['contribution_status_id'] == $pendingStatusId) {
+        if ($this->contribution['contribution_status_id'] == $pendingContributionStatusID) {
           // If this contribution is Pending, set it to Failed.
           $params = [
             'contribution_id' => $this->contribution['id'],
@@ -347,11 +353,7 @@ class CRM_Core_Payment_StripeIPN {
         }
 
         // If contribution is in Pending or Failed state record payment and transition to Completed
-        $statusesToUpdate = [
-          $pendingStatusId,
-          CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Failed'),
-        ];
-        if (in_array($this->contribution['contribution_status_id'], $statusesToUpdate)) {
+        if (in_array($this->contribution['contribution_status_id'], $statusesAllowedToComplete)) {
           $params = [
             'contribution_id' => $this->contribution['id'],
             'trxn_date' => $this->receive_date,
diff --git a/docs/releasenotes.md b/docs/releasenotes.md
index e3bc2df9f917f290647d80b95a2ca313cb828607..0e479ef712fba20908fb38cbbf9a8c8bca7855f1 100644
--- a/docs/releasenotes.md
+++ b/docs/releasenotes.md
@@ -45,6 +45,9 @@ Releases use the following numbering system:
 * Fix [#249](https://lab.civicrm.org/extensions/stripe/-/issues/249) 500 error on recurring contribution.
 * Update Stripe PHP library.
 
+#### Beta 3
+* Fix Failed->Completed status for recurring contributions/subscriptions.
+
 ## Release 6.4.2
 
 * Fix [#210](https://lab.civicrm.org/extensions/stripe/-/issues/210): If there are multiple reCaptcha on the page check and validate the one on the Stripe billing form only.