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