diff --git a/CRM/Core/Payment/MJWTrait.php b/CRM/Core/Payment/MJWTrait.php
index c367bb50ab53f6f6cbf3f338b8a9acaed6708a27..3fe66c2ee4a036d8bdf5b68cd0fe8d9116cf47b9 100644
--- a/CRM/Core/Payment/MJWTrait.php
+++ b/CRM/Core/Payment/MJWTrait.php
@@ -424,6 +424,32 @@ trait CRM_Core_Payment_MJWTrait {
     return $this->paymentProcessorSubscriptionID;
   }
 
+  /**
+   * In some cases a payment is still submitted via the payment processor with zero amount.
+   * See eg. https://lab.civicrm.org/extensions/stripe/-/issues/256.
+   * When you have a 0 membership option and a confirmation page.
+   * This function should be called in doPayment() before beginDoPayment()
+   *
+   * @param \Civi\Payment\PropertyBag $propertyBag
+   *
+   * @return array|false
+   */
+  protected function processZeroAmountPayment(\Civi\Payment\PropertyBag $propertyBag) {
+    // If we have a $0 amount, skip call to processor and set payment_status to Completed.
+    // https://github.com/civicrm/civicrm-core/blob/master/CRM/Core/Payment.php#L1362
+    if ($propertyBag->getAmount() == 0) {
+      return [
+        'payment_status_id' => CRM_Core_PseudoConstant::getKey(
+          'CRM_Contribute_BAO_Contribution',
+          'contribution_status_id', 'Completed'
+        )
+      ];
+    }
+    else {
+      return FALSE;
+    }
+  }
+
   /**
    * @param \Civi\Payment\PropertyBag $propertyBag
    *