沙盒模式下未触发 PayPal Webhook 事件

Posted

技术标签:

【中文标题】沙盒模式下未触发 PayPal Webhook 事件【英文标题】:PayPal Webhook events not being triggered in Sandbox mode 【发布时间】:2016-06-03 19:54:58 【问题描述】:

我正在使用 PayPal Rest API,更具体地说是用于我的项目的 PayPal .Net SDK,我非常喜欢它。

我的问题是直到现在我都无法从 PayPal 收到任何 webhook 通知。

注意:尽管 webhook 模拟器确实工作,我的处理程序从 PayPal 正确接收 json 帖子。

这些是我采取的步骤:

    成功创建新发票(使用我的 paypal 应用中的沙盒凭据)后,我将其发送到测试买家帐户。

    我打开通知中的 url 以查看发票,并使用买家测试帐户付款。

    服务商和买方都会收到发票已成功支付的通知。

    我的服务器上没有任何反应。我的侦听器端点没有收到任何 webhook 事件请求。

我的应用沙盒配置中有一个活动的 webhook 事件监听器,用于跟踪所有可能的事件。

PayPal 没有触发任何事件的原因可能是什么?

我还不想切换到 IPN,并且定期从搜索结果中遍历发票列表也不是一个有效的选择。


更新:

由于沙盒 webhook 事件服务器重新启动并运行,我使用以下代码(c# 控制器操作)让它工作。注意 webhook 模拟器发出的 json body 结构与支付发票后发送的不同。因此,我相应地从 PayPal SDK 扩展了 WebHookEvt 对象。

        /// <summary>
    /// PayPal Webhook handler
    /// </summary>
    /// <param name="evt"></param>
    /// <returns></returns>
    [HttpPost]
    public ActionResult EventHandler(WebhookEvent evt)  // [System.Web.Http.FromBody]

        string json = null;
        Capture capture = null;
        InvoiceObject invoiceObject = null;
        InvoiceExt invoice = null;
        WebHookEventCapture captureEvent = null;
        WebHookEventInvoice invoiceEvent = null;

        if (evt == null) 
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
         else 
            this.logger.Debug(string.Format("***** Webhook event type [0] received from PayPal *****", evt.event_type));
            logger.Debug(string.Format("Event id: 0", evt.id));
            logger.Debug(string.Format("Event create time: 0", evt.create_time));
            logger.Debug(string.Format("Event type: 0", evt.event_type));
            logger.Debug(string.Format("Event resource: 0", evt.resource));
            logger.Debug(string.Format("Event resource type: 0", evt.resource_type));
            logger.Debug(string.Format("Event summary: 0", evt.summary));

            using (Stream req = Request.InputStream) 
                req.Seek(0, System.IO.SeekOrigin.Begin);
                using (StreamReader sr = new StreamReader(req)) 
                    json = sr.ReadToEnd();
                
            

            logger.Debug(string.Format("WebHook request json form PayPal: ***0***", json));

            // Validate webhook event
            bool isValid = WebhookEvent.ValidateReceivedEvent(new PayPalPaymentProvider(IPN_SANDBOX_MODE).Context, Request.Headers, json, WEBHOOK_ID_INVOICE_PAID);
            logger.Debug(string.Format("Validating webhook event... Is valid = 0", isValid ? "TRUE" : "FALSE"));                

            try 
                if ("capture".Equals(evt.resource_type)) 
                    captureEvent = JsonConvert.DeserializeObject<WebHookEventCapture>(json);
                    capture = captureEvent.resource;
                 else if ("invoices".Equals(evt.resource_type)) 
                    invoiceEvent = JsonConvert.DeserializeObject<WebHookEventInvoice>(json);
                    invoiceObject = invoiceEvent.resource;
                    invoice = invoiceObject.invoice;
                

                //if (capture != null) 
                //    logger.Debug(string.Format("Capture amount: 0", capture.amount));
                //    logger.Debug(string.Format("Capture create time: 0", capture.create_time));
                //    logger.Debug(string.Format("Capture id: 0", capture.id));
                //    logger.Debug(string.Format("Capture is final: 0", capture.is_final_capture.HasValue ? capture.is_final_capture : false));
                //    logger.Debug(string.Format("Capture parent payment: 0", capture.parent_payment));
                //    logger.Debug(string.Format("Capture state: 0", capture.state));
                //    logger.Debug(string.Format("Capture transaction fee: 0", capture.transaction_fee));
                //    logger.Debug(string.Format("Capture update time: 0", capture.update_time));
                //

                if (isValid && invoice != null) 
                    logger.Debug(string.Format("Invoice [0]", invoice));
                    logger.Debug(string.Format("Invoice id: [0]", invoice.id));
                    logger.Debug(string.Format("Invoice merchant memo: [0]", invoice.merchant_memo));
                    logger.Debug(string.Format("Invoice date: [0]", invoice.invoice_date));
                    logger.Debug(string.Format("Invoice status: [0]", invoice.status));
                    logger.Debug(string.Format("Invoice total amount: [0]", invoice.total_amount != null ? invoice.total_amount.currency + invoice.total_amount.value : "??"));
                    if (invoice.billingInfo != null)  // billing_info
                        foreach (var billingInfo in invoice.billingInfo) 
                            logger.Debug(string.Format("Invoice billing info address: 0", billingInfo.address ?? new InvoiceAddress()));
                            logger.Debug(string.Format("Invoice billing info business name: 0", billingInfo.business_name ?? "??"));
                            logger.Debug(string.Format("Invoice billing info email: 0", billingInfo.email ?? "??"));
                            logger.Debug(string.Format("Invoice billing info first name: 0", billingInfo.first_name ?? "??"));
                            logger.Debug(string.Format("Invoice billing info last name: 0", billingInfo.last_name ?? "??"));
                            logger.Debug(string.Format("Invoice billing info language: 0", billingInfo.language ?? "??"));
                            logger.Debug(string.Format("Invoice billing info notification channel: 0", billingInfo.notification_channel ?? "??"));
                            logger.Debug(string.Format("Invoice billing info phone: 0", billingInfo.phone ?? new Phone()));
                        
                    

                    // Update clientproductpayment
                    SubscriptionRepository db = new SubscriptionRepository();
                    var subscription = db.Context.ClientProductPayments.Where(
                                q => invoice.id.Equals(q.TransactionId)).FirstOrDefault();
                    if (subscription != null) 
                        logger.Debug(string.Format("Subscription (ClientProductPayment) with transaction_id = [0] found", invoice.id));

                        // Update subscription
                        db.RegisterPayment(subscription, invoice.id);

                        logger.Debug(string.Format(
                            "Subscription (ClientProductPayment) with id = [0] updated successfully with transaction id = [1]",
                                subscription.Id, invoice.id));

                     else 
                        logger.Warn(string.Format("Subscription (ClientProductPayment) with transaction_id = [0] not found", invoice.id));
                    

                

            
            catch (Exception e) 
                logger.Error(e.Message, e);
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            

        
        logger.Debug("Sending Http status code 200 response...");
        return new HttpStatusCodeResult(HttpStatusCode.OK);            
    

扩展 WebHookEvt

using Newtonsoft.Json;
using PayPal.Api;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PaymentManager.Provider 
    public class WebHookEventCapture : WebhookEvent 

        [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "resource")]
        public new Capture resource  get; set; 

    

    public class WebHookEventInvoice : WebhookEvent 

        [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "resource")]
        public new InvoiceObject resource  get; set; 

    

    public class InvoiceObject 

        [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "invoice")]
        public InvoiceExt invoice  get; set; 

    

    public class InvoiceExt : Invoice 

        [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "billingInfo")]
        public List<BillingInfo> billingInfo  get; set; 

    


