Problem/Motivation
The problem is that when Stripe is trying to create a payment method Drupal checks in DB (in user__commerce_remote_id
table) if the user has a customer_id
from Stripe and when it is found in DB it tries to attach that customer to a payment method, but at that point, the customer_id
does not exist in Stripe (it was deleted for some reason) and the remaining execution of script was stopped.
/modules/contrib/commerce_stripe/src/Plugin/Commerce/PaymentGateway/Stripe.php
protected function doCreatePaymentMethod(PaymentMethodInterface $payment_method, array $payment_details) {
$stripe_payment_method_id = $payment_details['stripe_payment_method_id'];
$owner = $payment_method->getOwner();
$customer_id = NULL;
if ($owner && $owner->isAuthenticated()) {
$customer_id = $this->getRemoteCustomerId($owner); // Here it checks for commerce_remote_id, see the 'getRemoteCustomerId' function below.
}
try {
$stripe_payment_method = PaymentMethod::retrieve($stripe_payment_method_id);
if ($customer_id) {
$stripe_payment_method->attach(['customer' => $customer_id]);
$email = $owner->getEmail();
}
// If the user is authenticated, created a Stripe customer to attach the
// payment method to.
elseif ($owner && $owner->isAuthenticated()) {
$email = $owner->getEmail();
$customer = Customer::create([
'email' => $email,
'description' => $this->t('Customer for :mail', [':mail' => $email]),
'payment_method' => $stripe_payment_method_id,
]);
$customer_id = $customer->id;
$this->setRemoteCustomerId($owner, $customer_id);
$owner->save();
}
else {
$email = NULL;
}
if ($customer_id && $email) {
$payment_method_data = [
'email' => $email,
];
if ($billing_profile = $payment_method->getBillingProfile()) {
$billing_address = $billing_profile->get('address')->first()->toArray();
$payment_method_data['address'] = [
'city' => $billing_address['locality'],
'country' => $billing_address['country_code'],
'line1' => $billing_address['address_line1'],
'line2' => $billing_address['address_line2'],
'postal_code' => $billing_address['postal_code'],
'state' => $billing_address['administrative_area'],
];
$payment_method_data['name'] = $billing_address['given_name'] . ' ' . $billing_address['family_name'];
}
PaymentMethod::update($stripe_payment_method_id, ['billing_details' => $payment_method_data]);
}
}
catch (ApiErrorException $e) {
ErrorHelper::handleException($e);
}
return $stripe_payment_method->card;
}
/modules/contrib/commerce/modules/payment/src/Plugin/Commerce/PaymentGateway/PaymentGatewayBase.php
public function getRemoteCustomerId(UserInterface $account) {
$remote_id = NULL;
if ($account->isAuthenticated()) {
$provider = $this->parentEntity->id() . '|' . $this->getMode();
/** @var \Drupal\commerce\Plugin\Field\FieldType\RemoteIdFieldItemListInterface $remote_ids */
$remote_ids = $account->get('commerce_remote_id');
$remote_id = $remote_ids->getByProvider($provider);
// Gateways used to key customer IDs by module name, migrate that data.
if (!$remote_id) {
$remote_id = $remote_ids->getByProvider($this->pluginDefinition['provider']);
if ($remote_id) {
$remote_ids->setByProvider($this->pluginDefinition['provider'], NULL);
$remote_ids->setByProvider($provider, $remote_id);
$account->save();
}
}
}
return $remote_id;
}
Steps to reproduce
- To have wrong Stripe customer id in db table
user__commerce_remote_id
on column commerce_remote_id_remote_id
- Try to make payment
- Observe result
Proposed solution
The solution will be to check if the customer exists in Stripe, if not create a new customer and then continue further.