Paypal可以触发IPN/Webhook而不处理来自return_url的数据?

问题描述:

据我所知,IPN/Webhook是后端通知,并且当前端返回url无法使用时,可以帮助商户端系统可靠地接收事务更新。Paypal可以触发IPN/Webhook而不处理来自return_url的数据?

但是,这并不是我在开发过程中所经历的。除非处理前端退货,否则IPN或Webhook都不会触发。这打破了“后端通知”的全部目的。

我很想被证明是错误的,但我已经测试了Omnipay/PaypalSDK与Order/Auth/Sale的排列,并且在Sandbox/Live环境中等待Webhook/IPN,而且没有任何工作。

这里是我的设置:

  • PHP上Laravel,与Omnipay和Paypal SDK
  • 码头工人与港口正确转发,所以HTTP和HTTPS可以LAN外部访问
  • 在PayPal的REST启用网络挂接应用;在业务配置文件中启用IPN
  • 测试交易(销售,订单,捕捉)在沙盒模式下,所有的工作
  • 测试PayPal的IPN模拟器模拟网络挂接,无论是在工作
  • 测试订购&销售沙盒&现场模式
    1. 制造API调用来启动贝宝交易,收到重定向URL
    2. 打开网址,登录,确认付款,重定向回return_url与贝宝的网址查询,包括paymentIdPayerID
      • 在这个时候,return_url页面仅显示后面的网址查询,但不处理Paypal的返回回调
      • 没有收到IPN网络挂接callback_url小号
      • IPN Hisotry页和网络挂接活动显示新的事件
    3. 修改代码来处理PayPal的返回回调(即名为"/v1/payments/orders/{$this->getId()}/capture"),并刷新为return_url页面。
      • 在这个时候,return_url页面显示捕获的状态和事务的状态,这是成功
      • 难道callback_url小号
      • IPN Hisotry收到IPN网络挂接 page and Webhook活动页面显示新的事件

所以......都是PayPal的IPN和网络挂接两个并不意味着后端的通知,以及前端的退货处理是必须的?但我见过有人声称“为了避免不可靠的return_url不被用户端触发,我们应该使用IPN”等。我是否错过了某些东西,或者做错了什么?

