1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! Standard notification-trigger stack for the internal cex-notification API
//! (`/admin/api/v1/events/trigger`, Novu-shaped).
//!
//! Every service that needs to notify a user goes through this module instead of
//! hand-rolling the HTTP POST, so the wire contract lives in one place. The call is
//! an internal cluster request over `http://` (mesh mTLS handles transport).
//!
//! # Two levels
//!
//! **[`Notifier`]** is what a service should reach for. It answers the two questions
//! every trigger site has to answer — *who is the subscriber, and in what language* —
//! by delegating to an [`AudienceResolver`], then fires the workflow. The service only
//! describes **what happened**, by implementing [`NotificationEvent`]:
//!
//! ```ignore
//! struct FiatWithdrawalRefunded { amount: String, currency: String }
//!
//! impl NotificationEvent for FiatWithdrawalRefunded {
//! fn workflow(&self) -> &str { workflows::FIAT_WITHDRAWAL_FAILED_REFUNDED }
//! fn payload(&self) -> serde_json::Value {
//! serde_json::json!({ "amount": self.amount, "currency": self.currency })
//! }
//! }
//!
//! // The ledger holds an account id, cex-auth holds a subject — same call either way.
//! notifier.notify_detached(RecipientRef::AccountId(42), FiatWithdrawalRefunded { .. });
//! ```
//!
//! Wiring one up is a single line in the service's container — see
//! [`Notifier::from_env`], which reads the three environment variables the stack needs
//! and returns `None` when this service is not set up to notify.
//!
//! That is the whole extension point: a new notification is a new `NotificationEvent`
//! implementation plus a constant in [`workflows`]. No new client, no new config, no
//! copy of the locale lookup.
//!
//! **[`NotificationClient`] + [`TriggerRequest`]** are the raw wire layer underneath.
//! Use them directly only when the subscriber and locale are already in hand and an
//! [`AudienceResolver`] would be a pointless round-trip — cex-auth's OTP path, where
//! the session already carries both.
//!
//! # Delivery contact is not this module's job
//!
//! A trigger carries `subscriberId` and `locale`, never email or phone. cex-notification
//! backfills the delivery contact from cex-account itself
//! (`SubscriberContactEnrichmentService`), which is what keeps member PII out of every
//! service that merely wants to send a message. [`Recipient::email`] /
//! [`Recipient::phone`] exist for the callers that already hold a verified address and
//! would otherwise force a redundant lookup.
pub use ;
pub use ;
pub use ;
pub use NotificationEvent;
pub use mask_iban;
pub use ;
pub use ;
pub use ;