You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Stripe关联账户支付链接Metadata传递至主账户Webhook

问题:支付链接元数据无法传递到PaymentIntent Webhook

已在关联账户的支付链接中添加invoiceId元数据,且支付链接API的请求和响应中该元数据存在,但主账户Webhook接收的PaymentIntent元数据为空,无法获取invoiceId来更新系统内发票的支付状态,已确认Webhook请求体中Metadata确实为空。

生成支付链接的API代码

import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

export async function POST(req) {
    try {
        const body = await req.json();
        const { invoiceId, amount, currency, invoiceNumber, stripeAccountId } = body;

        const product = await stripe.products.create({
            name: `Invoice #${invoiceNumber}`
        }, {
            stripeAccount: stripeAccountId
        });

        const price = await stripe.prices.create({
            product: product.id,
            unit_amount: currency === "JPY" ? amount : amount * 100,
            currency: currency
        }, {
            stripeAccount: stripeAccountId // Specify the connected account ID
        });

        const paymentLink = await stripe.paymentLinks.create({
            line_items: [
                {
                    price: price.id,
                    quantity: 1,
                },
            ],
            metadata: {
                invoiceId: invoiceId // Add the invoice number here
            }
        }, {
            stripeAccount: stripeAccountId // Specify the connected account ID
        });

        return new Response(JSON.stringify({
            success: true,
            message: "Payment link generated successfully",
            url: paymentLink.url
        }), {
            status: 200,
            headers: {
                'Content-Type': 'application/json',
            },
        });
    }
    catch (error) {
        return new Response(JSON.stringify({
            success: false,
            message: error.message
        }), {
            status: 500,
            headers: {
                'Content-Type': 'application/json',
            },
        });
    }
}

主账户Webhook的API代码

import { NextResponse } from 'next/server';
import { stripe } from '@/utils/stripeAuth';

export async function POST(req) {
    let rawBody;

    try {
        // Read raw body as a string
        rawBody = await req.text();
    } catch (err) {
        console.error('Error reading raw body:', err);
        return NextResponse.json({
            success: false,
            message: `Failed to read request body: ${err.message}`,
        }, {
            status: 400,
        });
    }

    const sig = req.headers.get('stripe-signature');

    if (!sig) {
        return NextResponse.json({
            success: false,
            message: 'No stripe-signature header value was provided.',
        }, {
            status: 400,
        });
    }

    let event;

    try {
        // Convert the raw body string to a buffer with the correct encoding
        const buf = Buffer.from(rawBody, 'utf8');

        // Verify the event by constructing it using the raw body buffer and the signature
        event = stripe.webhooks.constructEvent(buf, sig, process.env.WEBHOOK_SECRET);
    } catch (err) {
        console.error('Webhook signature verification failed:', err);
        return NextResponse.json({
            success: false,
            message: `Webhook Error: ${err.message}`,
        }, {
            status: 400,
        });
    }

    let paymentIntent;

    // Handle the event
    switch (event.type) {
        case 'payment_intent.payment_failed':
            paymentIntent = event.data.object;
            // console.log(`PaymentIntent for ${paymentIntent.amount} failed.`);
            break;
        case 'payment_intent.succeeded':
            paymentIntent = event.data.object;
            const invoiceId = paymentIntent.metadata.invoiceId;
            // console.log(`PaymentIntent for ${paymentIntent.amount} succeeded.`);
            break;
        default:
            console.log(`Unhandled event type ${event.type}`);
    }

    return NextResponse.json({
        success: true,
        message: paymentIntent,
    }, {
        status: 200,
    });
}

解决方案

Stripe支付链接的元数据不会自动传递到后续生成的PaymentIntent中,需要在创建PaymentLink时,显式通过payment_intent_data.metadata字段指定要传递的元数据。

修改生成支付链接的代码,在stripe.paymentLinks.create的参数中添加payment_intent_data配置:

const paymentLink = await stripe.paymentLinks.create({
    line_items: [
        {
            price: price.id,
            quantity: 1,
        },
    ],
    metadata: {
        invoiceId: invoiceId // 保留原PaymentLink的元数据(可选)
    },
    // 添加此配置,将元数据传递给PaymentIntent
    payment_intent_data: {
        metadata: {
            invoiceId: invoiceId
        }
    }
}, {
    stripeAccount: stripeAccountId
});

这样用户通过支付链接完成支付时,生成的PaymentIntent会携带invoiceId元数据,主账户Webhook就能正常获取该值更新发票状态。

内容的提问来源于stack exchange,提问作者Skb_Developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 00:39:53