更新 - 包括代码

  1. 我的 “支付” 按钮发送POST到这个网址:https://*PUB_IP_ADDRESS*/v2/payment/paypal/salesaleorder其相应的动作)这JSON内容:

    { 
        "order_id":"test-23-order", 
        "amount":"0.01", 
        "currency":"CAD", 
        "description":"1753 total .01" 
    } 
    
  2. 在控制器/存储库中,我使用Omnipay和PaypalSDK进行了测试。

    a。随着Omnipay,这样做是:

    // PaymentController.php 
    ... 
    $result = $this->gateway 
        ->setOrderId($request->get('order_id')) 
        ->setAmount($request->get('amount')) 
        ->setCurrency($request->get('currency')) 
        ->setDescription($request->get('description')) 
        ->initPayment(); 
    ... 
    

    哪里$this->gateway是Omnipay的信用卡缴交订金。然后:

    // OmnipayRepository.php 
    ... 
    public function initPayment(){ 
        if (is_null($this->payment_intent)){ 
         throw ExceptionMapping(40000, 
          "You need to set payment intent first with setIntent()"); 
        } 
    
        $payment_params = $this->getPaymentParameters(); 
    
        switch ($this->payment_intent){ 
         case self::INTENT_PREAUTH: 
          $payment_action = $this->gateway->authorize($payment_params); 
          break; 
         case self::INTENT_SALE: 
          $payment_action = $this->gateway->purchase($payment_params); 
          break; 
         default: 
          throw ExceptionMapping(40000, 
           "{$this->payment_intent} is an invalid payment intent for this action"); 
        } 
    
        $response = $payment_action->send(); 
    
        return $this->handleTransactionResponse($response); 
    } 
    ... 
    

    其中handleTransactionResponse($response)获取返回的JSON并保存为模型。

    b。使用PayPal SDK:

    // PaymentController.php 
    ... 
    $paypal = new PaypalRepository(); 
        $payment = $paypal->initPayment(
         $request->get('amount'), 
         $request->get('currency'), 
         $request->get('description'), 
         URL::to("/").config("paypal.options.returnUrl"), 
         URL::to("/").config("paypal.options.cancelUrl"), 
         'order', 
         $request->get('order_id') 
        ); 
    
    
        if ($payment['error']) { 
         echo $payment['error']."ERR_PP"; 
        } else { 
         $approvalUrl = $payment['payment']->getApprovalLink(); 
         echo $approvalUrl; 
         exit; 
        } 
    ... 
    

    然后

    // PaypalRepository.php::initPayment() 
    ... 
    
        $payment = null; 
    
        $payer = new Payer(); 
        $payer->setPaymentMethod("paypal"); 
    
        $item1 = new Item(); 
        $item1->setName($descr) 
         ->setDescription($descr) 
         ->setCurrency($currency) 
         ->setQuantity(1) 
         ->setSku($sku)// Similar to `item_number` in Classic API 
         ->setPrice($total); 
    
        $itemList = new ItemList(); 
        $itemList->setItems(array($item1)); 
    
        $details = new Details(); 
        $details->setShipping(0); 
        $details->setTax(0); 
        $details->setSubtotal($total); 
    
        $amount = new Amount(); 
        $amount->setCurrency($currency); 
        $amount->setTotal($total); 
        $amount->setDetails($details); 
    
        $transaction = new Transaction(); 
        $transaction->setAmount($amount); 
        $transaction->setItemList($itemList); 
        $transaction->setDescription($descr); 
        $transaction->setInvoiceNumber(uniqid()); 
    
        $redirectUrls = new RedirectUrls(); 
        $redirectUrls->setReturnUrl($returnUrl); 
        $redirectUrls->setCancelUrl($cancelUrl); 
    
        $payment = new Payment(); 
        $payment->setIntent($intent); 
        $payment->setPayer($payer); 
        $payment->setRedirectUrls($redirectUrls); 
        $payment->setTransactions(array($transaction)); 
    
        $payment->create($this->apiContext); 
    
  3. 那么支付宝将与此JSON响应:

    { 
        "id":"PAY-4L424927Y2109544NLF2TY2A", 
        "intent":"sale", 
        "state":"created", 
        "payer":{ 
         "payment_method":"paypal" 
        }, 
        "transactions":[ 
         { 
         "amount":{ 
          "total":"0.01", 
          "currency":"CAD" 
         }, 
         "description":"1716 total .01", 
         "related_resources":[ 
    
         ] 
         } 
        ], 
        "create_time":"2017-07-24T00:16:40Z", 
        "links":[ 
         { 
         "href":"https:\/\/api.sandbox.paypal.com\/v1\/payments\/payment\/PAY-4L424927Y2109544NLF2TY2A", 
         "rel":"self", 
         "method":"GET" 
         }, 
         { 
         "href":"https:\/\/www.sandbox.paypal.com\/cgi-bin\/webscr?cmd=_express-checkout&token=EC-0GN55647HU250615H", 
         "rel":"approval_url", 
         "method":"REDIRECT" 
         }, 
         { 
         "href":"https:\/\/api.sandbox.paypal.com\/v1\/payments\/payment\/PAY-4L424927Y2109544NLF2TY2A\/execute", 
         "rel":"execute", 
         "method":"POST" 
         } 
        ] 
    } 
    
  4. 我然后转到这个approval_url,并与贝宝帐户登录完成销售订单交易的批准。

  5. 现在我们回到上面原始的第(3)和第(4)项,其中PayPal不会触发任何IPN或Webhook事件,除非我通过return_url处理数据以完成交易,在代码中这个:

    a。Omnipay:

    // PaymentRepository.php, WIP code for callback_return 
    ... 
    public function callback_return(Request $request) 
    { 
        $paymentId = $_GET['paymentId']; 
        $payerId = $_GET['PayerID']; 
    
        // Once the transaction has been approved, we need to complete it. 
        $transaction = $this->gateway->completePurchase(array(
         'payer_id'    => $payerId, 
         'transactionReference' => $paymentId, 
        )); 
        $response = $transaction->send(); 
        if ($response->isSuccessful()) { 
         // The customer has successfully paid. 
         echo "PAID"; 
        } else { 
         echo "ERROR"; 
         // There was an error returned by completePurchase(). You should 
         // check the error code and message from PayPal, which may be something 
         // like "card declined", etc. 
        } 
    } 
    ... 
    

    b。贝宝SDK:

    // paypal_return.php, code snippet on completing transaction 
    ... 
        $paypal = new paypal(); 
        try { 
         $payment = $paypal->executePaypalIntent(get2('paymentId'), get2('PayerID')); 
        } catch (\Exception $e) { 
         $payment = false; 
        } 
    ... 
    
+0

考虑系统有多少依赖于这方面的工作,包括一些我维护,我只是去不得不说出你的错误。 – rtfm

+0

我使用贝宝IPN作为后端通知服务,它在我的所有项目中都非常出色。请发布您的“付款”按钮和您的IPN代码。 –

+0

谢谢你的评论,@ patrick-simard。我用相对代码更新了我的问题。因此,在用户确认付款后,您的IPN触发器无需任何后处理? – user2276986

我注意到你不引用PayPal的IPN在你的PayPal按钮在所有...

尝试增加notify_url

这是我该怎么办它。

$_CONFIG['cart']['test'] = "1"; // 0 (paypal) | 1 (sandebox) 
$_CONFIG['cart']['return'] = "http://thx.com"; 
$_CONFIG['cart']['ipn'] = "http://dev.com/ipn.php"; 

if (!$_CONFIG['cart']['test']){ 
    $_CONFIG['cart']['webscr'] = "https://www.paypal.com/cgi-bin/webscr?"; // paypal 
    $_CONFIG['cart']['postback'] = "www.paypal.com"; 
    $_CONFIG['cart']['paypal'] = "[email protected]"; 
}else{ 
    $_CONFIG['cart']['webscr'] = "https://www.sandbox.paypal.com/cgi-bin/webscr?"; // sandebox 
    $_CONFIG['cart']['postback'] = "www.sandbox.paypal.com"; 
    $_CONFIG['cart']['paypal'] = "[email protected]"; 
} 

