簡體   English   中英

我正在嘗試使用 Stripe 和 NextJS 13.2.3 設置 webhook

[英]I am trying to setup of a webhook using Stripe and NextJS 13.2.3

我能夠通過內置的 Stripe 頁面成功結帳我的購物車,並且我被重定向到我的 successUrl 路由。 我的本地測試 webhook 按預期被調用。 但是,當我包含驗證請求來自 Stripe 的代碼時,出現了一些錯誤。

這是我在 NextJS 中的 webhook

import { stripe } from "@/lib/stripe"

export async function POST(req: Request){

    const payload = await req.json()
    
    const sig = req.headers.get('stripe-signature') as string
    console.log("sig: ", sig)

    let event;

    const endpointSecret = process.env.WEBHOOK_SECRET_LOCAL as string

    try {
        console.log("constructing event...")
        event = stripe.webhooks.constructEvent(payload, sig, endpointSecret)
    } catch (error) {
        console.error(error)
        return new Response(`Webhook error: ${error}`, {
            status: 400,
        })
    }
    
    return new Response("payment confirmation route received", {
        status: 200,
    })
}

我正在使用 Stripe CLI 監聽成功事件stripe listen --forward-to localhost:3000/api/checkout/payment-confirmation

查看我的購物車后,我看到...

2023-03-23 15:51:28 --> checkout.session.completed [evt_1MowyjE7TMviQOgJNbXch10w] 2023-03-23 15:51:28 --> charge.succeeded [evt_3MowyhE7TMviQOgJ09e3Q7Hu] 2023-03-23 15:51:29 --> payment_intent.succeeded [evt_3MowyhE7TMviQOgJ0OIjeNoH] 2023-03-23 15:51:29 --> payment_intent.created [evt_3MowyhE7TMviQOgJ0ovhouJM] 2023-03-23 15:51:32 <-- [400] POST http://localhost:3000/api/checkout/payment-confirmation [evt_3MowyhE7TMviQOgJ09e3Q7Hu] 2023-03-23 15:51:32 <-- [400] POST http://localhost:3000/api/checkout/payment-confirmation [evt_3MowyhE7TMviQOgJ0OIjeNoH] 2023-03-23 15:51:32 <-- [400] POST http://localhost:3000/api/checkout/payment-confirmation [evt_1MowyjE7TMviQOgJNbXch10w] 2023-03-23 15:51:32 <-- [400] POST http://localhost:3000/api/checkout/payment-confirmation [evt_3MowyhE7TMviQOgJ0ovhouJM]

從 NextJS 我看到...

StripeSignatureVerificationError: Webhook payload must be provided as a string or a Buffer (https://nodejs.org/api/buffer.html) instance representing the _raw_ request body.Payload was provided as a parsed JavaScript object instead. Signature verification is impossible without access to the original signed material. Learn more about webhook signing and explore webhook integration examples for various frameworks at https://github.com/stripe/stripe-node#webhook-signing

我認為這意味着我需要以某種方式修改有效負載參數。 我嘗試了payload.toString()JSON.stringify(payload) ,使用const buf = Buffer.from(payload)和其他各種東西,認為我需要以某種方式將這個 object 變成一個字符串或緩沖區,但我不明白問題。 非常感謝您的幫助!

- 更新 -

現在我已經更新了代碼,但我仍然收到一般錯誤......

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

我對 Stripe 的回應:“我的意思是,我這么認為???”

我也嘗試過導入“micro”或“raw-body”,就像我看到其他人使用的那樣,但它們會給我各種錯誤,這就是我使用 node.js 中的錯誤的原因。 req.body 是一個 ReadableStream,

'ReadableStream | 類型的參數 null' 不可分配給 'string | 類型的參數緩沖'

如果我嘗試將它作為 constructEvent 中的第一個參數傳遞。

import { stripe } from "@/lib/stripe"
import { NextRequest } from "next/server";
import { Buffer } from "node:buffer";

export async function POST(req: NextRequest){
    
    const buf = Buffer.from(req.toString())
// <Buffer 5b 6f 62 6a 65 63 74 20 52 65 71 75 65 73 74 5d>

    const headers = req.headers
    
    const sig = req.headers.get("stripe-signature") as string | string[]
// t=1679...,v1=888050e17...,v0=cc9b94a...
    let event;
    const endpointSecret = process.env.WEBHOOK_SECRET_LOCAL as string
// whsec_...
    

    try {
        console.log("constructing event...")
        event = stripe.webhooks.constructEvent(buf, sig, endpointSecret)
    } catch (error) {
        console.error(error)
        return new Response(`Webhook error: ${error}`, {
            status: 400,
        })
    }
    
    return new Response("payment confirmation route received", {
        status: 200,
    })
}

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

不要使用micro來緩沖主體,只需使用await req.text()將原始主體轉換為字符串,然后將字符串傳遞給stripe.webhooks.constructEvent

當 Stripe 將事件發送到您的 webhook 端點時,他們首先會根據事件的原始主體計算簽名。 當您處理數據時,您需要確保您查看的是與 Stripe 使用的完全相同的原始數據體。 如果您的代碼查看修改后的版本,即使它只是添加了一個空格或更改了屬性的順序,您也無法匹配簽名。

使用 stripe-node 時,開發人員經常會遇到簽名驗證問題。 問題是各種框架在收到 JSON 並已經解析它以供使用時試圖提供幫助。 因此,當您嘗試訪問數據時,即使其中的“數據”相似,您最終也會得到不同版本的有效負載。

您的代碼正在使用req.json()但您想要的是訪問通常通過request.body完成的 POST 請求的確切原始主體。 您可以在此處參考 Stripe 的文檔。 查看 Github 存儲庫上的這個流行問題對於 stripe-node 也很有用,它有許多替代解決方案,具體取決於您的整體環境。

問題是 Stripe 需要原始主體來計算簽名,而 Next.js 會自動解析主體。 您可以禁用主體解析器並將數據解析為緩沖區。 這是我的解決方案:

const stripe = require('stripe')(process.env.STRIPE_PRIVATE);

const buffer = (req) => {
  return new Promise((resolve, reject) => {
    const chunks = [];

    req.on('data', (chunk) => {
      chunks.push(chunk);
    });

    req.on('end', () => {
      resolve(Buffer.concat(chunks));
    });

    req.on('error', reject);
  });
};

const handler = async (req, res) => {
  if (!req.method === 'POST') {
    res.setHeader('Allow', 'POST');
    res.status(405).end('Method Not Allowed');
    return;
  }

  const sig = req.headers['stripe-signature'];

  let event;

  try {
    const body = await buffer(req);

    event = stripe.webhooks.constructEvent(
      body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (error) {
    console.log('Webhook invalid!', error);
    return res.status(400).send(`Webhook Error: ${error.message}`);
  }

  // Handle the checkout.session.completed event
  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;

    // Fulfill the purchase...
    console.log('SESSION: ', session);
  }

  // Return a response to acknowledge receipt of the event
  res.json({ received: true });
};

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

export default handler;

此解決方案改編自:

https://github.com/stripe/stripe-node/blob/master/examples/webhook-signing/nextjs/pages/api/webhooks.ts

我在這里找到的: https://github.com/stripe/stripe-node#webhook-signing

希望這可以幫助!

要創建條紋 webhook,您需要原始主體

在 Nextjs 13 中,您可以通過

const body= await req.text()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2025 STACKOOM.COM