1use std::collections::HashMap;
2
3use chrono::Utc;
4#[cfg(feature = "webhook-events")]
5use hmac::{Hmac, Mac};
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8#[cfg(feature = "webhook-events")]
9use sha2::Sha256;
10use smart_default::SmartDefault;
11
12use crate::error::WebhookError;
13use crate::resources::*;
14
15#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Hash, SmartDefault)]
16pub enum EventType {
17 #[serde(rename = "account.application.authorized")]
18 AccountApplicationAuthorized,
19 #[serde(rename = "account.application.deauthorized")]
20 AccountApplicationDeauthorized,
21 #[serde(rename = "account.external_account.created")]
22 AccountExternalAccountCreated,
23 #[serde(rename = "account.external_account.deleted")]
24 AccountExternalAccountDeleted,
25 #[serde(rename = "account.external_account.updated")]
26 AccountExternalAccountUpdated,
27 #[serde(rename = "account.updated")]
28 AccountUpdated,
29 #[serde(rename = "application_fee.created")]
30 ApplicationFeeCreated,
31 #[serde(rename = "application_fee.refund.updated")]
32 ApplicationFeeRefundUpdated,
33 #[serde(rename = "application_fee.refunded")]
34 ApplicationFeeRefunded,
35 #[serde(rename = "balance.available")]
36 BalanceAvailable,
37 #[serde(rename = "billing_portal.configuration.created")]
38 BillingPortalConfigurationCreated,
39 #[serde(rename = "billing_portal.configuration.updated")]
40 BillingPortalConfigurationUpdated,
41 #[serde(rename = "capability.updated")]
42 CapabilityUpdated,
43 #[serde(rename = "cash_balance.funds_available")]
44 CashBalanceFundsAvailable,
45 #[serde(rename = "charge.captured")]
46 ChargeCaptured,
47 #[serde(rename = "charge.dispute.closed")]
48 ChargeDisputeClosed,
49 #[serde(rename = "charge.dispute.created")]
50 ChargeDisputeCreated,
51 #[serde(rename = "charge.dispute.funds_reinstated")]
52 ChargeDisputeFundsReinstated,
53 #[serde(rename = "charge.dispute.funds_withdrawn")]
54 ChargeDisputeFundsWithdrawn,
55 #[serde(rename = "charge.dispute.updated")]
56 ChargeDisputeUpdated,
57 #[serde(rename = "charge.expired")]
58 ChargeExpired,
59 #[serde(rename = "charge.failed")]
60 ChargeFailed,
61 #[serde(rename = "charge.pending")]
62 ChargePending,
63 #[serde(rename = "charge.refund.updated")]
64 ChargeRefundUpdated,
65 #[serde(rename = "charge.refunded")]
66 ChargeRefunded,
67 #[serde(rename = "charge.succeeded")]
68 ChargeSucceeded,
69 #[serde(rename = "charge.updated")]
70 ChargeUpdated,
71 #[serde(rename = "checkout.session.async_payment_failed")]
72 CheckoutSessionAsyncPaymentFailed,
73 #[serde(rename = "checkout.session.async_payment_succeeded")]
74 CheckoutSessionAsyncPaymentSucceeded,
75 #[serde(rename = "checkout.session.completed")]
76 CheckoutSessionCompleted,
77 #[serde(rename = "checkout.session.expired")]
78 CheckoutSessionExpired,
79 #[serde(rename = "coupon.created")]
80 CouponCreated,
81 #[serde(rename = "coupon.deleted")]
82 CouponDeleted,
83 #[serde(rename = "coupon.updated")]
84 CouponUpdated,
85 #[serde(rename = "credit_note.created")]
86 CreditNoteCreated,
87 #[serde(rename = "credit_note.updated")]
88 CreditNoteUpdated,
89 #[serde(rename = "credit_note.voided")]
90 CreditNoteVoided,
91 #[serde(rename = "customer.created")]
92 CustomerCreated,
93 #[serde(rename = "customer.deleted")]
94 CustomerDeleted,
95 #[serde(rename = "customer.discount.created")]
96 CustomerDiscountCreated,
97 #[serde(rename = "customer.discount.deleted")]
98 CustomerDiscountDeleted,
99 #[serde(rename = "customer.discount.updated")]
100 CustomerDiscountUpdated,
101 #[serde(rename = "customer.source.created")]
102 CustomerSourceCreated,
103 #[serde(rename = "customer.source.deleted")]
104 CustomerSourceDeleted,
105 #[serde(rename = "customer.source.expiring")]
106 CustomerSourceExpiring,
107 #[serde(rename = "customer.source.updated")]
108 CustomerSourceUpdated,
109 #[serde(rename = "customer.subscription.created")]
110 CustomerSubscriptionCreated,
111 #[serde(rename = "customer.subscription.deleted")]
112 CustomerSubscriptionDeleted,
113 #[serde(rename = "customer.subscription.paused")]
114 CustomerSubscriptionPaused,
115 #[serde(rename = "customer.subscription.pending_update_applied")]
116 CustomerSubscriptionPendingUpdateApplied,
117 #[serde(rename = "customer.subscription.pending_update_expired")]
118 CustomerSubscriptionPendingUpdateExpired,
119 #[serde(rename = "customer.subscription.resumed")]
120 CustomerSubscriptionResumed,
121 #[serde(rename = "customer.subscription.trial_will_end")]
122 CustomerSubscriptionTrialWillEnd,
123 #[serde(rename = "customer.subscription.updated")]
124 CustomerSubscriptionUpdated,
125 #[serde(rename = "customer.tax_id.created")]
126 CustomerTaxIdCreated,
127 #[serde(rename = "customer.tax_id.deleted")]
128 CustomerTaxIdDeleted,
129 #[serde(rename = "customer.tax_id.updated")]
130 CustomerTaxIdUpdated,
131 #[serde(rename = "customer.updated")]
132 CustomerUpdated,
133 #[serde(rename = "file.created")]
134 FileCreated,
135 #[serde(rename = "identity.verification_session.canceled")]
136 IdentityVerificationSessionCanceled,
137 #[serde(rename = "identity.verification_session.created")]
138 IdentityVerificationSessionCreated,
139 #[serde(rename = "identity.verification_session.processing")]
140 IdentityVerificationSessionProcessing,
141 #[serde(rename = "identity.verification_session.redacted")]
142 IdentityVerificationSessionRedacted,
143 #[serde(rename = "identity.verification_session.requires_input")]
144 IdentityVerificationSessionRequiresInput,
145 #[serde(rename = "identity.verification_session.verified")]
146 IdentityVerificationSessionVerified,
147 #[serde(rename = "invoice.created")]
148 InvoiceCreated,
149 #[serde(rename = "invoice.deleted")]
150 InvoiceDeleted,
151 #[serde(rename = "invoice.finalization_failed")]
152 InvoiceFinalizationFailed,
153 #[serde(rename = "invoice.finalized")]
154 InvoiceFinalized,
155 #[serde(rename = "invoice.marked_uncollectible")]
156 InvoiceMarkedUncollectible,
157 #[serde(rename = "invoice.paid")]
158 InvoicePaid,
159 #[serde(rename = "invoice.payment_action_required")]
160 InvoicePaymentActionRequired,
161 #[serde(rename = "invoice.payment_failed")]
162 InvoicePaymentFailed,
163 #[serde(rename = "invoice.payment_succeeded")]
164 InvoicePaymentSucceeded,
165 #[serde(rename = "invoice.sent")]
166 InvoiceSent,
167 #[serde(rename = "invoice.upcoming")]
168 InvoiceUpcoming,
169 #[serde(rename = "invoice.updated")]
170 InvoiceUpdated,
171 #[serde(rename = "invoice.voided")]
172 InvoiceVoided,
173 #[serde(rename = "invoiceitem.created")]
174 InvoiceItemCreated,
175 #[serde(rename = "invoiceitem.deleted")]
176 InvoiceItemDeleted,
177 #[serde(rename = "invoiceitem.updated")]
178 InvoiceItemUpdated,
179 #[serde(rename = "issuing_authorization.created")]
180 IssuingAuthorizationCreated,
181 #[serde(rename = "issuing_authorization.request")]
182 IssuingAuthorizationRequest,
183 #[serde(rename = "issuing_authorization.updated")]
184 IssuingAuthorizationUpdated,
185 #[serde(rename = "issuing_card.created")]
186 IssuingCardCreated,
187 #[serde(rename = "issuing_card.updated")]
188 IssuingCardUpdated,
189 #[serde(rename = "issuing_cardholder.created")]
190 IssuingCardholderCreated,
191 #[serde(rename = "issuing_cardholder.updated")]
192 IssuingCardholderUpdated,
193 #[serde(rename = "issuing_dispute.closed")]
194 IssuingDisputeClosed,
195 #[serde(rename = "issuing_dispute.created")]
196 IssuingDisputeCreated,
197 #[serde(rename = "issuing_dispute.funds_reinstated")]
198 IssuingDisputeFundsReinstated,
199 #[serde(rename = "issuing_dispute.submitted")]
200 IssuingDisputeSubmitted,
201 #[serde(rename = "issuing_dispute.updated")]
202 IssuingDisputeUpdated,
203 #[serde(rename = "issuing_transaction.created")]
204 IssuingTransactionCreated,
205 #[serde(rename = "issuing_transaction.updated")]
206 IssuingTransactionUpdated,
207 #[serde(rename = "mandate.updated")]
208 MandateUpdated,
209 #[serde(rename = "order.created")]
210 OrderCreated,
211 #[serde(rename = "order.payment_failed")]
212 OrderPaymentFailed,
213 #[serde(rename = "order.payment_succeeded")]
214 OrderPaymentSucceeded,
215 #[serde(rename = "order.updated")]
216 OrderUpdated,
217 #[serde(rename = "order_return.created")]
218 OrderReturnCreated,
219 #[serde(rename = "order_return.updated")]
220 OrderReturnUpdated,
221 #[serde(rename = "payment_intent.amount_capturable_updated")]
222 PaymentIntentAmountCapturableUpdated,
223 #[serde(rename = "payment_intent.canceled")]
224 PaymentIntentCanceled,
225 #[serde(rename = "payment_intent.created")]
226 PaymentIntentCreated,
227 #[serde(rename = "payment_intent.partially_funded")]
228 PaymentIntentPartiallyFunded,
229 #[serde(rename = "payment_intent.payment_failed")]
230 PaymentIntentPaymentFailed,
231 #[serde(rename = "payment_intent.processing")]
232 PaymentIntentProcessing,
233 #[serde(rename = "payment_intent.requires_action")]
234 PaymentIntentRequiresAction,
235 #[serde(rename = "payment_intent.requires_capture")]
236 PaymentIntentRequiresCapture,
237 #[serde(rename = "payment_intent.succeeded")]
238 PaymentIntentSucceeded,
239 #[serde(rename = "payment_link.created")]
240 PaymentLinkCreated,
241 #[serde(rename = "payment_link.updated")]
242 PaymentLinkUpdated,
243 #[serde(rename = "payment_method.attached")]
244 PaymentMethodAttached,
245 #[serde(rename = "payment_method.automatically_updated")]
246 PaymentMethodAutomaticallyUpdated,
247 #[serde(rename = "payment_method.detached")]
248 PaymentMethodDetached,
249 #[serde(rename = "payment_method.updated")]
250 PaymentMethodUpdated,
251 #[serde(rename = "payout.canceled")]
252 PayoutCanceled,
253 #[serde(rename = "payout.created")]
254 PayoutCreated,
255 #[serde(rename = "payout.failed")]
256 PayoutFailed,
257 #[serde(rename = "payout.paid")]
258 PayoutPaid,
259 #[serde(rename = "payout.updated")]
260 PayoutUpdated,
261 #[serde(rename = "person.created")]
262 PersonCreated,
263 #[serde(rename = "person.deleted")]
264 PersonDeleted,
265 #[serde(rename = "person.updated")]
266 PersonUpdated,
267 #[serde(rename = "plan.created")]
268 PlanCreated,
269 #[serde(rename = "plan.deleted")]
270 PlanDeleted,
271 #[serde(rename = "plan.updated")]
272 PlanUpdated,
273 #[serde(rename = "price.created")]
274 PriceCreated,
275 #[serde(rename = "price.deleted")]
276 PriceDeleted,
277 #[serde(rename = "price.updated")]
278 PriceUpdated,
279 #[serde(rename = "product.created")]
280 ProductCreated,
281 #[serde(rename = "product.deleted")]
282 ProductDeleted,
283 #[serde(rename = "product.updated")]
284 ProductUpdated,
285 #[serde(rename = "promotion_code.created")]
286 PromotionCodeCreated,
287 #[serde(rename = "promotion_code.updated")]
288 PromotionCodeUpdated,
289 #[serde(rename = "quote.accepted")]
290 QuoteAccepted,
291 #[serde(rename = "quote.canceled")]
292 QuoteCanceled,
293 #[serde(rename = "quote.created")]
294 QuoteCreated,
295 #[serde(rename = "quote.finalized")]
296 QuoteFinalized,
297 #[serde(rename = "radar.early_fraud_warning.created")]
298 RadarEarlyFraudWarningCreated,
299 #[serde(rename = "radar.early_fraud_warning.updated")]
300 RadarEarlyFraudWarningUpdated,
301 #[serde(rename = "recipient.created")]
302 RecipientCreated,
303 #[serde(rename = "recipient.deleted")]
304 RecipientDeleted,
305 #[serde(rename = "recipient.updated")]
306 RecipientUpdated,
307 #[serde(rename = "refund.created")]
308 RefundCreated,
309 #[serde(rename = "refund.updated")]
310 RefundUpdated,
311 #[serde(rename = "reporting.report_run.failed")]
312 ReportingReportRunFailed,
313 #[serde(rename = "reporting.report_run.succeeded")]
314 ReportingReportRunSucceeded,
315 #[serde(rename = "reporting.report_type.updated")]
316 ReportingReportTypeUpdated,
317 #[serde(rename = "review.closed")]
318 ReviewClosed,
319 #[serde(rename = "review.opened")]
320 ReviewOpened,
321 #[serde(rename = "setup_intent.canceled")]
322 SetupIntentCanceled,
323 #[serde(rename = "setup_intent.created")]
324 SetupIntentCreated,
325 #[serde(rename = "setup_intent.requires_action")]
326 SetupIntentRequiresAction,
327 #[serde(rename = "setup_intent.setup_failed")]
328 SetupIntentSetupFailed,
329 #[serde(rename = "setup_intent.succeeded")]
330 SetupIntentSucceeded,
331 #[serde(rename = "sigma.scheduled_query_run.created")]
332 SigmaScheduledQueryRunCreated,
333 #[serde(rename = "sku.created")]
334 SkuCreated,
335 #[serde(rename = "sku.deleted")]
336 SkuDeleted,
337 #[serde(rename = "sku.updated")]
338 SkuUpdated,
339 #[serde(rename = "source.canceled")]
340 SourceCanceled,
341 #[serde(rename = "source.chargeable")]
342 SourceChargeable,
343 #[serde(rename = "source.failed")]
344 SourceFailed,
345 #[serde(rename = "source.mandate_notification")]
346 SourceMandateNotification,
347 #[serde(rename = "source.refund_attributes_required")]
348 SourceRefundAttributesRequired,
349 #[serde(rename = "source.transaction.created")]
350 SourceTransactionCreated,
351 #[serde(rename = "source.transaction.updated")]
352 SourceTransactionUpdated,
353 #[serde(rename = "subscription_schedule.aborted")]
354 SubscriptionScheduleAborted,
355 #[serde(rename = "subscription_schedule.canceled")]
356 SubscriptionScheduleCanceled,
357 #[serde(rename = "subscription_schedule.completed")]
358 SubscriptionScheduleCompleted,
359 #[serde(rename = "subscription_schedule.created")]
360 SubscriptionScheduleCreated,
361 #[serde(rename = "subscription_schedule.expiring")]
362 SubscriptionScheduleExpiring,
363 #[serde(rename = "subscription_schedule.released")]
364 SubscriptionScheduleReleased,
365 #[serde(rename = "subscription_schedule.updated")]
366 SubscriptionScheduleUpdated,
367 #[serde(rename = "tax_rate.created")]
368 TaxRateCreated,
369 #[serde(rename = "tax_rate.updated")]
370 TaxRateUpdated,
371 #[serde(rename = "terminal.reader.action_failed")]
372 TerminalReaderActionFailed,
373 #[serde(rename = "terminal.reader.action_succeeded")]
374 TerminalReaderActionSucceeded,
375 #[serde(rename = "test_helpers.test_clock.advancing")]
376 TestHelpersTestClockAdvancing,
377 #[serde(rename = "test_helpers.test_clock.created")]
378 TestHelpersTestClockCreated,
379 #[serde(rename = "test_helpers.test_clock.deleted")]
380 TestHelpersTestClockDeleted,
381 #[serde(rename = "test_helpers.test_clock.internal_failure")]
382 TestHelpersTestClockInternalFailure,
383 #[serde(rename = "test_helpers.test_clock.ready")]
384 TestHelpersTestClockReady,
385 #[serde(rename = "topup.canceled")]
386 TopupCanceled,
387 #[serde(rename = "topup.created")]
388 TopupCreated,
389 #[serde(rename = "topup.failed")]
390 TopupFailed,
391 #[serde(rename = "topup.reversed")]
392 TopupReversed,
393 #[serde(rename = "topup.succeeded")]
394 TopupSucceeded,
395 #[serde(rename = "transfer.created")]
396 TransferCreated,
397 #[serde(rename = "transfer.failed")]
398 TransferFailed,
399 #[serde(rename = "transfer.paid")]
400 TransferPaid,
401 #[serde(rename = "transfer.reversed")]
402 TransferReversed,
403 #[serde(rename = "transfer.updated")]
404 TransferUpdated,
405 #[serde(other)]
406 #[default]
407 Unknown,
408}
409
410impl std::fmt::Display for EventType {
411 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
412 self.serialize(f)
413 }
414}
415
416#[derive(Clone, Debug, Deserialize, Serialize, Default)]
421pub struct NotificationEventData {
422 pub object: EventObject,
423 #[serde(skip_serializing_if = "Option::is_none")]
424 pub previous_attributes: Option<HashMap<String, Value>>,
425}
426
427#[derive(Clone, Debug, Deserialize, Serialize)]
428#[serde(tag = "object", rename_all = "snake_case")]
429pub enum EventObject {
430 Account(Account),
431 #[serde(rename = "capability")]
432 AccountCapabilities(AccountCapabilities),
433 Application(Application),
434 ApplicationFee(ApplicationFee),
435 #[serde(rename = "fee_refund")]
436 ApplicationFeeRefund(ApplicationFeeRefund),
437 Balance(Balance),
438 BankAccount(BankAccount),
439 #[serde(rename = "billing_portal.configuration")]
440 BillingPortalConfiguration(BillingPortalConfiguration),
441 Card(Card),
442 Charge(Charge),
443 #[serde(rename = "checkout.session")]
444 CheckoutSession(CheckoutSession),
445 Coupon(Coupon),
446 Customer(Customer),
447 Discount(Discount),
448 Dispute(Dispute),
449 File(File),
450 Invoice(Invoice),
451 #[serde(rename = "invoiceitem")]
452 InvoiceItem(InvoiceItem),
453 #[serde(rename = "issuing.authorization")]
454 IssuingAuthorization(IssuingAuthorization),
455 #[serde(rename = "issuing.card")]
456 IssuingCard(IssuingCard),
457 #[serde(rename = "issuing.cardholder")]
458 IssuingCardholder(IssuingCardholder),
459 #[serde(rename = "issuing.dispute")]
460 IssuingDispute(IssuingDispute),
461 #[serde(rename = "issuing.transaction")]
462 IssuingTransaction(IssuingTransaction),
463 Mandate(Mandate),
464 PaymentIntent(PaymentIntent),
465 PaymentLink(PaymentLink),
466 PaymentMethod(PaymentMethod),
467 Payout(Payout),
468 Person(Person),
469 Plan(Plan),
470 Price(Price),
471 Product(Product),
472 PromotionCode(PromotionCode),
473 Quote(Quote),
474 Refund(Refund),
475 Review(Review),
476 SetupIntent(SetupIntent),
477 Subscription(Subscription),
478 SubscriptionSchedule(SubscriptionSchedule),
479 TaxId(TaxId),
480 TaxRate(TaxRate),
481 #[serde(rename = "test_helpers.test_clock")]
482 TestHelpersTestClock(TestHelpersTestClock),
483 Topup(Topup),
484 Transfer(Transfer),
485}
486
487impl Default for EventObject {
488 fn default() -> Self {
489 EventObject::Account(Account::default())
490 }
491}
492
493#[cfg(feature = "webhook-events")]
494pub struct Webhook {
495 current_timestamp: i64,
496}
497
498#[cfg(feature = "webhook-events")]
499impl Webhook {
500 pub fn construct_event(payload: &str, sig: &str, secret: &str) -> Result<Event, WebhookError> {
509 Self { current_timestamp: Utc::now().timestamp() }.do_construct_event(payload, sig, secret)
510 }
511
512 pub fn construct_event_with_timestamp(
525 payload: &str,
526 sig: &str,
527 secret: &str,
528 timestamp: i64,
529 ) -> Result<Event, WebhookError> {
530 Self { current_timestamp: timestamp }.do_construct_event(payload, sig, secret)
531 }
532
533 fn do_construct_event(
534 self,
535 payload: &str,
536 sig: &str,
537 secret: &str,
538 ) -> Result<Event, WebhookError> {
539 let signature = Signature::parse(sig)?;
541 let signed_payload = format!("{}.{}", signature.t, payload);
542
543 let mut mac =
546 Hmac::<Sha256>::new_from_slice(secret.as_bytes()).map_err(|_| WebhookError::BadKey)?;
547 mac.update(signed_payload.as_bytes());
548
549 let sig = hex::decode(signature.v1).map_err(|_| WebhookError::BadSignature)?;
550
551 mac.verify_slice(sig.as_slice()).map_err(|_| WebhookError::BadSignature)?;
552
553 if (self.current_timestamp - signature.t).abs() > 300 {
555 return Err(WebhookError::BadTimestamp(signature.t));
556 }
557
558 Ok(serde_json::from_str(payload)?)
559 }
560}
561
562#[cfg(feature = "webhook-events")]
563#[derive(Debug)]
564struct Signature<'r> {
565 t: i64,
566 v1: &'r str,
567}
568
569#[cfg(feature = "webhook-events")]
570impl<'r> Signature<'r> {
571 fn parse(raw: &'r str) -> Result<Signature<'r>, WebhookError> {
572 let headers: HashMap<&str, &str> = raw
573 .split(',')
574 .map(|header| {
575 let mut key_and_value = header.split('=');
576 let key = key_and_value.next();
577 let value = key_and_value.next();
578 (key, value)
579 })
580 .filter_map(|(key, value)| match (key, value) {
581 (Some(key), Some(value)) => Some((key, value)),
582 _ => None,
583 })
584 .collect();
585 let t = headers.get("t").ok_or(WebhookError::BadSignature)?;
586 let v1 = headers.get("v1").ok_or(WebhookError::BadSignature)?;
587 Ok(Signature { t: t.parse::<i64>().map_err(WebhookError::BadHeader)?, v1 })
588 }
589}
590
591#[cfg(test)]
592mod tests {
593 #[cfg(feature = "webhook-events")]
594 #[test]
595 fn test_event_type_display() {
596 use super::EventType;
597
598 assert_eq!(
600 EventType::CustomerSubscriptionCreated.to_string(),
601 "customer.subscription.created"
602 );
603 }
604
605 #[cfg(feature = "webhook-events")]
606 #[test]
607 fn test_signature_parse() {
608 use super::Signature;
609
610 let raw_signature =
611 "t=1492774577,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd";
612 let signature = Signature::parse(raw_signature).unwrap();
613 assert_eq!(signature.t, 1492774577);
614 assert_eq!(
615 signature.v1,
616 "5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
617 );
618
619 let raw_signature_with_test_mode = "t=1492774577,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd,v0=6ffbb59b2300aae63f272406069a9788598b792a944a07aba816edb039989a39";
620 let signature = Signature::parse(raw_signature_with_test_mode).unwrap();
621 assert_eq!(signature.t, 1492774577);
622 assert_eq!(
623 signature.v1,
624 "5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
625 );
626 }
627
628 #[cfg(feature = "webhook-events")]
629 #[test]
630 fn test_webhook_construct_event() {
631 let payload = r#"{
632 "id": "evt_123",
633 "object": "event",
634 "account": "acct_123",
635 "api_version": "2017-05-25",
636 "created": 1533204620,
637 "data": {
638 "object": {
639 "id": "ii_123",
640 "object": "invoiceitem",
641 "amount": 1000,
642 "currency": "usd",
643 "customer": "cus_123",
644 "date": 1533204620,
645 "description": "Test Invoice Item",
646 "discountable": false,
647 "invoice": "in_123",
648 "livemode": false,
649 "metadata": {},
650 "period": {
651 "start": 1533204620,
652 "end": 1533204620
653 },
654 "proration": false,
655 "quantity": 3
656 }
657 },
658 "livemode": false,
659 "pending_webhooks": 1,
660 "request": {
661 "id": "req_123",
662 "idempotency_key": "idempotency-key-123"
663 },
664 "type": "invoiceitem.created"
665}
666"#;
667 let event_timestamp = 1533204620;
668 let secret = "webhook_secret".to_string();
669 let signature = format!("t={},v1=82216eca827bcb7b34b8055eb2d2d9e6bc13b9ac39ded14a61e69f70c565f53a,v0=63f3a72374a733066c4be69ed7f8e5ac85c22c9f0a6a612ab9a025a9e4ee7eef", event_timestamp);
670
671 let webhook = super::Webhook { current_timestamp: event_timestamp };
672
673 let event = webhook
674 .do_construct_event(payload, &signature, &secret)
675 .expect("Failed to construct event");
676
677 assert_eq!(event.type_, super::EventType::InvoiceItemCreated);
678 assert_eq!(event.id, "evt_123".parse::<crate::EventId>().unwrap());
679 assert_eq!(event.account, "acct_123".parse().ok());
680 assert_eq!(event.created, 1533204620);
681 }
682}