cfn.yaml (604 lines of code) (raw):
AWSTemplateFormatVersion: "2010-09-09"
Description: Zuora invoice management such as refunds, downloading PDF invoices, invoice preview
Parameters:
Stage:
Description: Stage name
Type: String
AllowedValues:
- CODE
- PROD
Default: CODE
Mappings:
StageMap:
PROD:
ConfigVersion: 1
DomainName: invoicing-api.support.guardianapis.com
CODE:
ConfigVersion: 2
DomainName: invoicing-api-code.support.guardianapis.com
Conditions:
IsProd: !Equals [!Ref "Stage", "PROD"]
Resources:
# ****************************************************************************
# Alarms
# ****************************************************************************
FailedInvoicesAlarm:
Type: AWS::CloudWatch::Alarm
Condition: IsProd
Properties:
AlarmName: "URGENT 9-5 - PROD: Failed to fetch Zuora invoice by account"
AlarmDescription: |
- manage-frontend user will not be able to see/download invoices
- https://github.com/guardian/invoicing-api/blob/main/src/main/scala/com/gu/invoicing/invoice/README.md
- export AWS_DEFAULT_REGION=eu-west-1 && awslogs get --profile membership /aws/lambda/invoicing-api-invoices-PROD --start='DD/mm/yyyy HH:MM' --end='DD/mm/yyyy HH:MM'
AlarmActions:
- !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:alarms-handler-topic-PROD
ComparisonOperator: GreaterThanOrEqualToThreshold
Dimensions:
- Name: FunctionName
Value: !Ref InvoicesLambda
EvaluationPeriods: 1
MetricName: Errors
Namespace: AWS/Lambda
Period: 900
Statistic: Sum
Threshold: 5
TreatMissingData: notBreaching
Tags:
- Key: App
Value: invoicing-api
DependsOn:
- InvoicingLambdaRole
FailedPdfAlarm:
Type: AWS::CloudWatch::Alarm
Condition: IsProd
Properties:
AlarmName: "URGENT 9-5 - PROD: Failed to download invoice PDF"
AlarmDescription: |
- manage-frontend user will not be able to download invoice PDF
- https://github.com/guardian/invoicing-api/blob/main/src/main/scala/com/gu/invoicing/pdf/README.md
- export AWS_DEFAULT_REGION=eu-west-1 && awslogs get --profile membership /aws/lambda/invoicing-api-pdf-PROD --start='DD/mm/yyyy HH:MM' --end='DD/mm/yyyy HH:MM'
AlarmActions:
- !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:alarms-handler-topic-PROD
ComparisonOperator: GreaterThanOrEqualToThreshold
Dimensions:
- Name: FunctionName
Value: !Ref PdfLambda
EvaluationPeriods: 1
MetricName: Errors
Namespace: AWS/Lambda
Period: 900
Statistic: Sum
Threshold: 5
TreatMissingData: notBreaching
Tags:
- Key: App
Value: invoicing-api
DependsOn:
- InvoicingLambdaRole
NextInvoiceDateAlarm:
Type: AWS::CloudWatch::Alarm
Condition: IsProd
Properties:
AlarmName: "URGENT 9-5 - PROD: Failed to determine next invoice date"
AlarmDescription: |
- At least holiday or delivery problem credit processor will not be able to determine when to apply the credit
- https://github.com/guardian/invoicing-api/blob/main/src/main/scala/com/gu/invoicing/nextinvoicedate/README.md
- export AWS_DEFAULT_REGION=eu-west-1 && awslogs get --profile membership /aws/lambda/invoicing-api-nextinvoicedate-PROD --start='DD/mm/yyyy HH:MM' --end='DD/mm/yyyy HH:MM'
AlarmActions:
- !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:alarms-handler-topic-PROD
ComparisonOperator: GreaterThanOrEqualToThreshold
Dimensions:
- Name: FunctionName
Value: !Ref NextInvoiceDateLambda
EvaluationPeriods: 1
MetricName: Errors
Namespace: AWS/Lambda
Period: 300
Statistic: Sum
Threshold: 1
TreatMissingData: notBreaching
Tags:
- Key: App
Value: invoicing-api
DependsOn:
- InvoicingLambdaRole
PreviewAlarm:
Type: AWS::CloudWatch::Alarm
Condition: IsProd
Properties:
AlarmName: "URGENT 9-5 - PROD: Failed to preview affected publications within date range"
AlarmDescription: |
- At least holiday-stop-api will not be able to predict which publications should be suspended
- https://github.com/guardian/invoicing-api/blob/main/src/main/scala/com/gu/invoicing/preview/README.md
- export AWS_DEFAULT_REGION=eu-west-1 && awslogs get --profile membership /aws/lambda/invoicing-api-preview-PROD --start='DD/mm/yyyy HH:MM' --end='DD/mm/yyyy HH:MM'
AlarmActions:
- !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:alarms-handler-topic-PROD
ComparisonOperator: GreaterThanOrEqualToThreshold
Dimensions:
- Name: FunctionName
Value: !Ref PreviewLambda
EvaluationPeriods: 1
MetricName: Errors
Namespace: AWS/Lambda
Period: 300
Statistic: Sum
Threshold: 1
TreatMissingData: notBreaching
Tags:
- Key: App
Value: invoicing-api
DependsOn:
- InvoicingLambdaRole
# ****************************************************************************
# Lambdas
# ****************************************************************************
RefundLambda:
Type: AWS::Lambda::Function
Properties:
Description: Apply refund to Zuora invoice by adjusting invoice items
FunctionName: !Sub invoicing-api-refund-${Stage}
Code:
S3Bucket: membership-dist
S3Key: !Sub support/${Stage}/invoicing-api/invoicing-api.jar
Handler: com.gu.invoicing.refund.Lambda::handleRequest
Environment:
Variables:
Stage: !Ref Stage
Config:
!Sub
- '{{resolve:ssm:/invoicing-api/${Stage}/config:${Version}}}'
- Version: !FindInMap [StageMap, !Ref Stage, ConfigVersion]
Role: !GetAtt InvoicingLambdaRole.Arn
MemorySize: 3008
Runtime: java11
Timeout: 900
DependsOn:
- InvoicingLambdaRole
InvoicesLambda:
Type: AWS::Lambda::Function
Properties:
Description: Retrieve all invoices with payment methods by accountId
FunctionName: !Sub invoicing-api-invoices-${Stage}
Code:
S3Bucket: membership-dist
S3Key: !Sub support/${Stage}/invoicing-api/invoicing-api.jar
Handler: com.gu.invoicing.invoice.Lambda::handleRequest
Environment:
Variables:
Stage: !Ref Stage
Config:
!Sub
- '{{resolve:ssm:/invoicing-api/${Stage}/config:${Version}}}'
- Version: !FindInMap [StageMap, !Ref Stage, ConfigVersion]
Role: !GetAtt InvoicingLambdaRole.Arn
MemorySize: 3008
Runtime: java11
Timeout: 900
DependsOn:
- InvoicingLambdaRole
PdfLambda:
Type: AWS::Lambda::Function
Properties:
Description: Download PDF invoice by invoiceId
FunctionName: !Sub invoicing-api-pdf-${Stage}
Code:
S3Bucket: membership-dist
S3Key: !Sub support/${Stage}/invoicing-api/invoicing-api.jar
Handler: com.gu.invoicing.pdf.Lambda::handleRequest
Environment:
Variables:
Stage: !Ref Stage
Config:
!Sub
- '{{resolve:ssm:/invoicing-api/${Stage}/config:${Version}}}'
- Version: !FindInMap [StageMap, !Ref Stage, ConfigVersion]
Role: !GetAtt InvoicingLambdaRole.Arn
MemorySize: 3008
Runtime: java11
Timeout: 900
DependsOn:
- InvoicingLambdaRole
NextInvoiceDateLambda:
Type: AWS::Lambda::Function
Properties:
Description: Get next invoice date (day after last invoice period)
FunctionName: !Sub invoicing-api-nextinvoicedate-${Stage}
Code:
S3Bucket: membership-dist
S3Key: !Sub support/${Stage}/invoicing-api/invoicing-api.jar
Handler: com.gu.invoicing.nextinvoicedate.Lambda::handleRequest
Environment:
Variables:
Stage: !Ref Stage
Config:
!Sub
- '{{resolve:ssm:/invoicing-api/${Stage}/config:${Version}}}'
- Version: !FindInMap [StageMap, !Ref Stage, ConfigVersion]
Role: !GetAtt InvoicingLambdaRole.Arn
MemorySize: 3008
Runtime: java11
Timeout: 900
DependsOn:
- InvoicingLambdaRole
PreviewLambda:
Type: AWS::Lambda::Function
Properties:
Description: Preview affected publications within date range
FunctionName: !Sub invoicing-api-preview-${Stage}
Code:
S3Bucket: membership-dist
S3Key: !Sub support/${Stage}/invoicing-api/invoicing-api.jar
Handler: com.gu.invoicing.preview.Lambda::handleRequest
Environment:
Variables:
Stage: !Ref Stage
Config:
!Sub
- '{{resolve:ssm:/invoicing-api/${Stage}/config:${Version}}}'
- Version: !FindInMap [StageMap, !Ref Stage, ConfigVersion]
Role: !GetAtt InvoicingLambdaRole.Arn
MemorySize: 3008
Runtime: java11
Timeout: 900
DependsOn:
- InvoicingLambdaRole
RefundErroneousPaymentLambda:
Type: AWS::Lambda::Function
Properties:
Description: Apply refund to Zuora invoice by shuffling credit balances.
FunctionName: !Sub invoicing-api-refund-erroneous-payment-${Stage}
Code:
S3Bucket: membership-dist
S3Key: !Sub support/${Stage}/invoicing-api/invoicing-api.jar
Handler: com.gu.invoicing.refundErroneousPayment.Lambda::handleRequest
Environment:
Variables:
Stage: !Ref Stage
Config:
!Sub
- '{{resolve:ssm:/invoicing-api/${Stage}/config:${Version}}}'
- Version: !FindInMap [StageMap, !Ref Stage, ConfigVersion]
Role: !GetAtt InvoicingLambdaRole.Arn
MemorySize: 3008
Runtime: java11
Timeout: 900
DependsOn:
- InvoicingLambdaRole
# ****************************************************************************
# API
# ****************************************************************************
InvoicingApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: !Sub invoicing-api-${Stage}
Description: Zuora invoice management (refunds, list invoices, download PDF, invoice preview)
BinaryMediaTypes:
- application/pdf
InvoicingApiStage:
Type: AWS::ApiGateway::Stage
Properties:
RestApiId: !Ref InvoicingApi
DeploymentId: !Ref InvoicingApiDeployment
StageName: !Sub ${Stage}
DependsOn:
- RefundMethod
InvoicingApiDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId: !Ref InvoicingApi
DependsOn:
- RefundMethod
InvoicingApiUsagePlan:
Type: AWS::ApiGateway::UsagePlan
Properties:
UsagePlanName: invoicing-api
ApiStages:
- ApiId: !Ref InvoicingApi
Stage: !Ref Stage
DependsOn:
- InvoicingApi
- InvoicingApiStage
InvoicingApiKey:
Type: AWS::ApiGateway::ApiKey
Properties:
Description: Used by https://github.com/guardian/invoicing-api
Enabled: true
Name: !Sub invoicing-api-key-${Stage}
StageKeys:
- RestApiId: !Ref InvoicingApi
StageName: !Sub ${Stage}
DependsOn:
- InvoicingApi
- InvoicingApiStage
InvoicingApiUsagePlanKey:
Type: AWS::ApiGateway::UsagePlanKey
Properties:
KeyId: !Ref InvoicingApiKey
KeyType: API_KEY
UsagePlanId: !Ref InvoicingApiUsagePlan
DependsOn:
- InvoicingApiKey
- InvoicingApiUsagePlan
# ****************************************************************************
# POST /refund
# ****************************************************************************
RefundEndpoint:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref InvoicingApi
ParentId: !GetAtt InvoicingApi.RootResourceId
PathPart: refund
DependsOn: InvoicingApi
RefundMethod:
Type: AWS::ApiGateway::Method
Properties:
ApiKeyRequired: true
AuthorizationType: NONE
RestApiId: !Ref InvoicingApi
ResourceId: !Ref RefundEndpoint
HttpMethod: POST
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST # this for the interaction between API Gateway and Lambda and MUST be POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RefundLambda.Arn}/invocations
DependsOn:
- InvoicingApi
- RefundEndpoint
- RefundLambda
# ****************************************************************************
# POST /refund-erroneous-payment
# ****************************************************************************
RefundErroneousPaymentEndpoint:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref InvoicingApi
ParentId: !GetAtt InvoicingApi.RootResourceId
PathPart: refund-erroneous-payment
DependsOn: InvoicingApi
RefundErroneousPaymentMethod:
Type: AWS::ApiGateway::Method
Properties:
ApiKeyRequired: true
AuthorizationType: NONE
RestApiId: !Ref InvoicingApi
ResourceId: !Ref RefundErroneousPaymentEndpoint
HttpMethod: POST
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST # this for the interaction between API Gateway and Lambda and MUST be POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RefundErroneousPaymentLambda.Arn}/invocations
DependsOn:
- InvoicingApi
- RefundErroneousPaymentEndpoint
- RefundErroneousPaymentLambda
# ****************************************************************************
# GET /invoices
# ****************************************************************************
InvoicesEndpoint:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref InvoicingApi
ParentId: !GetAtt InvoicingApi.RootResourceId
PathPart: invoices
DependsOn: InvoicingApi
InvoicesEndpointGetMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: AWS_IAM
ApiKeyRequired: true
RestApiId: !Ref InvoicingApi
ResourceId: !Ref InvoicesEndpoint
HttpMethod: GET
RequestParameters:
method.request.path.accountId: true
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST # this for the interaction between API Gateway and Lambda and MUST be POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${InvoicesLambda.Arn}/invocations
DependsOn:
- InvoicingApi
- InvoicesEndpoint
- InvoicesLambda
# ****************************************************************************
# GET /invoices/{invoiceId}
# ****************************************************************************
PdfInvoiceIdPath:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref InvoicingApi
ParentId: !Ref InvoicesEndpoint
PathPart: "{invoiceId}"
DependsOn:
- InvoicingApi
- InvoicesEndpoint
PdfInvoiceIdPathGetMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: AWS_IAM
ApiKeyRequired: true
RestApiId: !Ref InvoicingApi
ResourceId: !Ref PdfInvoiceIdPath
HttpMethod: GET
RequestParameters:
method.request.path.invoiceId: true
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST # this for the interaction between API Gateway and Lambda and MUST be POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PdfLambda.Arn}/invocations
DependsOn:
- InvoicingApi
- PdfLambda
- PdfInvoiceIdPath
# ****************************************************************************
# GET /next-invoice-date/{subscriptionName}
# ****************************************************************************
NextInvoiceDateEndpoint: # /next-invoice-date path
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref InvoicingApi
ParentId: !GetAtt InvoicingApi.RootResourceId
PathPart: next-invoice-date
DependsOn:
- InvoicingApi
NextInvoiceDatePath: # {subscriptionName} path parameter
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref InvoicingApi
ParentId: !Ref NextInvoiceDateEndpoint
PathPart: "{subscriptionName}"
DependsOn:
- InvoicingApi
- NextInvoiceDateEndpoint
NextInvoiceDatePathGetMethod: # GET verb
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
ApiKeyRequired: true
RestApiId: !Ref InvoicingApi
ResourceId: !Ref NextInvoiceDatePath
HttpMethod: GET
RequestParameters:
method.request.path.subscriptionName: true
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST # this for the interaction between API Gateway and Lambda and MUST be POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${NextInvoiceDateLambda.Arn}/invocations
DependsOn:
- InvoicingApi
- NextInvoiceDateLambda
- NextInvoiceDateEndpoint
- NextInvoiceDatePath
# ****************************************************************************
# GET /preview/{subscriptionName}?startDate=yyyy-mm-dd&endDate=yyyy-mm-dd
# ****************************************************************************
PreviewEndpoint: # /preview path
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref InvoicingApi
ParentId: !GetAtt InvoicingApi.RootResourceId
PathPart: preview
DependsOn:
- InvoicingApi
PreviewEndpointPath: # {subscriptionName} path parameter
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref InvoicingApi
ParentId: !Ref PreviewEndpoint
PathPart: "{subscriptionName}"
DependsOn:
- InvoicingApi
- PreviewEndpoint
PreviewEndpointPathGetMethod: # GET verb
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
ApiKeyRequired: true
RestApiId: !Ref InvoicingApi
ResourceId: !Ref PreviewEndpointPath
HttpMethod: GET
RequestParameters:
method.request.path.subscriptionName: true
method.request.querystring.startDate: true
method.request.querystring.endDate: true
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST # this for the interaction between API Gateway and Lambda and MUST be POST
Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PreviewLambda.Arn}/invocations
DependsOn:
- PreviewLambda
- InvoicingApi
- PreviewEndpoint
- PreviewEndpointPath
# ****************************************************************************
# Access control
# ****************************************************************************
RefundLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
FunctionName: !Sub invoicing-api-refund-${Stage}
Principal: apigateway.amazonaws.com
DependsOn:
- RefundLambda
- RefundMethod
InvoicesLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
FunctionName: !Sub invoicing-api-invoices-${Stage}
Principal: apigateway.amazonaws.com
DependsOn:
- InvoicesLambda
PdfLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
FunctionName: !Sub invoicing-api-pdf-${Stage}
Principal: apigateway.amazonaws.com
DependsOn:
- PdfLambda
NextInvoiceDateLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
FunctionName: !Sub invoicing-api-nextinvoicedate-${Stage}
Principal: apigateway.amazonaws.com
DependsOn:
- NextInvoiceDateLambda
PreviewLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
FunctionName: !Sub invoicing-api-preview-${Stage}
Principal: apigateway.amazonaws.com
DependsOn:
- PreviewLambda
InvoicingLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: LambdaPolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- lambda:InvokeFunction
Resource:
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/invoicing-api-refund-${Stage}:log-stream:*
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/invoicing-api-invoices-${Stage}:log-stream:*
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/invoicing-api-pdf-${Stage}:log-stream:*
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/invoicing-api-nextinvoicedate-${Stage}:log-stream:*
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/invoicing-api-preview-${Stage}:log-stream:*
- !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/invoicing-api-refund-erroneous-payment-${Stage}:log-stream:*
- PolicyName: ReadPrivateCredentials
PolicyDocument:
Statement:
- Effect: Allow
Action: s3:GetObject
Resource: !Sub arn:aws:s3:::gu-reader-revenue-private/membership/support-service-lambdas/${Stage}/zuoraRest-${Stage}*.json
- PolicyName: ReadFromDeploymentBucket
PolicyDocument:
Statement:
- Effect: Allow
Action: s3:GetObject
Resource: arn:aws:s3::*:membership-dist/*
RefundErroneousPaymentLambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
FunctionName: !Sub invoicing-api-refund-erroneous-payment-${Stage}
Principal: apigateway.amazonaws.com
DependsOn:
- RefundErroneousPaymentLambda
- RefundErroneousPaymentMethod
InvoicingApiDomainName:
Type: "AWS::ApiGateway::DomainName"
Properties:
RegionalCertificateArn: # only for *.support.guardianapis.com
!Sub arn:aws:acm:${AWS::Region}:${AWS::AccountId}:certificate/b384a6a0-2f54-4874-b99b-96eeff96c009
DomainName: !FindInMap [ StageMap, !Ref Stage, DomainName ]
EndpointConfiguration:
Types:
- REGIONAL
InvoicingApiBasePathMapping:
Type: "AWS::ApiGateway::BasePathMapping"
Properties:
RestApiId: !Ref InvoicingApi
DomainName: !Ref InvoicingApiDomainName
Stage: !Sub ${Stage}
DependsOn:
- InvoicingApi
- InvoicingApiDomainName
- InvoicingApiStage
InvoicingApiDNSRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: support.guardianapis.com.
Name: !FindInMap [ StageMap, !Ref Stage, DomainName ]
Type: CNAME
TTL: '120'
ResourceRecords:
- !GetAtt InvoicingApiDomainName.RegionalDomainName