diff --git a/CRM/Contribute/Form/ContributionBase.php b/CRM/Contribute/Form/ContributionBase.php
index 963044d2aa6c..e44a816bae8f 100644
--- a/CRM/Contribute/Form/ContributionBase.php
+++ b/CRM/Contribute/Form/ContributionBase.php
@@ -1138,40 +1138,33 @@ public function cancelRecurring() {
    * Arguably the form should start to build $this->_params in the pre-process main page & use that array consistently throughout.
    */
   protected function setRecurringMembershipParams() {
-    $selectedMembershipTypeID = $this->_params['selectMembership'] ?? NULL;
-    if ($selectedMembershipTypeID) {
-      // @todo the price_x fields will ALWAYS allow us to determine the membership - so we should ignore
-      // 'selectMembership' and calculate from the price_x fields so we have one method that always works
-      // this is lazy & only catches when selectMembership is set, but the worst of all worlds would be to fix
-      // this with an else (calculate for price set).
-      $membershipTypes = CRM_Price_BAO_PriceSet::getMembershipTypesFromPriceSet($this->_priceSetId);
-      if (in_array($selectedMembershipTypeID, $membershipTypes['autorenew_required'])
-        || (in_array($selectedMembershipTypeID, $membershipTypes['autorenew_optional']) &&
-          !empty($this->_params['is_recur']))
-      ) {
-        $this->_params['auto_renew'] = TRUE;
-      }
+    $priceFieldId = array_key_first($this->_values['fee']);
+    // Why is this an array in CRM_Contribute_Form_Contribution_Main::submit and a string in CRM_Contribute_Form_Contribution_Confirm::preProcess()?
+    if (is_array($this->_params["price_{$priceFieldId}"])) {
+      $priceFieldValue = array_key_first($this->_params["price_{$priceFieldId}"]);
+    }
+    else {
+      $priceFieldValue = $this->_params["price_{$priceFieldId}"];
+    }
+    $selectedMembershipTypeID = $this->_values['fee'][$priceFieldId]['options'][$priceFieldValue]['membership_type_id'] ?? NULL;
+    if (!$selectedMembershipTypeID) {
+      return;
     }
-    if ((!empty($this->_params['selectMembership']) || !empty($this->_params['priceSetId']))
-      && !empty($this->_paymentProcessor['is_recur']) &&
-      !empty($this->_params['auto_renew'])
-      && empty($this->_params['is_recur']) && empty($this->_params['frequency_interval'])
-    ) {
 
+    $membershipTypes = CRM_Price_BAO_PriceSet::getMembershipTypesFromPriceSet($this->_priceSetId);
+    if (in_array($selectedMembershipTypeID, $membershipTypes['autorenew_required'])
+      || (in_array($selectedMembershipTypeID, $membershipTypes['autorenew_optional']) &&
+        !empty($this->_params['is_recur']))
+        && !empty($this->_paymentProcessor['is_recur'])
+    ) {
+      $this->_params['auto_renew'] = TRUE;
       $this->_params['is_recur'] = $this->_values['is_recur'] = 1;
-      // check if price set is not quick config
-      if (!empty($this->_params['priceSetId']) && !CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $this->_params['priceSetId'], 'is_quick_config')) {
-        list($this->_params['frequency_interval'], $this->_params['frequency_unit']) = CRM_Price_BAO_PriceSet::getRecurDetails($this->_params['priceSetId']);
-      }
-      else {
-        // FIXME: set interval and unit based on selected membership type
-        $this->_params['frequency_interval'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
-          $this->_params['selectMembership'], 'duration_interval'
-        );
-        $this->_params['frequency_unit'] = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType',
-          $this->_params['selectMembership'], 'duration_unit'
-        );
-      }
+      $membershipTypeDetails = \Civi\Api4\MembershipType::get(FALSE)
+        ->addWhere('id', '=', $selectedMembershipTypeID)
+        ->execute()
+        ->first();
+      $this->_params['frequency_interval'] = $this->_params['frequency_interval'] ?? $this->_values['fee'][$priceFieldId]['options'][$priceFieldValue]['membership_num_terms'];
+      $this->_params['frequency_unit'] = $this->_params['frequency_unit'] ?? $membershipTypeDetails['duration_unit'];
     }
   }
 
diff --git a/CRM/Price/BAO/PriceSet.php b/CRM/Price/BAO/PriceSet.php
index acc70725784d..b09f917bbced 100644
--- a/CRM/Price/BAO/PriceSet.php
+++ b/CRM/Price/BAO/PriceSet.php
@@ -1247,28 +1247,6 @@ public static function checkAutoRenewForPriceSet($priceSetId) {
     return $autoRenewOption;
   }
 
-  /**
-   * Retrieve auto renew frequency and interval.
-   *
-   * @param int $priceSetId
-   *   Price set id.
-   *
-   * @return array
-   *   associate array of frequency interval and unit
-   */
-  public static function getRecurDetails($priceSetId) {
-    $query = 'SELECT mt.duration_interval, mt.duration_unit
-            FROM civicrm_price_field_value pfv
-            INNER JOIN civicrm_membership_type mt ON pfv.membership_type_id = mt.id
-            INNER JOIN civicrm_price_field pf ON pfv.price_field_id = pf.id
-            WHERE pf.price_set_id = %1 LIMIT 1';
-
-    $params = [1 => [$priceSetId, 'Integer']];
-    $dao = CRM_Core_DAO::executeQuery($query, $params);
-    $dao->fetch();
-    return [$dao->duration_interval, $dao->duration_unit];
-  }
-
   /**
    * @return object
    */
diff --git a/tests/phpunit/CRM/Contribute/Form/Contribution/MainTest.php b/tests/phpunit/CRM/Contribute/Form/Contribution/MainTest.php
index 04ddd1f0caa9..1423778ba6ea 100644
--- a/tests/phpunit/CRM/Contribute/Form/Contribution/MainTest.php
+++ b/tests/phpunit/CRM/Contribute/Form/Contribution/MainTest.php
@@ -18,6 +18,24 @@
  */
 class CRM_Contribute_Form_Contribution_MainTest extends CiviUnitTestCase {
 
+  /**
+   * The id of the contribution page.
+   * @var int
+   */
+  private int $contributionPageId;
+
+  /**
+   * The id of the contribution page's payment processor.
+   * @var int
+   */
+  private int $paymentProcessorId;
+
+  /**
+   * The price set of the contribution page.
+   * @var int
+   */
+  private int $priceSetId;
+
   /**
    * Clean up DB.
    */
@@ -26,53 +44,84 @@ public function tearDown(): void {
     parent::tearDown();
   }
 
+  /**
+   * Given a membership type ID, return the price field value.
+   */
+  private function getPriceFieldValue($membershipTypeId) {
+    return $this->callAPISuccessGetValue('PriceFieldValue', ['membership_type_id' => $membershipTypeId, 'return' => 'id']);
+  }
+
+  /**
+   * Establish a standard list of submit params to more accurately test the submission.
+   */
+  private function getSubmitParams() {
+    return [
+      'id' => $this->contributionPageId,
+      'amount' => 80,
+      'first_name' => 'Billy',
+      'last_name' => 'Gruff',
+      'email' => 'billy@goat.gruff',
+      'payment_processor_id' => $this->paymentProcessorId,
+      'credit_card_number' => '4111111111111111',
+      'credit_card_type' => 'Visa',
+      'credit_card_exp_date' => ['M' => 9, 'Y' => 2040],
+      'cvv2' => 123,
+      'auto_renew' => 1,
+      'priceSetId' => $this->priceSetId,
+    ];
+  }
+
   /**
    * Test that the membership is set to recurring if the membership type is always autorenew.
    */
   public function testSetRecurFunction() {
     $membershipTypeID = $this->membershipTypeCreate(['auto_renew' => 2, 'minimum_fee' => 80]);
     $form = $this->getContributionForm();
-    $form->testSubmit([
-      'selectMembership' => $membershipTypeID,
-    ]);
+    $priceFieldValueId = $this->getPriceFieldValue($membershipTypeID);
+    $form->testSubmit(array_merge($this->getSubmitParams(), [
+      'price_' . $this->priceSetId => $priceFieldValueId,
+    ]));
     $this->assertEquals(1, $form->_params['is_recur']);
   }
 
   /**
-   * Test that the membership is set to recurring if the membership type is always autorenew.
+   * Test that the membership is set to recurring if the membership type is optionally autorenew and is_recur is true.
    */
   public function testSetRecurFunctionOptionalYes() {
     $membershipTypeID = $this->membershipTypeCreate(['auto_renew' => 1, 'minimum_fee' => 80]);
     $form = $this->getContributionForm();
-    $form->testSubmit([
-      'selectMembership' => $membershipTypeID,
+    $priceFieldValueId = $this->getPriceFieldValue($membershipTypeID);
+    $form->testSubmit(array_merge($this->getSubmitParams(), [
+      'price_' . $this->priceSetId => $priceFieldValueId,
       'is_recur' => 1,
-    ]);
+    ]));
     $this->assertEquals(1, $form->_params['is_recur']);
   }
 
   /**
-   * Test that the membership is set to recurring if the membership type is always autorenew.
+   * Test that the membership is not set to recurring if the membership type is optionally autorenew and is_recur is false.
    */
   public function testSetRecurFunctionOptionalNo() {
     $membershipTypeID = $this->membershipTypeCreate(['auto_renew' => 1, 'minimum_fee' => 80]);
     $form = $this->getContributionForm();
-    $form->testSubmit([
-      'selectMembership' => $membershipTypeID,
+    $priceFieldValueId = $this->getPriceFieldValue($membershipTypeID);
+    $form->testSubmit(array_merge($this->getSubmitParams(), [
+      'price_' . $this->priceSetId => $priceFieldValueId,
       'is_recur' => 0,
-    ]);
+    ]));
     $this->assertEquals(0, $form->_params['is_recur']);
   }
 
   /**
-   * Test that the membership is set to recurring if the membership type is always autorenew.
+   * Test that the membership doesn't have an "is_recur" key if the membership type can never autorenew.
    */
   public function testSetRecurFunctionNotAvailable() {
     $membershipTypeID = $this->membershipTypeCreate(['auto_renew' => 0, 'minimum_fee' => 80]);
     $form = $this->getContributionForm();
-    $form->testSubmit([
-      'selectMembership' => $membershipTypeID,
-    ]);
+    $priceFieldValueId = $this->getPriceFieldValue($membershipTypeID);
+    $form->testSubmit(array_merge($this->getSubmitParams(), [
+      'price_' . $this->priceSetId => $priceFieldValueId,
+    ]));
     $this->assertArrayNotHasKey('is_recur', $form->_params);
   }
 
@@ -82,11 +131,16 @@ public function testSetRecurFunctionNotAvailable() {
    * @return \CRM_Contribute_Form_Contribution_Main
    */
   protected function getContributionForm($params = []) {
-    $params['priceSetID'] = $params['priceSetID'] ?? $this->callAPISuccessGetValue('PriceSet', [
+    $this->priceSetId = $params['priceSetID'] ?? $this->callAPISuccessGetValue('PriceSet', [
       'name' => 'default_membership_type_amount',
       'return' => 'id',
     ]);
 
+    $paymentProcessor = $this->paymentProcessorCreate([
+      'payment_processor_type_id' => 'Dummy',
+      'is_test' => 0,
+    ]);
+
     $contributionPageParams = (array_merge($params, [
       'currency' => 'NZD',
       'goal_amount' => 6000,
@@ -95,10 +149,7 @@ protected function getContributionForm($params = []) {
       'pay_later_text' => 'Front up',
       'pay_later_receipt' => 'Ta',
       'is_email_receipt' => 1,
-      'payment_processor' => $this->paymentProcessorCreate([
-        'payment_processor_type_id' => 'Dummy',
-        'is_test' => 0,
-      ]),
+      'payment_processor' => $paymentProcessor,
       'amount_block_is_active' => 1,
     ]));
 
@@ -106,9 +157,12 @@ protected function getContributionForm($params = []) {
     $form = $this->getFormObject('CRM_Contribute_Form_Contribution_Main');
     $contributionPage = reset($this->contributionPageCreate($contributionPageParams)['values']);
     $form->set('id', $contributionPage['id']);
-    CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $contributionPage['id'], $params['priceSetID']);
+    CRM_Price_BAO_PriceSet::addTo('civicrm_contribution_page', $contributionPage['id'], $this->priceSetId);
     $form->preProcess();
     $form->buildQuickForm();
+    // Need these values to create more realistic submit params (in getSubmitParams).
+    $this->paymentProcessorId = $paymentProcessor;
+    $this->contributionPageId = (int) $contributionPage['id'];
     return $form;
   }
 
diff --git a/tests/phpunit/api/v3/ContributionPageTest.php b/tests/phpunit/api/v3/ContributionPageTest.php
index 74ca273263ca..952443e07a57 100644
--- a/tests/phpunit/api/v3/ContributionPageTest.php
+++ b/tests/phpunit/api/v3/ContributionPageTest.php
@@ -1406,12 +1406,18 @@ public function setUpMultiIntervalMembershipContributionPage(): void {
     $contributionPage = $this->callAPISuccess($this->_entity, 'create', $this->params);
     $this->_ids['contribution_page'] = $contributionPage['id'];
 
-    $this->ids['MembershipType'] = $this->membershipTypeCreate([
+    $this->ids['MembershipTypeMonth'] = $this->membershipTypeCreate([
       // force auto-renew
       'auto_renew' => 2,
       'duration_unit' => 'month',
     ]);
 
+    $this->ids['MembershipTypeYear'] = $this->membershipTypeCreate([
+      // force auto-renew
+      'auto_renew' => 2,
+      'duration_unit' => 'year',
+    ]);
+
     $priceSet = $this->callAPISuccess('PriceSet', 'create', [
       'is_quick_config' => 0,
       'extends' => 'CiviMember',
@@ -1433,18 +1439,29 @@ public function setUpMultiIntervalMembershipContributionPage(): void {
       'label' => 'CRM-21177 - Monthly',
       'amount' => 20,
       'membership_num_terms' => 1,
-      'membership_type_id' => $this->ids['MembershipType'],
+      'membership_type_id' => $this->ids['MembershipTypeMonth'],
       'price_field_id' => $this->_ids['price_field'],
       'financial_type_id' => 'Member Dues',
     ]);
     $this->_ids['price_field_value_monthly'] = $priceFieldValueMonthly['id'];
 
+    $priceFieldValue12Months = $this->callAPISuccess('price_field_value', 'create', [
+      'name' => 'CRM-21177_12_Months',
+      'label' => 'CRM-21177 - 12 Months',
+      'amount' => 200,
+      'membership_num_terms' => 12,
+      'membership_type_id' => $this->ids['MembershipTypeMonth'],
+      'price_field_id' => $this->_ids['price_field'],
+      'financial_type_id' => 'Member Dues',
+    ]);
+    $this->_ids['price_field_value_12_months'] = $priceFieldValue12Months['id'];
+
     $priceFieldValueYearly = $this->callAPISuccess('price_field_value', 'create', [
       'name' => 'CRM-21177_Yearly',
       'label' => 'CRM-21177 - Yearly',
       'amount' => 200,
-      'membership_num_terms' => 12,
-      'membership_type_id' => $this->ids['MembershipType'],
+      'membership_num_terms' => 1,
+      'membership_type_id' => $this->ids['MembershipTypeYear'],
       'price_field_id' => $this->_ids['price_field'],
       'financial_type_id' => 'Member Dues',
     ]);
@@ -1458,7 +1475,7 @@ public function setUpMultiIntervalMembershipContributionPage(): void {
       'is_required' => TRUE,
       'is_separate_payment' => FALSE,
       'is_active' => TRUE,
-      'membership_type_default' => $this->ids['MembershipType'],
+      'membership_type_default' => $this->ids['MembershipTypeMonth'],
     ]);
   }
 
@@ -1488,13 +1505,21 @@ public function testSubmitMultiIntervalMembershipContributionPage(): void {
     $submitParams['price_' . $this->_ids['price_field']] = $this->_ids['price_field_value_yearly'];
     $this->callAPISuccess('contribution_page', 'submit', $submitParams);
 
+    $submitParams['price_' . $this->_ids['price_field']] = $this->_ids['price_field_value_12_months'];
+    $this->callAPISuccess('contribution_page', 'submit', $submitParams);
+
     $contribution = $this->callAPISuccess('Contribution', 'get', [
       'contribution_page_id' => $this->_ids['contribution_page'],
       'sequential' => 1,
       'api.ContributionRecur.getsingle' => [],
     ]);
     $this->assertEquals(1, $contribution['values'][0]['api.ContributionRecur.getsingle']['frequency_interval']);
-    //$this->assertEquals(12, $contribution['values'][1]['api.ContributionRecur.getsingle']['frequency_interval']);
+    $this->assertEquals(1, $contribution['values'][1]['api.ContributionRecur.getsingle']['frequency_interval']);
+    $this->assertEquals(12, $contribution['values'][2]['api.ContributionRecur.getsingle']['frequency_interval']);
+
+    $this->assertEquals('month', $contribution['values'][0]['api.ContributionRecur.getsingle']['frequency_unit']);
+    $this->assertEquals('year', $contribution['values'][1]['api.ContributionRecur.getsingle']['frequency_unit']);
+    $this->assertEquals('month', $contribution['values'][2]['api.ContributionRecur.getsingle']['frequency_unit']);
   }
 
   /**