Skip to main content

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.disputes.missing_evidence_reminder" => Ok(keys::SYNC_DISPUTER_DISPUTES_MISSING_EVIDENCE_REMINDER),
138                    "sync.disputer.evidences.submitted" => Ok(keys::SYNC_DISPUTER_EVIDENCES_SUBMITTED),
139                    "sync.connection.not_created.nudge_day_1" => Ok(keys::SYNC_CONNECTION_NUDGE_DAY1),
140                    // connection nudges
141                    "sync.connection.not_created.nudge_day_2"  => Ok(keys::SYNC_CONNECTION_NUDGE_DAY2),
142                    "sync.connection.not_created.nudge_day_4"  => Ok(keys::SYNC_CONNECTION_NUDGE_DAY4),
143                    "sync.connection.not_created.nudge_day_7"  => Ok(keys::SYNC_CONNECTION_NUDGE_DAY7),
144                    "sync.connection.not_created.nudge_day_30" => Ok(keys::SYNC_CONNECTION_NUDGE_DAY30),
145
146                    // chargeback + response
147                    // "sync.chargeback.detected"             => Ok(keys::SYNC_CHARGEBACK_DETECTED),
148                    "sync.response.deadline.approaching"   => Ok(keys::SYNC_RESPONSE_DEADLINE_APPROACHING),
149
150                    // stopper
151                    // "sync.stopper.pre_chargeback_alert"    => Ok(keys::SYNC_STOPPER_PRE_CHARGEBACK_ALERT),
152                    // "sync.stopper.auto_refund_executed"    => Ok(keys::SYNC_STOPPER_AUTO_REFUND_EXECUTED),
153
154                    // rules
155                    // "sync.rules.created"                   => Ok(keys::SYNC_RULES_CREATED),
156                    // "sync.rules.triggered.auto_refund"     => Ok(keys::SYNC_RULES_TRIGGERED_AUTO_REFUND),
157
158                    // account extras
159                    "account.existing_chargebacks"         => Ok(keys::ACCOUNT_EXISTING_CHARGEBACKS),
160                    "account.high_chargeback_ratio_warning"=> Ok(keys::ACCOUNT_HIGH_CHARGEBACK_RATIO_WARNING),
161                    "account.provider_disconnected"        => Ok(keys::ACCOUNT_PROVIDER_DISCONNECTED),
162
163                    // billing (mapped under Account variant)
164                    "billing.payment_failed"               => Ok(keys::BILLING_PAYMENT_FAILED),
165                    "billing.invoice_ready"                => Ok(keys::BILLING_INVOICE_READY),
166
167                    unknown => {
168                        // Optionally log unknown keys for debugging
169                        eprintln!("Unknown event key '{}', expected a known key", unknown);
170                        Err(de::Error::custom(format!("Unknown event key: {}", unknown)))
171                    }
172                }
173            }
174        }
175
176        deserializer.deserialize_str(EventKeyVisitor)
177    }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
181pub enum AccountEventKey {
182    Welcome,
183    TierUpgrade,
184
185    // NEW account.* keys
186    ExistingChargebacks,
187    HighChargebackRatioWarning,
188    ProviderDisconnected,
189
190    // NEW billing.* (mapped under Account)
191    BillingPaymentFailed,
192    BillingInvoiceReady,
193}
194
195impl AccountEventKey {
196    pub fn as_str(&self) -> &'static str {
197        match self {
198            AccountEventKey::Welcome                     => "account.welcome",
199            AccountEventKey::TierUpgrade                 => "account.tier_upgrade",
200            AccountEventKey::ExistingChargebacks         => "account.existing_chargebacks",
201            AccountEventKey::HighChargebackRatioWarning  => "account.high_chargeback_ratio_warning",
202            AccountEventKey::ProviderDisconnected        => "account.provider_disconnected",
203            AccountEventKey::BillingPaymentFailed        => "billing.payment_failed",
204            AccountEventKey::BillingInvoiceReady         => "billing.invoice_ready",
205        }
206    }
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
210pub enum SyncEventKey {
211    Connection(ConnectionEventKey),
212    Disputer(DisputerEventKey),
213
214    // already there:
215    ConnectionNudge(ConnectionNudgeKey),
216
217    // NEW:
218    // Chargeback(ChargebackEventKey),
219    Response(ResponseEventKey),
220    // Stopper(StopperEventKey),
221    // Rules(RulesEventKey),
222}
223
224impl SyncEventKey {
225    pub fn as_str(&self) -> &'static str {
226        match self {
227            SyncEventKey::Connection(key)      => key.as_str(),
228            SyncEventKey::Disputer(key)        => key.as_str(),
229            SyncEventKey::ConnectionNudge(key) => key.as_str(),
230            // SyncEventKey::Chargeback(key)      => key.as_str(),
231            SyncEventKey::Response(key)        => key.as_str(),
232            // SyncEventKey::Stopper(key)         => key.as_str(),
233            // SyncEventKey::Rules(key)           => key.as_str(),
234        }
235    }
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
239pub enum ConnectionEventKey {
240    Finished,
241    Added,
242}
243
244impl ConnectionEventKey {
245    pub fn as_str(&self) -> &'static str {
246        match self {
247            ConnectionEventKey::Finished => "sync.connection.finished",
248            ConnectionEventKey::Added => "sync.connection.added",
249        }
250    }
251}
252
253#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
254pub enum DisputerEventKey {
255    PendingCharges(PendingChargeEventKey),
256    Disputes(DisputeEventKey),
257    Evidences(EvidencesEventKey),
258}
259
260impl DisputerEventKey {
261    pub fn as_str(&self) -> &'static str {
262        match self {
263            DisputerEventKey::PendingCharges(key) => key.as_str(),
264            DisputerEventKey::Disputes(key) => key.as_str(),
265            DisputerEventKey::Evidences(key) => key.as_str(),
266        }
267    }
268}
269
270#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
271pub enum DisputeEventKey {
272    Created,
273    Won,
274    Lost,
275    MissingEvidenceReminder
276}
277
278impl DisputeEventKey {
279    pub fn as_str(&self) -> &'static str {
280        match self {
281            DisputeEventKey::Created => "sync.disputer.disputes.created",
282            DisputeEventKey::Won => "sync.disputer.disputes.won",
283            DisputeEventKey::Lost => "sync.disputer.disputes.lost",
284            DisputeEventKey::MissingEvidenceReminder => "sync.disputer.disputes.missing_evidence_reminder",
285        }
286    }
287}
288
289#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
290pub enum PendingChargeEventKey {
291    Payment,
292    LimitReached,
293    Added,
294}
295
296impl PendingChargeEventKey {
297    pub fn as_str(&self) -> &'static str {
298        match self {
299            PendingChargeEventKey::Payment => "sync.disputer.pending_charges.payment",
300            PendingChargeEventKey::LimitReached => "sync.disputer.pending_charges.limit_reached",
301            PendingChargeEventKey::Added => "sync.disputer.pending_charges.added",
302        }
303    }
304}
305
306#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
307pub enum EvidencesEventKey {
308    Submitted,
309}
310
311impl EvidencesEventKey {
312    pub fn as_str(&self) -> &'static str {
313        match self {
314            EvidencesEventKey::Submitted => "sync.disputer.evidences.submitted",
315        }
316    }
317}
318
319// --- Connection Nudges (extend what you already have) ---
320#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
321pub enum ConnectionNudgeKey {
322    Day1,
323    Day2,
324    Day4,
325    Day7,
326    Day30,
327}
328
329impl ConnectionNudgeKey {
330    pub fn as_str(&self) -> &'static str {
331        match self {
332            ConnectionNudgeKey::Day1  => "sync.connection.not_created.nudge_day_1",
333            ConnectionNudgeKey::Day2  => "sync.connection.not_created.nudge_day_2",
334            ConnectionNudgeKey::Day4  => "sync.connection.not_created.nudge_day_4",
335            ConnectionNudgeKey::Day7  => "sync.connection.not_created.nudge_day_7",
336            ConnectionNudgeKey::Day30 => "sync.connection.not_created.nudge_day_30",
337        }
338    }
339}
340
341// // --- Chargeback ---
342// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
343// pub enum ChargebackEventKey {
344//     Detected,
345// }
346// impl ChargebackEventKey {
347//     pub fn as_str(&self) -> &'static str {
348//         match self {
349//             ChargebackEventKey::Detected => "sync.chargeback.detected",
350//         }
351//     }
352// }
353
354// --- Response (deadlines etc) ---
355#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
356pub enum ResponseEventKey {
357    DeadlineApproaching,
358}
359impl ResponseEventKey {
360    pub fn as_str(&self) -> &'static str {
361        match self {
362            ResponseEventKey::DeadlineApproaching => "sync.response.deadline.approaching",
363        }
364    }
365}
366
367// --- Stopper (pre-chargeback / actions) ---
368// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
369// pub enum StopperEventKey {
370//     PreChargebackAlert,
371//     AutoRefundExecuted,
372// }
373// impl StopperEventKey {
374//     pub fn as_str(&self) -> &'static str {
375//         match self {
376//             StopperEventKey::PreChargebackAlert => "sync.stopper.pre_chargeback_alert",
377//             StopperEventKey::AutoRefundExecuted => "sync.stopper.auto_refund_executed",
378//         }
379//     }
380// }
381
382// // --- Rules (created / triggered) ---
383// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
384// pub enum RulesEventKey {
385//     Created,
386//     TriggeredAutoRefund,
387// }
388// impl RulesEventKey {
389//     pub fn as_str(&self) -> &'static str {
390//         match self {
391//             RulesEventKey::Created => "sync.rules.created",
392//             RulesEventKey::TriggeredAutoRefund => "sync.rules.triggered.auto_refund",
393//         }
394//     }
395// }
396
397pub mod keys {
398    use super::*;
399
400    pub const ACCOUNT_WELCOME: EventKey = EventKey::Account(AccountEventKey::Welcome);
401    pub const ACCOUNT_TIER_UPGRADE: EventKey = EventKey::Account(AccountEventKey::TierUpgrade);
402
403    pub const SYNC_CONNECTION_FINISHED: EventKey =
404        EventKey::Sync(SyncEventKey::Connection(ConnectionEventKey::Finished));
405        
406    pub const SYNC_CONNECTION_ADDED: EventKey = EventKey::Sync(SyncEventKey::Connection(ConnectionEventKey::Added));
407
408    pub const SYNC_DISPUTER_PENDING_CHARGES_PAYMENT: EventKey = EventKey::Sync(SyncEventKey::Disputer(
409        DisputerEventKey::PendingCharges(PendingChargeEventKey::Payment),
410    ));
411    pub const SYNC_DISPUTER_PENDING_CHARGES_LIMIT_REACHED: EventKey = EventKey::Sync(SyncEventKey::Disputer(
412        DisputerEventKey::PendingCharges(PendingChargeEventKey::LimitReached),
413    ));
414    pub const SYNC_DISPUTER_PENDING_CHARGES_ADDED: EventKey = EventKey::Sync(SyncEventKey::Disputer(
415        DisputerEventKey::PendingCharges(PendingChargeEventKey::Added),
416    ));
417    pub const SYNC_DISPUTER_DISPUTES_CREATED: EventKey = EventKey::Sync(SyncEventKey::Disputer(
418        DisputerEventKey::Disputes(DisputeEventKey::Created),
419    ));
420    pub const SYNC_DISPUTER_DISPUTES_WON: EventKey =
421        EventKey::Sync(SyncEventKey::Disputer(DisputerEventKey::Disputes(DisputeEventKey::Won)));
422    pub const SYNC_DISPUTER_DISPUTES_LOST: EventKey = EventKey::Sync(SyncEventKey::Disputer(
423        DisputerEventKey::Disputes(DisputeEventKey::Lost),
424    ));
425    pub const SYNC_DISPUTER_DISPUTES_MISSING_EVIDENCE_REMINDER: EventKey = EventKey::Sync(SyncEventKey::Disputer(
426        DisputerEventKey::Disputes(DisputeEventKey::MissingEvidenceReminder),
427    ));
428    pub const SYNC_DISPUTER_EVIDENCES_SUBMITTED: EventKey = EventKey::Sync(SyncEventKey::Disputer(
429        DisputerEventKey::Evidences(EvidencesEventKey::Submitted),
430    ));
431    
432    pub const SYNC_CONNECTION_NUDGE_DAY1: EventKey = EventKey::Sync(SyncEventKey::ConnectionNudge(ConnectionNudgeKey::Day1));
433
434    pub const SYNC_CONNECTION_NUDGE_DAY2: EventKey =
435        EventKey::Sync(SyncEventKey::ConnectionNudge(ConnectionNudgeKey::Day2));
436    pub const SYNC_CONNECTION_NUDGE_DAY4: EventKey =
437        EventKey::Sync(SyncEventKey::ConnectionNudge(ConnectionNudgeKey::Day4));
438    pub const SYNC_CONNECTION_NUDGE_DAY7: EventKey =
439        EventKey::Sync(SyncEventKey::ConnectionNudge(ConnectionNudgeKey::Day7));
440    pub const SYNC_CONNECTION_NUDGE_DAY30: EventKey =
441        EventKey::Sync(SyncEventKey::ConnectionNudge(ConnectionNudgeKey::Day30));
442
443    // pub const SYNC_CHARGEBACK_DETECTED: EventKey =
444    //     EventKey::Sync(SyncEventKey::Chargeback(ChargebackEventKey::Detected));
445
446    pub const SYNC_RESPONSE_DEADLINE_APPROACHING: EventKey =
447        EventKey::Sync(SyncEventKey::Response(ResponseEventKey::DeadlineApproaching));
448
449    // pub const SYNC_STOPPER_PRE_CHARGEBACK_ALERT: EventKey =
450    //     EventKey::Sync(SyncEventKey::Stopper(StopperEventKey::PreChargebackAlert));
451    // pub const SYNC_STOPPER_AUTO_REFUND_EXECUTED: EventKey =
452    //     EventKey::Sync(SyncEventKey::Stopper(StopperEventKey::AutoRefundExecuted));
453
454    // pub const SYNC_RULES_CREATED: EventKey =
455    //     EventKey::Sync(SyncEventKey::Rules(RulesEventKey::Created));
456    // pub const SYNC_RULES_TRIGGERED_AUTO_REFUND: EventKey =
457    //     EventKey::Sync(SyncEventKey::Rules(RulesEventKey::TriggeredAutoRefund));
458
459    // Account-side new keys
460    pub const ACCOUNT_EXISTING_CHARGEBACKS: EventKey =
461        EventKey::Account(AccountEventKey::ExistingChargebacks);
462    pub const ACCOUNT_HIGH_CHARGEBACK_RATIO_WARNING: EventKey =
463        EventKey::Account(AccountEventKey::HighChargebackRatioWarning);
464    pub const ACCOUNT_PROVIDER_DISCONNECTED: EventKey =
465        EventKey::Account(AccountEventKey::ProviderDisconnected);
466
467    // Billing (kept under Account to avoid new top-level variant)
468    pub const BILLING_PAYMENT_FAILED: EventKey =
469        EventKey::Account(AccountEventKey::BillingPaymentFailed);
470    pub const BILLING_INVOICE_READY: EventKey =
471        EventKey::Account(AccountEventKey::BillingInvoiceReady);
472
473}