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

Stripe Connect退款API报no such payment_intent错误排查求助

问题:Stripe退款API返回"no such payment_intent"错误排查

我基于Firebase和FlutterFlow搭建支付系统,集成了Stripe的checkout.session.completed事件Webhook做异步处理。核心逻辑是在购买服务创建出错时自动发起退款,但现在不管是代码里调用还是用Postman测试Stripe退款API,都返回**"no such payment_intent"**错误;只有直接通过/v1/payment_intents接口处理支付时,退款API才能正常工作。已经确认令牌配置正确,也联系了Stripe支持团队,现在需要帮忙排查解决这个问题。

相关代码

const functions = require('firebase-functions');
const admin = require('firebase-admin');
// To avoid deployment errors, do not call admin.initializeApp() in your code 

const axios = require('axios');
const stripe = require("stripe")(functions.config().stripe.token);
const stripeAccount = 'acct_1OT33cQlnlPZnFaP';

exports.stripeWebhook = functions
  .runWith({
    timeoutSeconds: 60,
    memory: '256MB'
  })
  .https.onRequest(async (req, res) => {
    let clientReferenceId, amountTotal, paymentIntent, serviceRef, assoRef, stableRef, session;
    let userDoc, servicesRef, assoRefDoc;
    try {
      const event = stripe.webhooks.constructEvent(
        req.rawBody,
        req.headers["stripe-signature"],
        functions.config().stripe.payments_webhook_secret
      );

      if (event.type === 'checkout.session.completed') {

        const db = admin.firestore();

        await db.runTransaction(async (transaction) => {
        const session = event.data.object;

        clientReferenceId = session.client_reference_id;
        amountTotal = session.amount_total;
        paymentIntent = session.payment_intent;
        serviceRef = session.metadata.serviceRef;
        assoRef = session.metadata.assoRef;
        stableRef = session.metadata.stableRef;

        userDoc = admin.firestore().collection("user").doc(clientReferenceId);
        servicesRef = admin.firestore().collection("stable").doc(stableRef)
                      .collection("Services").doc(serviceRef);
        assoRefDoc = assoRef ? admin.firestore().collection("user").doc(clientReferenceId).collection("Associated").doc(assoRef) : null;
        paymentDoc = db.collection("stable").doc(stableRef).collection("Payment").doc();

          const serviceSnapshot = await transaction.get(servicesRef);
          const serviceData = serviceSnapshot.data();

          if (serviceData.IsByAssociated && !assoRef) {
            throw new Error('AssocRefId is required for this service.');
          }

          throw new Error('ECCEZIONE DI TEST');
          return;
        
          transaction.set(paymentDoc, {
            Typo: 'Payment',
            AssociatedRef: assoRefDoc ?? '',
            ServicesRef: servicesRef ?? '',
            UserRef: userDoc ?? '',
            NeedAction: false,
            ManualAction: 'Nessuna',
            PricePaid: amountTotal / 100,
            Status: 'PAGAMENTO CONFERMATO',
            CreationDay: new Date(),
          });

          const currentDate = new Date();
          const expirationDate = new Date(currentDate.getTime() + (serviceData.DurationValidityDay * 24 * 60 * 60 * 1000));

          transaction.set(userDoc.collection("BuyedServices").doc(), {
            IsActive: true,
            Description: serviceData.ShortDescription,
            Amount: serviceData.Amout || 0,
            PayedPrice: amountTotal / 100,
            Residual: serviceData.Amout || 0,
            IsLezione: serviceData.IsLesson,
            IsByAssociated: serviceData.IsByAssociated,
            AssociatedId: assoRefDoc ?? '',
            CreationDay: new Date(),
            ExpirationDay: expirationDate,
            PaymentIntent: paymentIntent
          });

        });

        res.sendStatus(200);
      } else {
        res.status(400).send('Unhandled event type');
      }
    } catch (err) {
        //session = event.data.object;
        clientReferenceId = session.client_reference_id;
        amountTotal = session.amount_total;
        paymentIntent = session.payment_intent;
        serviceRef = session.metadata.serviceRef;
        assoRef = session.metadata.assoRef;
        stableRef = session.metadata.stableRef;
      
    
      try {
        const refundResponse = await stripe.refunds.create({
  payment_intent: paymentIntent, // Assicurati che paymentIntent sia un ID valido
  // Altri dati rilevanti per il rimborso, se necessario
}, {
  stripeAccount: stripeAccount, // Passa l'account ID qui
});
        
        console.log(refundResponse.data);
        
        // Aggiorna il documento Firestore con le informazioni sul rimborso
        await admin.firestore().collection("stable").doc(stableRef)
          .collection("Payment").doc().set({
            Typo: 'Refund',
            AssociatedRef: assoRefDoc ?? '',
            ServicesRef: servicesRef ?? '',
            UserRef: userDoc ?? '',
            NeedAction: false,
            ManualAction: 'None',
            PricePaid: amountTotal / 100,
            Status: 'REFUND CONFIRMED'
          });

          console.error("Error processing webhook, Service Creation API call failed, refound done", err);
          res.status(500).send('Error processing webhook, Service Creation API call failed, refound done');
          
      } catch (refundError) {

        await admin.firestore().collection("stable").doc(stableRef)
        .collection("Payment").doc().set({
          Typo: 'RefundError',
          AssociatedRef: assoRefDoc ?? '',
          ServicesRef: servicesRef ?? '',
          UserRef: userDoc ?? '',
          NeedAction: true,
          ManualAction: 'Proceed with manual refund',
          PricePaid: amountTotal / 100,
          Status: 'ERROR DURING REFUND ATTEMPT'
        });
        // Se si verifica un errore durante il rimborso, gestisci la situazione di conseguenza
        // Potresti voler registrare il problema o prendere azioni manuali
        // Invia una risposta al webhook indicando l'errore
        console.error("Error processing refund:", refundError);
        res.status(500).send("Error processing refund. Need Human Action");
      }
    }
  });

错误说明

Postman测试时返回"no such payment_intent"错误提示。


排查方向及解决办法

  • 账号上下文不匹配
    代码退款时指定了stripeAccount: 'acct_1OT33cQlnlPZnFaP',但Checkout Session对应的Payment Intent可能属于主账号而非该关联账号。检查Checkout Session创建时是否使用了相同的stripeAccount参数,确保支付和退款操作的账号上下文一致。
  • Payment Intent ID获取失效
    Webhook的catch块中session = event.data.object;被注释,导致paymentIntent变量未从事件对象中获取有效ID,取消注释该行代码即可:
    session = event.data.object;
    
  • Payment Intent有效性校验缺失
    部分场景下Checkout Session的payment_intent可能未生成或为临时值,退款前需校验ID格式(以pi_开头):
    if (!paymentIntent || !paymentIntent.startsWith('pi_')) {
      throw new Error('无效的Payment Intent ID');
    }
    
  • API权限不足
    确认调用Stripe API的令牌拥有refunds:write权限,且能访问目标账号的Payment Intent资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:32:33