From fb5ee2a32538f8e07bc0ce3451a40e9f397339c9 Mon Sep 17 00:00:00 2001
From: Matthew Wire <mjw@mjwconsult.co.uk>
Date: Fri, 4 Nov 2022 21:08:18 +0000
Subject: [PATCH] Update email address at Stripe if contact has a Stripe
 Customer

---
 CRM/Stripe/BAO/StripeCustomer.php             |  6 +++++-
 .../Action/StripeCustomer/UpdateStripe.php    | 21 ++++++++++++-------
 docs/faqs.md                                  |  6 +++++-
 stripe.php                                    | 14 +++++++++++--
 4 files changed, 36 insertions(+), 11 deletions(-)

diff --git a/CRM/Stripe/BAO/StripeCustomer.php b/CRM/Stripe/BAO/StripeCustomer.php
index 3abb14ae..76c34679 100644
--- a/CRM/Stripe/BAO/StripeCustomer.php
+++ b/CRM/Stripe/BAO/StripeCustomer.php
@@ -34,13 +34,17 @@ class CRM_Stripe_BAO_StripeCustomer extends CRM_Stripe_DAO_StripeCustomer {
       // Stripe does not include the Customer Name when exporting payments, just the customer
       // description, so we stick the name in the description.
       'description' => $description ?? $contactDisplayName . ' (CiviCRM)',
-      'email' => $email ?? '',
       'metadata' => [
         'CiviCRM Contact ID' => $contactID,
         'CiviCRM URL' => CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$contactID}", TRUE, NULL, FALSE, FALSE, TRUE),
         'CiviCRM Version' => CRM_Utils_System::version() . ' ' . $extVersion,
       ],
     ];
+    $email = $email ?? $contactDisplayName['email_primary.email'] ?? $contactDisplayName['email_billing.email'] ?? NULL;
+    if ($email) {
+      $stripeCustomerParams['email'] = $email;
+    }
+
     // This is used for new subscriptions/invoices as the default payment method
     if (!empty($invoiceSettings)) {
       $stripeCustomerParams['invoice_settings'] = $invoiceSettings;
diff --git a/Civi/Api4/Action/StripeCustomer/UpdateStripe.php b/Civi/Api4/Action/StripeCustomer/UpdateStripe.php
index b59a073e..d223b1f2 100644
--- a/Civi/Api4/Action/StripeCustomer/UpdateStripe.php
+++ b/Civi/Api4/Action/StripeCustomer/UpdateStripe.php
@@ -69,20 +69,27 @@ class UpdateStripe extends \Civi\Api4\Generic\AbstractAction {
     if (empty($this->customerID) && empty($this->contactID)) {
       throw new \CRM_Core_Exception('Missing customerID or contactID');
     }
-    if (empty($this->paymentProcessorID)) {
-      throw new \CRM_Core_Exception('Missing paymentProcessorID');
-    }
     if (empty($this->customerID) && !empty($this->contactID)) {
-      $this->customerID = \Civi\Api4\StripeCustomer::get(FALSE)
+      $existingStripeCustomer = \Civi\Api4\StripeCustomer::get(FALSE)
         ->addWhere('contact_id', '=', $this->contactID)
         ->execute()
-        ->first()['customer_id'];
+        ->first();
+      $this->customerID = $existingStripeCustomer['customer_id'];
     }
     if (empty($this->contactID) && !empty($this->customerID)) {
-      $this->contactID = \Civi\Api4\StripeCustomer::get(FALSE)
+      $existingStripeCustomer = \Civi\Api4\StripeCustomer::get(FALSE)
         ->addWhere('customer_id', '=', $this->customerID)
         ->execute()
-        ->first()['contact_id'];
+        ->first();
+      $this->contactID = $existingStripeCustomer['contact_id'];
+    }
+    if (empty($this->paymentProcessorID)) {
+      if (!empty($existingStripeCustomer)) {
+        $this->paymentProcessorID = $existingStripeCustomer['processor_id'];
+      }
+      else {
+        throw new \CRM_Core_Exception('Missing paymentProcessorID');
+      }
     }
 
     /** @var \CRM_Core_Payment_Stripe $paymentProcessor */
diff --git a/docs/faqs.md b/docs/faqs.md
index f6a53022..44270f0c 100644
--- a/docs/faqs.md
+++ b/docs/faqs.md
@@ -61,7 +61,11 @@ When we create a contribution in CiviCRM (Stripe Invoice/Charge) we add some met
 
 A new Stripe [**Customer**](https://stripe.com/docs/api/customers) is created the first time a contribution is created by them in CiviCRM.
 
-Each time a new contribution is created the Stripe Customer metadata is updated.
+### When is Stripe metadata updated?
+* Each time a new payment/contribution is created.
+* If the primary contact email address is updated.
+
+### What metadata is sent to Stripe?
 
 The following metadata is created for a Stripe Customer:
 
diff --git a/stripe.php b/stripe.php
index 6f6d8692..d19da17e 100644
--- a/stripe.php
+++ b/stripe.php
@@ -271,9 +271,19 @@ function stripe_civicrm_post($op, $objectName, $objectId, &$objectRef) {
         }
       }
     case 'Email':
-      if ($op === 'edit') {
+      if (in_array($op, ['create', 'edit']) && ((int)$objectRef->is_primary === 1)) {
         try {
-          CRM_Stripe_BAO_StripeCustomer::updateMetadataForContact($objectRef->contact_id);
+          // Does the contact have a Stripe customer record?
+          $stripeCustomers = \Civi\Api4\StripeCustomer::get(FALSE)
+            ->addWhere('contact_id', '=', $objectRef->contact_id)
+            ->execute();
+          // Update the email address at Stripe for each customer associated with this contact
+          foreach ($stripeCustomers as $stripeCustomer) {
+            \Civi\Api4\StripeCustomer::updateStripe(FALSE)
+              ->setCustomerID($stripeCustomer['customer_id'])
+              ->setEmail($objectRef->email)
+              ->execute();
+          }
         }
         catch (Exception $e) {
           \Civi::log(E::SHORT_NAME)->error('Stripe Contact update email failed: ' . $e->getMessage());
-- 
GitLab