[英]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#webhook-signing
希望這可以幫助!
要創建條紋 webhook,您需要原始主體
在 Nextjs 13 中,您可以通過
const body= await req.text()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.