验证请求签名时出错- Next.JS 13 +条带

utugiqy6  于 2023-06-22  发布在  其他
关注(0)|答案(2)|浏览(107)

我尝试在Stripe webhook端点中验证请求签名,但一直收到错误:

No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe?

我已经尝试了多种解决方案,多种解析原始请求体的方法。我也试过手动验证HMAC信号。什么都没用。
我的环境:
Next.js 13.3.2 Stripe SDK 12.4.0
复制步骤:
1.在以下位置创建端点:/pages/api/p/test
代码:

import Stripe from 'stripe';
import { NextApiRequest, NextApiResponse } from 'next';
import safe from 'colors/safe';
import { buffer } from 'micro';

const handler = async (
    req: NextApiRequest,
    res: NextApiResponse
): Promise<void> => {
    const stripe = new Stripe(process.env.STRIPE_SECRET as string, {
        apiVersion: '2022-11-15',
    });

    const webhookSecret: string = "whsec_33244....";

    if (req.method === 'POST') {
        const sig = req.headers['stripe-signature'] as string;
        const buf = await buffer(req);
        const body = buf.toString();

        let event: Stripe.Event;

        try {
            console.log(safe.bgGreen(body))
            event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
        } catch (err) {
            // On error, log and return the error message
            console.log(`❌ Error message: ${err.message}`);
            res.status(400).send(`Webhook Error: ${err.message}`);
            return;
        }

        // Successfully constructed event
        console.log('✅ Success:', event.id);

        // Cast event data to Stripe object
        if (event.type === 'payment_intent.succeeded') {
            const stripeObject: Stripe.PaymentIntent = event.data
                .object as Stripe.PaymentIntent;
            console.log(`💰 PaymentIntent status: ${stripeObject.status}`);
        } else if (event.type === 'charge.succeeded') {
            const charge = event.data.object as Stripe.Charge;
            console.log(`💵 Charge id: ${charge.id}`);
        } else {
            console.warn(`🤷‍♀️ Unhandled event type: ${event.type}`);
        }

        // Return a response to acknowledge receipt of the event
        res.json({ received: true });
    } else {
        res.setHeader('Allow', 'POST');
        res.status(405).end('Method Not Allowed');
    }
};

export const config = {
    api: {
        bodyParser: false,
    },
};

export default handler;

1.启动dev服务器并启动条带侦听器

stripe listen --forward-to localhost:3000/api/p/test

1.复制生成的webhook签名密钥并粘贴到代码中
1.启动模拟事件

stripe trigger payment_intent.succeeded

我在网上搜遍了所有的方法。我完全被难倒了。我正在使用微服务包中的buffer,但我也在Stripe的文档中尝试了示例中的缓冲区功能。它似乎正确地解析了主体:

console.log(body)

{
  "id": "evt_3NIzhvEcQkKxoTiV1xjEMh02",
  "object": "event",
  "api_version": "2022-11-15",
  "created": 1686772219,
  "data": {
    "object": {
      "id": "pi_3NIzhvEcQkKxoTiV1kuXEmH6",
      "object": "payment_intent",
      "amount": 2000,
      "amount_capturable": 0,
      "amount_details": {
        "tip": {
        }
      },
      "amount_received": 0,
      "application": null,
      "application_fee_amount": null,
      "automatic_payment_methods": null,
      "canceled_at": null,
      "cancellation_reason": null,
      "capture_method": "automatic",
      "client_secret": "pi_3NIzhvEcQkKxoTiV1kuXEmH6_secret_ZRnfaga3uRF5EhNXE0KwZpDpp",
      "confirmation_method": "automatic",
      "created": 1686772219,
      "currency": "usd",
      "customer": null,
      "description": "(created by Stripe CLI)",
      "invoice": null,
      "last_payment_error": null,
      "latest_charge": null,
      "livemode": false,
      "metadata": {
      },
      "next_action": null,
      "on_behalf_of": null,
      "payment_method": null,
      "payment_method_options": {
        "card": {
          "installments": null,
          "mandate_options": null,
          "network": null,
          "request_three_d_secure": "automatic"
        }
      },
      "payment_method_types": [
        "card"
      ],
      "processing": null,
      "receipt_email": null,
      "review": null,
      "setup_future_usage": null,
      "shipping": {
        "address": {
          "city": "San Francisco",
          "country": "US",
          "line1": "510 Townsend St",
          "line2": null,
          "postal_code": "94103",
          "state": "CA"
        },
        "carrier": null,
        "name": "Jenny Rosen",
        "phone": null,
        "tracking_number": null
      },
      "source": null,
      "statement_descriptor": null,
      "statement_descriptor_suffix": null,
      "status": "requires_payment_method",
      "transfer_data": null,
      "transfer_group": null
    }
  },
  "livemode": false,
  "pending_webhooks": 2,
  "request": {
    "id": "req_MfPOwrw4MZsykJ",
    "idempotency_key": "10108c32-75db-4e1e-abb3-b3b2673df025"
  },
  "type": "payment_intent.created"
}

如果有人有任何想法,我将不胜感激!

zpqajqem

zpqajqem1#

问题是Next.JS有一个中间件来处理请求的原始体。尝试为您的路线关闭bodyParser:https://nextjs.org/docs/pages/building-your-application/routing/api-routes#custom-config,否则您需要确保获得原始请求。
在将body传递给constructEvent()之前,在端点中记录body时,应该会看到一堆二进制(这就是缓冲区数据类型的样子)。这是一个迹象,你实际上有原始的身体。如果您看到JSON或字符串,那么您知道原始主体已被操作,并且您的Webhook签名验证将失败。

aor9mmx1

aor9mmx12#

男孩我觉得愚蠢。我的测试密钥末尾缺少一个字符。😡

相关问题