Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a queue before the paymentIntentIssues lambda #2164

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
114 changes: 114 additions & 0 deletions cdk/lib/stripe-webhook-endpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import type { GuStackProps } from "@guardian/cdk/lib/constructs/core";
import { GuStack } from "@guardian/cdk/lib/constructs/core";
import { GuLambdaFunction } from "@guardian/cdk/lib/constructs/lambda";
import type { App } from "aws-cdk-lib";
import { Duration } from "aws-cdk-lib";
import * as IAM from 'aws-cdk-lib/aws-iam';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as ApiGW from 'aws-cdk-lib/aws-apigateway';

const appName = "stripe-webhook-endpoints";

export class AwsSqsDirectIntegrationStack extends GuStack {
constructor(scope: App, id: string, props: GuStackProps) {
super(scope, id, props);

const queueName = `stripe-webhook-endpoints-${props.stage}`;

// role
const integrationRole = new IAM.Role(this, 'integration-role', {
assumedBy: new IAM.ServicePrincipal('apigateway.amazonaws.com'),
});

// queue
const queue = new sqs.Queue(this,`${appName}Queue`, {
encryption: sqs.QueueEncryption.KMS_MANAGED,
});

// grant sqs:SendMessage* to Api Gateway Role
queue.grantSendMessages(integrationRole);

// Api Gateway Direct Integration
const sendMessageIntegration = new ApiGW.AwsIntegration({
service: 'sqs',
path: `${process.env.CDK_DEFAULT_ACCOUNT}/${queue.queueName}`,
integrationHttpMethod: 'POST',
options: {
credentialsRole: integrationRole,
requestParameters: {
'integration.request.header.Content-Type': `'application/x-www-form-urlencoded'`,
},
requestTemplates: {
'application/json': 'Action=SendMessage&MessageBody=$input.body',
},
integrationResponses: [
{
statusCode: '200',
},
{
statusCode: '400',
},
{
statusCode: '500',
}
]
},
});

// Rest Api
const api = new ApiGW.RestApi(this, 'api', {});

// post method
api.root.addMethod('POST', sendMessageIntegration, {
methodResponses: [
{
statusCode: '400',
},
{
statusCode: '200',
},
{
statusCode: '500',
}
]
});
}

// Create a role
const role = new Role(this, "stripe-webhook-endpoints-sqs-lambda-role", {
roleName: `sqs-lambda-${this.stage}`,
assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
});
role.addToPolicy(
new PolicyStatement({
actions: [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
resources: ["*"],
})
);
role.addToPolicy(
new PolicyStatement({
actions: ["ssm:GetParameter"],
resources: [
`arn:aws:ssm:${this.region}:${this.account}:parameter/${appName}/${props.stage}/gcp-wif-credentials-config`,
],
})
);

new GuLambdaFunction(this, `${appName}Lambda`, {
app: appName,
runtime: Runtime.JAVA_11_CORRETTO,
fileName: `${appName}.jar`,
functionName: `${appName}-${props.stage}`,
handler: "com.gu.paymentIntentIssues.Lambda::handler",
events: [eventSource],
timeout: Duration.minutes(2),
role,
});
}
}


253 changes: 233 additions & 20 deletions handlers/stripe-webhook-endpoints/cfn.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,198 @@ Parameters:
Type: String
Default: membership-dist

Conditions:
IsProd: !Equals [ !Ref Stage, "PROD" ]

