├── .gitattributes ├── .github ├── FUNDING.yml ├── issue_template.md └── workflows │ └── php.yml ├── Block └── Adminhtml │ ├── System │ └── Config │ │ ├── Form │ │ ├── Composer │ │ │ └── Version.php │ │ ├── Field │ │ │ └── Link.php │ │ └── Module │ │ │ └── Version.php │ │ └── ValidateConfigButton.php │ └── ValidateConfig.php ├── Controller └── Adminhtml │ └── Validateconfig │ └── Index.php ├── Helper └── Data.php ├── Mail └── Smtp.php ├── Model ├── Config │ └── Source │ │ └── Authtype.php ├── Email.php └── Store.php ├── Plugin └── Mail │ └── TransportPlugin.php ├── README.md ├── composer.json ├── etc ├── acl.xml ├── adminhtml │ ├── routes.xml │ └── system.xml ├── config.xml ├── di.xml ├── email_templates.xml └── module.xml ├── i18n └── en_US.csv ├── registration.php └── view └── adminhtml ├── email ├── magento_email_test.html └── zend_email_test.html ├── layout └── magepalsmtp_validateconfig_index.xml ├── requirejs-config.js ├── templates ├── system │ └── config │ │ └── validateConfigButton.phtml └── validateConfig │ └── result.phtml └── web ├── css └── styles.less └── js └── validate-config.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.phtml linguist-language=PHP 2 | *.html linguist-language=PHP 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | #github: [srenon] 4 | custom: ['https://www.magepal.com/custom-smtp.html?utm_source=smtp&utm_medium=github%20sponsor'] 5 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #### Magento version #: 10 | 11 | #### Edition (EE, CE, OS, etc): 12 | 13 | #### Expected behavior: 14 | 15 | #### Actual behavior: 16 | 17 | #### Steps to reproduce: 18 | 19 | #### Preconditions 20 | 21 | 22 | 23 | 24 | 40 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHPCS 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@master 8 | - name: PHPCS 9 | run: docker run --rm -v $PWD:/code:ro domw/phpcs phpcs --colors --standard=Magento2 --report=full,summary,gitblame --extensions=php,phtml ./ 10 | - name: compatibility 11 | run: docker run --rm -v $PWD:/code:ro domw/phpcompatibility phpcs --standard=PHPCompatibility --runtime-set testVersion 5.6-7.4 --colors --warning-severity=0 --report=full,summary --extensions=php,phtml ./ 12 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/Composer/Version.php: -------------------------------------------------------------------------------- 1 | deploymentConfig = $deploymentConfig; 52 | $this->componentRegistrar = $componentRegistrar; 53 | $this->readFactory = $readFactory; 54 | parent::__construct($context, $data); 55 | } 56 | 57 | /** 58 | * Render button 59 | * 60 | * @param AbstractElement $element 61 | * @return string 62 | */ 63 | public function render(AbstractElement $element) 64 | { 65 | // Remove scope label 66 | $element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue(); 67 | return parent::render($element); 68 | } 69 | 70 | /** 71 | * Return element html 72 | * 73 | * @param AbstractElement $element 74 | * @return string 75 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 76 | */ 77 | protected function _getElementHtml(AbstractElement $element) 78 | { 79 | return 'v' . $this->getVersion(); 80 | } 81 | 82 | /** 83 | * Get Module version number 84 | * 85 | * @return string 86 | */ 87 | public function getVersion() 88 | { 89 | return $this->getComposerVersion($this->getModuleName()); 90 | } 91 | 92 | /** 93 | * Get module composer version 94 | * 95 | * @param $moduleName 96 | * @return string 97 | */ 98 | public function getComposerVersion($moduleName) 99 | { 100 | $path = $this->componentRegistrar->getPath( 101 | ComponentRegistrar::MODULE, 102 | $moduleName 103 | ); 104 | 105 | try { 106 | $directoryRead = $this->readFactory->create($path); 107 | $composerJsonData = $directoryRead->readFile('composer.json'); 108 | 109 | if ($composerJsonData) { 110 | $data = json_decode($composerJsonData); 111 | return !empty($data->version) ? $data->version : __('Unknown'); 112 | } 113 | } catch (Exception $e) { 114 | return 'Unknown'; 115 | } 116 | 117 | return 'Unknown'; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/Field/Link.php: -------------------------------------------------------------------------------- 1 | unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue(); 27 | return parent::render($element); 28 | } 29 | 30 | /** 31 | * Return element html 32 | * 33 | * @param AbstractElement $element 34 | * @return string 35 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 36 | */ 37 | protected function _getElementHtml(AbstractElement $element) 38 | { 39 | return sprintf( 40 | '%s', 41 | rtrim($this->_urlBuilder->getUrl('adminhtml/system_config/edit/section/system'), '/'), 42 | __('Stores > Configuration > Advanced > System > SMTP Configuration and Settings') 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/Form/Module/Version.php: -------------------------------------------------------------------------------- 1 | _moduleList = $moduleList; 36 | } 37 | 38 | /** 39 | * Render button 40 | * 41 | * @param AbstractElement $element 42 | * @return string 43 | * @throws LocalizedException 44 | */ 45 | public function render(AbstractElement $element) 46 | { 47 | // Remove scope label 48 | $element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue(); 49 | return parent::render($element); 50 | } 51 | 52 | /** 53 | * Return element html 54 | * 55 | * @param AbstractElement $element 56 | * @return string 57 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 58 | */ 59 | protected function _getElementHtml(AbstractElement $element) 60 | { 61 | return 'v' . $this->getVersion(); 62 | } 63 | 64 | /** 65 | * Get Module version number 66 | * 67 | * @return string 68 | */ 69 | public function getVersion() 70 | { 71 | $moduleInfo = $this->_moduleList->getOne($this->getModuleName()); 72 | return $moduleInfo['setup_version']; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Block/Adminhtml/System/Config/ValidateConfigButton.php: -------------------------------------------------------------------------------- 1 | _urlBuilder = $context->getUrlBuilder(); 34 | parent::__construct($context, $data); 35 | } 36 | 37 | /** 38 | * Set template 39 | * 40 | * @return void 41 | */ 42 | protected function _construct() 43 | { 44 | parent::_construct(); 45 | $this->setTemplate('MagePal_GmailSmtpApp::system/config/validateConfigButton.phtml'); 46 | } 47 | 48 | /** 49 | * Generate button html 50 | * 51 | * @return string 52 | * @throws LocalizedException 53 | */ 54 | public function getButtonHtml() 55 | { 56 | $button = $this->getLayout()->createBlock( 57 | Button::class 58 | )->setData( 59 | [ 60 | 'id' => 'gmailsmtpapp_debug_result_button', 61 | 'label' => __('Send Test Email') 62 | ] 63 | ); 64 | 65 | return $button->toHtml(); 66 | } 67 | 68 | public function getAdminUrl() 69 | { 70 | return $this->_urlBuilder->getUrl( 71 | 'magepalsmtp/validateconfig', 72 | ['store' => $this->_request->getParam('store')] 73 | ); 74 | } 75 | 76 | /** 77 | * Render button 78 | * 79 | * @param AbstractElement $element 80 | * @return string 81 | */ 82 | public function render(AbstractElement $element) 83 | { 84 | // Remove scope label 85 | $element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue(); 86 | return parent::render($element); 87 | } 88 | 89 | /** 90 | * Return element html 91 | * 92 | * @param AbstractElement $element 93 | * @return string 94 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 95 | */ 96 | protected function _getElementHtml(AbstractElement $element) 97 | { 98 | return $this->_toHtml(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Block/Adminhtml/ValidateConfig.php: -------------------------------------------------------------------------------- 1 | '', 62 | 'name' => '', 63 | 'auth' => '', 64 | 'ssl' => '', 65 | 'smtphost' => '', 66 | 'smtpport' => '', 67 | 'username' => '', 68 | 'password' => '', 69 | 'set_reply_to' => '', 70 | 'set_from' => '', 71 | 'set_return_path' => '', 72 | 'return_path_email' => '', 73 | 'custom_from_email' => '', 74 | 'email' => '', 75 | 'from_email' => '' 76 | ]; 77 | 78 | /** 79 | * @var EmailAddress 80 | */ 81 | protected $emailAddressValidator; 82 | 83 | /** 84 | * EmailTest constructor. 85 | * @param Context $context 86 | * @param Data $dataHelper 87 | * @param Email $email 88 | * @param EmailAddress $emailAddressValidator 89 | * @param array $data 90 | */ 91 | public function __construct( 92 | Context $context, 93 | Data $dataHelper, 94 | Email $email, 95 | EmailAddress $emailAddressValidator, 96 | array $data = [] 97 | ) { 98 | parent::__construct($context, $data); 99 | $this->_dataHelper = $dataHelper; 100 | $this->_email = $email; 101 | $this->emailAddressValidator = $emailAddressValidator; 102 | 103 | $this->init(); 104 | } 105 | 106 | /** 107 | * @param $id 108 | * @return $this 109 | */ 110 | public function setStoreId($id) 111 | { 112 | $this->storeId = $id; 113 | return $this; 114 | } 115 | 116 | /** 117 | * @return string|null 118 | */ 119 | public function getStoreId() 120 | { 121 | return $this->storeId; 122 | } 123 | 124 | /** 125 | * @param null $key 126 | * @return array|mixed|string 127 | */ 128 | public function getConfig($key = null) 129 | { 130 | if ($key === null) { 131 | return $this->configFields; 132 | } elseif (!array_key_exists($key, $this->configFields)) { 133 | return ''; 134 | } else { 135 | return $this->configFields[$key]; 136 | } 137 | } 138 | 139 | /** 140 | * @param string $key 141 | * @param string $value 142 | * @return array|mixed|string 143 | */ 144 | public function setConfig($key, $value = null) 145 | { 146 | if (array_key_exists($key, $this->configFields)) { 147 | $this->configFields[$key] = $value; 148 | } 149 | 150 | return $this; 151 | } 152 | 153 | /** 154 | * Load default config if config is lock using "bin/magento config:set" 155 | */ 156 | public function loadDefaultConfig() 157 | { 158 | $request = $this->getRequest(); 159 | $formPostArray = (array) $request->getPost(); 160 | 161 | if ($request->getParam('website', false)) { 162 | $scopeCode = $request->getParam('website'); 163 | $scopeType = ScopeInterface::SCOPE_WEBSITE; 164 | } elseif ($request->getParam('store', false)) { 165 | $scopeCode = $request->getParam('store'); 166 | $scopeType = ScopeInterface::SCOPE_STORE; 167 | } else { 168 | $scopeCode = null; 169 | $scopeType = ScopeInterface::SCOPE_STORE; 170 | } 171 | 172 | $fields = array_keys($this->configFields); 173 | foreach ($fields as $field) { 174 | if (!array_key_exists($field, $formPostArray)) { 175 | $this->setConfig($field, $this->_dataHelper->getConfigValue($field, $scopeType, $scopeCode)); 176 | } else { 177 | $this->setConfig($field, $request->getPost($field)); 178 | } 179 | } 180 | 181 | //if password mask (6 stars) 182 | if ($this->getConfig('password') === '******') { 183 | $password = $this->_dataHelper->getConfigPassword($scopeType, $scopeCode); 184 | $this->setConfig('password', $password); 185 | } 186 | 187 | return $this; 188 | } 189 | 190 | /** 191 | * @return void 192 | */ 193 | protected function init() 194 | { 195 | $request = $this->getRequest(); 196 | 197 | $this->loadDefaultConfig(); 198 | 199 | $this->toAddress = $this->getConfig('email') ? $this->getConfig('email') : $this->getConfig('username'); 200 | 201 | $this->fromAddress = trim((string) $this->getConfig('from_email')); 202 | 203 | if (!$this->emailAddressValidator->isValid($this->fromAddress)) { 204 | $this->fromAddress = $this->toAddress; 205 | } 206 | 207 | $this->hash = time() . '.' . rand(300000, 900000); 208 | } 209 | 210 | /** 211 | * @return array 212 | */ 213 | public function verify() 214 | { 215 | $settings = [ 216 | 'server_email' => 'validateServerEmailSetting', 217 | 'magento_email_setting' => 'validateMagentoEmailStatus', 218 | 'module_email_setting' => 'validateModuleEmailStatus', 219 | 'magento_email' => 'validateMagentoEmailSetting' 220 | ]; 221 | 222 | $result = $this->error(); 223 | $hasError = false; 224 | 225 | foreach ($settings as $functionName) { 226 | $result = $this->$functionName(); 227 | 228 | if (array_key_exists('has_error', $result)) { 229 | if ($result['has_error'] === true) { 230 | $hasError = true; 231 | break; 232 | } 233 | } else { 234 | $hasError = true; 235 | $result = $this->error(true, 'MP103 - Unknown Error'); 236 | break; 237 | } 238 | } 239 | 240 | if (!$hasError) { 241 | $result['msg'] = __('Please check your email') . ' ' . $this->toAddress . ' ' . 242 | __('and flush your Magento cache'); 243 | } 244 | 245 | return [$result]; 246 | } 247 | 248 | /** 249 | * @return array 250 | * @throws \Magento\Framework\Exception\NoSuchEntityException 251 | */ 252 | protected function validateServerEmailSetting() 253 | { 254 | $request = $this->getRequest(); 255 | 256 | $username = $this->getConfig('username'); 257 | $password = $this->getConfig('password'); 258 | 259 | $auth = strtolower($this->getConfig('auth')); 260 | 261 | //if default view 262 | //see https://github.com/magento/magento2/issues/3019 263 | if (!$request->getParam('store', false)) { 264 | if ($auth != 'none' && (empty($username) || empty($password))) { 265 | return $this->error( 266 | true, 267 | __('Please enter a valid username/password') 268 | ); 269 | } 270 | } 271 | 272 | $name = 'Test from MagePal SMTP'; 273 | $from = trim((string) $this->getConfig('from_email')); 274 | $from = filter_var($from, FILTER_VALIDATE_EMAIL) ? $from : $username; 275 | $this->fromAddress = filter_var($username, FILTER_VALIDATE_EMAIL) ? $username : $from; 276 | $htmlBody = $this->_email->setTemplateVars(['hash' => $this->hash])->getEmailBody(); 277 | 278 | $message = new SymfonyEmail(); 279 | $message->to(new Address($this->toAddress, 'MagePal SMTP')) 280 | ->from(new Address($this->fromAddress, $name)) 281 | ->subject('Hello from MagePal SMTP (1 of 2)') 282 | ->html($htmlBody); 283 | 284 | $result = $this->error(); 285 | 286 | try { 287 | $transport = $this->getMailTransportSmtp(); 288 | $transport->send($message); 289 | } catch (Exception $e) { 290 | $result = $this->error(true, __($e->getMessage())); 291 | } 292 | 293 | return $result; 294 | } 295 | 296 | public function getMailTransportSmtp() 297 | { 298 | $username = $this->getConfig('username'); 299 | $password = $this->getConfig('password'); 300 | $auth = strtolower($this->getConfig('auth')); 301 | $name = $this->getConfig('name'); 302 | $host = $this->getConfig('smtphost'); 303 | $port = $this->getConfig('smtpport'); 304 | $ssl = $this->getConfig('ssl'); 305 | 306 | $tls = ($auth !== 'none') && ($ssl !== 'starttls'); 307 | 308 | /** @var SymfonyTransportInterface $transport */ 309 | $transport = new EsmtpTransport($host, $port, $tls); 310 | 311 | if ($auth != 'none') { 312 | if ($username) { 313 | $transport->setUsername($username); 314 | } 315 | 316 | if ($password) { 317 | $transport->setPassword($password); 318 | } 319 | } 320 | 321 | switch ($auth) { 322 | case 'plain': 323 | $transport->setAuthenticators([new PlainAuthenticator()]); 324 | break; 325 | case 'login': 326 | $transport->setAuthenticators([new LoginAuthenticator()]); 327 | break; 328 | case 'none': 329 | break; 330 | default: 331 | throw new \InvalidArgumentException('Invalid authentication type: ' . $auth); 332 | } 333 | 334 | return $transport; 335 | } 336 | 337 | /** 338 | * @return array 339 | */ 340 | protected function validateMagentoEmailSetting() 341 | { 342 | $result = $this->error(); 343 | 344 | $this->_dataHelper->setTestMode(true); 345 | $this->_dataHelper->setStoreId($this->getStoreId()); 346 | $this->_dataHelper->setTestConfig($this->getConfig()); 347 | 348 | try { 349 | $this->_email 350 | ->setTemplateVars(['hash' => $this->hash]) 351 | ->send( 352 | ['email' => $this->fromAddress, 'name' => 'Test from MagePal SMTP'], 353 | ['email' => $this->toAddress, 'name' => "MagePal SMTP"] 354 | ); 355 | } catch (Exception $e) { 356 | $result = $this->error(true, __($e->getMessage())); 357 | } 358 | 359 | $this->_dataHelper->setTestMode(false); 360 | 361 | return $result; 362 | } 363 | 364 | /** 365 | * @return array 366 | */ 367 | public function validateMagentoEmailStatus() 368 | { 369 | $result = $this->error(); 370 | // system_smtp_disable 371 | 372 | if ($this->_dataHelper->getScopeConfigValue('system/smtp/disable')) { 373 | $result = $this->error( 374 | true, 375 | __('"Disable Email Communications" is set is "Yes", please set to "NO" in "Mail Sending Setting"') 376 | ); 377 | } 378 | 379 | return $result; 380 | } 381 | 382 | /** 383 | * @return array 384 | */ 385 | public function validateModuleEmailStatus() 386 | { 387 | $result = $this->error(); 388 | 389 | if (!$this->getConfig('active')) { 390 | $result = $this->error( 391 | true, 392 | __('SMTP module is not enabled') 393 | ); 394 | } 395 | 396 | return $result; 397 | } 398 | 399 | /** 400 | * Format error msg 401 | * @param string $s 402 | * @return string 403 | */ 404 | public function formatErrorMsg($s) 405 | { 406 | return preg_replace( 407 | '@(https?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@', 408 | '$1', 409 | nl2br($s) 410 | ); 411 | } 412 | 413 | /** 414 | * @param bool $hasError 415 | * @param string $msg 416 | * @return array 417 | */ 418 | public function error($hasError = false, $msg = '') 419 | { 420 | return [ 421 | 'has_error' => (bool) $hasError, 422 | 'msg' => (string) $msg 423 | ]; 424 | } 425 | } 426 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Validateconfig/Index.php: -------------------------------------------------------------------------------- 1 | resultFactory->create(ResultFactory::TYPE_LAYOUT); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Helper/Data.php: -------------------------------------------------------------------------------- 1 | testConfig; 34 | } elseif (!array_key_exists($key, $this->testConfig)) { 35 | return ''; 36 | } else { 37 | return $this->testConfig[$key]; 38 | } 39 | } 40 | 41 | /** 42 | * @param null $fields 43 | * @return $this 44 | */ 45 | public function setTestConfig($fields) 46 | { 47 | $this->testConfig = (array) $fields; 48 | return $this; 49 | } 50 | 51 | /** 52 | * @param null $store_id 53 | * @return bool 54 | */ 55 | public function isActive($scopeType = null, $scopeCode = null) 56 | { 57 | //use global store 58 | if ($scopeType === null) { 59 | $scopeType = ScopeInterface::SCOPE_STORE; 60 | } 61 | 62 | //use global store id 63 | if ($scopeCode === null && $this->getStoreId() > 0) { 64 | $scopeCode = $this->getStoreId(); 65 | } 66 | 67 | return $this->scopeConfig->isSetFlag( 68 | 'system/gmailsmtpapp/active', 69 | $scopeType, 70 | $scopeCode 71 | ); 72 | } 73 | 74 | /** 75 | * Get local client name 76 | * 77 | * @param null $scopeType 78 | * @param null $scopeCode 79 | * @return string 80 | */ 81 | public function getConfigName($scopeType = null, $scopeCode = null) 82 | { 83 | return $this->getConfigValue('name', $scopeType, $scopeCode); 84 | } 85 | 86 | /** 87 | * Get system config password 88 | * 89 | * @param null $scopeType 90 | * @param null $scopeCode 91 | * @return string 92 | */ 93 | public function getConfigPassword($scopeType = null, $scopeCode = null) 94 | { 95 | return $this->getConfigValue('password', $scopeType, $scopeCode); 96 | } 97 | 98 | /** 99 | * Get system config username 100 | * 101 | * @param null $scopeType 102 | * @param null $scopeCode 103 | * @return string 104 | */ 105 | public function getConfigUsername($scopeType = null, $scopeCode = null) 106 | { 107 | return $this->getConfigValue('username', $scopeType, $scopeCode); 108 | } 109 | 110 | /** 111 | * Get system config auth 112 | * 113 | * @param null $scopeType 114 | * @param null $scopeCode 115 | * @return string 116 | */ 117 | public function getConfigAuth($scopeType = null, $scopeCode = null) 118 | { 119 | return $this->getConfigValue('auth', $scopeType, $scopeCode); 120 | } 121 | 122 | /** 123 | * Get system config ssl 124 | * 125 | * @param null $scopeType 126 | * @param null $scopeCode 127 | * @return string 128 | */ 129 | public function getConfigSsl($scopeType = null, $scopeCode = null) 130 | { 131 | return $this->getConfigValue('ssl', $scopeType, $scopeCode); 132 | } 133 | 134 | /** 135 | * Get system config host 136 | * 137 | * @param null $scopeType 138 | * @param null $scopeCode 139 | * @return string 140 | */ 141 | public function getConfigSmtpHost($scopeType = null, $scopeCode = null) 142 | { 143 | return $this->getConfigValue('smtphost', $scopeType, $scopeCode); 144 | } 145 | 146 | /** 147 | * Get system config port 148 | * 149 | * @param null $scopeType 150 | * @param null $scopeCode 151 | * @return string 152 | */ 153 | public function getConfigSmtpPort($scopeType = null, $scopeCode = null) 154 | { 155 | return $this->getConfigValue('smtpport', $scopeType, $scopeCode); 156 | } 157 | 158 | /** 159 | * Get system config reply to 160 | * 161 | * @param null $scopeType 162 | * @param null $scopeCode 163 | * @return bool 164 | */ 165 | public function getConfigSetReplyTo($scopeType = null, $scopeCode = null) 166 | { 167 | //use global store 168 | if ($scopeType === null) { 169 | $scopeType = ScopeInterface::SCOPE_STORE; 170 | } 171 | 172 | //use global store id 173 | if ($scopeCode === null && $this->getStoreId() > 0) { 174 | $scopeCode = $this->getStoreId(); 175 | } 176 | 177 | return $this->scopeConfig->isSetFlag( 178 | 'system/gmailsmtpapp/set_reply_to', 179 | $scopeType, 180 | $scopeCode 181 | ); 182 | } 183 | 184 | /** 185 | * Get system config set return path 186 | * 187 | * @param null $scopeType 188 | * @param null $scopeCode 189 | * @return int 190 | */ 191 | public function getConfigSetReturnPath($scopeType = null, $scopeCode = null) 192 | { 193 | return (int) $this->getConfigValue('set_return_path', $scopeType, $scopeCode); 194 | } 195 | 196 | /** 197 | * Get system config return path email 198 | * 199 | * @param null $scopeType 200 | * @param null $scopeCode 201 | * @return string 202 | */ 203 | public function getConfigReturnPathEmail($scopeType = null, $scopeCode = null) 204 | { 205 | return $this->getConfigValue('return_path_email', $scopeType, $scopeCode); 206 | } 207 | 208 | /** 209 | * Get system config from 210 | * 211 | * @param null $scopeType 212 | * @param null $scopeCode 213 | * @return string 214 | */ 215 | public function getConfigSetFrom($scopeType = null, $scopeCode = null) 216 | { 217 | return (int) $this->getConfigValue('set_from', $scopeType, $scopeCode); 218 | } 219 | 220 | /** 221 | * Get system config from 222 | * 223 | * @param null $scopeType 224 | * @param null $scopeCode 225 | * @return string 226 | */ 227 | public function getConfigCustomFromEmail($scopeType = null, $scopeCode = null) 228 | { 229 | return $this->getConfigValue('custom_from_email', $scopeType, $scopeCode); 230 | } 231 | 232 | /** 233 | * Get system config 234 | * 235 | * @param String $path path 236 | * @param null $scopeType 237 | * @param null $scopeCode 238 | * @return string 239 | */ 240 | public function getConfigValue($path, $scopeType = null, $scopeCode = null) 241 | { 242 | //send test mail 243 | if ($this->isTestMode()) { 244 | return $this->getTestConfig($path); 245 | } 246 | 247 | //return value from core config 248 | return $this->getScopeConfigValue( 249 | "system/gmailsmtpapp/{$path}", 250 | $scopeType, 251 | $scopeCode 252 | ); 253 | } 254 | 255 | /** 256 | * @param String path 257 | * @param null $scopeType 258 | * @param null $scopeCode 259 | * @return mixed 260 | */ 261 | public function getScopeConfigValue($path, $scopeType = null, $scopeCode = null) 262 | { 263 | //use global store id 264 | if ($scopeType === null) { 265 | $scopeType = ScopeInterface::SCOPE_STORE; 266 | } 267 | 268 | //use global store id 269 | if ($scopeCode === null && $this->getStoreId() > 0) { 270 | $scopeCode = $this->getStoreId(); 271 | } 272 | 273 | //return value from core config 274 | return $this->scopeConfig->getValue( 275 | $path, 276 | $scopeType, 277 | $scopeCode 278 | ); 279 | } 280 | 281 | /** 282 | * @return int/null 283 | */ 284 | public function getStoreId() 285 | { 286 | return $this->storeId; 287 | } 288 | 289 | /** 290 | * @param int/null $storeId 291 | */ 292 | public function setStoreId($storeId = null) 293 | { 294 | $this->storeId = $storeId; 295 | return $this; 296 | } 297 | 298 | /** 299 | * @return bool 300 | */ 301 | public function isTestMode() 302 | { 303 | return (bool) $this->testMode; 304 | } 305 | 306 | /** 307 | * @param bool $testMode 308 | * @return Data 309 | */ 310 | public function setTestMode($testMode) 311 | { 312 | $this->testMode = (bool) $testMode; 313 | return $this; 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /Mail/Smtp.php: -------------------------------------------------------------------------------- 1 | dataHelper = $dataHelper; 47 | $this->storeModel = $storeModel; 48 | } 49 | 50 | /** 51 | * @param Data $dataHelper 52 | * @return Smtp 53 | */ 54 | public function setDataHelper(Data $dataHelper) 55 | { 56 | $this->dataHelper = $dataHelper; 57 | return $this; 58 | } 59 | 60 | /** 61 | * @param Store $storeModel 62 | * @return Smtp 63 | */ 64 | public function setStoreModel(Store $storeModel) 65 | { 66 | $this->storeModel = $storeModel; 67 | return $this; 68 | } 69 | 70 | /** 71 | * @param SymfonyMessage $message 72 | * @throws MailException 73 | */ 74 | public function sendSmtpMessage( 75 | $message 76 | ) { 77 | $dataHelper = $this->dataHelper; 78 | $dataHelper->setStoreId($this->storeModel->getStoreId()); 79 | 80 | $this->setReplyToPath($message); 81 | $this->setSender($message); 82 | 83 | try { 84 | $host = $dataHelper->getConfigSmtpHost(); 85 | $port = $dataHelper->getConfigSmtpPort(); 86 | $username = $dataHelper->getConfigUsername(); 87 | $password = $dataHelper->getConfigPassword(); 88 | $auth = strtolower($dataHelper->getConfigAuth()); 89 | $ssl = $dataHelper->getConfigSsl(); 90 | 91 | $tls = ($auth !== 'none') && ($ssl !== 'starttls'); 92 | 93 | /** @var SymfonyTransportInterface $transport */ 94 | $transport = new EsmtpTransport($host, $port, $tls); 95 | 96 | if ($username) { 97 | $transport->setUsername($username); 98 | } 99 | 100 | if ($password) { 101 | $transport->setPassword($password); 102 | } 103 | 104 | switch ($auth) { 105 | case 'plain': 106 | $transport->setAuthenticators([new PlainAuthenticator()]); 107 | break; 108 | case 'login': 109 | $transport->setAuthenticators([new LoginAuthenticator()]); 110 | break; 111 | case 'none': 112 | break; 113 | default: 114 | throw new \InvalidArgumentException('Invalid authentication type: ' . $auth); 115 | } 116 | 117 | 118 | $mailer = new Mailer($transport); 119 | $mailer->send($message); 120 | 121 | } catch (Exception $e) { 122 | throw new MailException( 123 | new Phrase($e->getMessage()), 124 | $e 125 | ); 126 | } 127 | } 128 | 129 | /** 130 | * 131 | * @param SymfonyMessage $message 132 | */ 133 | protected function setSender($message) 134 | { 135 | $dataHelper = $this->dataHelper; 136 | $messageFromAddress = $this->getMessageFromAddressObject($message); 137 | 138 | //Set from address 139 | switch ($dataHelper->getConfigSetFrom()) { 140 | case 1: 141 | $setFromEmail = $messageFromAddress; 142 | break; 143 | case 2: 144 | $setFromEmail = $dataHelper->getConfigCustomFromEmail(); 145 | break; 146 | default: 147 | $setFromEmail = null; 148 | break; 149 | } 150 | 151 | if ($setFromEmail !== null && $dataHelper->getConfigSetFrom()) { 152 | if ($setFromEmail instanceof Address) { 153 | $message->getHeaders()->addMailboxListHeader('Sender', [$setFromEmail]); 154 | } elseif (!empty($setFromEmail)) { 155 | $name = $messageFromAddress instanceof Address ? $messageFromAddress->getName() : $setFromEmail; 156 | 157 | $message->getHeaders()->addMailboxListHeader( 158 | 'Sender', 159 | [new Address($setFromEmail, $name)] 160 | ); 161 | } 162 | } 163 | } 164 | 165 | /** 166 | * @param SymfonyMessage $message 167 | */ 168 | protected function setReplyToPath($message) 169 | { 170 | $dataHelper = $this->dataHelper; 171 | $messageFromAddress = $this->getMessageFromAddressObject($message); 172 | /* 173 | * Set reply-to path 174 | * 0 = No 175 | * 1 = From 176 | * 2 = Custom 177 | */ 178 | switch ($dataHelper->getConfigSetReturnPath()) { 179 | case 1: 180 | $returnPathEmail = $messageFromAddress; 181 | break; 182 | case 2: 183 | $returnPathEmail = $dataHelper->getConfigReturnPathEmail(); 184 | break; 185 | default: 186 | $returnPathEmail = null; 187 | break; 188 | } 189 | 190 | if (empty($message->getHeaders()->get('reply-to')?->getAddresses()) && $dataHelper->getConfigSetReplyTo()) { 191 | if ($returnPathEmail instanceof Address) { 192 | $message->getHeaders()->addMailboxListHeader('reply-to', [$returnPathEmail]); 193 | } elseif (!empty($returnPathEmail)) { 194 | $name = $messageFromAddress instanceof Address ? $messageFromAddress->getName() : $returnPathEmail; 195 | 196 | $message->getHeaders()->addMailboxListHeader( 197 | 'reply-to', 198 | [new Address($returnPathEmail, $name)] 199 | ); 200 | } 201 | } 202 | } 203 | 204 | /** 205 | * 206 | * @param SymfonyMessage $message 207 | * @return null|Address 208 | */ 209 | protected function getMessageFromAddressObject($message) 210 | { 211 | if (!empty($fromAddresses = $message->getHeaders()->get('From')?->getAddresses())) { 212 | reset($fromAddresses); 213 | return current($fromAddresses); 214 | } 215 | 216 | return null; 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /Model/Config/Source/Authtype.php: -------------------------------------------------------------------------------- 1 | 'none', 'label' => __('None')], 20 | ['value' => 'ssl', 'label' => 'SSL'], 21 | ['value' => 'tls', 'label' => 'TLS'], 22 | ['value' => 'starttls', 'label' => 'STARTTLS'] 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Model/Email.php: -------------------------------------------------------------------------------- 1 | dataHelper = $dataHelper; 96 | $this->templateFactory = $templateFactory; 97 | $this->scopeConfig = $scopeConfig; 98 | $this->storeManager = $storeManager; 99 | $this->inlineTranslation = $inlineTranslation; 100 | $this->_transportBuilder = $transportBuilder; 101 | } 102 | 103 | /** 104 | * @param Mixed $senderInfo 105 | * @param Mixed $receiverInfo 106 | * @return $this 107 | * @throws NoSuchEntityException 108 | */ 109 | public function generateTemplate($senderInfo, $receiverInfo) 110 | { 111 | $templateId = $this->getTemplateId(self::XML_PATH_EMAIL_TEMPLATE_MAGENTO_TEST); 112 | $this->getTransportBuilder() 113 | ->setTemplateIdentifier($templateId) 114 | ->setTemplateOptions( 115 | [ 116 | 'area' => Area::AREA_ADMINHTML, 117 | 'store' => $this->getStore()->getId(), 118 | ] 119 | ) 120 | ->setTemplateVars($this->templateVars) 121 | ->setFrom($senderInfo) 122 | ->addTo($receiverInfo['email'], $receiverInfo['name']); 123 | 124 | return $this; 125 | } 126 | 127 | /** 128 | * @param $senderInfo 129 | * @param $receiverInfo 130 | * @throws MailException 131 | * @throws NoSuchEntityException 132 | * @throws LocalizedException 133 | */ 134 | public function send($senderInfo, $receiverInfo) 135 | { 136 | $this->inlineTranslation->suspend(); 137 | $this->generateTemplate($senderInfo, $receiverInfo); 138 | $transport = $this->_transportBuilder->getTransport(); 139 | $result = $transport->sendMessage(); 140 | $this->inlineTranslation->resume(); 141 | 142 | return $result; 143 | } 144 | 145 | /** 146 | * @return TemplateInterface 147 | * @throws NoSuchEntityException 148 | */ 149 | protected function getTemplate() 150 | { 151 | $this->setTemplateOptions( 152 | [ 153 | 'area' => Area::AREA_ADMINHTML, 154 | 'store' => $this->getStore()->getId(), 155 | ] 156 | ); 157 | 158 | $templateIdentifier = $this->getTemplateId(self::XML_PATH_EMAIL_TEMPLATE_ZEND_TEST); 159 | 160 | return $this->templateFactory->get($templateIdentifier, $this->templateModel) 161 | ->setVars($this->templateVars) 162 | ->setOptions($this->templateOptions); 163 | } 164 | 165 | /** 166 | * @return mixed 167 | * @throws NoSuchEntityException 168 | */ 169 | public function getEmailBody() 170 | { 171 | return $this->getTemplate()->processTemplate(); 172 | } 173 | 174 | /** 175 | * Return template id according to store 176 | * 177 | * @param $xmlPath 178 | * @return mixed 179 | * @throws NoSuchEntityException 180 | */ 181 | public function getTemplateId($xmlPath) 182 | { 183 | return $this->getConfigValue($xmlPath, $this->getStore()->getStoreId()); 184 | } 185 | 186 | /** 187 | * Return store configuration value of your template field that which id you set for template 188 | * 189 | * @param string $path 190 | * @param int $storeId 191 | * @return mixed 192 | */ 193 | protected function getConfigValue($path, $storeId) 194 | { 195 | return $this->scopeConfig->getValue( 196 | $path, 197 | ScopeInterface::SCOPE_STORE, 198 | $storeId 199 | ); 200 | } 201 | 202 | /** 203 | * Return store 204 | * 205 | * @return StoreInterface 206 | * @throws NoSuchEntityException 207 | */ 208 | public function getStore() 209 | { 210 | return $this->storeManager->getStore(); 211 | } 212 | 213 | /** 214 | * @param mixed $templateVars 215 | * @return Email 216 | */ 217 | public function setTemplateVars($templateVars) 218 | { 219 | $this->templateVars = (array) $templateVars; 220 | return $this; 221 | } 222 | 223 | /** 224 | * @param mixed $templateOptions 225 | * @return Email 226 | */ 227 | public function setTemplateOptions($templateOptions) 228 | { 229 | $this->templateOptions = (array) $templateOptions; 230 | return $this; 231 | } 232 | 233 | /** 234 | * Set template model 235 | * 236 | * @param string $templateModel 237 | * @return $this 238 | */ 239 | public function setTemplateModel($templateModel) 240 | { 241 | $this->templateModel = $templateModel; 242 | return $this; 243 | } 244 | 245 | /** 246 | * @return TransportBuilder 247 | */ 248 | public function getTransportBuilder() 249 | { 250 | return $this->_transportBuilder; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /Model/Store.php: -------------------------------------------------------------------------------- 1 | store_id; 26 | } 27 | 28 | /** 29 | * @param $store_id 30 | * @return $this 31 | */ 32 | public function setStoreId($store_id) 33 | { 34 | $this->store_id = $store_id; 35 | return $this; 36 | } 37 | 38 | /** 39 | * @return string|array 40 | */ 41 | public function getFrom() 42 | { 43 | return $this->from; 44 | } 45 | 46 | /** 47 | * @param string|array $from 48 | * @return $this 49 | */ 50 | public function setFrom($from) 51 | { 52 | $this->from = $from; 53 | return $this; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Plugin/Mail/TransportPlugin.php: -------------------------------------------------------------------------------- 1 | dataHelper = $dataHelper; 48 | $this->storeModel = $storeModel; 49 | $this->smtpFactory = $smtpFactory; 50 | } 51 | 52 | /** 53 | * @param TransportInterface $subject 54 | * @param Closure $proceed 55 | * @throws MailException 56 | */ 57 | public function aroundSendMessage( 58 | TransportInterface $subject, 59 | Closure $proceed 60 | ) { 61 | if ($this->dataHelper->isActive()) { 62 | if (method_exists($subject, 'getStoreId')) { 63 | $this->storeModel->setStoreId($subject->getStoreId()); 64 | } 65 | 66 | $message = $subject->getMessage()->getSymfonyMessage(); 67 | 68 | if ($message instanceof SymfonyMessage) { 69 | /** @var Smtp $smtp */ 70 | $smtp = $this->smtpFactory->create( 71 | ['dataHelper' => $this->dataHelper, 'storeModel' => $this->storeModel] 72 | ); 73 | $smtp->sendSmtpMessage($message); 74 | } else { 75 | $proceed(); 76 | } 77 | } else { 78 | $proceed(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Magento 2 SMTP Extension - Gmail, G Suite, Amazon SES, Office 365, Mailgun, SendGrid, Mandrill and other SMTP servers. 4 | 5 | [![Total Downloads](https://poser.okvpn.org/magepal/magento2-gmailsmtpapp/downloads)](https://www.magepal.com/magento2/extensions/custom-smtp.html) 6 | [![Latest Stable Version](https://poser.okvpn.org/magepal/magento2-gmailsmtpapp/v/stable)](https://www.magepal.com/magento2/extensions/custom-smtp.html) 7 | [![GitHub stars](https://img.shields.io/github/stars/magepal/magento2-gmail-smtp-app.svg)](https://www.magepal.com/magento2/extensions/custom-smtp.html) 8 | [![GitHub forks](https://img.shields.io/github/forks/magepal/magento2-gmail-smtp-app.svg)](https://www.magepal.com/magento2/extensions/custom-smtp.html) 9 | 10 | ##### For Magento / Adobe Commerce 2.0.x, 2.1.x, 2.2.x, 2.3.x and 2.4.x 11 | 12 | 13 | ### OAuth SMTP Extension 14 | Tired of dealing with email password issues? Upgrade to our OAuth SMTP Extension for Magento 2 and enjoy secure, password-free email delivery. Learn more about our OAuth SMTP Extension and get started today! 15 | 16 | Configure Magento 2 / Adobe Commerce to send all transactional emails using Google App, Gmail, Amazon Simple Email Service (SES), Microsoft Office365 or any other SMTP servers. 17 | 18 | Magento SMTP Extension 19 | 20 | Sending transactional emails to customers is a vital part of running an e-commerce store. Our free custom Magento extension integrates with all major email service providers and third-party SMTP servers to reliably deliver messages to customers' inboxes. 21 | 22 | #### What is SMTP - Simple Mail Transfer Protocol 23 | SMTP or Simple Mail Transfer Protocol allows you to send emails from your Magento 2 store through a specific third-party mail SMTP server. For example, if you want to use your Gmail, Amazon, Microsoft or any other mail server account to send email from your Magento web store, all you need is to configure that mail server setting in our extension in Magento without having to do any complex server configuration. 24 | 25 | Magento SMTP Email Extension 26 | 27 | #### Why use a Custom SMTP Server with Magento 28 | 29 | By default, most hosting companies' mail servers are configured to send email from unauthorized senders which prevents emails from reliable delivered to recipients. Therefore, most Magento store owners struggle to limit the number of transactional emails that end up in clients' junk mail. Take full control of your email sending settings in Magento 2 and reduce sending emails to your valuable customers' junk mail folder. Emails are delivered instantaneously to their intended recipients without delays or get trap in the spam folder. 30 | 31 | Out of the box, Magento 2 doesn't provide the ability to specify your custom SMTP settings for outgoing emails using an external SMTP server. Using this extension bridges the gap and allows your Magento store to connect to your preferred email provider securely and easily. 32 | 33 | All you need is either an (i) free Gmail account, (ii) paid Google Apps account or any other SMTP service (i.e Amazon Simple Email Service / Amazon SES, Microsoft Office365). 34 | 35 | Learn more about our [custom SMTP](https://www.magepal.com/magento2/extensions/custom-smtp.html?utm_source=Custom%20SMTP&utm_medium=GitHub%20Learn%20More) extension. 36 | 37 | 38 | > ### NOTE - Gmail / Google Account 39 | >To help keep your account secure, starting May 30, 2022, ​​Google will no longer support the use of third-party apps or devices which ask you to sign in to your Google Account using only your username and password. 40 | 41 | >Please note this deadline does not apply to Google Workspace or Google Cloud Identity customers. The enforcement date for these customers will be announced on the Workspace blog at a later date. 42 | 43 | >For more information, please continue reading. 44 | 45 | >Special Note on Apple Device Sign-Ins. Users who have not recently signed into their Google Account using only username and password will be able to only make new sign in attempts using the Google account type starting from February 28, 2022. Existing users may continue to sign into their Google Account using their username and password until May 30, 2022. 46 | 47 | 48 | ### Benefits of using Gmail SMTP 49 | Since Google's, Gmail and G Suite SMTP server does not use Port 25, you'll reduce the probability that an ISP might block your email or flag it as SPAM. Also, all your emails sent from Magento will be searchable and backed-up in your email account on Google's servers. 50 | 51 | ### Features 52 | * Send emails through virtually any external SMTP server from your Magento store 53 | * Easily configure Magento 2 SMTP settings from within Magento2 store admin 54 | * Complete control of custom SMTP server settings: Hostname, Port, Username, Password, ... 55 | * Self-test option, which lets you verify your email credentials are correct before saving 56 | * Support Multi-store, configurable different email providers/accounts per store 57 | * Support secure SMTP servers: TLS / SSL, Plain-text, username/password, CRAM-MD5 authentication 58 | * Customize email headers: From / Reply-To / Return-Path 59 | * Easily disable/enable our extension from admin 60 | * Developer Friendly 61 | * Integrate with any third-party SMTP server 62 | 63 | Get more from your order confirmation emails by promoting other complementary products and services. 64 | Learn more about our new Enhanced Transactional Email extension. 65 | 66 | ### Documentation 67 | 68 | - [How to Install SMTP Magento 2 / Adobe Commerce Extension](https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=github#installation) 69 | 70 | - [How to setup Magento 2 / Adobe Commerce SMTP Extension](https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=github#configuration) 71 | 72 | - [How to debugging Magento 2 / Adobe Commerce SMTP Extension](https://www.magepal.com/help/docs/smtp-magento/?utm_source=smtp&utm_medium=github#debug) 73 | 74 | 75 | ### SMTP Service Providers 76 | * Gmail 77 | * Google App 78 | * G Suite 79 | * Amazon Simple Email Service (SES) 80 | * Microsoft Office365 81 | * Outlook 82 | * SparkPost 83 | * GoDaddy 84 | * Mandrill 85 | * MailGun 86 | * SendGrid 87 | * Elastic Email 88 | * Hotmail 89 | * AOL Mail 90 | * Yahoo Mail 91 | * AT&T 92 | * Verizon 93 | * Postmark 94 | * O2 Mail 95 | * Zoho 96 | * Mailjet 97 | * Mail.com 98 | * Your Company SMTP Server 99 | * and many other SMTP servers 100 | 101 | ### How to Install Magento SMTP Extension 102 | 103 | ##### Using Composer (recommended) 104 | 105 | ```sh 106 | composer require magepal/magento2-gmailsmtpapp 107 | ``` 108 | 109 | Contribution 110 | --- 111 | Want to contribute to this extension? The quickest way is to open a [pull request on GitHub](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/about-pull-requests). 112 | 113 | Support 114 | --- 115 | If you encounter any problems or bugs, please open an issue on [GitHub](https://github.com/magepal/magento2-gmail-smtp-app/issues). For fast Premium Support visit our [Custom SMTP Extension](https://www.magepal.com/magento2/extensions/custom-smtp.html?utm_source=Custom%20SMTP&utm_medium=GitHub%20Premium%20Support) product page for detail. 116 | 117 | Need help setting up or want to customize this extension to meet your business needs? Please email support@magepal.com and if we like your idea we will add this feature for free or at a discounted rate. 118 | 119 | Magento 2 Extensions 120 | --- 121 | - [Custom SMTP](https://www.magepal.com/magento2/extensions/custom-smtp.html) 122 | - [Catalog Hover Image for Adobe Commerce](https://www.magepal.com/magento2/extensions/catalog-hover-image-for-magento.html) 123 | - [Enhanced Success Page for Magento 2](https://www.magepal.com/magento2/extensions/enhanced-success-page.html) 124 | - [Enhanced Transactional Emails for Magento 2](https://www.magepal.com/magento2/extensions/enhanced-transactional-emails.html) 125 | - [Google Tag Manager](https://www.magepal.com/magento2/extensions/google-tag-manager.html) 126 | - [Enhanced E-commerce](https://www.magepal.com/magento2/extensions/enhanced-ecommerce-for-google-tag-manager.html) 127 | - [Reindex](https://www.magepal.com/magento2/extensions/reindex.html) 128 | - [Custom Shipping Method](https://www.magepal.com/magento2/extensions/custom-shipping-rates-for-magento-2.html) 129 | - [Preview Order Confirmation](https://www.magepal.com/magento2/extensions/preview-order-confirmation-page-for-magento-2.html) 130 | - [Guest to Customer](https://www.magepal.com/magento2/extensions/guest-to-customer.html) 131 | - [Admin Form Fields Manager](https://www.magepal.com/magento2/extensions/admin-form-fields-manager-for-magento-2.html) 132 | - [Customer Dashboard Links Manager](https://www.magepal.com/magento2/extensions/customer-dashboard-links-manager-for-magento-2.html) 133 | - [Lazy Loader](https://www.magepal.com/magento2/extensions/lazy-load.html) 134 | - [Order Confirmation Page Miscellaneous Scripts](https://www.magepal.com/magento2/extensions/order-confirmation-miscellaneous-scripts-for-magento-2.html) 135 | - [HTML Minifier for Magento2](https://www.magepal.com/magento2/extensions/html-minifier.html) 136 | 137 | © MagePal LLC. | [www.magepal.com](https://www.magepal.com) 138 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "magepal/magento2-gmailsmtpapp", 3 | "description":"Magento 2 SMTP Extension - Configure Magento 2 to send all transactional email using Gmail, G Suite, Amazon SES, Office360, Mailgun, SendGrid, Mandrill or any other SMTP servers", 4 | "keywords": [ 5 | "magento 2", 6 | "gmail smtp", 7 | "google app", 8 | "magento2 email", 9 | "Amazon Simple Email Service", 10 | "Amazon SES", 11 | "magento2 smtp", 12 | "magento2 email setup", 13 | "send email magento2", 14 | "g suite", 15 | "how to configure magento email", 16 | "how to setup email magento2" 17 | ], 18 | "require": { 19 | "php": "~8.2.0||~8.3.0||~8.4.0", 20 | "magento/module-backend": "102.0.*", 21 | "magento/framework": "103.0.*", 22 | "magento/module-email": "101.1.8||101.1.8-p1||101.1.8-p2", 23 | "magepal/magento2-core":">1.1.0" 24 | }, 25 | "type": "magento2-module", 26 | "version": "3.0.2", 27 | "license": [ 28 | "proprietary" 29 | ], 30 | "homepage": "https://www.magepal.com/", 31 | "support": { 32 | "email": "support@magepal.com", 33 | "issues": "https://github.com/magepal/magento2-gmail-smtp-app/issues/" 34 | }, 35 | "authors": [ 36 | { 37 | "name": "Renon Stewart", 38 | "email": "renon@magepal.com", 39 | "homepage": "https://www.magepal.com/", 40 | "role": "Leader" 41 | } 42 | ], 43 | "autoload": { 44 | "files": [ 45 | "registration.php" 46 | ], 47 | "psr-4": { 48 | "MagePal\\GmailSmtpApp\\": "" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /etc/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /etc/adminhtml/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | magepal 17 | MagePal_GmailSmtpApp::magepal_gmailsmtpapp 18 | 19 | 1 20 | 21 | 22 | 24 | Copyright © 2025 MagePal, LLC 25 | Documentation 26 | Support 27 | Latest Version 28 | Extension Detail 29 | More Extensions 30 | 31 |
32 | Get more from your order confirmation emails by promoting other complementary products! 33 | Learn more about our new Email Promotional Products extension. 34 |
35 |
36 | ]]> 37 |
38 | 39 | 40 | MagePal\Core\Block\Adminhtml\System\Config\Composer\Version 41 | 42 | 43 | 44 | MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\Form\Field\Link 45 | 46 |
47 |
48 |
49 | 50 | 51 | 52 | 54 | Copyright © 2025 MagePal, LLC 55 | Documentation 56 | Support 57 | Latest Version 58 | Extension Detail 59 | More Extensions 60 | 61 |
62 | Get more from your order confirmation emails by promoting other complementary products! 63 | Learn more about our new Enhanced Transactional Email extension. 64 |

