Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions stripe-eventbridge-lambda/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Stripe EventBridge Integration with AWS Lambda

This pattern demonstrates how to use the Stripe Amazon EventBridge SaaS integration and AWS Lambda to process events from Stripe. This pattern is leveraging the Stripe Amazon EventBridge SaaS integration to send payment events from the customer's Stripe account to their AWS account, via an Amazon EventBridge Partner event bus. Once the Stripe events are in the customer's account, an Amazon EventBridge rule routes failed payment events to a downstream Lambda function. The Lambda function transforms the event, stores the failed payment details in a DynamoDB table, and can potentially trigger additional actions such as sending a notification email to the customer using SES.

Amazon CloudWatch Log Groups are provisioned for debugging and auditing of all Stripe events as well as the failed payment events specifically. This pattern deploys two EventBridge rules, one Lambda function, two CloudWatch Log Groups, and a DynamoDB table to store the failed payment details.

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/stripe-eventbridge-lambda

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [AWS CDK CLI](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) (AWS CDK) installed
* [Create a Stripe account](https://stripe.com/register) if you do not already have one and log in.
* [Set up the Stripe Amazon EventBridge SaaS integration](https://docs.stripe.com/event-destinations/eventbridge) if you have not already configured the integration.

## Deployment Instructions

1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
```
git clone https://github.com/aws-samples/serverless-patterns
```
2. Change directory to the pattern directory:
```
cd stripe-eventbridge-lambda
```
3. From the command line, use AWS CDK to deploy the AWS resources for the pattern as specified in the app.py file. A command-line argument is needed to deploy the CDK stack, "stripeEventBusName". This argument should be the name of the **SaaS event bus** associated with your Stripe [partner event source](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-saas.html).
```
cdk deploy --parameters stripeEventBusName=SAAS_EVENT_BUS_NAME_HERE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this missing any instructions? I am getting the below error

/serverless-patterns/stripe-eventbridge-lambda/app.py", line 3, in
import aws_cdk as cdk
ModuleNotFoundError: No module named 'aws_cdk'

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe so, I do not see that error

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let me take a deeper look

```

4. Note the outputs from the CDK deployment process. These contain the resource names and/or ARNs which are used for testing. This stack will output the name of the Lambda function deployed for testing to the CLI. See the example below.

```
Outputs:
StripeIntegrationStack.StripeProcessFailedPaymentLambdaOutput = StripeIntegrationStack-StripeProcessFailedPaymentL-nIPhn7sPQx9e
```

## How it works

This service interaction uses an existing Stripe SaaS integration in the customer's AWS account. If you do not have the Stripe SaaS integration set up in your AWS account, please set it up before deploying this pattern. View the [integration on the Stripe Docs](https://docs.stripe.com/event-destinations/eventbridge). See [Stripe's documentation](https://stripe.com/docs/api/events/types) for available events.

This pattern demonstrates how to:
1. Write EventBridge rules that match Stripe's event pattern
2. Send events from Stripe's EventBridge SaaS integration to Amazon CloudWatch for logging and debugging
3. Transform Stripe events using AWS Lambda
4. Store failed payment details in a DynamoDB table

See the below architecture diagram from the data flow of this pattern.

![Architecture Diagram](./img/readme-arch-diagram.png)

## Testing

### Test the AWS Lambda Function

The event.json file included in this pattern is a sample EventBridge event from Stripe. This event can be used to test the Lambda function and EventBridge rules deployed by this pattern.

To test the Lambda function via the CLI, copy and paste the following command, replacing the variables in <> with your own values:
```
aws lambda invoke --function-name <StripeIntegrationStack-StripeProcessFailedPaymentL-nIPhn7sPQx9e> --payload file://event.json response.json
```
Comment on lines +64 to +65
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you need to add --cli-binary-format raw-in-base64-out because the input is expected to be in base64


You should receive a response that looks like:
```
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
Comment on lines +69 to +72
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get an error message instead (account id redacted):

{"statusCode": 500, "body": "An error occurred (AccessDeniedException) when calling the PutItem operation: User: arn:aws:sts::123456789012:assumed-role/StripeIntegrationStack-stripecdklambdarole246618B3-lLn1HaA8rFMz/StripeIntegrationStack-StripeProcessFailedPaymentL-BwLehI3Ed8BA is not authorized to perform: dynamodb:PutItem on resource: arn:aws:dynamodb:eu-west-1:123456789012:table/StripeIntegrationStack-StripeFailedPaymentsTableC8BBF5D3-SSP3IAPIWRPJ because no identity-based policy allows the dynamodb:PutItem action"}%

```

The command should have create a response.json file in your directory. If you open this file, you see the output of the Lambda function.

### Test the EventBridge Rule

You cannot put events on the Stripe SaaS event bus, only Stripe can publish events to this event bus. To test that the EventBridge rules deployed by this pattern were successfully deployed, follow these instructions:

1. Navigate to the Amazon EventBridge console. Select "Rules".

2. From the Event bus list, choose the SaaS event bus associated with your Stripe partner event source.

3. From the Rules list, select the "StripeIntegrationStack-StripeFailedPaymentEventsRul-cSTSp1wtPkkB"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suffix is randomly generated and will not match the users resources. It's probably better to word this differently


![EventBridge Console](./img/EBconsole-rules.png)

4. Choose "Edit" to enter the rule editor. Click through to "Step 2. Build Event Pattern."

![EventBridge Console](./img/BuildEvent.png)

5. Scroll down to "Sample event - optional." Select "Enter my own," and delete the pre-populated event. Copy the contents of event.json into the event editor.

![EventBridge Console](./img/SampleEvent.png)

6. Scroll down to "Event pattern." Choose "Test Pattern."

![EventBridge Console](./img/TestEvent.png)

You should see a green box appear that says "Sample event matched the event pattern." This means that the rule will successfully route incoming events to the AWS Lambda function.

![EventBridge Console](./img/TestEventSuccessful.png)

## Cleanup

1. Delete the stack
```bash
cdk destroy
```

----
Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
123 changes: 123 additions & 0 deletions stripe-eventbridge-lambda/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@

import os
import aws_cdk as cdk
from aws_cdk import (
Stack,
aws_dynamodb as dynamodb,
aws_events as events,
aws_events_targets as targets,
aws_iam as iam,
aws_lambda as _lambda,
aws_logs as logs,
CfnOutput,
CfnParameter,
Duration,
RemovalPolicy
)
from constructs import Construct

class StripeIntegrationStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)

# Stack inputs
STRIPE_PARTNER_EVENT_BUS = CfnParameter(
self, id="stripeEventBusName", type="String",
description="The name of event bus the the Stripe Partner EventSource is associated with."
)

# Turn partner event bus from string to IEventBus object
partner_event_bus = events.EventBus.from_event_bus_name(
scope=self, id='partner-event-bus', event_bus_name=STRIPE_PARTNER_EVENT_BUS.value_as_string
)

# CloudWatch Logs Group that stores all events sent by Stripe for debugging or archive
log_group_all_events = logs.LogGroup(
self, "Stripe-all-events",
retention=logs.RetentionDays.ONE_DAY,
removal_policy=RemovalPolicy.DESTROY
)

# CloudWatch Logs Group that stores all failed payment events from Stripe for debugging or archive
log_group_failed_payments = logs.LogGroup(
self, "Stripe-failedpayment-events",
retention=logs.RetentionDays.ONE_DAY,
removal_policy=RemovalPolicy.DESTROY
)

lambda_role = iam.Role(
scope=self, id='stripe-cdk-lambda-role',
assumed_by=iam.ServicePrincipal('lambda.amazonaws.com'),
managed_policies=[
iam.ManagedPolicy.from_aws_managed_policy_name(
'service-role/AWSLambdaBasicExecutionRole'
)
]
)

# DynamoDB table to store failed payment events
stripe_failed_payments_table = dynamodb.Table(
self, "StripeFailedPaymentsTable",
partition_key=dynamodb.Attribute(name="payment_intent_id", type=dynamodb.AttributeType.STRING),
billing_mode=dynamodb.BillingMode.PAY_PER_REQUEST,
removal_policy=RemovalPolicy.DESTROY
)

# A Lambda function to consume and process failed payment events
stripe_process_failed_payment_lambda = _lambda.Function(
self,
id='StripeProcessFailedPaymentLambda',
runtime=_lambda.Runtime.PYTHON_3_8,
code=_lambda.Code.from_asset('src'),
handler='StripeProcessFailedPayment.handler',
role=lambda_role,
timeout=Duration.seconds(15),
environment={
"StripeFailedPayments": stripe_failed_payments_table.table_name
}
)

# EventBridge Stripe all events rule
stripe_all_events_rule = events.Rule(
self,
id="StripeAllEventsRule",
event_bus=partner_event_bus
)

# Add event pattern to rule
stripe_all_events_rule.add_event_pattern(
source=events.Match.prefix('aws.partner/stripe.com')
)

# CloudWatch Log Group as target for EventBridge Rule
stripe_all_events_rule.add_target(targets.CloudWatchLogGroup(log_group_all_events))

# EventBridge Stripe failed payment events
stripe_failed_payment_events_rule = events.Rule(
self,
id="StripeFailedPaymentEventsRule",
event_bus=partner_event_bus
)

# Add rule to the event bus for the "failed_payment" pattern
stripe_failed_payment_events_rule.add_event_pattern(
source=events.Match.prefix('aws.partner/stripe.com'),
detail_type=["charge.failed"],
)

# Lambda as target for EventBridge Rule
stripe_failed_payment_events_rule.add_target(targets.LambdaFunction(stripe_process_failed_payment_lambda))

# CloudWatch Log Group as target for EventBridge Rule
stripe_failed_payment_events_rule.add_target(targets.CloudWatchLogGroup(log_group_failed_payments))

# Output the IAM role ARN for the Lambda function
CfnOutput(self, "StripeProcessFailedPaymentLambdaOutput", value=stripe_process_failed_payment_lambda.function_name)

# Output the DynamoDB table name
CfnOutput(self, "StripeFailedPaymentsTableOutput", value=stripe_failed_payments_table.table_name)

app = cdk.App()
description = "Stripe EventBridge Integration"
StripeIntegrationStack(app, "StripeIntegrationStack", description=description)
app.synth()
25 changes: 25 additions & 0 deletions stripe-eventbridge-lambda/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"app": "python3 app.py",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"requirements*.txt",
"source.bat",
"**/__init__.py",
"python/__pycache__",
"tests"
]
},
"context": {
"@aws-cdk/core:stackRelativeExports": true,
"@aws-cdk/aws-lambda:recognizeVersionProps": true,
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:target-partitions": [
"aws"
]
}
}
Loading