错误验证请求签名 – Next.JS 13 + Stripe

huangapple go评论71阅读模式
英文:

Error Verifying Request Signature - Next.JS 13 + Stripe

问题

I'm trying to verify the request signature in my Stripe webhook endpoint and I keep getting error:

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

I've tried a multitude of solutions, multiple ways of parsing the raw request body. I've also tried manually verifying the HMAC sig. Nothing has worked.

My envinronment:

Next.js 13.3.2
Stripe SDK 12.4.0

Steps to reproduce:

  1. Create an endpoint at: /pages/api/p/test

Code:

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. Start dev server and start a stripe listener
stripe listen --forward-to localhost:3000/api/p/test
  1. Copy the webhook signing secret generated and paste in code

  2. Initiate a simulated event

stripe trigger payment_intent.succeeded

I've scoured the internet and tried everything. I'm totally stumped. I'm using buffer from microservices package but I've tried the buffer function in the example in Stripe's docs as well. It seems to be parsing the body correctly:

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,": 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",
          "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"
}

If anyone has ANY ides, I would be most grateful!

英文:

I'm trying to verify the request signature in my Stripe webhook endpoint and I keep getting error:

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

I've tried a multitude of solutions, multiple ways of parsing the raw request body. I've also tried manually verifying the HMAC sig. Nothing has worked.

My envinronment:

Next.js 13.3.2
Stripe SDK 12.4.0

Steps to reproduce:

  1. Create an endpoint at: /pages/api/p/test

Code:

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

const handler = async (
    req: NextApiRequest,
    res: NextApiResponse
): Promise&lt;void&gt; =&gt; {
    const stripe = new Stripe(process.env.STRIPE_SECRET as string, {
        apiVersion: &#39;2022-11-15&#39;,
    });

    const webhookSecret: string = &quot;whsec_33244....&quot;;

    if (req.method === &#39;POST&#39;) {
        const sig = req.headers[&#39;stripe-signature&#39;] 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(&#39; Success:&#39;, event.id);

        // Cast event data to Stripe object
        if (event.type === &#39;payment_intent.succeeded&#39;) {
            const stripeObject: Stripe.PaymentIntent = event.data
                .object as Stripe.PaymentIntent;
            console.log(`&#128176; PaymentIntent status: ${stripeObject.status}`);
        } else if (event.type === &#39;charge.succeeded&#39;) {
            const charge = event.data.object as Stripe.Charge;
            console.log(`&#128181; Charge id: ${charge.id}`);
        } else {
            console.warn(`&#129335;‍♀️ Unhandled event type: ${event.type}`);
        }

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

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

export default handler;
  1. Start dev server and start a stripe listener
stripe listen --forward-to localhost:3000/api/p/test
  1. Copy the webhook signing secret generated and paste in code

  2. Initiate a simulated event

stripe trigger payment_intent.succeeded

I've scoured the internet and tried everything. I'm totally stumped. I'm using buffer from microservices package but I've tried the buffer function in the example in Stripe's docs as well. It seems to be parsing the body correctly:

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

If anyone has ANY ides, I would be most grateful!

答案1

得分: 0

问题在于Next.JS有一个中间件,用来操作请求的原始主体。尝试为您的路由关闭bodyParser:https://nextjs.org/docs/pages/building-your-application/routing/api-routes#custom-config,否则您需要确保获取原始请求。

当您在将body传递给constructEvent()之前记录端点中的body时,您应该看到一堆二进制数据(这是缓冲数据类型的样子)。这表示您实际上有原始主体。如果您看到JSON或字符串,则说明原始主体已被修改,您的Webhook签名验证将失败。

英文:

The issue is that Next.JS has middleware that manipulates the raw body of the request. Try turning off bodyParser for your route: https://nextjs.org/docs/pages/building-your-application/routing/api-routes#custom-config, otherwise you are going to need to make sure you get the raw request.

When you log the body in your endpoint before passing it to constructEvent() you should see a bunch of binary (which is what a buffer data type looks like). That is an indication you actually have the raw body. If you see JSON or a string then you know that the raw body has been manipulated and your Webhook signature verification will fail.

答案2

得分: 0

问题是我在测试密钥的末尾缺少一个字符。

英文:

My problem was that I had a character missing from the end of my test key.

答案3

得分: 0

由于Stripe Webhook有两个密钥,一个用于本地主机,另一个用于生产环境,所以您可能会遇到密钥问题。您可以获取这个密钥,进入开发者选项,然后选择Webhook,再选择签名密钥。

错误验证请求签名 – Next.JS 13 + Stripe

英文:

You may have keys issue because stripe webhook have two keys one for localhost and second for production, you can grab this key go to developer, then webhook and then signing secret, 错误验证请求签名 – Next.JS 13 + Stripe

huangapple
  • 本文由 发表于 2023年6月15日 04:03:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/76477168.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定