Resources:
ApiGateway: #to have API created in AWS Console
Type: AWS::Serverless::Api #Creates a collection of Amazon API Gateway resources and methods that can be invoked through HTTPS endpoints.
Properties:
StageName: !Ref Stage
Description: Gateway for Stripe to make POST requests to
Name: !Sub stripe-webhook-endpoints-${Stage}
Cors:
AllowMethods: "'*'"
AllowHeaders: "'*'"
AllowOrigin: "'*'"
DefinitionBody:
swagger: "2.0"
info:
title: !Ref Stack
description: API Gateway to handle Stripe events
x-amazon-apigateway-request-validators:
body-only:
validateRequestBody: true
validateRequestParameters: false
params-only:
validateRequestBody: false
validateRequestParameters: true
x-amazon-apigateway-request-validator: body-only
securityDefinitions:
authorizer:
type: apiKey
name: Authorization
in: header
x-amazon-apigateway-authtype: oauth2
x-amazon-apigateway-authorizer:
type: token
authorizerUri: !Join [ "", [ !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/", !GetAtt StripeAuthorizer.Arn, "/invocations" ] ]
authorizerCredentials: !GetAtt ApiGatewayToSQSRole.Arn
identityValidationExpression: "Bearer [A-Za-z0-9_-]+.[A-Za-z0-9_-]+.[A-Za-z0-9_-]+"
authorizerResultTtlInSeconds: 300
paths:
"/":
post:
summary: receive a new stripe payment failed event
consumes:
- "application/json"
produces:
- "application/json"
responses:
"200":
description: "200 response"
schema:
$ref: "#/definitions/Empty"
security:
- authorizer: [ ]
parameters:
- in: body
name: PaymentFailedEventBody
required: true
schema:
$ref: "#/definitions/PaymentFailedEventBody"
x-amazon-apigateway-request-validator: body-only
x-amazon-apigateway-integration:
credentials: !GetAtt ApiGatewayToSQSRole.Arn
uri: !Sub "arn:aws:apigateway:${AWS::Region}:sqs:path//"
responses:
default:
statusCode: "200"
requestParameters:
integration.request.header.Content-Type: "'application/x-www-form-urlencoded'"
requestTemplates:
application/json: !Sub "Action=SendMessage##\n&QueueUrl=$util.urlEncode('${StripePaymentFailureMessagesQueue}')##\n\
&MessageBody=$util.urlEncode($input.body)##\n"
passthroughBehavior: "never"
httpMethod: "POST"
type: "aws"
definitions:
Empty:
type: object
title: Empty
PaymentFailedEventBody:
title: PaymentFailedEvent
type: object
properties:
id:
type: string
description: The unique identifier for the payment intent.
object:
type: string
description: The type of object, always 'payment_intent'.
amount:
type: integer
description: The amount of the payment intent in the smallest currency unit.
currency:
type: string
description: The currency of the payment intent.
status:
type: string
description: The status of the payment intent. Should be 'failed'.
failure_message:
type: string
description: A message indicating why the payment intent failed.
created:
type: integer
description: Timestamp indicating when the payment intent was created.
payment_method:
type: string
description: The ID of the payment method used in the payment intent.
payment_method_types:
type: array
items:
type: string
description: The list of payment method types (e.g. card) that this PaymentIntent is allowed to use.
customer:
type: string
description: The ID of the customer associated with the payment intent.
metadata:
type: object
description: Any additional metadata associated with the payment intent.
required:
- id
- object
- amount
- currency
- status
- created
- payment_method
- payment_method_types
- customer

# authorization lambda
StripeAuthorizer:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub ${App}-authorization-${Stage}
Description: Authorize API Gateway requests
Handler: com.gu.paymentIntentIssues.StripeAuthorizerLambda::handler
Runtime: java11
MemorySize: 512
Timeout: 300
Environment:
Variables:
App: payment-intent-issues
Stack: !Ref Stack
Stage: !Ref Stage
CodeUri:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}/${App}.jar
Policies:
- AWSLambdaBasicExecutionRole
- Statement:
Effect: Allow
Action:
- ssm:GetParametersByPath
Resource:
- !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${Stage}/membership/payment-intent-issues
- Statement:
- Effect: Allow
Action: s3:GetObject
Resource:
- arn:aws:s3::*:membership-dist/*
ApiGatewayToSQSRole: #to allow API Gateway to push message to SQS
Type: AWS::IAM::Role
Properties:
Path: /
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
RoleName: !Sub "stripe-webhook-endpoints-apigateway-to-sqs"
Policies:
- PolicyName: ApiQueuePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sqs:ReceiveMessage
- sqs:SendMessage
- sqs:SetQueueAttributes
Resource: !GetAtt StripePaymentFailureMessagesQueue.Arn
- PolicyName: LambdaPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource: !GetAtt StripeAuthorizer.Arn

PaymentIntentIssuesLambda:
Type: AWS::Serverless::Function
Properties:
Expand All @@ -44,24 +232,49 @@ Resources:
Bucket: !Ref DeployBucket
Key: !Sub ${Stack}/${Stage}/${App}/${App}.jar
Policies:
- AWSLambdaBasicExecutionRole
- Statement:
Effect: Allow
Action:
- ssm:GetParametersByPath
Resource:
- !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${Stage}/membership/payment-intent-issues
- Statement:
- Effect: Allow
Action: s3:GetObject
Resource:
- arn:aws:s3::*:membership-dist/*
- AWSLambdaBasicExecutionRole
- Statement:
Effect: Allow
Action:
- ssm:GetParametersByPath
Resource:
- !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${Stage}/membership/payment-intent-issues
- Statement:
- Effect: Allow
Action: s3:GetObject
Resource:
- arn:aws:s3::*:membership-dist/*
- AWSLambdaSQSQueueExecutionRole # This policy is required to allow the lambda to read from the queue
- Statement:
Effect: Allow
Action:
- sqs:ReceiveMessage
- sqs:DeleteMessage
- sqs:GetQueueAttributes
Resource:
- !GetAtt StripePaymentFailureMessagesQueue.Arn
Events:
AcquisitionEvent:
Type: Api
Properties:
Path: '/payment-intent-issue'
Method: post
MySQSEvent:
Type: SQS
Properties:
Queue: !GetAtt StripePaymentFailureMessagesQueue.Arn
BatchSize: 10
Enabled: false

StripePaymentFailureMessagesQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub stripe-payment-failure-messages-queue-${Stage}
VisibilityTimeout: 1800
RedrivePolicy:
deadLetterTargetArn: !GetAtt StripePaymentFailureMessagesDLQ.Arn
maxReceiveCount: 10

StripePaymentFailureMessagesDLQ:
Type: AWS::SQS::Queue
Properties:
QueueName: !Sub stripe-payment-failure-messages-queue-dlq-${Stage}

CustomerUpdatedLambda:
Type: AWS::Serverless::Function
Properties:
Expand Down
Loading
Loading