timestamp = request.headers.fetch('X-Signature-Timestamp')
signature = request.headers.fetch('X-Signature')
payload = request.raw_post
data = "#{timestamp}.#{payload}"
expected = OpenSSL::HMAC.hexdigest('SHA256', ENV.fetch('WEBHOOK_SECRET'), data)
event_id = request.headers.fetch('X-Event-Id')
timestamp = request.headers.fetch('X-Signature-Timestamp').to_i
raise ActionController::BadRequest, 'stale request' if Time.now.to_i - timestamp > 300
raise ActionController::BadRequest, 'replay detected' if WebhookEvent.exists?(external_id: event_id)
class CreateProcessedEvents < ActiveRecord::Migration[7.1]
def change
create_table :processed_events do |t|
t.string :event_key, null: false
t.string :job_class, null: false
t.jsonb :metadata, null: false, default: {}
import crypto from 'crypto';
interface VerifyOptions {
rawBody: Buffer;
signatureHeader: string | undefined;
secret: string;
class AddUniqueIndexToInventorySnapshots < ActiveRecord::Migration[7.1]
disable_ddl_transaction!
def change
add_index :inventory_snapshots,
[:warehouse_id, :sku],
import { Injectable } from '@nestjs/common';
export interface RateLimitResult {
allowed: boolean;
remaining: number;
limit: number;
<?php
namespace App\Http\Controllers;
use App\Jobs\ProcessWebhook;
use App\Support\WebhookSignature;
require 'sinatra/base'
require 'json'
require_relative 'signature_verifier'
class WebhookApp < Sinatra::Base
configure do
const crypto = require('crypto');
function computeSignature(secret, timestamp, payload) {
return crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${payload}`, 'utf8')
class CreateStripeEvents < ActiveRecord::Migration[7.1]
def change
create_table :stripe_events do |t|
t.string :stripe_event_id, null: false
t.string :event_type, null: false
t.string :status, null: false, default: "received"
import { Queue } from "bullmq";
import { createHash } from "crypto";
export const connection = { host: "127.0.0.1", port: 6379 };
export interface ChargePayload {
module Webhooks
class StripeController < ApplicationController
skip_before_action :verify_authenticity_token
def create
payload = request.body.read