This is a reported bug with Magento. It appears to have been around since v2.3.5
https://github.com/magento/magento2/issues/29248
Javascript expects a unique ID to be passed to know which payment method on the page to expand (think multiple vaulted cards for the one customer).
But that is passed to PHP despite PHP expecting a more generic payment method code to know which payment validator logic to load.
Until this is patched via core code, this is a plugin that I wrote that I'm using.
File: app/code/Prominc/Quote/Plugin/Model/Quote/Payment.php
<?php
namespace Prominc\Quote\Plugin\Model\Quote;
class Payment
{
public function beforeImportData(
\Magento\Quote\Model\Quote\Payment $subject,
$data
) {
$vaultMethod = preg_replace( '/^([a-z_]+vault)_\d+$/i', '$1', $data['method'] );
if( !is_null( $vaultMethod ) && $vaultMethod !== $data['method'] ) {
$data['method'] = $vaultMethod;
}
return [$data];
}
}
File: app/code/Prominc/Quote/registration.php
<?php
use \Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Prominc_Quote',
__DIR__
);
File: app/code/Prominc/Quote/etc/module.xml
<?xml version="1.0" encoding="utf-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Prominc_Quote" setup_version="0.0.1">
<sequence>
<module name="Magento_Quote"/>
</sequence>
</module>
</config>
File: app/code/Prominc/Quote/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Quote\Model\Quote\Payment">
<plugin disabled="false" name="Prominc_Quote_Plugin_Model_Quote_Payment" sortOrder="10" type="Prominc\Quote\Plugin\Model\Quote\Payment"/>
</type>
</config>
Don't forget to run a setup:di:compile
after adding this module/plugin.
braintree_cc_vault_xxxx
wherexxxx
is an ID (for the vaulted card??). I'm confused that others haven't run into this as well... still thinking through how I best solve this though.