Skip to content

Instantly share code, notes, and snippets.

@sergiojoker11
Last active June 5, 2025 12:08
Show Gist options
  • Save sergiojoker11/c232865debc542877f0977fbad0d05d3 to your computer and use it in GitHub Desktop.
Save sergiojoker11/c232865debc542877f0977fbad0d05d3 to your computer and use it in GitHub Desktop.
Module: Stripe Event Bridge Webhook using custom Event bus
data "aws_caller_identity" "current" {}
locals {
aws_account_id = data.aws_caller_identity.current.account_id
update_request_payload = {
name = "event-bridge-bus-${var.environment}"
description = "Event Bridge bus ${var.environment}"
include = []
enabled_events = var.stripe_webhook_events
metadata = {
environment = var.environment
}
}
create_request_payload = merge(local.update_request_payload, {
type = "amazon_eventbridge"
event_payload = "snapshot"
events_from = ["self"]
amazon_eventbridge = {
aws_account_id = local.aws_account_id
aws_region = var.aws_region
}
})
}
provider "restapi" {
alias = "stripe_v2"
# Docs: https://docs.stripe.com/api/v2/core/event_destinations?lang=curl
uri = "https://api.stripe.com/v2"
write_returns_object = true
create_returns_object = true
headers = {
Authorization = "Bearer ${var.stripe_admin_api_key}"
Stripe-Version = var.api_version
Content-Type = "application/json"
}
}
resource "restapi_object" "stripe_event_destination" {
provider = restapi.stripe_v2
path = "/core/event_destinations"
id_attribute = "id"
ignore_changes_to = [
"amazon_eventbridge",
"created",
"id",
"include",
"livemode",
"object",
"snapshot_api_version",
"status",
"status_details",
"updated"
]
data = jsonencode(local.create_request_payload)
update_data = jsonencode(local.update_request_payload)
create_method = "POST"
create_path = "/core/event_destinations"
read_method = "GET"
read_path = "/core/event_destinations/{id}"
update_method = "POST"
update_path = "/core/event_destinations/{id}"
destroy_method = "DELETE"
destroy_path = "/core/event_destinations/{id}"
}
locals {
destination_id = restapi_object.stripe_event_destination.api_data["id"]
full_destination_id = "aws.partner/stripe.com/${local.destination_id}"
}
resource "aws_cloudwatch_event_bus" "stripe" {
name = local.full_destination_id
event_source_name = local.full_destination_id
dynamic "dead_letter_config" {
for_each = var.event_bus_dlq != null ? [var.event_bus_dlq] : []
content {
arn = dead_letter_config.value.arn
}
}
}
resource "aws_cloudwatch_event_rule" "stripe_event_rules" {
for_each = var.streams_to_return
name = "stripe-events-${each.value}-${var.environment}"
description = "Contains all Stripe events defined in Stripe event-destination API"
event_bus_name = aws_cloudwatch_event_bus.stripe.name
event_pattern = jsonencode({
"source" = [{
"prefix" : "aws.partner/stripe.com"
}]
})
}
output "stripe_event_rules" {
description = "Map of Stripe event rules with event bus and rule names"
value = {
for stream_key, rule in aws_cloudwatch_event_rule.stripe_event_rules : stream_key => {
event_bus_name = rule.event_bus_name
event_bus_rule_name = rule.name
event_bus_rule_arn = rule.arn
}
}
}
variable "stripe_webhook_events" {
description = "List of Stripe webhook events to listen for"
type = list(string)
default = [
"customer.subscription.created",
"customer.subscription.updated",
"customer.subscription.deleted",
"invoice.payment_succeeded",
"invoice.payment_failed",
"customer.subscription.trial_will_end",
"checkout.session.completed"
]
}
variable "event_bus_dlq" {
type = object({
arn = optional(string, null)
})
description = <<EOT
[Lifted from original docs] Configuration details of the Amazon SQS queue for EventBridge to use as a dead-letter queue (DLQ).
This block supports the following arguments:
arn - (Optional) The ARN of the SQS queue specified as the target for the dead-letter queue.
EOT
default = null
}
variable "streams_to_return" {
type = set(string)
description = "EventBridge fanout. It creates as many EventBridge rules off the event bus as items present in this set parameter."
default = ["all-events"]
}
variable "api_version" {
type = string
default = "2025-05-28.basil"
}
variable "environment" {
type = string
}
variable "aws_region" {
type = string
}
variable "stripe_admin_api_key" {
type = string
}
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.86"
}
restapi = {
source = "Mastercard/restapi"
version = "~> 2.0.1"
}
}
}
@sergiojoker11
Copy link
Author

sergiojoker11 commented Jun 5, 2025

Stripe webhook via Event Bridge(AWS)
The arquitecture in terms of IaC resources looks like the following:
event destination (Stripe side) -> event bus named in a certain way that matches Stripe naming -> event bridge rule(s) acting as the fanout -> event bridge target -> SQS.

The first 3 belong to the module.
The other 2 appear as resources under the instantiation of the module. Have a look at this

The interesting file is 00-stripe-webhook.tf as no stripe tf provider has implemented it yet as of the date of this comment.

Notes:

  • ignore_changes_to is necessary to avoid unwanted diffs on tf plan/apply.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment