Bug: [FEATURE] Implement email service



implement an email service to OSS. The integration allows us to send emails to our merchants directly from our OSS and helps us to enhance the application experience. Use AWS SES (Simple Email Service) service to develop and build email communications.
diff --git a/Cargo.lock b/Cargo.lock
index b504c3db426..da0033c8336 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -598,9 +598,9 @@ dependencies = [
 
 [[package]]
 name = "aws-credential-types"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4232d3729eefc287adc0d5a8adc97b7d94eefffe6bbe94312cc86c7ab6b06ce"
+checksum = "4cb57ac6088805821f78d282c0ba8aec809f11cbee10dda19a97b03ab040ccc2"
 dependencies = [
  "aws-smithy-async",
  "aws-smithy-types",
@@ -612,9 +612,9 @@ dependencies = [
 
 [[package]]
 name = "aws-endpoint"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f04ab03b3f1cca91f7cccaa213056d732accb14e2e65debfacc1d28627d162"
+checksum = "9c5f6f84a4f46f95a9bb71d9300b73cd67eb868bc43ae84f66ad34752299f4ac"
 dependencies = [
  "aws-smithy-http",
  "aws-smithy-types",
@@ -626,9 +626,9 @@ dependencies = [
 
 [[package]]
 name = "aws-http"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5ad8c53f7560baaf635b6aa811f3213d39b50555d100f83e43801652d4e318e"
+checksum = "a754683c322f7dc5167484266489fdebdcd04d26e53c162cad1f3f949f2c5671"
 dependencies = [
  "aws-credential-types",
  "aws-smithy-http",
@@ -701,6 +701,31 @@ dependencies = [
  "url",
 ]
 
+[[package]]
+name = "aws-sdk-sesv2"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28ec96086c4bda28c512b10c5c951d031651be454a512e511cf5fe91b21b9cc9"
+dependencies = [
+ "aws-credential-types",
+ "aws-endpoint",
+ "aws-http",
+ "aws-sig-auth",
+ "aws-smithy-async",
+ "aws-smithy-client",
+ "aws-smithy-http",
+ "aws-smithy-http-tower",
+ "aws-smithy-json",
+ "aws-smithy-types",
+ "aws-types",
+ "bytes",
+ "http",
+ "regex",
+ "tokio-stream",
+ "tower",
+ "tracing",
+]
+
 [[package]]
 name = "aws-sdk-sso"
 version = "0.26.0"
@@ -754,9 +779,9 @@ dependencies = [
 
 [[package]]
 name = "aws-sig-auth"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d77d879ab210e958ba65a6d3842969a596738c024989cd3e490cf9f9b560ec"
+checksum = "84dc92a63ede3c2cbe43529cb87ffa58763520c96c6a46ca1ced80417afba845"
 dependencies = [
  "aws-credential-types",
  "aws-sigv4",
@@ -769,9 +794,9 @@ dependencies = [
 
 [[package]]
 name = "aws-sigv4"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ab4eebc8ec484fb9eab04b15a5d1e71f3dc13bee8fdd2d9ed78bcd6ecbd7192"
+checksum = "392fefab9d6fcbd76d518eb3b1c040b84728ab50f58df0c3c53ada4bea9d327e"
 dependencies = [
  "aws-smithy-eventstream",
  "aws-smithy-http",
@@ -790,9 +815,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-async"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88573bcfbe1dcfd54d4912846df028b42d6255cbf9ce07be216b1bbfd11fc4b9"
+checksum = "ae23b9fe7a07d0919000116c4c5c0578303fbce6fc8d32efca1f7759d4c20faf"
 dependencies = [
  "futures-util",
  "pin-project-lite",
@@ -823,9 +848,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-client"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2f52352bae50d3337d5d6151b695d31a8c10ebea113eca5bead531f8301b067"
+checksum = "5230d25d244a51339273b8870f0f77874cd4449fb4f8f629b21188ae10cfc0ba"
 dependencies = [
  "aws-smithy-async",
  "aws-smithy-http",
@@ -847,9 +872,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-eventstream"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168f08f8439c8b317b578a695e514c5cd7b869e73849a2d6b71ced4de6ce193d"
+checksum = "22d2a2bcc16e5c4d949ffd2b851da852b9bbed4bb364ed4ae371b42137ca06d9"
 dependencies = [
  "aws-smithy-types",
  "bytes",
@@ -858,9 +883,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-http"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03bcc02d7ed9649d855c8ce4a735e9848d7b8f7568aad0504c158e3baa955df8"
+checksum = "b60e2133beb9fe6ffe0b70deca57aaeff0a35ad24a9c6fab2fd3b4f45b99fdb5"
 dependencies = [
  "aws-smithy-eventstream",
  "aws-smithy-types",
@@ -881,9 +906,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-http-tower"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da88b3a860f65505996c29192d800f1aeb9480440f56d63aad33a3c12045017a"
+checksum = "3a4d94f556c86a0dd916a5d7c39747157ea8cb909ca469703e20fee33e448b67"
 dependencies = [
  "aws-smithy-http",
  "aws-smithy-types",
@@ -897,9 +922,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-json"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b0c1e87d75cac889dca2a7f5ba280da2cde8122448e7fec1d614194dfa00c70"
+checksum = "5ce3d6e6ebb00b2cce379f079ad5ec508f9bcc3a9510d9b9c1840ed1d6f8af39"
 dependencies = [
  "aws-smithy-types",
 ]
@@ -916,9 +941,9 @@ dependencies = [
 
 [[package]]
 name = "aws-smithy-types"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd0afc731fd1417d791f9145a1e0c30e23ae0beaab9b4814017708ead2fc20f1"
+checksum = "58db46fc1f4f26be01ebdb821751b4e2482cd43aa2b64a0348fb89762defaffa"
 dependencies = [
  "base64-simd",
  "itoa",
@@ -938,9 +963,9 @@ dependencies = [
 
 [[package]]
 name = "aws-types"
-version = "0.55.1"
+version = "0.55.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9b082e329d9a304d39e193ad5c7ab363a0d6507aca6965e0673a746686fb0cc"
+checksum = "de0869598bfe46ec44ffe17e063ed33336e59df90356ca8ff0e8da6f7c1d994b"
 dependencies = [
  "aws-credential-types",
  "aws-smithy-async",
@@ -1795,11 +1820,16 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
 name = "external_services"
 version = "0.1.0"
 dependencies = [
+ "async-trait",
  "aws-config",
  "aws-sdk-kms",
+ "aws-sdk-sesv2",
+ "aws-smithy-client",
  "base64 0.21.0",
  "common_utils",
+ "dyn-clone",
  "error-stack",
+ "masking",
  "once_cell",
  "router_env",
  "serde",
@@ -2970,7 +3000,7 @@ dependencies = [
 [[package]]
 name = "opentelemetry"
 version = "0.18.0"
-source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
+source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
 dependencies = [
  "opentelemetry_api",
  "opentelemetry_sdk",
@@ -2979,7 +3009,7 @@ dependencies = [
 [[package]]
 name = "opentelemetry-otlp"
 version = "0.11.0"
-source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
+source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
 dependencies = [
  "async-trait",
  "futures",
@@ -2996,7 +3026,7 @@ dependencies = [
 [[package]]
 name = "opentelemetry-proto"
 version = "0.1.0"
-source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
+source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
 dependencies = [
  "futures",
  "futures-util",
@@ -3008,7 +3038,7 @@ dependencies = [
 [[package]]
 name = "opentelemetry_api"
 version = "0.18.0"
-source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
+source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
 dependencies = [
  "fnv",
  "futures-channel",
@@ -3023,7 +3053,7 @@ dependencies = [
 [[package]]
 name = "opentelemetry_sdk"
 version = "0.18.0"
-source = "git+https://github.com/open-telemetry/opentelemetry-rust/?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
+source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=44b90202fd744598db8b0ace5b8f0bad7ec45658#44b90202fd744598db8b0ace5b8f0bad7ec45658"
 dependencies = [
  "async-trait",
  "crossbeam-channel",
diff --git a/config/config.example.toml b/config/config.example.toml
index cf656f0ae8c..85b7ad83c84 100644
--- a/config/config.example.toml
+++ b/config/config.example.toml
@@ -242,6 +242,12 @@ paypal = { currency = "USD,INR", country = "US" }
 key_id = "" # The AWS key ID used by the KMS SDK for decrypting data.
 region = "" # The AWS region used by the KMS SDK for decrypting data.
 
+# EmailClient configuration. Only applicable when the `email` feature flag is enabled.
+[email]
+from_email = "notify@example.com" # Sender email
+aws_region = "" # AWS region used by AWS SES 
+base_url = "" # Base url used when adding links that should redirect to self
+
 [dummy_connector]
 payment_ttl = 172800             # Time to live for dummy connector payment in redis
 payment_duration = 1000          # Fake delay duration for dummy connector payment
diff --git a/config/development.toml b/config/development.toml
index 044b5bc616a..692712b79c4 100644
--- a/config/development.toml
+++ b/config/development.toml
@@ -149,6 +149,11 @@ stream = "SCHEDULER_STREAM"
 disabled = false
 consumer_group = "SCHEDULER_GROUP"
 
+[email]
+from_email = "notify@example.com"
+aws_region = ""
+base_url = ""
+
 [bank_config.eps]
 stripe = { banks = "arzte_und_apotheker_bank,austrian_anadi_bank_ag,bank_austria,bankhaus_carl_spangler,bankhaus_schelhammer_und_schattera_ag,bawag_psk_ag,bks_bank_ag,brull_kallmus_bank_ag,btv_vier_lander_bank,capital_bank_grawe_gruppe_ag,dolomitenbank,easybank_ag,erste_bank_und_sparkassen,hypo_alpeadriabank_international_ag,hypo_noe_lb_fur_niederosterreich_u_wien,hypo_oberosterreich_salzburg_steiermark,hypo_tirol_bank_ag,hypo_vorarlberg_bank_ag,hypo_bank_burgenland_aktiengesellschaft,marchfelder_bank,oberbank_ag,raiffeisen_bankengruppe_osterreich,schoellerbank_ag,sparda_bank_wien,volksbank_gruppe,volkskreditbank_ag,vr_bank_braunau" }
 adyen = { banks = "bank_austria,bawag_psk_ag,dolomitenbank,easybank_ag,erste_bank_und_sparkassen,hypo_tirol_bank_ag,posojilnica_bank_e_gen,raiffeisen_bankengruppe_osterreich,schoellerbank_ag,sparda_bank_wien,volksbank_gruppe,volkskreditbank_ag" }
diff --git a/crates/external_services/Cargo.toml b/crates/external_services/Cargo.toml
index 385c2a70cbb..008699b34b9 100644
--- a/crates/external_services/Cargo.toml
+++ b/crates/external_services/Cargo.toml
@@ -9,6 +9,7 @@ license = "Apache-2.0"
 
 [features]
 kms = ["dep:aws-config", "dep:aws-sdk-kms"]
+email = ["dep:aws-config"]
 
 [dependencies]
 aws-config = { version = "0.55.1", optional = true }
@@ -19,7 +20,12 @@ once_cell = "1.17.1"
 serde = { version = "1.0.160", features = ["derive"] }
 thiserror = "1.0.40"
 tokio = "1.27.0"
+dyn-clone = "1.0.11"
+async-trait = "0.1.66"
+aws-sdk-sesv2 = "0.27.0"
+aws-smithy-client =  "0.55.0"
 
 # First party crates
 common_utils = { version = "0.1.0", path = "../common_utils" }
 router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }
+masking = { version = "0.1.0", path = "../masking" }
diff --git a/crates/external_services/src/email.rs b/crates/external_services/src/email.rs
new file mode 100644
index 00000000000..2da15bead56
--- /dev/null
+++ b/crates/external_services/src/email.rs
@@ -0,0 +1,123 @@
+//! Interactions with the AWS SES SDK
+
+use aws_config::meta::region::RegionProviderChain;
+use aws_sdk_sesv2::{
+    config::Region,
+    operation::send_email::SendEmailError,
+    types::{Body, Content, Destination, EmailContent, Message},
+    Client,
+};
+use common_utils::{errors::CustomResult, pii};
+use error_stack::{IntoReport, ResultExt};
+use masking::PeekInterface;
+use serde::Deserialize;
+
+/// Custom Result type alias for Email operations.
+pub type EmailResult<T> = CustomResult<T, EmailError>;
+
+/// A trait that defines the methods that must be implemented to send email.
+#[async_trait::async_trait]
+pub trait EmailClient: Sync + Send + dyn_clone::DynClone {
+    /// Sends an email to the specified recipient with the given subject and body.
+    async fn send_email(
+        &self,
+        recipient: pii::Email,
+        subject: String,
+        body: String,
+    ) -> EmailResult<()>;
+}
+
+dyn_clone::clone_trait_object!(EmailClient);
+
+/// Struct that contains the settings required to construct an EmailClient.
+#[derive(Debug, Clone, Default, Deserialize)]
+pub struct EmailSettings {
+    /// Sender email.
+    pub from_email: String,
+
+    /// The AWS region to send SES requests to.
+    pub aws_region: String,
+
+    /// Base-url used when adding links that should redirect to self
+    pub base_url: String,
+}
+
+/// Client for AWS SES operation
+#[derive(Debug, Clone)]
+pub struct AwsSes {
+    ses_client: Client,
+    from_email: String,
+}
+
+impl AwsSes {
+    /// Constructs a new AwsSes client
+    pub async fn new(conf: &EmailSettings) -> Self {
+        let region_provider = RegionProviderChain::first_try(Region::new(conf.aws_region.clone()));
+        let sdk_config = aws_config::from_env().region(region_provider).load().await;
+
+        Self {
+            ses_client: Client::new(&sdk_config),
+            from_email: conf.from_email.clone(),
+        }
+    }
+}
+
+#[async_trait::async_trait]
+impl EmailClient for AwsSes {
+    async fn send_email(
+        &self,
+        recipient: pii::Email,
+        subject: String,
+        body: String,
+    ) -> EmailResult<()> {
+        self.ses_client
+            .send_email()
+            .from_email_address(self.from_email.to_owned())
+            .destination(
+                Destination::builder()
+                    .to_addresses(recipient.peek())
+                    .build(),
+            )
+            .content(
+                EmailContent::builder()
+                    .simple(
+                        Message::builder()
+                            .subject(Content::builder().data(subject).build())
+                            .body(
+                                Body::builder()
+                                    .text(Content::builder().data(body).charset("UTF-8").build())
+                                    .build(),
+                            )
+                            .build(),
+                    )
+                    .build(),
+            )
+            .send()
+            .await
+            .map_err(AwsSesError::SendingFailure)
+            .into_report()
+            .change_context(EmailError::EmailSendingFailure)?;
+
+        Ok(())
+    }
+}
+
+/// Errors that could occur from EmailClient.
+#[derive(Debug, thiserror::Error)]
+pub enum EmailError {
+    /// An error occurred when building email client.
+    #[error("Error building email client")]
+    ClientBuildingFailure,
+
+    /// An error occurred when sending email
+    #[error("Error sending email to recipient")]
+    EmailSendingFailure,
+}
+
+/// Errors that could occur during SES operations.
+#[derive(Debug, thiserror::Error)]
+pub enum AwsSesError {
+    /// An error occurred in the SDK while sending email.
+    #[error("Failed to Send Email {0:?}")]
+    SendingFailure(aws_smithy_client::SdkError<SendEmailError>),
+}
diff --git a/crates/external_services/src/lib.rs b/crates/external_services/src/lib.rs
index 0a3b1333384..b995276d586 100644
--- a/crates/external_services/src/lib.rs
+++ b/crates/external_services/src/lib.rs
@@ -3,6 +3,9 @@
 #![forbid(unsafe_code)]
 #![warn(missing_docs, missing_debug_implementations)]
 
+#[cfg(feature = "email")]
+pub mod email;
+
 #[cfg(feature = "kms")]
 pub mod kms;
 
diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml
index 088f6129236..ae59a299c41 100644
--- a/crates/router/Cargo.toml
+++ b/crates/router/Cargo.toml
@@ -13,10 +13,11 @@ build = "src/build.rs"
 default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector"]
 s3 = ["dep:aws-sdk-s3","dep:aws-config"]
 kms = ["external_services/kms","dep:aws-config"]
+email = ["external_services/email","dep:aws-config"]
 basilisk = ["kms"]
 stripe = ["dep:serde_qs"]
-sandbox = ["kms", "stripe", "basilisk", "s3"]
-production = ["kms", "stripe", "basilisk", "s3"]
+sandbox = ["kms", "stripe", "basilisk", "s3", "email"]
+production = ["kms", "stripe", "basilisk", "s3", "email"]
 olap = []
 oltp = []
 kv_store = []
diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs
index 40960960551..d7d0d788169 100644
--- a/crates/router/src/configs/settings.rs
+++ b/crates/router/src/configs/settings.rs
@@ -7,6 +7,8 @@ use std::{
 use api_models::enums;
 use common_utils::ext_traits::ConfigExt;
 use config::{Environment, File};
+#[cfg(feature = "email")]
+use external_services::email::EmailSettings;
 #[cfg(feature = "kms")]
 use external_services::kms;
 use redis_interface::RedisSettings;
@@ -69,6 +71,8 @@ pub struct Settings {
     pub connector_customer: ConnectorCustomer,
     #[cfg(feature = "dummy_connector")]
     pub dummy_connector: DummyConnector,
+    #[cfg(feature = "email")]
+    pub email: EmailSettings,
 }
 
 #[derive(Debug, Deserialize, Clone, Default)]
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index faa8eab994a..dbfde139d24 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -1,4 +1,6 @@
 use actix_web::{web, Scope};
+#[cfg(feature = "email")]
+use external_services::email::{AwsSes, EmailClient};
 use tokio::sync::oneshot;
 
 #[cfg(feature = "dummy_connector")]
@@ -22,12 +24,16 @@ pub struct AppState {
     pub flow_name: String,
     pub store: Box<dyn StorageInterface>,
     pub conf: Settings,
+    #[cfg(feature = "email")]
+    pub email_client: Box<dyn EmailClient>,
 }
 
 pub trait AppStateInfo {
     fn conf(&self) -> Settings;
     fn flow_name(&self) -> String;
     fn store(&self) -> Box<dyn StorageInterface>;
+    #[cfg(feature = "email")]
+    fn email_client(&self) -> Box<dyn EmailClient>;
 }
 
 impl AppStateInfo for AppState {
@@ -40,6 +46,10 @@ impl AppStateInfo for AppState {
     fn store(&self) -> Box<dyn StorageInterface> {
         self.store.to_owned()
     }
+    #[cfg(feature = "email")]
+    fn email_client(&self) -> Box<dyn EmailClient> {
+        self.email_client.to_owned()
+    }
 }
 
 impl AppState {
@@ -56,10 +66,15 @@ impl AppState {
             StorageImpl::Mock => Box::new(MockDb::new(&conf).await),
         };
 
+        #[cfg(feature = "email")]
+        #[allow(clippy::expect_used)]
+        let email_client = Box::new(AwsSes::new(&conf.email).await);
         Self {
             flow_name: String::from("default"),
             store,
             conf,
+            #[cfg(feature = "email")]
+            email_client,
         }
     }
 

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
Integrate an email service to OSS. The integration allows us to send emails to our merchants directly from our OSS and helps us to enhance the application experience. Use AWS SES (Simple Email Service) service to develop and build email communications.

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [x] This PR modifies application configuration/environment variables

## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless its an obvious bug or documentation fix
that will have little conversation).
-->
The inclusion of an email service within the application plays a crucial role for notifying merchants when their API keys are about to expire. 

## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
Manual

## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed submitted code
- [ ] I added unit tests for my changes where possible
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
