据我所知,IPN / Webhook是后端通知,并且假设当前端return-url无法使用时,帮助商家方系统可靠地接收交易更新。
但是,这完全不是我在开发过程中遇到的情况。除非处理前端返回,否则IPN或Webhook都不会触发。这违背了“后端通知”的全部目的。
我很想被证明是错的,但我已经使用Order / Auth / Sale测试了Omnipay / PaypalSDK的排列,并在Sandbox / Live环境中等待Webhook / IPN,但都没有。
以下是我的设置:
return_url
,其中包含来自Paypal的网址查询,其中包括paymentId
和PayerID
return_url
页面仅显示网址查询,但不会处理Paypal的返回回调。callback_url
"/v1/payments/orders/{$this->getId()}/capture"
),并刷新return_url
页面。
return_url
页面显示捕获状态和事务状态,成功 callback_url
所以... Paypal的IPN和Webhook都不适用于后端通知,前端返回处理是必须的吗?但我看到人们声称“为了避免不可靠的return_url不是由用户端触发,我们应该使用IPN”等等。我错过了什么,或做错了什么?
更新 - 包含代码
我的“付费”按钮会向此网址发送一个POST:https://*PUB_IP_ADDRESS*/v2/payment/paypal/sale
(sale
或order
,以了解相应的操作)以及此JSON内容:
{
"order_id":"test-23-order",
"amount":"0.01",
"currency":"CAD",
"description":"1753 total .01"
}
在控制器/存储库中,我使用Omnipay和PaypalSDK进行了测试。
一个。有了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的Paypal网关。然后:
// 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并保存到模型。
湾使用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);
然后Paypal会回复这个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"
}
]
}
然后我转到approval_url
,并使用Paypal帐户登录以完成对 sale 或订单交易的批准。
现在我们回到上面的原始要点(3)和(4),其中Paypal不会触发任何IPN或Webhook事件,除非我通过return_url
处理数据以完成交易,在代码中,这是:
一个。 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.
}
}
...
湾Paypal 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 :(得分:0)
我注意到你没有在paypal按钮中引用paypal ipn ......
尝试添加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'] = "commandes@dev.org";
}else{
$_CONFIG['cart']['webscr'] = "https://www.sandbox.paypal.com/cgi-bin/webscr?"; // sandebox
$_CONFIG['cart']['postback'] = "www.sandbox.paypal.com";
$_CONFIG['cart']['paypal'] = "info-facilitator@dev.com";
}
$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的错误lol
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 = "you@website.com";
send_mail($subject, $messagez, $de, 'you@website.com');
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();
}
?>