我正在尝试将完整的请求正文作为消息发送到 SQS。但是,API Gateway 只发送部分请求正文。有什么办法解决这个问题吗?
我与 Postman 一起使用的示例标头:
Content-Type: application/json
stripe-signature: t=1702112345,v1=82872ac5c8be8f30a963c6922ab47ce47f123456be9f992492fe78107f86b2fe,v0=f01bd2a1da1234567a487860b7f5625d225e5cd5aae76c7a786fc886f2e6c04c
我与 Postman 一起使用的示例请求正文:
{
"id": "evt_1OLJecAYjghwsx12QOJm8jS6",
"object": "event",
"api_version": "2023-10-16",
"created": 1702101886,
"data": {
"object": {
"id": "cs_test_a1GNkCx4LlmaoVdoDVWKDffNjXUpOPOPO1n05SDyGHYcHqpbpsqowAhkuR",
"object": "checkout.session",
"after_expiration": null,
"allow_promotion_codes": null,
"amount_subtotal": 111,
"amount_total": 111,
"automatic_tax": {
"enabled": false,
"status": null
},
"billing_address_collection": null,
"cancel_url": "http://localhost:3000/search-params?search=hour&success=false",
"client_reference_id": null,
"client_secret": null,
"consent": null,
"consent_collection": null,
"created": 1702101865,
"currency": "cad",
"currency_conversion": null,
"custom_fields": [
],
"custom_text": {
"after_submit": null,
"shipping_address": null,
"submit": null,
"terms_of_service_acceptance": null
},
"customer": "cus_I9i1CAD0zq2XaR",
"customer_creation": null,
"customer_details": {
"address": {
"city": null,
"country": "CA",
"line1": null,
"line2": null,
"postal_code": "V3F 5Y3",
"state": null
},
"email": "[email protected]",
"name": "adfsaf",
"phone": null,
"tax_exempt": "none",
"tax_ids": [
]
},
"customer_email": null,
"expires_at": 1702188265,
"invoice": null,
"invoice_creation": {
"enabled": false,
"invoice_data": {
"account_tax_ids": null,
"custom_fields": null,
"description": null,
"footer": null,
"metadata": {
},
"rendering_options": null
}
},
"livemode": false,
"locale": null,
"metadata": {
},
"mode": "payment",
"payment_intent": "pi_3OLJeaAYasdfgh452me8h1Px",
"payment_link": null,
"payment_method_collection": "if_required",
"payment_method_configuration_details": null,
"payment_method_options": {
},
"payment_method_types": [
"card"
],
"payment_status": "paid",
"phone_number_collection": {
"enabled": false
},
"recovered_from": null,
"setup_intent": null,
"shipping_address_collection": null,
"shipping_cost": null,
"shipping_details": null,
"shipping_options": [
],
"status": "complete",
"submit_type": null,
"subscription": null,
"success_url": "http://localhost:3000/search-params?search=hour&success=true",
"total_details": {
"amount_discount": 0,
"amount_shipping": 0,
"amount_tax": 0
},
"ui_mode": "hosted",
"url": null
}
},
"livemode": false,
"pending_webhooks": 1,
"request": {
"id": null,
"idempotency_key": null
},
"type": "checkout.session.completed"
}
API 网关发送到 SQS 的示例请求正文。请注意它只是请求正文的一部分:
2023-12-09T06:12:36.193Z 8a8bb59f-02ef-5eb3-8df2-e871056a9c62 INFO Processed message {"id":"evt_1OLJecASdfghjkl45QOJm8jS6","object":"event","api_version":"2023-10-16","created":1702101886,"data":{"object":{"id":"cs_test_a1GNkCx4LlprvVdoDVWKDffNjXUpPOIUY1n05SDyGHYcHasdfghjkAhkuR","object":"checkout.session","after_expiration":null,"allow_promotion_codes":null,"amount_subtotal":111,"amount_total":111,"automatic_tax":{"enabled":false,"status":null},"billing_address_collection":null,"cancel_url":"http://localhost:3000/search-params?search=hour
用于生成 API 网关的 Terraform 代码:
# Create an HTTP API Gateway
resource "aws_api_gateway_rest_api" "stripe_webhook_api" {
name = "stripe-webhook-http-api"
description = "stripe webhook"
}
#Configure API Gateway resource
resource "aws_api_gateway_resource" "stripe_webhook_api_resource" {
rest_api_id = aws_api_gateway_rest_api.stripe_webhook_api.id
parent_id = aws_api_gateway_rest_api.stripe_webhook_api.root_resource_id
path_part = var.path
}
#Configure API Gateway method request
resource "aws_api_gateway_method" "stripe_webhook_api_method" {
rest_api_id = aws_api_gateway_rest_api.stripe_webhook_api.id
resource_id = aws_api_gateway_resource.stripe_webhook_api_resource.id
http_method = "POST"
authorization = "NONE"
request_parameters = {
"method.request.header.stripe-signature" = true,
}
}
#Configure API Gateway integration
resource "aws_api_gateway_integration" "stripe_webhook_api_integration" {
rest_api_id = aws_api_gateway_rest_api.stripe_webhook_api.id
resource_id = aws_api_gateway_resource.stripe_webhook_api_resource.id
http_method = aws_api_gateway_method.stripe_webhook_api_method.http_method
integration_http_method = "POST"
type = "AWS"
uri = join("", ["arn:aws:apigateway:", var.region, ":sqs:path/", data.aws_caller_identity.current.account_id, "/", aws_sqs_queue.stripe_webhook_sqs.name])
credentials = aws_iam_role.stripe_webhook_APIGW_to_SQS_Role.arn
request_parameters = {
"integration.request.header.Content-Type" = "'application/x-www-form-urlencoded'"
"integration.request.querystring.MessageAttribute.1.Name" = "'stripeSignature'"
"integration.request.querystring.MessageAttribute.1.Value.DataType" = "'String'"
"integration.request.querystring.MessageAttribute.1.Value.StringValue" = "method.request.header.stripe-signature"
}
request_templates = {
"application/json" = "Action=SendMessage&MessageBody=$input.json('$')"
}
passthrough_behavior = "NEVER"
}
# Configure API Gateway to push all logs to CloudWatch Logs
resource "aws_api_gateway_method_settings" "StripeWebhookGatewaySettings" {
rest_api_id = aws_api_gateway_rest_api.stripe_webhook_api.id
stage_name = aws_api_gateway_stage.StripeWebhookGatewayStage.stage_name
method_path = "*/*"
settings {
# Enable CloudWatch logging and metrics
metrics_enabled = true
logging_level = "INFO"
}
}
# Configure API Gateway integration response
resource "aws_api_gateway_integration_response" "stripe_webhook_api_integration_response" {
rest_api_id = aws_api_gateway_rest_api.stripe_webhook_api.id
resource_id = aws_api_gateway_resource.stripe_webhook_api_resource.id
http_method = aws_api_gateway_method.stripe_webhook_api_method.http_method
status_code = aws_api_gateway_method_response.stripe_webhook_api_method_response.status_code
}
# Configure API Gateway method response
resource "aws_api_gateway_method_response" "stripe_webhook_api_method_response" {
rest_api_id = aws_api_gateway_rest_api.stripe_webhook_api.id
resource_id = aws_api_gateway_resource.stripe_webhook_api_resource.id
http_method = aws_api_gateway_method.stripe_webhook_api_method.http_method
status_code = "200"
response_models = {
"application/json" = "Empty"
}
}
# Create a new API Gateway deployment for the created rest api
resource "aws_api_gateway_deployment" "stripe_webhook_api_deployment" {
depends_on = [aws_api_gateway_integration.stripe_webhook_api_integration]
rest_api_id = aws_api_gateway_rest_api.stripe_webhook_api.id
lifecycle {
create_before_destroy = true
}
}
# Create a Log Group for API Gateway to push logs to
resource "aws_cloudwatch_log_group" "stripe_webhook_APIGW_logGroup" {
name_prefix = "/aws/stripe-webhook-APIGW/terraform"
}
# Create a Log Policy to allow Cloudwatch to Create log streams and put logs
resource "aws_cloudwatch_log_resource_policy" "stripe_webhook_APIGW_logPolicy" {
policy_name = "Terraform-stripe_webhook_APIGW_logPolicy-${data.aws_caller_identity.current.account_id}"
policy_document = data.template_file.stripe_webhook_APIGW_logPolicy_template.rendered
}
# Create a new API Gateway stage with logging enabled
resource "aws_api_gateway_stage" "StripeWebhookGatewayStage" {
deployment_id = aws_api_gateway_deployment.stripe_webhook_api_deployment.id
rest_api_id = aws_api_gateway_rest_api.stripe_webhook_api.id
stage_name = "default"
access_log_settings {
destination_arn = aws_cloudwatch_log_group.stripe_webhook_APIGW_logGroup.arn
format = join(", ", ["{ \"requestId\":\"$context.requestId\"",
"\"ip\":\"$context.identity.sourceIp\"",
"\"requestTime\":\"$context.requestTime\"",
"\"httpMethod\":\"$context.httpMethod\"",
"\"routeKey\":\"$context.routeKey\"",
"\"status\":\"$context.status\",\"protocol\":\"$context.protocol\"",
"\"responseLength\":\"$context.responseLength\"",
"\"authorizererror\":\"$context.authorizer.error\"",
"\"errormessage\":\"$context.error.message\"",
"\"errormessageString\":\"$context.error.messageString\"",
"\"errorresponseType\":\"$context.error.responseType\"",
"\"integrationerror\":\"$context.integration.error\"",
"\"integrationErrorMessage\":\"$context.integrationErrorMessage\" }"])
}
}
我还确保为 SQS 创建正确的角色和策略以从 API 网关接收消息:
API 网关到 SQS 角色:
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Principal" : {
"Service" : "apigateway.amazonaws.com"
},
"Action" : "sts:AssumeRole"
}
]
}
SQS 策略的 API 网关:
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Action" : [
"sqs:SendMessage",
"sqs:ListQueues"
],
"Resource" : "${sqsarn}"
}
]
}
如果正文有 & 字符,则需要 $util.urlEncode 方法。
这里是更详细地描述问题和解决方案的链接。
我的下一步是弄清楚如何让 Terraform 在 API 发生更改时重新部署 API。我正在研究的一种解决方案是
https://developer.hashicorp.com/terraform/language/resources/terraform-data
这看起来很糟糕,但它比从 AWS 控制台手动重新部署 API 更好。如果有人对此有任何建议,请随时发表评论。