65 | Tired of dealing with email password issues? Upgrade to our OAuth SMTP Extension for Magento 2 and enjoy secure, password-free email delivery. Learn more about our OAuth SMTP Extension and get started today! 66 |
67 |
68 | ]]> 69 |
70 | 71 | 72 | 73 | MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\Form\Composer\Version 74 | 75 | 76 | 77 | Magento\Config\Model\Config\Source\Yesno 78 | 79 | 80 | 81 | Default: localhost 82 | 83 | 1 84 | 85 | 86 | 87 | 88 | Magento\Config\Model\Config\Source\Email\Smtpauth 89 | Default: login 90 | 91 | 1 92 | 93 | 94 | 95 | 96 | MagePal\GmailSmtpApp\Model\Config\Source\Authtype 97 | Default: ssl 98 | 99 | 1 100 | 101 | 102 | 103 | 104 | The server name (eg smtp.gmail.com). 105 | 106 | 1 107 | 108 | 109 | 110 | 111 | validate-number 112 | validate-number 113 | Use 465 (ssl) or 587 (tls) if port 25 is throttled or blocked. 114 | 115 | 1 116 | 117 | 118 | 119 | 120 | Email Address or Account ID. 121 | 122 | 1 123 | 124 | 125 | 126 | 127 | Magento\Config\Model\Config\Backend\Encrypted 128 | 129 | 1 130 | 131 | 132 | 133 | 134 | Magento\Config\Model\Config\Source\Yesno 135 | 136 | 1 137 | 138 | 139 | 140 | 141 | Magento\Config\Model\Config\Source\Yesnocustom 142 | Use Return-Path email address for the From address rather than the Magento supplied value. 143 | 144 | 1 145 | 146 | 147 | 148 | 149 | validate-email 150 | Magento\Config\Model\Config\Backend\Email\Address 151 | Use specify From Address instead of Magento supplied value. 152 | 153 | 2 154 | 1 155 | 156 | 157 | 158 | 159 | Magento\Config\Model\Config\Source\Yesnocustom 160 | 161 | 1 162 | 163 | 164 | 165 | 166 | validate-email 167 | Magento\Config\Model\Config\Backend\Email\Address 168 | 169 | 2 170 | 1 171 | 172 | 173 | 174 | 175 | 1 176 | 177 | 178 | 0 179 | Magento\Config\Block\System\Config\Form\Fieldset 180 | 181 | 182 | Email address to send test to. 183 | validate-email 184 | 185 | 186 | 187 | Leave blank to use Username instead 188 | validate-email 189 | 190 | 191 | MagePal\GmailSmtpApp\Block\Adminhtml\System\Config\ValidateConfigButton 192 | 193 | 194 |
195 |
196 |
197 |
198 | -------------------------------------------------------------------------------- /etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 0 14 | localhost 15 | ssl 16 | LOGIN 17 | smtp.gmail.com 18 | 465 19 | 1 20 | 1 21 | 0 22 | 23 | magepal_smtp_zend_email_test 24 | magepal_smtp_magento_email_test 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 1 18 | 1 19 | 1 20 | 1 21 | 1 22 | 1 23 | 1 24 | 1 25 | 1 26 | 1 27 | 1 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /etc/email_templates.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |