safeapp_notifier/
event_keys.rs

1use serde::{
2    Deserialize, Deserializer, Serialize, Serializer,
3    de::{self, Visitor},
4};
5use std::fmt;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum EventKey {
9    Account(AccountEventKey),
10    Sync(SyncEventKey),
11}
12
13impl EventKey {
14    pub fn as_str(&self) -> &'static str {
15        match self {
16            EventKey::Account(key) => key.as_str(),
17            EventKey::Sync(key) => key.as_str(),
18        }
19    }
20
21//    pub fn to_subject(&self) -> String {
22//         match self {
23//             EventKey::Account(account_key) => match account_key {
24//                 // account.*
25//                 AccountEventKey::Welcome => "Welcome to Safe — your AI guard against chargebacks".to_string(),
26//                 AccountEventKey::TierUpgrade => "You’ve been upgraded".to_string(),
27//                 AccountEventKey::ExistingChargebacks => "You have open chargebacks".to_string(),
28//                 AccountEventKey::HighChargebackRatioWarning => "Warning: chargeback rate rising".to_string(),
29//                 AccountEventKey::ProviderDisconnected => "Action needed: connection disconnected".to_string(),
30
31//                 // billing.* (modeled under Account)
32//                 AccountEventKey::BillingPaymentFailed => "Payment failed".to_string(),
33//                 AccountEventKey::BillingInvoiceReady => "Invoice ready".to_string(),
34//             },
35
36//             EventKey::Sync(sync_key) => match sync_key {
37//                 // sync.connection.*
38//                 SyncEventKey::Connection(connection_key) => match connection_key {
39//                     ConnectionEventKey::Finished => "Sync complete — Safe is watching for disputes".to_string(),
40//                     ConnectionEventKey::Added => "Connection successful — monitoring enabled".to_string(),
41//                 },
42
43//                 // sync.disputer.*
44//                 SyncEventKey::Disputer(disputer_key) => match disputer_key {
45//                     DisputerEventKey::PendingCharges(pending_key) => match pending_key {
46//                         PendingChargeEventKey::Payment => "Pending charges payment processed".to_string(),
47//                         PendingChargeEventKey::LimitReached => "Pending charges limit reached".to_string(),
48//                         PendingChargeEventKey::Added => "New pending charge added".to_string(),
49//                     },
50//                     DisputerEventKey::Disputes(dispute_key) => match dispute_key {
51//                         DisputeEventKey::Created => "New dispute detected".to_string(),
52//                         DisputeEventKey::Won => "🎉 You won a chargeback".to_string(),
53//                         DisputeEventKey::Lost => "Outcome: dispute lost".to_string(),
54//                     },
55//                     DisputerEventKey::Evidences(evidence_key) => match evidence_key {
56//                         EvidencesEventKey::Submitted => "Dispute response submitted".to_string(),
57//                     },
58//                 },
59
60//                 // sync.connection.not_created.nudge_day_*
61//                 SyncEventKey::ConnectionNudge(nudge_key) => match nudge_key {
62//                     ConnectionNudgeKey::Day1  => "Connect your account to start protection".to_string(),
63//                     ConnectionNudgeKey::Day2  => "Prevent disputes before they start".to_string(),
64//                     ConnectionNudgeKey::Day4  => "Win disputes automatically with Disputer".to_string(),
65//                     ConnectionNudgeKey::Day7  => "You’re close — finish connecting".to_string(),
66//                     ConnectionNudgeKey::Day30 => "Still here when you’re ready".to_string(),
67//                 },
68
69//                 // sync.chargeback.*
70//                 SyncEventKey::Chargeback(cb_key) => match cb_key {
71//                     ChargebackEventKey::Detected => "New chargeback detected".to_string(),
72//                 },
73
74//                 // sync.response.*
75//                 SyncEventKey::Response(resp_key) => match resp_key {
76//                     ResponseEventKey::DeadlineApproaching => "Urgent: response deadline approaching".to_string(),
77//                 },
78
79//                 // sync.stopper.*
80//                 SyncEventKey::Stopper(stopper_key) => match stopper_key {
81//                     StopperEventKey::PreChargebackAlert => "At-risk transaction detected".to_string(),
82//                     StopperEventKey::AutoRefundExecuted => "Auto-refund executed".to_string(),
83//                 },
84
85//                 // sync.rules.*
86//                 SyncEventKey::Rules(rules_key) => match rules_key {
87//                     RulesEventKey::Created => "Rule created".to_string(),
88//                     RulesEventKey::TriggeredAutoRefund => "Rule triggered: Auto-refund".to_string(),
89//                 },
90//             },
91//         }
92//     }
93}
94
95// Custom serialization (unchanged)
96impl Serialize for EventKey {
97    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
98    where
99        S: Serializer,
100    {
101        serializer.serialize_str(self.as_str())
102    }
103}
104
105// Custom deserialization with fallback for unknown keys
106impl<'de> Deserialize<'de> for EventKey {
107    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
108    where
109        D: Deserializer<'de>,
110    {
111        struct EventKeyVisitor;
112
113        impl<'de> Visitor<'de> for EventKeyVisitor {
114            type Value = EventKey;
115
116            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
117                formatter.write_str("a valid event key string")
118            }
119
120            fn visit_str<E>(self, value: &str) -> Result<EventKey, E>
121            where
122                E: de::Error,
123            {
124                match value {
125                    "account.welcome" => Ok(keys::ACCOUNT_WELCOME),
126                    "account.tier_upgrade" => Ok(keys::ACCOUNT_TIER_UPGRADE),
127                    "sync.connection.finished" => Ok(keys::SYNC_CONNECTION_FINISHED),
128                    "sync.connection.added" => Ok(keys::SYNC_CONNECTION_ADDED),
129                    "sync.disputer.pending_charges.payment" => Ok(keys::SYNC_DISPUTER_PENDING_CHARGES_PAYMENT),
130                    "sync.disputer.pending_charges.limit_reached" => {
131                        Ok(keys::SYNC_DISPUTER_PENDING_CHARGES_LIMIT_REACHED)
132                    }
133                    "sync.disputer.pending_charges.added" => Ok(keys::SYNC_DISPUTER_PENDING_CHARGES_ADDED),
134                    "sync.disputer.disputes.created" => Ok(keys::SYNC_DISPUTER_DISPUTES_CREATED),
135                    "sync.disputer.disputes.won" => Ok(keys::SYNC_DISPUTER_DISPUTES_WON),
136                    "sync.disputer.disputes.lost" => Ok(keys::SYNC_DISPUTER_DISPUTES_LOST),
137                    "sync.disputer.evidences.submitted" => Ok(keys::SYNC_DISPUTER_EVIDENCES_SUBMITTED),
138                    "sync.connection.not_created.nudge_day_1" => Ok(keys::SYNC_CONNECTION_NUDGE_DAY1),
139                    // connection nudges
140                    "sync.connection.not_created.nudge_day_2"  => Ok(keys::SYNC_CONNECTION_NUDGE_DAY2),
141                    "sync.connection.not_created.nudge_day_4"  => Ok(keys::SYNC_CONNECTION_NUDGE_DAY4),
142                    "sync.connection.not_created.nudge_day_7"  => Ok(keys::SYNC_CONNECTION_NUDGE_DAY7),
143                    "sync.connection.not_created.nudge_day_30" => Ok(keys::SYNC_CONNECTION_NUDGE_DAY30),
144
145                    // chargeback + response
146                    // "sync.chargeback.detected"             => Ok(keys::SYNC_CHARGEBACK_DETECTED),
147                    "sync.response.deadline.approaching"   => Ok(keys::SYNC_RESPONSE_DEADLINE_APPROACHING),
148
149                    // stopper
150                    // "sync.stopper.pre_chargeback_alert"    => Ok(keys::SYNC_STOPPER_PRE_CHARGEBACK_ALERT),
151                    // "sync.stopper.auto_refund_executed"    => Ok(keys::SYNC_STOPPER_AUTO_REFUND_EXECUTED),
152
153                    // rules
154                    // "sync.rules.created"                   => Ok(keys::SYNC_RULES_CREATED),
155                    // "sync.rules.triggered.auto_refund"     => Ok(keys::SYNC_RULES_TRIGGERED_AUTO_REFUND),
156
157                    // account extras
158                    "account.existing_chargebacks"         => Ok(keys::ACCOUNT_EXISTING_CHARGEBACKS),
159                    "account.high_chargeback_ratio_warning"=> Ok(keys::ACCOUNT_HIGH_CHARGEBACK_RATIO_WARNING),
160                    "account.provider_disconnected"        => Ok(keys::ACCOUNT_PROVIDER_DISCONNECTED),
161
162                    // billing (mapped under Account variant)
163                    "billing.payment_failed"               => Ok(keys::BILLING_PAYMENT_FAILED),
164                    "billing.invoice_ready"                => Ok(keys::BILLING_INVOICE_READY),
165
166                    unknown => {
167                        // Optionally log unknown keys for debugging
168                        eprintln!("Unknown event key '{}', expected a known key", unknown);
169                        Err(de::Error::custom(format!("Unknown event key: {}", unknown)))
170                    }
171                }
172            }
173        }
174
175        deserializer.deserialize_str(EventKeyVisitor)
176    }
177}
178
179#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
180pub enum AccountEventKey {
181    Welcome,
182    TierUpgrade,
183
184    // NEW account.* keys
185    ExistingChargebacks,
186    HighChargebackRatioWarning,
187    ProviderDisconnected,
188
189    // NEW billing.* (mapped under Account)
190    BillingPaymentFailed,
191    BillingInvoiceReady,
192}
193
194impl AccountEventKey {
195    pub fn as_str(&self) -> &'static str {
196        match self {
197            AccountEventKey::Welcome                     => "account.welcome",
198            AccountEventKey::TierUpgrade                 => "account.tier_upgrade",
199            AccountEventKey::ExistingChargebacks         => "account.existing_chargebacks",
200            AccountEventKey::HighChargebackRatioWarning  => "account.high_chargeback_ratio_warning",
201            AccountEventKey::ProviderDisconnected        => "account.provider_disconnected",
202            AccountEventKey::BillingPaymentFailed        => "billing.payment_failed",
203            AccountEventKey::BillingInvoiceReady         => "billing.invoice_ready",
204        }
205    }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
209pub enum SyncEventKey {
210    Connection(ConnectionEventKey),
211    Disputer(DisputerEventKey),
212
213    // already there:
214    ConnectionNudge(ConnectionNudgeKey),
215
216    // NEW:
217    // Chargeback(ChargebackEventKey),
218    Response(ResponseEventKey),
219    // Stopper(StopperEventKey),
220    // Rules(RulesEventKey),
221}
222
223impl SyncEventKey {
224    pub fn as_str(&self) -> &'static str {
225        match self {
226            SyncEventKey::Connection(key)      => key.as_str(),
227            SyncEventKey::Disputer(key)        => key.as_str(),
228            SyncEventKey::ConnectionNudge(key) => key.as_str(),
229            // SyncEventKey::Chargeback(key)      => key.as_str(),
230            SyncEventKey::Response(key)        => key.as_str(),
231            // SyncEventKey::Stopper(key)         => key.as_str(),
232            // SyncEventKey::Rules(key)           => key.as_str(),
233        }
234    }
235}
236
237#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
238pub enum ConnectionEventKey {
239    Finished,
240    Added,
241}
242
243impl ConnectionEventKey {
244    pub fn as_str(&self) -> &'static str {
245        match self {
246            ConnectionEventKey::Finished => "sync.connection.finished",
247            ConnectionEventKey::Added => "sync.connection.added",
248        }
249    }
250}
251
252#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
253pub enum DisputerEventKey {
254    PendingCharges(PendingChargeEventKey),
255    Disputes(DisputeEventKey),
256    Evidences(EvidencesEventKey),
257}
258
259impl DisputerEventKey {
260    pub fn as_str(&self) -> &'static str {
261        match self {
262            DisputerEventKey::PendingCharges(key) => key.as_str(),
263            DisputerEventKey::Disputes(key) => key.as_str(),
264            DisputerEventKey::Evidences(key) => key.as_str(),
265        }
266    }
267}
268
269#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
270pub enum DisputeEventKey {
271    Created,
272    Won,
273    Lost,
274}
275
276impl DisputeEventKey {
277    pub fn as_str(&self) -> &'static str {
278        match self {
279            DisputeEventKey::Created => "sync.disputer.disputes.created",
280            DisputeEventKey::Won => "sync.disputer.disputes.won",
281            DisputeEventKey::Lost => "sync.disputer.disputes.lost",
282        }
283    }
284}
285
286#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
287pub enum PendingChargeEventKey {
288    Payment,
289    LimitReached,
290    Added,
291}
292
293impl PendingChargeEventKey {
294    pub fn as_str(&self) -> &'static str {
295        match self {
296            PendingChargeEventKey::Payment => "sync.disputer.pending_charges.payment",
297            PendingChargeEventKey::LimitReached => "sync.disputer.pending_charges.limit_reached",
298            PendingChargeEventKey::Added => "sync.disputer.pending_charges.added",
299        }
300    }
301}
302
303#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
304pub enum EvidencesEventKey {
305    Submitted,
306}
307
308impl EvidencesEventKey {
309    pub fn as_str(&self) -> &'static str {
310        match self {
311            EvidencesEventKey::Submitted => "sync.disputer.evidences.submitted",
312        }
313    }
314}
315
316// --- Connection Nudges (extend what you already have) ---
317#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
318pub enum ConnectionNudgeKey {
319    Day1,
320    Day2,
321    Day4,
322    Day7,
323    Day30,
324}
325
326impl ConnectionNudgeKey {
327    pub fn as_str(&self) -> &'static str {
328        match self {
329            ConnectionNudgeKey::Day1  => "sync.connection.not_created.nudge_day_1",
330            ConnectionNudgeKey::Day2  => "sync.connection.not_created.nudge_day_2",
331            ConnectionNudgeKey::Day4  => "sync.connection.not_created.nudge_day_4",
332            ConnectionNudgeKey::Day7  => "sync.connection.not_created.nudge_day_7",
333            ConnectionNudgeKey::Day30 => "sync.connection.not_created.nudge_day_30",
334        }
335    }
336}
337
338// // --- Chargeback ---
339// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
340// pub enum ChargebackEventKey {
341//     Detected,
342// }
343// impl ChargebackEventKey {
344//     pub fn as_str(&self) -> &'static str {
345//         match self {
346//             ChargebackEventKey::Detected => "sync.chargeback.detected",
347//         }
348//     }
349// }
350
351// --- Response (deadlines etc) ---
352#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
353pub enum ResponseEventKey {
354    DeadlineApproaching,
355}
356impl ResponseEventKey {
357    pub fn as_str(&self) -> &'static str {
358        match self {
359            ResponseEventKey::DeadlineApproaching => "sync.response.deadline.approaching",
360        }
361    }
362}
363
364// --- Stopper (pre-chargeback / actions) ---
365// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
366// pub enum StopperEventKey {
367//     PreChargebackAlert,
368//     AutoRefundExecuted,
369// }
370// impl StopperEventKey {
371//     pub fn as_str(&self) -> &'static str {
372//         match self {
373//             StopperEventKey::PreChargebackAlert => "sync.stopper.pre_chargeback_alert",
374//             StopperEventKey::AutoRefundExecuted => "sync.stopper.auto_refund_executed",
375//         }
376//     }
377// }
378
379// // --- Rules (created / triggered) ---
380// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
381// pub enum RulesEventKey {
382//     Created,
383//     TriggeredAutoRefund,
384// }
385// impl RulesEventKey {
386//     pub fn as_str(&self) -> &'static str {
387//         match self {
388//             RulesEventKey::Created => "sync.rules.created",
389//             RulesEventKey::TriggeredAutoRefund => "sync.rules.triggered.auto_refund",
390//         }
391//     }
392// }
393
394pub mod keys {
395    use super::*;
396
397    pub const ACCOUNT_WELCOME: EventKey = EventKey::Account(AccountEventKey::Welcome);
398    pub const ACCOUNT_TIER_UPGRADE: EventKey = EventKey::Account(AccountEventKey::TierUpgrade);
399
400    pub const SYNC_CONNECTION_FINISHED: EventKey =
401        EventKey::Sync(SyncEventKey::Connection(ConnectionEventKey::Finished));
402        
403    pub const SYNC_CONNECTION_ADDED: EventKey = EventKey::Sync(SyncEventKey::Connection(ConnectionEventKey::Added));
404
405    pub const SYNC_DISPUTER_PENDING_CHARGES_PAYMENT: EventKey = EventKey::Sync(SyncEventKey::Disputer(
406        DisputerEventKey::PendingCharges(PendingChargeEventKey::Payment),
407    ));
408    pub const SYNC_DISPUTER_PENDING_CHARGES_LIMIT_REACHED: EventKey = EventKey::Sync(SyncEventKey::Disputer(
409        DisputerEventKey::PendingCharges(PendingChargeEventKey::LimitReached),
410    ));
411    pub const SYNC_DISPUTER_PENDING_CHARGES_ADDED: EventKey = EventKey::Sync(SyncEventKey::Disputer(
412        DisputerEventKey::PendingCharges(PendingChargeEventKey::Added),
413    ));
414    pub const SYNC_DISPUTER_DISPUTES_CREATED: EventKey = EventKey::Sync(SyncEventKey::Disputer(
415        DisputerEventKey::Disputes(DisputeEventKey::Created),
416    ));
417    pub const SYNC_DISPUTER_DISPUTES_WON: EventKey =
418        EventKey::Sync(SyncEventKey::Disputer(DisputerEventKey::Disputes(DisputeEventKey::Won)));
419    pub const SYNC_DISPUTER_DISPUTES_LOST: EventKey = EventKey::Sync(SyncEventKey::Disputer(
420        DisputerEventKey::Disputes(DisputeEventKey::Lost),
421    ));
422    pub const SYNC_DISPUTER_EVIDENCES_SUBMITTED: EventKey = EventKey::Sync(SyncEventKey::Disputer(
423        DisputerEventKey::Evidences(EvidencesEventKey::Submitted),
424    ));
425    
426    pub const SYNC_CONNECTION_NUDGE_DAY1: EventKey = EventKey::Sync(SyncEventKey::ConnectionNudge(ConnectionNudgeKey::Day1));
427
428    pub const SYNC_CONNECTION_NUDGE_DAY2: EventKey =
429        EventKey::Sync(SyncEventKey::ConnectionNudge(ConnectionNudgeKey::Day2));
430    pub const SYNC_CONNECTION_NUDGE_DAY4: EventKey =
431        EventKey::Sync(SyncEventKey::ConnectionNudge(ConnectionNudgeKey::Day4));
432    pub const SYNC_CONNECTION_NUDGE_DAY7: EventKey =
433        EventKey::Sync(SyncEventKey::ConnectionNudge(ConnectionNudgeKey::Day7));
434    pub const SYNC_CONNECTION_NUDGE_DAY30: EventKey =
435        EventKey::Sync(SyncEventKey::ConnectionNudge(ConnectionNudgeKey::Day30));
436
437    // pub const SYNC_CHARGEBACK_DETECTED: EventKey =
438    //     EventKey::Sync(SyncEventKey::Chargeback(ChargebackEventKey::Detected));
439
440    pub const SYNC_RESPONSE_DEADLINE_APPROACHING: EventKey =
441        EventKey::Sync(SyncEventKey::Response(ResponseEventKey::DeadlineApproaching));
442
443    // pub const SYNC_STOPPER_PRE_CHARGEBACK_ALERT: EventKey =
444    //     EventKey::Sync(SyncEventKey::Stopper(StopperEventKey::PreChargebackAlert));
445    // pub const SYNC_STOPPER_AUTO_REFUND_EXECUTED: EventKey =
446    //     EventKey::Sync(SyncEventKey::Stopper(StopperEventKey::AutoRefundExecuted));
447
448    // pub const SYNC_RULES_CREATED: EventKey =
449    //     EventKey::Sync(SyncEventKey::Rules(RulesEventKey::Created));
450    // pub const SYNC_RULES_TRIGGERED_AUTO_REFUND: EventKey =
451    //     EventKey::Sync(SyncEventKey::Rules(RulesEventKey::TriggeredAutoRefund));
452
453    // Account-side new keys
454    pub const ACCOUNT_EXISTING_CHARGEBACKS: EventKey =
455        EventKey::Account(AccountEventKey::ExistingChargebacks);
456    pub const ACCOUNT_HIGH_CHARGEBACK_RATIO_WARNING: EventKey =
457        EventKey::Account(AccountEventKey::HighChargebackRatioWarning);
458    pub const ACCOUNT_PROVIDER_DISCONNECTED: EventKey =
459        EventKey::Account(AccountEventKey::ProviderDisconnected);
460
461    // Billing (kept under Account to avoid new top-level variant)
462    pub const BILLING_PAYMENT_FAILED: EventKey =
463        EventKey::Account(AccountEventKey::BillingPaymentFailed);
464    pub const BILLING_INVOICE_READY: EventKey =
465        EventKey::Account(AccountEventKey::BillingInvoiceReady);
466
467}