From 39379028d50efa99021fa30ef751378ce419940f Mon Sep 17 00:00:00 2001 From: "Paolo Cunti @codencode" Date: Sat, 29 Aug 2026 11:12:49 +0200 Subject: [PATCH] Fix newsletter verification and confirmation during account registration Handle newsletter verification and confirmation emails when a customer subscribes during account registration. * Reuse the existing subscription confirmation flow for voucher and confirmation emails. * Handle `NW_VERIFICATION_EMAIL` during account creation and keep the customer unsubscribed until verification is completed. * Preserve already verified guest subscriptions without requesting a second verification. * Remove the guest subscription record when it is transferred to the customer account. * Clarify in the module configuration that verification and confirmation email settings also apply during account registration. --- ps_emailsubscription.php | 94 +++++++++++++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 11 deletions(-) diff --git a/ps_emailsubscription.php b/ps_emailsubscription.php index 2b92863..c0604a2 100644 --- a/ps_emailsubscription.php +++ b/ps_emailsubscription.php @@ -774,7 +774,7 @@ public function confirmEmail($token) * * @param string $email Email where to send the confirmation * - * @note the email has been verified and might not yet been registered. Called by AuthController::processCustomerNewsletter + * @note The email has already been verified or verification is not required. */ public function confirmSubscription($email) { @@ -953,7 +953,50 @@ public function hookActionFrontControllerSetMedia() } /** - * Deletes duplicates email in newsletter table. + * Returns the status of an existing guest newsletter subscription. + * + * @param string $email + * @param int $idShop + * + * @return bool|null True when active, false when pending verification, null when not found + */ + private function getGuestNewsletterSubscriptionStatus($email, $idShop) + { + $status = Db::getInstance()->getValue( + 'SELECT `active` + FROM `' . _DB_PREFIX_ . 'emailsubscription` + WHERE `id_shop` = ' . (int) $idShop . ' + AND `email` = \'' . pSQL($email) . '\'' + ); + + if ($status === false) { + return null; + } + + return (bool) $status; + } + + /** + * Marks a customer newsletter subscription as pending verification. + * + * @param int $idCustomer + * @param int $idShop + * + * @return bool + */ + private function setCustomerNewsletterPending($idCustomer, $idShop) + { + return Db::getInstance()->execute( + 'UPDATE `' . _DB_PREFIX_ . 'customer` + SET `newsletter` = 0 + WHERE `id_customer` = ' . (int) $idCustomer . ' + AND `id_shop` = ' . (int) $idShop + ); + } + + /** + * Handles newsletter registration when a customer account is created and removes + * any matching guest subscription to prevent duplicates. * * @param array $params * @@ -961,28 +1004,55 @@ public function hookActionFrontControllerSetMedia() */ public function hookActionCustomerAccountAdd($params) { - //if e-mail of the created user address has already been added to the newsletter through the ps_emailsubscription module, - //we delete it from ps_emailsubscription table to prevent duplicates if (empty($params['newCustomer'])) { return false; } - $id_shop = $params['newCustomer']->id_shop; - $email = $params['newCustomer']->email; + $customer = $params['newCustomer']; + $idShop = (int) $customer->id_shop; + $email = $customer->email; if (!Validate::isEmail($email)) { return false; } - if ($params['newCustomer']->newsletter) { - if ($code = Configuration::get('NW_VOUCHER_CODE')) { - $this->sendVoucher($email, $code); + // Keep an existing guest subscription untouched when the newly created account + // has not opted in to the newsletter. + if (!$customer->newsletter) { + return true; + } + + $guestSubscriptionStatus = $this->getGuestNewsletterSubscriptionStatus($email, $idShop); + + if (Configuration::get('NW_VERIFICATION_EMAIL') && $guestSubscriptionStatus !== true) { + // The customer is already stored with newsletter = 1 at this point. Set it back + // to 0 until the verification link is confirmed, so the existing token flow can + // activate the subscription through confirmEmail(). + if (!$this->setCustomerNewsletterPending((int) $customer->id, $idShop)) { + return false; + } + + if (!$token = $this->getToken($email, self::CUSTOMER_NOT_REGISTERED)) { + return false; } - return Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'emailsubscription` WHERE id_shop = ' . (int) $id_shop . ' AND email = "' . pSQL($email) . '"'); + $this->sendVerificationEmail($email, $token); + } elseif ($guestSubscriptionStatus !== true) { + // No verification is required: send the voucher and confirmation email + // according to the module configuration. + $this->confirmSubscription($email); + } elseif ($code = Configuration::get('NW_VOUCHER_CODE')) { + // Preserve the existing behavior for an already active guest subscription. + $this->sendVoucher($email, $code); } - return true; + // If the customer's email was already added to the newsletter through + // the ps_emailsubscription module, remove it to prevent duplicates. + return Db::getInstance()->execute( + 'DELETE FROM `' . _DB_PREFIX_ . 'emailsubscription` + WHERE `id_shop` = ' . $idShop . ' + AND `email` = \'' . pSQL($email) . '\'' + ); } public function hookActionObjectCustomerUpdateBefore($params) @@ -1067,6 +1137,7 @@ public function renderForm() 'type' => 'switch', 'label' => $this->trans('Would you like to send a verification email after subscription?', [], 'Modules.Emailsubscription.Admin'), 'name' => 'NW_VERIFICATION_EMAIL', + 'desc' => $this->trans('This setting also applies when a customer subscribes to the newsletter during account registration.', [], 'Modules.Emailsubscription.Admin'), 'values' => [ [ 'id' => 'active_on', @@ -1084,6 +1155,7 @@ public function renderForm() 'type' => 'switch', 'label' => $this->trans('Would you like to send a confirmation email after subscription?', [], 'Modules.Emailsubscription.Admin'), 'name' => 'NW_CONFIRMATION_EMAIL', + 'desc' => $this->trans('This setting also applies when a customer subscribes to the newsletter during account registration.', [], 'Modules.Emailsubscription.Admin'), 'values' => [ [ 'id' => 'active_on',