【问题讨论】:

暂时我决定切换到 IPN 作为替代实现,这似乎可行。 【参考方案1】:

对此我们深感抱歉。我们这边有一个暂时的问题,已经解决了。您现在应该能够接收 Invoicing webhook 通知。请尝试一下,让我们知道情况如何。

谢谢

【讨论】:

谢谢,我在 2 天前的日志文件中发现了 webhook 通知。但是,我在解析的响应中没有看到任何发票详细信息。我有两个实现运行,IPN 和 Webhook。现在 IPN 很活跃,就像一个魅力。所以我希望你计划再支持 IPN 一段时间。 是的,IPN 会存在一段时间,但我们建议您迁移到 Webhooks。 :) 您能否分享解析后的响应中缺少哪些发票详细信息?谢谢。 我刚刚完成了一些测试,注意到模拟器中的 de webhook 事件 json 与支付发票后自发源自 PayPal 的 json 主体之间存在细微差别。 resource":"id":"INV2-8FSD-3HT6-BRHR-UHYV",..."resource":"**invoice**":"id":"INV2-W85R-B5Z3-ZLNY-KVZP",... 我假设后者是在生产中使用的布局。这个假设正确吗?

以上是关于沙盒模式下未触发 PayPal Webhook 事件的主要内容,如果未能解决你的问题,请参考以下文章

Paypal 不会在沙盒中的 BILLING.SUBSCRIPTION.CANCELLED 上触发 webhook

iPhone的横向模式下未显示PayPal视图[重复]

关于无法从开发者沙箱触发 Paypal REST API webhook 事件

PayPal Sandbox 订阅 webhook 响应未收到

REST 客户端未在沙盒中生成 Webhook 事件

PayPal Rest API (PHP SDK) webhook 未显示在沙盒事件列表中