StripeKit 0.1.1
Stripe integration toolkit for C++
Loading...
Searching...
No Matches
Webhooks.h
1/** Webhooks.h
2 *
3 * Stripe-style webhook construction and de-duplicated processing helpers.
4 *
5 * @author Hans de Ruiter
6 *
7 * @License See LICENSE for details.
8 */
9
10#pragma once
11
12#include "StripeKit/Async.h"
13#include "StripeKit/Events.h"
14#include "StripeKit/Logging.h"
15#include "StripeKit/WebhookDedupStore.h"
16#include "StripeKit/WebhookVerifier.h"
17
18#include <chrono>
19#include <functional>
20#include <string>
21#include <string_view>
22
24
25/**
26 * @addtogroup webhooks
27 * @{
28 */
29
30/** A verified Stripe webhook event. */
32
33/** Tuning for construct_event(). The defaults are what Stripe recommends. */
35 /** Current time, used to check the signature's timestamp. Override in tests. */
36 std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
37
38 /**
39 * How far the signature's timestamp may be from #now.
40 *
41 * This is what stops an old delivery being replayed. Widening it past a
42 * few minutes weakens that protection; if verification fails because the
43 * clocks disagree, fix the clock rather than the tolerance.
44 */
45 std::chrono::seconds tolerance = std::chrono::minutes(5);
46};
47
48/** What a handler reports back about an event. Shared with EventDispatcher. */
50
51/**
52 * The application's webhook handler.
53 *
54 * Called after the signature has been checked and the event id claimed.
55 * Completed events are skipped; failed or interrupted attempts may run again.
56 * Make the handler safe to retry. Return quickly: Stripe treats a slow endpoint as a failed
57 * delivery, so queue any long work rather than doing it here.
58 */
59using EventHandler = std::function<Task<ProcessResult>(const Event&)>;
60
61/** Tuning for process_event_once(). The defaults suit most applications. */
63 /** Current time, used for signature checks and claim timing. Override in tests. */
64 std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
65
66 /** How far the signature's timestamp may be from #now. */
67 std::chrono::seconds tolerance = std::chrono::minutes(5);
68
69 /**
70 * How long before a claim left behind by a crashed process is reclaimed.
71 *
72 * Set this comfortably longer than the handler's worst-case runtime, or a
73 * slow handler risks a second delivery being let through while it is still
74 * working.
75 */
77
78 /** Where to report store and handler failures. No logging when null. */
79 Logger* logger = nullptr;
80};
81
82/** What happened to a webhook delivery. */
84 /** Whether to answer Stripe with success, or ask it to retry. */
86
87 /** Whether this delivery was the one that got to run the handler. */
89
90 /** Id of the event, when it could be parsed. */
91 std::string event_id;
92
93 /** Type of the event, when it could be parsed. */
94 std::string event_type;
95};
96
97/**
98 * Checks that a webhook delivery really came from Stripe, and parses it.
99 *
100 * Pass the request body exactly as received. The signature covers those precise
101 * bytes, so anything that re-encodes or reformats the body first will cause
102 * verification to fail.
103 *
104 * This verifies but does not de-duplicate. Stripe delivers the same event more
105 * than once, so a handler that changes state should use process_event_once()
106 * instead.
107 *
108 * @code
109 * const auto event = stripekit::webhooks::construct_event(
110 * request_body, request.header("stripe-signature"), endpoint_secret);
111 * @endcode
112 *
113 * @param payload Raw request body, unmodified.
114 * @param signature_header Value of the "Stripe-Signature" header.
115 * @param endpoint_secret Signing secret for this endpoint, of the form
116 * "whsec_...". It differs between the Stripe CLI and each registered
117 * endpoint.
118 * @param options Timestamp tolerance and current time.
119 * @return The verified event.
120 * @throws SignatureVerificationException The signature does not match, or its
121 * timestamp is outside the tolerance. Answer the request with HTTP 400.
122 * @throws ConfigurationException The payload is not a usable Stripe event.
123 *
124 * @sa https://docs.stripe.com/webhooks#verify-events
125 */
126Event construct_event(std::string_view payload,
127 std::string_view signature_header,
128 std::string_view endpoint_secret,
129 ConstructEventOptions options = {});
130
131/**
132 * Runs the handler for an already-verified event unless it was already completed.
133 *
134 * Reserves the event id in @p dedup_store first, so repeated deliveries of the
135 * same completed event are skipped while its record is retained. Failed attempts
136 * release their claims for retry; crashed attempts wait for the stale timeout.
137 * Use this overload when the event has already been through construct_event().
138 *
139 * @param event The verified event.
140 * @param dedup_store Where claims are recorded. Must outlive the call, and must
141 * be shared by every process that handles this endpoint.
142 * @param handler Called only for the delivery that wins the claim.
143 * @param options Claim timing and logging.
144 * @return What happened, including the status to send Stripe.
145 */
147 WebhookDedupStore& dedup_store,
148 EventHandler handler,
149 ProcessEventOptions options = {});
150
151/**
152 * Verifies a webhook delivery and handles it unless it was already completed.
153 *
154 * This is the whole inbound path in one call: check the signature, reserve the
155 * event id, run the handler, and report the status to send back to Stripe. It
156 * is what an adapter's webhook route should call.
157 *
158 * @code
159 * auto result = co_await stripekit::webhooks::process_event_once(
160 * body, signature_header, endpoint_secret, dedup_store,
161 * [](const stripekit::webhooks::Event& event)
162 * -> stripekit::Task<stripekit::webhooks::ProcessResult> {
163 * co_return stripekit::webhooks::ProcessResult::Handled;
164 * });
165 * @endcode
166 *
167 * @param payload Raw request body, unmodified.
168 * @param signature_header Value of the "Stripe-Signature" header.
169 * @param endpoint_secret Signing secret for this endpoint.
170 * @param dedup_store Where claims are recorded. Must outlive the call.
171 * @param handler Called only for the delivery that wins the claim.
172 * @param options Tolerance, claim timing, and logging.
173 * @return What happened, including the status to send Stripe.
174 * @throws SignatureVerificationException The delivery cannot be trusted.
175 * Answer with HTTP 400 rather than retrying.
176 * @throws ConfigurationException The payload is not a usable Stripe event.
177 */
179 std::string_view signature_header,
180 std::string_view endpoint_secret,
181 WebhookDedupStore& dedup_store,
182 EventHandler handler,
183 ProcessEventOptions options = {});
184
185/** @} */
186
187} // namespace stripekit::webhooks
Receives StripeKit's log entries.
Definition Logging.h:74
The result of an asynchronous StripeKit operation.
Definition Async.h:45
Remembers which webhook events have been handled.
Task< ProcessEventOnceResult > process_event_once(const Event &event, WebhookDedupStore &dedup_store, EventHandler handler, ProcessEventOptions options={})
Runs the handler for an already-verified event unless it was already completed.
WebhookResponseAction
What to tell Stripe about a delivery.
ClaimOutcome
The result of trying to reserve an event for processing.
Event construct_event(std::string_view payload, std::string_view signature_header, std::string_view endpoint_secret, ConstructEventOptions options={})
Checks that a webhook delivery really came from Stripe, and parses it.
EventDispatchResult ProcessResult
What a handler reports back about an event.
Definition Webhooks.h:49
EventDispatchResult
What a webhook handler reports back about an event.
Definition Events.h:30
constexpr std::chrono::minutes defaultStaleInflightTimeout
How long an unfinished claim is honoured before another process may take it.
std::function< Task< ProcessResult >(const Event &)> EventHandler
The application's webhook handler.
Definition Webhooks.h:59
EventEnvelope Event
A verified Stripe webhook event.
Definition Webhooks.h:31
@ FatalStoreError
The store failed in a non-recoverable way.
Webhooks.h.
Definition Webhooks.h:23
A Stripe event, as delivered by a webhook.
Definition Models.h:196
Tuning for construct_event().
Definition Webhooks.h:34
std::chrono::seconds tolerance
How far the signature's timestamp may be from now.
Definition Webhooks.h:45
std::chrono::system_clock::time_point now
Current time, used to check the signature's timestamp.
Definition Webhooks.h:36
What happened to a webhook delivery.
Definition Webhooks.h:83
ClaimOutcome claim_outcome
Whether this delivery was the one that got to run the handler.
Definition Webhooks.h:88
std::string event_id
Id of the event, when it could be parsed.
Definition Webhooks.h:91
std::string event_type
Type of the event, when it could be parsed.
Definition Webhooks.h:94
WebhookResponseAction response_action
Whether to answer Stripe with success, or ask it to retry.
Definition Webhooks.h:85
Tuning for process_event_once().
Definition Webhooks.h:62
std::chrono::seconds tolerance
How far the signature's timestamp may be from now.
Definition Webhooks.h:67
std::chrono::minutes stale_inflight_timeout
How long before a claim left behind by a crashed process is reclaimed.
Definition Webhooks.h:76
Logger * logger
Where to report store and handler failures.
Definition Webhooks.h:79
std::chrono::system_clock::time_point now
Current time, used for signature checks and claim timing.
Definition Webhooks.h:64