PSGI, LWP::UserAgent & PayPal IPN - perl

I have been trying for some time to get a simple PayPal IPN module working but keep getting a 400 Bad Request error from LWP::UserAgent. I am not sure why this is happening. PayPal pings me fine (I'm using the IPN simulator) and I can see the process in my app logs. I can call the PayPal validation URL via LWP::UserAgent without form content and that works fine, but once I include the request content for validation I get error 400. If anyone knows about this please let me know.
-$self->{'_req'} is of type Plack::Request
my $url = $test ? $VERIFY_URL_DEV : $VERIFY_URL;
my $ua = new LWP::UserAgent();
my $req = new HTTP::Request('POST', $url);
my $query = 'cmd=_notify-validate&' . $self->{'_req'}->raw_body;
$req->content_type('application/x-www-form-urlencoded');
$req->content( $query );
my $res = $ua->request($req);
if ($res->is_error)
{
# HTTP error, indicate an invalid notification.
warn "There was an error validating this IPN.";
warn $res->message;
warn $res->error_as_HTML;
return 0;
}

The problem was on the PayPal side. When I tested in sandbox, not the IPN simulator, it worked fine. That was very frustrating.

Related

Facebook Messenger webhook setup, but not triggered

So I'm trying to setup a bot for the new Facebook Messenger API.
I'm following the quickstart.
I setup the webhook ok, and see it in my webhooks,
I called this:
https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=%3Ctoken%3E
and it did not throw any errors,
But when I go to the Page that I generated the access token on, and send a message, it does not call my webhook.
I check the httpaccess, and it does not call it.
Any way to debug this or any ideas?
Also, one thing I'm still puzzled over is how to support managing multiple pages from one Facebook app? Anyone know the answer to this, or do you need to create anew app and get permission for every page?
I have recently worked with the new chat bot API and there's a lot that can go wrong. So, here are some Ideas.
Make sure you've verified your webhook under the product settings tab.
subscribe your app to the page using your page access token. It returns {"success" : "true"} if everything goes right.
Important
Make sure the Facebook user from which you're sending the message is listed as the Admin or Developer or Tester in your app roles (https://developers.facebook.com/apps/YOUR_APP_ID/roles/). Messages from other users won't work unless your app is approved and publicly released.
Have you received any call back from the facebook api ? or is it just the messages? Take a look at the logs of your web server and check if you're getting any hits on the webhook. Also check the error logs.
Try hitting your webhook manually and see if it responds. You can use
curl to generate a manual request. This is what the request from
Facebook looks like:
Command:
curl -i -X POST -H 'Content-Type: application/json' -d '{"object":"page","entry":[{"id":43674671559,"time":1460620433256,"messaging":[{"sender":{"id":123456789},"recipient":{"id":987654321},"timestamp":1460620433123,"message":{"mid":"mid.1460620432888:f8e3412003d2d1cd93","seq":12604,"text":"Testing Chat Bot .."}}]}]}' https://www.YOUR_WEBHOOK_URL_HERE
So my issue was I was calling GET when trying to subscribe instead of POST
https://graph.facebook.com/v2.6/:pageid/subscribed_apps?access_token=:token
GET will return the current subscriptions (empty {[]}), POST returns {"success" : "true"}
Some other gotchas I hit were,
the examples use https://graph.facebook.com/v2.6/me/.. but I seemed to need to use, https://graph.facebook.com/v2.6/:pageid
the access token is the messenger access token, not your API access token
if your webhook throws a error, Facebook will stop sending you messages for a while
One thing I'm still puzzled over is how to support managing multiple pages from one Facebook app? Anyone know the answer to this, or do you need to create anew app and get permission for every page?
Another thing which can prevent some responses from being sent to your webhook is when a message type gets blocked in a queue.
If a particular message type is delivered to your webhook but doesn't receive it's 200 response within 20 seconds it will keep trying to send you that message again for hours.
What's more facebook messenger will stop sending you any more of that message type until the first one has been acknowledged. It essentially puts them into a queue.
In the meantime, other message types will continue to send fine.
This happened to me when I accidentally introduced an undeclared variable inside my code which handled standard messages. It meant that postback messages all worked fine, but quick replies and normal messages would never get sent to my webhook. As soon as you fix the error, they all come piling through at once.
As mentioned by others, using a service such as POSTMAN to send messages to your webhook is a great way to find this kind of errors, otherwise, messenger just fails silently.
Exluding of your bot rout from CSRF verification can help if you use framework. This helps for me (Laravel 5.4, app/Http/Middleware/VerifyCsrfToken.php):
protected $except = [
'/your_bot_route'
];
I too had the same issue when I was working on a bot couple of days ago. Followed this gist and modified the code as below, and everything is working fine.
public function index()
{
$challenge = $_REQUEST['hub_challenge'];
$verify_token = $_REQUEST['hub_verify_token'];
// Set this Verify Token Value on your Facebook App
if ($verify_token === 'MyVerifyToken!') {
echo $challenge;
}
$input = json_decode(file_get_contents('php://input'), true);
// Get the Senders Graph ID
$sender = $input['entry'][0]['messaging'][0]['sender']['id'];
// Get the returned message
$message = $input['entry'][0]['messaging'][0]['message']['text'];
//$senderName = $input['entry'][0]['messaging'][0]['sender']['name'];
$reply="Sorry, I don't understand you";
switch($message)
{
case 'hello':
$reply = "Hello, Greetings from MyApp.";
break;
case 'pricing':
$reply = "Sample reply for pricing";
break;
case 'contact':
$reply = "Sample reply for contact query";
break;
case 'webinar':
$reply = "Sample reply for webinar";
break;
case 'support':
$reply = "sample reply for support";
break;
default:
$reply="Sorry, I don't understand you";
}
//API Url and Access Token, generate this token value on your Facebook App Page
$url = 'https://graph.facebook.com/v2.6/me/messages?access_token=MYACCESSTOKEN';
//Initiate cURL.
$ch = curl_init($url);
//The JSON data.
$jsonData = '{
"recipient":{
"id":"' . $sender . '"
},
"message":{
"text":"'.$reply.'"
}
}';
//Tell cURL that we want to send a POST request.
curl_setopt($ch, CURLOPT_POST, 1);
//Attach our encoded JSON string to the POST fields.
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
//Set the content type to application/json
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
//Execute the request but first check if the message is not empty.
if (!empty($input['entry'][0]['messaging'][0]['message'])) {
$result = curl_exec($ch);
}
}
Note : Ensure the user roles within the application page to get the responses from the web hook. I have set Administrator, and Tester user. Only there were able to get the responses. Other users will get once this is published. Also, change verify token, and page token accordingly.
There is an option that is asked while publishing the app about the number of business this bot going to be used by. But I have no idea how to use it. Still searching that though.
If you still can not solve your problem, try to check and update your Privacy policy link.
I updated worry link to Privacy policy, and Facebook show 404 error even the webhoob is verified...
You can link multiple pages to your app, under Add or Remove Pages tab in your Messenger Settings

