Expand description
Webhooks: calls from payment gateways and other services, received safely. For each call Renox:
- checks it came from the provider (
Webhook::verify, usually a signature over the raw body), answering 401 otherwise; - stores it in
webhook_calls, once per provider event id, so the provider’s retries of the same event are answered 200 and not processed twice; - answers 200 at once and runs
Webhook::handlein a queue worker, retrying on errors;webhook:failedlists what failed andwebhook:retry <id>runs a call again.
use renox::webhook;
struct Xendit;
impl Webhook for Xendit {
const PROVIDER: &'static str = "xendit";
fn verify(req: &WebhookRequest, state: &AppState) -> Result {
let token = webhook::secret(state, "XENDIT_CALLBACK_TOKEN")?;
webhook::ensure(req.header("x-callback-token").is_some_and(|t| webhook::same(t, &token)))
}
fn event_id(req: &WebhookRequest) -> Result<String> {
let invoice: Invoice = req.json()?;
Ok(format!("{}:{}", invoice.id, invoice.status))
}
async fn handle(call: WebhookCall, ctx: JobContext) -> Result {
let invoice: Invoice = call.json()?;
mark_paid(&ctx.state.db, &invoice.external_id).await
}
}
// Module::routes: Routes::new().webhook::<Xendit>("/webhooks/xendit")
// Module::register: app.webhook::<Xendit>();Webhook routes skip CSRF (callers have no session) and keep working in maintenance mode (calls are stored and processed as usual).
Structs§
- Webhook
Call - A stored call, as
handlegets it. - Webhook
Request - The incoming call: headers and the raw body, exactly as signed.
Enums§
- Webhook
Status - Where a stored webhook call is, in
webhook_calls.status.
Traits§
- Webhook
- Tells Renox how to receive one provider’s webhooks.
Functions§
- ensure
Okifvalid, otherwise the errorverifyreturns for a forged call.- hmac_
sha256_ hex - Lowercase hex HMAC-SHA256 of
datawithkey. - hmac_
sha512_ hex - Lowercase hex HMAC-SHA512 of
datawithkey. - retry
- Runs a stored call again (e.g. after fixing a bug);
falseif there’s no such call. - same
- Compares two strings in time independent of where they differ.
- secret
- A secret from
.env(orConfig::vars), e.g.secret(state, "MIDTRANS_SERVER_KEY"); missing is an error. - sha256_
hex - Lowercase hex SHA-256 of
data. - sha512_
hex - Lowercase hex SHA-512 of
data(Midtrans’signature_key). - verify_
hmac_ sha256 - Checks a hex HMAC-SHA256
signatureofbody, with or without asha256=prefix (GitHub, Shopify-style hex, …), ignoring hex case. - verify_
timestamped - Checks a Stripe-style signature header,
t=<unix time>,v1=<hex>[,v1=…], where eachv1is HMAC-SHA256 of"{t}.{body}", and refuses one older or newer thantolerance(Stripe uses five minutes) so it can’t be replayed.