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

如何在Blogger页面展示PayPal IPN变量?解决Google Apps Script报错

解决PayPal IPN监听器Web App的doGet错误与IPN变量展示问题

咱们先把问题的核心理清楚:PayPal的IPN是POST请求推送到你的Web App,而你嵌入Blogger的iframe是用GET请求加载页面——这是两个完全独立的请求场景,直接混在一起处理肯定会出问题。

1. 先解决报错根源,分离请求逻辑

第一个错误Script function not found: doGet是因为Web App被iframe以GET方式访问,但你的脚本只有处理POST的doPost;后来加了doGet直接调用doPost报错,是因为GET请求根本没有e.postData.contents这个属性,自然会读取undefined报错。

下面是修正后的完整代码,把POST(IPN处理)和GET(页面展示)的逻辑彻底分开:

// 专门处理PayPal IPN的POST请求
function doPost(e) {
  var isProduction = false;
  var paypalURL = isProduction ? "https://ipnpb.paypal.com/cgi-bin/webscr" : "https://ipnpb.sandbox.paypal.com/cgi-bin/webscr";

  // 和PayPal做验证握手
  var payload = "cmd=_notify-validate&" + e.postData.contents;
  var options = {
    "method": "post",
    "payload": payload
  };
  
  var resp = UrlFetchApp.fetch(paypalURL, options);
  
  if (resp.getContentText() === 'VERIFIED') {
    // 验证收款邮箱和支付状态
    if (e.parameter.payment_status === 'Completed' && e.parameter.receiver_email === 'my-email@example.com') {
      // 替换成你实际的金额验证逻辑
      var amountValid = true; 
      if (amountValid) {
        // 把IPN数据临时存储到脚本属性里,供页面展示用
        PropertiesService.getScriptProperties().setProperty('latestIPN', JSON.stringify(e.parameter));
        
        // 给PayPal返回纯文本确认即可,IPN不需要HTML响应
        return ContentService.createTextOutput('OK').setMimeType(ContentService.MimeType.TEXT);
      }
    }
  }
  
  // 验证失败或不符合条件时返回INVALID
  return ContentService.createTextOutput('INVALID').setMimeType(ContentService.MimeType.TEXT);
}

// 专门处理Blogger iframe的GET请求,展示IPN数据
function doGet(e) {
  // 读取存储的IPN数据
  var ipnData = PropertiesService.getScriptProperties().getProperty('latestIPN');
  var displayContent = '';
  
  if (ipnData) {
    var parsedData = JSON.parse(ipnData);
    // 生成展示IPN变量的HTML结构
    displayContent = '<h3>PayPal IPN 变量详情</h3><ul style="line-height:1.8;">';
    for (var key in parsedData) {
      displayContent += `<li><strong>${key}</strong>: ${parsedData[key]}</li>`;
    }
    displayContent += '</ul>';
    // 可选:展示后清空存储,避免重复显示同一条数据
    PropertiesService.getScriptProperties().deleteProperty('latestIPN');
  } else {
    displayContent = '<p style="color:#666;">暂无IPN数据,请完成PayPal结账后刷新页面查看。</p>';
  }
  
  // 返回允许嵌入iframe的HTML
  return HtmlService.createHtmlOutput(displayContent)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME)
    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

2. 关键修改点说明

  • 请求分离:doPost只负责IPN的验证和数据存储,返回纯文本给PayPal(IPN只需要确认收到,不需要页面);doGet专门生成页面内容给Blogger的iframe。
  • 数据存储:用PropertiesService临时存储最新的IPN数据,如果你需要保留历史记录,可以改成用Google Sheets持久化存储。
  • 彻底避错:不再在doGet里调用doPost,各自处理对应请求的逻辑,直接解决了Cannot read properties of undefined (reading 'contents')的错误。

3. 后续注意事项

  • 保持Web App的部署设置不变:以我身份执行,所有人均可访问。
  • 测试用PayPal沙箱环境,确认IPN能正确发送到你的Web App地址(即https://script.google.com/macros/s/My_Script_Id/exec)。
  • 一定要完善金额验证逻辑,替换代码里的var amountValid = true;,比如对比e.parameter.mc_gross和你预期的收款金额,防止恶意请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 18:35:26