PAYPAL RESTful API execute call occasional error

I have fully integrated my website with paypal using paypal RESTful's API PHP SDK. Now my problem is every once in a while on the payment process, once the user approves the payment and it comes back to my website for execution, the API execution request comes back with the following responses from paypal. The HTTP response code's in these scenarios are 400 and 500. Below I have listed the error name and error message which comes as a part of the JSON response I get form paypal on execute action (https://api.paypal.com/v1/payments/payment/PAY-xxxxxxxxxxxxxxxxxxxxxxxxxx/execute):
400 PAYMENT_NOT_APPROVED_FOR_EXECUTION,Payer has not approved payment
500 INTERNAL_SERVICE_ERROR,An internal service error has occurred
In terms of the code I use to make that call I have added the function which executes the payment. I should add that this is working in 98% of times. Below is the code:
public function getExecute()
{
// Payment is cancelled
if (!(Input::get('success') && Input::get('success') == 'true'))
return $this->runPaymentExitProcess();
// No paymentId
if (!Session::has('paymentId'))
return "No payment id to execute";
$paymentId = Session::get('paymentId');
$this->payment = Payment::get($paymentId, $this->apiContext);
// The payer_id is added to the request query parameters when the user is redirected from paypal back to your site
$this->execution->setPayer_id(Input::get('PayerID'));
$response = $this->payment->execute($this->execution, $this->apiContext);
// Check for paypal errors
if (isset($response->name)) {
// When we have a paypal error
if ($this->hasPaypalError($response)) {
$error = array('name' => $response->name, 'message' => $response->message);
Session::put('error', $error);
return (MODE == 'LIVE') ? Redirect::to('/paypalerror') : Redirect::to('/paypalerror'.MODE_PARAM);
die;
}
}
// Unset session
Session::forget('paymentId');
// execution successful
if ($this->addPaymentIdToUser($paymentId))
return (MODE == 'LIVE') ? Redirect::to('/') : Redirect::to('/'.MODE_PARAM);
}
The code is in laravel. Please note that the $this->apiContext is set in the constructor of the class. Also this is the function is the success url which is set in the API under redirect_urls.
Can someone please help me figure out if this issue is coming from my side or Paypal's side?
For the 400 error, this would occur if the user decided not to approve the payment. If you think the payment was actually approved in the web flow then that would be an issue to look into further.
Can you provide the debugId and approximate time (with timezone, or GMT) of a 500 internal server error response, and a 400 if you believe the payment was actually approved by user.

PayPal Sandbox suddenly outputting proxy error for adaptive payments

Edit: For some reason it's working again. I did not need to log in to developer.paypal.com either. If anyone knows why, that would be useful. Thanks!
For the past few months I have been developing a site using PayPal's adaptive payments, specifically the chained payments method. I am using the embedded payment method from a site that uses SSL. The integration had been working perfectly for months until a day or two ago. It was even still working right after I imported my old sandbox accounts to the new developer.paypal.com sandbox.
After submitting the payment from the site's form, the browser spins for a minute or two. The error generated after a few minutes is the following:
The proxy server could not handle the request GET
/webapps/adaptivepayment/flow/corepay Reason: Error during SSL
Handshake with remote server
$API_UserName = "us business account from developer.paypal.com sandbox";
$API_Password = "password from developer.paypal.com sandbox";
$API_Signature = "singature from developer.paypal.com sandbox";
// AppID is preset for sandbox use
// If your application goes live, you will be assigned a value for the live environment by PayPal as part of the live onboarding process
$API_AppID = "APP-80W284485P519543T";
$API_Endpoint = "";
if ($Env == "sandbox")
{
$API_Endpoint = "https://svcs.sandbox.paypal.com/AdaptivePayments";
}
else
{
$API_Endpoint = "https://svcs.paypal.com/AdaptivePayments";
}
If I am not mistaken, I believe the error is being generated when the redirect happens from this function here:
function RedirectToPayPal ( $cmd )
{
// Redirect to paypal.com here
global $Env;
$payPalURL = "";
if ($Env == "sandbox")
{
$payPalURL = 'https://www.sandbox.paypal.com/webapps/adaptivepayment/flow/pay?expType='.$_POST['expType'].$cmd;
}
else
{
$payPalURL = "https://www.paypal.com/webscr?" . $cmd;
}
header("Location: ".$payPalURL);
exit;
}
Edit: I changed the $payPalURL to "https://developer.paypal.com..." as suggested and got the following:
Service Temporarily Unavailable
The server is temporarily unable to service your request due to
maintenance downtime or capacity problems. Please try again later.
After the recent code change this flow now requires you to log in to https://developer.paypal.com first. I am checking to see if this was intentional or is a code bug.
There appears to have been an issue with the sandbox.paypal.com server that disabled adaptive payments for a couple days. Everything seems to be functioning as it had been previously. Thank you to Dennis for his input.
Edit:
This appears to be ongoing and intermittent

How to integrate Paypal with SOAP API?

I have some problem when i integrate PayPal with SOAP API.
Now, This my code
// Set paypal
// Include NuSOAP
$url_nusoap = "xxx/nusoap/nusoap.php";
include($url_nusoap);
$wsdl_URL = "https://www.sandbox.paypal.com/wsdl/PayPalSvc.wsdl";
$s_URL = "https://api-3t.sandbox.paypal.com/2.0/";
$s_Ver = "94.0";
$header = "";
$header .= "<RequesterCredentials xmlns='urn:ebay:api:PayPalAPI' xsi:type='ebl:CustomSecurityHeaderType'>";
$header .= "<Credentials xmlns='urn:ebay:apis:eBLBaseComponents' xsi:type='ebl:UserIdPasswordType'>";
$header .= "<Username>#api_username#</Username>";
$header .= "<Password>#api_password#</Password>";
$header .= "<Signature>#api_signature#</Signature>";
$header .= "</Credentials>";
$header .= "</RequesterCredentials>";
$s = new soap_client($wsdl_URL, true);
$err = $s->getError();
if ($err) die("Soap client constructor err.. check wsdl url");
//set end point
$s->setEndpoint($s_URL);
$s->setHeaders($header);
$bodyReq = "?"
$result = $s->call("?", $bodyReq);
And my problem is i don't know what should i call API in "$bodyReq" and $s->call(); ?
This my situation :
In my site can select pay method
Paypal
xxx
xxx
When i choose "1 Paypal" and submit - Redirect to "pay_checker" if pay_method == "Paypal" redirect to "PAYPAL SOAP API Page" (if pay_method == "xxx" redirect it to some API in other pay services)
I need if i select "PayPal method" and redirect to "PAYPAL SOAP API" page it's call some API to pass $amount ,$product_name and redirect to PAYPAL Billing page
somebody help me please
Thank you.
Edit ------ Now i learn "ExpressCheckout" but i don't know where point to learn it
What you currently have is building your credentials only, which would then get included in your actual XML request, which is where $bodyReq would come into play.
PayPal provides plenty of documentation for Express Checkout, including this document specific to the SOAP API. Chapter 6 covers Express Checkout.

Paypal IPN listener issue

I currently have a problem with my Paypal IPN listener and have been receiving the following email from paypal each day:
"Please check your server that handles PayPal Instant Payment Notifications (IPN). IPNs sent to the following URL(s) are failing:
http://www.mysales.ie/create_promo_listener.php"
I have a demo site which enables users to create ads and works perfectly (no problems with IPN), however the proper site that I am developing has this issue (It is on a different host).
I have contacted the host provider and they have said it is not an issue on their side. I have tried php error logs but cant find any issues. I have the exact same code on both sites so I cant understand what the problem is.
<?php include 'ipn_handler.class.php';
/**
* Logs IPN messages to a file.
*/
class Logging_Ipn_Handler extends IPN_Handler
{
public function process(array $post_data)
{
$data = parent::process($post_data);
if($data === FALSE)
{
header('HTTP/1.0 400 Bad Request', true, 400);
exit;
}
$random_number = $_POST['custom'];
file_put_contents( 'logs/listenerTest.txt', "listener = " . $random_number, FILE_APPEND);
header("location:create_promo_creator.php?random_number=" . $random_number);
}
}
date_default_timezone_set('Europe/Oslo');
$handler = new Logging_Ipn_Handler();
$handler->process($_POST);
I have been trying to find the root of the problem for a long time but cant figure it out.
Seems that your $data = parent::process($post_data);
method is returning FALSE.
Do you have any logging instrumentation in your code to verify this?
The easy workaround is to return a 200 response to keep PayPal's IPN system happy, and to log the error for further review.