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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
//! What a provider posts when a payment finishes without us asking.
use fmt;
use crateError;
use crate;
use crateMoney;
use crate;
use crateRaw;
/// One delivery from a provider, exactly as it arrived.
///
/// Headers and bytes, and nothing parsed: **a signature is over the bytes the
/// provider sent**, and a body that has been through a JSON parser and back is
/// a different sequence of bytes that will not verify. A web framework hands
/// the body over as `&[u8]` before anything else touches it, and that is what
/// belongs here.
///
/// Header names are matched without regard to case, because HTTP/2 lowercases
/// them and HTTP/1.1 does not.
/// What a delivery says happened.
///
/// `Other` is not an error and must never become one. A provider adding an
/// event type is normal, and a handler that answers an error for one it does
/// not know drives the provider into a redelivery loop that can run for days
/// — for something nobody wanted in the first place.
/// What a provider told us happened, once it has been shown to be theirs.
///
/// Every field is public and the struct is open, for the same reason
/// [`Charge`](crate::Charge) is: an adapter in someone else's repository has
/// to be able to build one.
///
/// # Why this is not an enum of [`Charge`](crate::Charge) and [`Refund`](crate::Refund)
///
/// It was the other candidate, and it asks each adapter to build a whole
/// charge out of a delivery. Stripe's webhook carries a PaymentIntent and can;
/// PayTR's notice signs three fields and cannot say what currency they are in;
/// Mollie's carries an identifier and nothing else at all. The shape that
/// survives all three is this one — what happened, to which payment, and the
/// body for everything else — and what the caller does next is read the
/// payment back, which is a call they already have.
/// Checks that a delivery is the provider's, and says what it means.
///
/// Separate from [`Provider`](crate::Provider) because it is a separate thing
/// to hold: verification needs a webhook secret the API credentials do not
/// carry, and a process that takes payments does not always handle the
/// callbacks for them.
///
/// # Verification is not one mechanism
///
/// `verify` is `async` because for two of the four providers that implement it
/// here, checking a delivery is a network call rather than a hash:
///
/// | | how a delivery is shown to be theirs |
/// |---|---|
/// | Stripe | HMAC-SHA256 over `timestamp.body`, with a tolerance window |
/// | PayTR | HMAC-SHA256 over three of the notice's fields |
/// | Mollie | **nothing is signed** — the delivery carries an identifier, and the payment is read back over the merchant's own authenticated connection |
/// | PayPal | PayPal verifies it, at `/v1/notifications/verify-webhook-signature` |
///
/// A trait that took only `(headers, body) -> Result<Event, Error>`
/// synchronously would fit the first two and force the other two to lie.
///
/// # Answering the provider is not this trait's business
///
/// An `Err` here says **do not act on this**. It does not say what to answer:
/// PayTR retries any reply that is not exactly `OK` for days, so a handler
/// that turns [`ErrorKind::Untrusted`](crate::ErrorKind::Untrusted) into a 500
/// has arranged for a forged notice to be delivered again every hour. Answer
/// the provider what the provider documents, and act only on `Ok`.