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

Expressjs集成Paypal REST SDK后沙箱无交易记录问题排查

PayPal支付跳转成功但沙箱无交易记录?你漏了关键的一步!

我看了你的Express代码,能跳转到success页面说明支付授权的第一步是成功的,但沙箱里没交易、没扣款的核心原因很明确:你只完成了PayPal支付流程的一半——创建支付并获取授权链接,但没有执行最终的支付确认步骤。

先贴一下你的原代码方便对照:

router.post('/buy' , ( req , res ) => { 
  const amt = req.body.quantity*req.body.price 
  /** 
   * Paypal payment json to be passed 
   */ 
  const payment_json = { 
    "intent": "sale", 
    "payer": { "payment_method": "paypal" }, 
    "redirect_urls": { 
      "return_url": "https://localhost:4200/success", 
      "cancel_url": "https://localhost:4200/err" 
    }, 
    "transactions": [{ 
      "amount": { 
        "total": '' + Math.round((amt)*100)/100, 
        "currency": "CAD", 
        "details": { 
          "subtotal": '' + Math.round((amt)*100)/100, 
          "tax": "0.00", 
          "shipping": "0.00" 
        } 
      }, 
      "description": "This is payment description.", 
      "item_list": { 
        "items":[{ 
          "quantity": req.body.quantity, 
          "name": "collection", 
          "price": '' + req.body.price, 
          "sku": req.body.collid, 
          "currency":"CAD" 
        }] 
      } 
    }] 
  } 
  /** 
   * Paypal payment object created 
   */ 
  paypal.paypal.payment.create(payment_json, function (error, payment) { 
    if (error) { 
      res.status(500).send({message: error}) 
    } else { 
      console.log('payment:', payment) 
      for(let i=0; i<payment.links.length; i++) { 
        if(payment.links[i].rel === 'approval_url') { 
          res.json({forwardLink: payment.links[i].href}); 
        } 
      } 
    } 
  }); 
});

问题拆解

PayPal的REST API支付流程需要两个必选步骤:

  1. 创建支付:你的代码已经实现了这一步,生成了approval_url让用户跳转PayPal页面授权支付。此时PayPal只是记录了一个待确认的支付请求,还不会扣款。
  2. 执行支付:用户在PayPal页面完成授权后,PayPal会跳转到你的return_url,同时带回paymentId和PayerID两个参数。你必须用这两个参数调用PayPal的payment.execute接口,才能真正完成扣款并生成交易记录。

修复方案

第一步:Angular前端接收授权参数并调用后端执行接口

在你的Angular success页面组件里,获取URL里的paymentId和PayerID,然后传给后端的新接口:

import { ActivatedRoute } from '@angular/router';
import { HttpClient } from '@angular/common/http';

@Component({
  // 组件配置
})
export class SuccessComponent implements OnInit {
  constructor(
    private route: ActivatedRoute,
    private http: HttpClient
  ) {}

  ngOnInit(): void {
    // 从URL查询参数中获取PayPal返回的关键值
    const paymentId = this.route.snapshot.queryParamMap.get('paymentId');
    const payerId = this.route.snapshot.queryParamMap.get('PayerID');

    if (paymentId && payerId) {
      // 调用后端执行支付的接口
      this.http.post('/api/paypal/execute', { paymentId, payerId })
        .subscribe({
          next: (response) => {
            console.log('支付完成!', response);
            // 这里可以显示订单成功页面、更新本地订单状态等
          },
          error: (err) => {
            console.error('支付执行失败', err);
            // 跳转错误页面或提示用户
          }
        });
    }
  }
}

第二步:Express后端添加执行支付的接口

在你的路由里新增一个/execute接口,完成最终的支付确认:

router.post('/execute', (req, res) => {
  const { paymentId, payerId } = req.body;

  // 构造执行支付的参数
  const executePayload = {
    "payer_id": payerId
  };

  // 调用PayPal的execute接口完成支付
  paypal.paypal.payment.execute(paymentId, executePayload, function (error, payment) {
    if (error) {
      console.error('支付执行出错:', error);
      return res.status(500).send({ 
        message: '支付确认失败', 
        error: error.message 
      });
    }

    // 执行成功后,PayPal会返回完整的交易信息,此时沙箱账户就能看到记录了
    console.log('交易完成,详细信息:', payment);
    res.json({
      success: true,
      transactionId: payment.id,
      amount: payment.transactions[0].amount.total,
      message: '支付成功'
    });
  });
});

额外检查点

  • 确认沙箱账户:确保你用的是商家沙箱账户创建支付,用户跳转的是PayPal沙箱环境(不是生产环境)。
  • 金额格式:你代码里的Math.round((amt)*100)/100要保证输出是两位小数(比如10.00而不是10),PayPal对金额格式要求严格。
  • 日志排查:在/buy接口里打印生成的payment对象,确认state字段是created;执行支付后,state会变成approved,这才是交易完成的标志。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 22:13:15