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

React/Node环境下PayPal新订阅捕获失败问题求助

捕获PayPal订阅时的接口错误问题

问题场景

我需要在后端捕获前端发起的PayPal新订阅,并存入MongoDB。从createSubscription控制器拿到订阅ID后,调用捕获接口时遇到两个错误:

  1. 不传入请求体时,收到错误日志:
captureData {
  name: 'INVALID_REQUEST',
  message: 'Request is not well-formed, syntactically incorrect, or violates schema.',
  details: [
    {
      location: 'body',
      issue: 'MISSING_REQUEST_BODY',
      description: 'Request body is missing.'
    }
  ]
}
  1. 传入capture_type: 'OUTSTANDING_BALANCE'请求体(参考官方文档)时,收到错误:
captureData {
  name: 'UNPROCESSABLE_ENTITY',
  message: 'The requested action could not be performed, semantically incorrect, or failed business validation.',

  details: [
    {
      issue: 'ZERO_OUTSTANDING_BALANCE',
      description: 'Current outstanding balance should be greater than zero.'
    }
  ],
}

不确定是请求体参数错误还是后端逻辑问题,相关代码如下:


PayPalScriptProvider 组件

<PayPalScriptProvider options={initialOptions}>
    <PayPalSubscriptionButton/>
</PayPalScriptProvider>

PayPal按钮组件

{isPending ? <LoadingMedium /> : null}
<PayPalButtons
  createSubscription={(data, actions) => {
    return axios
      .post(
        '/api/subscription',
      )
      .then((response) => {
        return response.data.id;
      });
  }}
  onApprove={(data, actions) => {
    axios
      .post(`/api/subscription/${data.subscriptionID}/capture`)
      .then(() => {
        axios
          .patch(
            `/api/activesubscription`,
            {
              id: activeSub[0]?._id,
              subscriptionID: data.subscriptionID,
            }
          )
      });
  }}
/>

createSubscription 路由

router.route('/subscription').post(async (req, res) => {
  const searchPlan = await SubscriptionAmount.find();
  console.log(searchPlan[0]?.subscriptionAmount);
  const subscription = await paypalFee.createSubscription(
    searchPlan[0]?.subscriptionAmount
  );
  res.json(subscription);
});

onApprove 捕获路由

router.post('/subscription/:subscriptionID/capture', async (req, res) => {
  const { subscriptionID } = req.params;
  console.log('subscriptionID', subscriptionID);
  const captureData = await paypalFee.captureSubscription(subscriptionID);
  console.log('captureData', captureData);
  res.json(captureData);
});

createSubscription 控制器

async function createSubscription(planId) {
  const accessToken = await generateAccessToken();
  const url = `${base}/v1/billing/subscriptions`;
  const response = await fetch(url, {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${accessToken}`,
    },
    body: JSON.stringify({
      intent: 'subscription',
      plan_id: planId,
    }),
  });
  const data = await response.json();
  console.log('data', data);
  return data;
}

captureSubscription 控制器

async function captureSubscription(subscriptionId) {
  const accessToken = await generateAccessToken();
  const url = `${base}/v1/billing/subscriptions/${subscriptionId}/capture`;
  const response = await fetch(url, {
    method: 'post',
    body: JSON.stringify({
     // capture_type: 'OUTSTANDING_BALANCE',
    }),
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${accessToken}`,
    },
  });
  const data = await response.json();
  console.log('data', data);
  return data;
}

问题分析与解决方案

错误1:缺少请求体

PayPal的/capture接口要求必须传入请求体,哪怕是空对象也可以,但不能完全不传。当前代码注释参数后生成的空对象是符合要求的,但如果是完全省略body字段就会触发该错误,确保请求体至少是一个空JSON对象即可。

错误2:未出账余额为零

OUTSTANDING_BALANCE用于捕获订阅的未结清余额,但你的订阅大概率是预付费定期订阅——用户批准后PayPal会自动完成首次付款,此时没有未出账余额,调用/capture自然会报错。

正确处理逻辑:

  1. 依赖Webhook而非主动捕获:预付费订阅无需调用/capture接口,PayPal会自动处理首次及后续付款。配置Webhook监听BILLING.SUBSCRIPTION.CREATED(订阅创建)和PAYMENT.SALE.COMPLETED(付款完成)事件,后端收到事件后再更新MongoDB数据,这是最可靠的方式,避免前端请求失败导致数据不一致。
  2. 验证订阅状态替代捕获:如果需要主动确认订阅状态,调用/v1/billing/subscriptions/{subscription_id}接口查询订阅详情即可,无需调用/capture。
  3. 调整前端逻辑:移除onApprove中的/capture请求,改为提示用户订阅成功,等待Webhook异步更新数据库状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 15:45:19