$grand_total = "9.99"; 

// Bouton Paypal 
$paypalURL .= $_CONFIG['cart']['webscr'].'&'; 
$paypalURL .= 'item_name=ITEM NAME&'; 
$paypalURL .= 'cmd=_xclick&'; 
$paypalURL .= 'business='.urlencode($_CONFIG['cart']['paypal']).'&'; 
$paypalURL .= 'return='.urlencode($_CONFIG['cart']['return']).'&'; 
$paypalURL .= 'lc=CA&amount='.sprintf("%01.2f", $grand_total).'&'; 
$paypalURL .= 'currency_code=CAD&'; 
$paypalURL .= 'button_subtype=products&'; 
$paypalURL .= 'cn=Ajouter%20des%20instructions%20particuli%c3%a8res%20pour%20le%20vendeur&'; 
$paypalURL .= 'no_shipping=2&'; 
$paypalURL .= 'shipping=0&'; 
$paypalURL .= 'bn=PP%2dBuyNowBF%3abtn_buynowCC_LG%2egif%3aNonHosted&'; 
$paypalURL .= 'notify_url='.urlencode($_CONFIG['cart']['ipn']); 

您可以使用该URL作为按钮或重定向。只要确保您验证了成功交易支付的金额。

您可以在IPN代码中执行的一件事是在执行时触发邮件提醒并将您的自己发送给$ _POST。至少你知道IPN何时访问该页面。只要确保将其添加到顶部以防止PHP错误停止执行。如果收到邮件后没有再处理的事务,你知道什么是错的,它不是PayPal的故障笑

IPN

<?php 

// read the post from PayPal system and add 'cmd' 
$req = 'cmd=_notify-validate'; 
foreach ($_POST as $key => $value) { 
    $value = urlencode(stripslashes($value)); 
    $req .= "&$key=$value"; 
    $mail .= "_POST[$key]: $value<br>"; 
} 

// post back to PayPal system to validate 
$header .= "POST /cgi-bin/webscr HTTP/1.1\r\n"; 
$header .= "Content-Type: application/x-www-form-urlencoded\r\n"; 
$header .= "Host: ".$_CONFIG['cart']['postback']."\r\n"; 
$header .= "Content-Length: " . strlen($req) . "\r\n"; 
$header .= "Connection: close\r\n\r\n"; 
$fp = fsockopen ('ssl://'.$_CONFIG['cart']['postback'], 443, $errno, $errstr, 30); 

// Si le fsockopen a fonctionner on poursuit 
if ($fp) { 

    fputs ($fp, $header . $req); 
    while (!feof($fp)) { 

     $res = fgets ($fp, 1024); 
     if (trim($res)=="VERIFIED") { 


     }elseif ($res=="INVALID") { 


     }else{ 


     } 

    } 

    // On ferme la connexion 
    fclose ($fp); 
}else{ 
    echo "404"; 
} 

$subject = "IPN-DEBUG"; 
$messagez = "Paypal info:<br />$mail<br /><br />$prod_mail"; 
$de = "[email protected]"; 
send_mail($subject, $messagez, $de, '[email protected]'); 

function send_mail($subject, $message, $de, $to){ 

    require_once ('module/phpmailer/MailClass.php');//send_mail 

    $send_mail = new FreakMailer(); 
    $send_mail->Subject = $subject; 
    $send_mail->Body = $message; 
    $send_mail->isHTML(true); 
    $send_mail->From = $de; 
    $send_mail->AddAddress($to); 

    if(!$send_mail->Send()){return 'false';}else{return 'true';} 
    $send_mail->ClearAddresses(); 
    $send_mail->ClearAttachments(); 

} 
?> 
+0

谢谢你的代码,并提出了!我一定会再次用'notify_url'测试代码并回报。奇怪的是,虽然,我认为我已经测试过,但可能没有正确配置它。在测试过程中,我也没有太在意这个部分,因为我认为IPN和Webhook会依靠PayPal的开发人员门户和商业简档设置中的初始设置URL。事实上,我读过一些开发者遇到他们的'notify_url'设置不会对初始设置生效......此外,_webhook_不会触发什么呢? – user2276986

+0

原来,Omnipay和PayPal的SDK都没有'setNotifyUrl()'方法。所以我尝试了你的代码(通过修改诸如'$ _CONFIG ['cart'] ['ipn']','$ _CONFIG ['cart'] ['paypal']'等)的选项......但是,当我访问该网址时,Paypal仅显示一个错误页面,表示_“事情目前似乎没有工作,请稍后再试。”_我想我们正在使用不同的PayPal付款方式?现在我再次陷入死胡同...... – user2276986

+0

我认为你需要回到基础。上面是一个可用的IPN shell和一个可用的paypal链接。你做错了什么。更改paypal电子邮件,设置一个临时paypal ipn(直接url到php文件)只修改邮件程序()函数来接收邮件并从那里调试你的方式。 –