This is likely due to syncing of databases, switching Braintree accounts, or purging the Sandbox. In essence the site
believes that a user should be in the vault, but it cannot find them. Best solution for this is detailed below.

It happens because we run generate $token = Braintree_ClientToken::generate($arguments); , which could fail if it has already been passed a customerId which (for one reason or another) is not valid. Therefore a check beforehand would be ideal to ensure the ID exists before using it. If it does not exist, then we reset on the Drupal end, and move on.

diff --git a/web/sites/all/modules/contrib/commerce_braintree/modules/commerce_braintree_dropin/commerce_braintree_dropin.module b/web/sites/all/modules/contrib/commerce_braintree/modules/commerce_braintree_dropin/commerce_braintree_dropin.module
index d54b75c..bfbaf53 100644
--- a/web/sites/all/modules/contrib/commerce_braintree/modules/commerce_braintree_dropin/commerce_braintree_dropin.module
+++ b/web/sites/all/modules/contrib/commerce_braintree/modules/commerce_braintree_dropin/commerce_braintree_dropin.module
@@ -253,6 +253,23 @@ function commerce_braintree_dropin_submit_form_elements($payment_method, $order)
 
   // Initialize Braintree and create a token.
   commerce_braintree_initialize($payment_method);
+
+  // If this token cannot be found in the Braintree system AND Braintree has
+  // initialized successfully, then we need to strip it from the database (as
+  // we have liked switched Braintree accounts, or purged Sandbox data)
+  try {
+    // Try to find the user.  if this fails, we get an Exception back
+    $token_test = Braintree_Customer::find($arguments['customerId']);
+  } catch (Exception $e) {
+    if (!empty($user)) {
+      // Unset the variables, and remove permanently from this user (as we will
+      // now be saving the new customerId instead)
+      unset($arguments['customerId']);
+      unset($user->data['braintree_vault']);
+      user_save($user);
+    }
+  }
+
   $token = Braintree_ClientToken::generate($arguments);
 
   // The custom token is required to generate the Drop-in payment form.

Above is the code I tried, I will attach a patch to a comment in a second.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

retrodans created an issue. See original summary.

retrodans’s picture

Attached is the patch mentioned previously

andyg5000’s picture

This is a good find. What do you think about storing the braintree information with an account identifier from the payment method instead?

For example: $user->['braintree_vault'][MERCHANT_ACCOUNT_ID]

Do you think that's necessary? If not, we can roll with your approach.

retrodans’s picture

Okay, found a bug with the code, in that when there was a mistake with payment card details, it was completely bombing out, so have altered the patch slightly to handle this better (mainly logging to watchdog too so we can observe how often this happens too in the future. It might be worth holding on rolling it in for a couple of weeks just whilst I test this out myself on our site. But signs so far are good.

Also, with regards you suggestion, do you have a diff, just unsure where you are thinking of putting that line, and what it would gain us.

Thanks,
Dan

jruel’s picture

Updated the patch to apply the same logic to the Hosted Fields module as well.

jruel’s picture

cleaned up/updated patch.