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