Facebook Credits Example on App Engine? - facebook

Are there any examples of using facebook credits on Google App Engine?
I found this blog post , but it's not complete
http://blog.suinova.com/2011/01/integrating-facebook-credits-api-into.html
I got the sample runwithfriends example working on the App Engine, tried to expand it with Credits, no luck so far.
Also searched for the FB developer forums, got nothing.
Any resources you can point me to?
What's not working:
1) When I click on the "pay with Facebook" button, I get an "Application Error" , without any error code.
-Checked the javascript console
-Checked the fb app settings
-Tried on local server and production server
2) The callback.py isn't complete, because i could not parse the signed request (no code available in py for me to learn from)
3) What I basically did was to add code from Suinova Designs (link above) to the existing Run With Friends app code. Didn't turn out as expected.
my code so far:
//payment_page.html
<html>
<table>
<tr><th>Name</th><th>Price</th><th> </th></tr>
<tr><td>Something to buy</td><td>10 FC</td><td><a href="" onclick="return buyit();">
<img src="http://www.facebook.com/connect/button.php?app_id=215638625132268&feature=payments&type=light_l" />
</a></td></tr>
</table>
// javascript
function buyit(){
FB.ui({
method:'pay',
purchase_type:'item',
order_info:{
item_id:'myitem',
title:'Something to buy',
price:2,
description:'Whatever',
image_url:'http://www.facebook.com/images/gifts/21.png',
product_url:'http://www.facebook.com/images/gifts/21.png'}
},
function(resp){
if(resp.order_id) window.top.location='http://apps.facebook.com/runwithfriends trial'; else alert(resp.error_message);
});
return false;
}
//callback.py
class FacebookPaymentRequest(webapp.RequestHandler):
def post(self):
signed_request = parse_signed_request(self.request.get('signed_request'),conf.FACEBOOK_APP_SECRET)
payload = signed_request['credits'] #credits:{buyer:int,order_id:int,order_info:{},receiver:int}
order_id = payload['order_id']
method = web.request.get('method')
return_data = {'method':method}
if method == 'payments_get_items':
order_info = payload['order_info'] #order_info:{item_id:'',title:'',description:'',price:0,image_url:'',product_url:''}
item = simplejson.loads(order_info) #needs to convert JSON string to dict
#check item,price,etc and reply
return_data['content'] = [item]
elif method == 'payments_status_update':
status = payload['status']
return_data['content'] = {'status':'settled','order_id':order_id}
if status == 'placed':
#just return settled status to Facebook, this may be called twice
order_details = simplejson.loads(payload['order_details'])
#check and log here
elif status == 'settled':
order_details = simplejson.loads(payload['order_details'])
#order_details:{order_id:0,buyer:0,app:0,receiver:0,amount:0,update_time:0,time_placed:0,data:'',items:[{}],status:'placed'}
buyer = order_details['buyer']
item = order_details['items'][0]
#now save this transaction into our database
else:
#may be refunded, canceled, log the activity here
return_data['content']['status'] = status
self.response.out.write(simplejson.dumps(return_data))

Your python code looks fairly normal so I would guess that you are simply having trouble with your authorization. Depending upon how you authorize (a process a fair amount more complicated that the credits system), you are likely being given a signed request that is only partially authorized... meaning you are authorized to access only certain parts of facebook, but generally not authorized to access the active/logged-in user (i.e. me).
You can verify this by determining if you signed_request is a full 80+ characters (as opposed to around 40). Generally I try to authenticate by deciphering the profile (signed_request), if that fails then I try to use a previously stored cookie, then if that fails I try to relogin the user. I determine failure by placing try/except around my calls to get a "me" object through the GraphAPI.

Related

Service Account for google sheets returns not found

I am trying to read a spreadsheet using a service account (I cannot use OAuth, which works, since the process will be running on a server to periodically check sheet data)
I tried several approaches. If I follow the example using oauth I can see the sheet values. However, I need the run the script without any GUI on the background.
I have found this tutorial https://github.com/juampynr/google-spreadsheet-reader
I have created a projec, service account, added viewer role, shared the spreadsheet with the service account email. Generated the key.
It seems that the test program can connect to the google services but the moment it request the spreadsheet the end result is "404 not found".
require 'vendor/autoload.php';
$service_account_file = '/secrets/readsheetmar2019-08b737d1c1cb._portfolio_test.json';
$spreadsheet_id = '1TAWybckPrnWlQxBZh0ScDsFOvftwi2dvTBNGarSdY30';
$spreadsheet_range = '';
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . $service_account_file);
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope(Google_Service_Sheets::SPREADSHEETS_READONLY);
$client->fetchAccessTokenWithAssertion();
$service = new Google_Service_Sheets($client);
//added by me
if ($client->isAccessTokenExpired()) {
print "expired\n";
}else{
print "not expired\n";
}
$result = $service->spreadsheets_values->get($spreadsheet_id, $spreadsheet_range);
var_dump($result->getValues());
Error:PHP Fatal error: Uncaught exception 'Google_Service_Exception' with message '
Error 404 (Not Found)!!1
When the access token retrieved by OAuth2 is used, the Spreadsheet of $spreadsheet_id = '1TAWybckPrnWlQxBZh0ScDsFOvftwi2dvTBNGarSdY30'; can retrieve the values.
When the access token retrieved by Service Account is used, Error 404 (Not Found)!!1 is returned.
If my understanding is correct, please confirm the following points.
Confirmation points:
As a test run, please set the range $spreadsheet_range = '';.
For example, it's $spreadsheet_range = 'Sheet1'.
If the error message of The caller does not have permission is returned, please confirm as follows.
Whether the Spreadsheet of 1TAWybckPrnWlQxBZh0ScDsFOvftwi2dvTBNGarSdY30 is sharing the email of Service Account.
If you didn't share the Service Account to the Spreadsheet, please share the email of client_email in the file of readsheetmar2019-08b737d1c1cb._portfolio_test.json to the Spreadsheet you want to access.
If the error message of Google Sheets API has not been used in project ### before or it is disabled. is returned, please enable Sheets API.
If this was not the solution for your issue, I apologize.

Retrieve User ID of Facebook App Invitor

In the context of a given Facebook app, suppose User A invited user B to start using it. Once User B accepts to use the app, is there any way to retrieve the ID of User A programmatically (via either PHP/JS SDK) ? This doesn't seem quite documented.
For what it's worth, A/B users are friends, if it's any use.
when user comes following the app request, you can get request id's using
$_GET['request_ids']
then retrieve all the request ids with which you can call graph api to get the corresponding request details like below:
if(isset($_GET['request_ids']))
{
$request_ids = $_GET['request_ids'];
}
$request_ids = explode(",", $request_ids);
foreach($request_ids as $request_id)
{
$request_object = $facebook->api($request_id);
//this $request_object have sender facebook id in the field uid_from
}
If you look here:
http://developers.facebook.com/docs/reference/dialogs/requests/
You can see the object layout. Of note is the data property:
Optional, additional data you may pass for tracking. This will be
stored as part of the request objects created. The maximum length is
255 characters.
In this object you can add your referring UserId and then when the request is claimed, you can then process it on your end.
Hope this helps.

Nothing except "None" returned for my Python web.py Facebook app when I turn on "OAuth 2.0 for Canvas"

I am a beginning Facebook app developer, but I'm an experienced developer. I'm using web.py as my web framework, and to make matters a bit worse, I'm new to Python.
I'm running into an issue, where when I try to switch over to using the newer "OAuth 2.0 for Canvas", I simply can't get anything to work. The only thing being returned in my Facebook app is "None".
My motivation for turning on OAuth 2.0 is because it sounds like Facebook is going to force it by July, and I might as well learn it now and now have to rewrite it in a few weeks.
I turned on "OAuth 2.0 for Canvas" in the Advanced Settings, and rewrote my code to look for "signed_request" that is POSTed to my server whenever my test user tries to access my app.
My code is the following (I've removed debugging statements and error checking for brevity):
#!/usr/bin/env python
import base64
import web
import minifb
import urllib
import json
FbApiKey = "AAAAAA"
FbActualSecret = "BBBBBB"
CanvasURL = "http://1.2.3.4/fb/"
RedirectURL="http://apps.facebook.com/CCCCCCCC/"
RegURL = 'https://graph.facebook.com/oauth/authorize?client_id=%s&redirect_uri=%s&type=user_agent&display=page' % (FbApiKey, RedirectURL)
urls = (
'/fb/', 'index',
)
app = web.application(urls, locals())
def authorize():
args = web.input()
signed_request = args['signed_request']
#split the signed_request via the .
strings = signed_request.split('.')
hmac = strings[0]
encoded = strings[1]
#since uslsafe_b64decode requires padding, add the proper padding
numPads = len(encoded) % 4
encoded = encoded + "=" * numPads
unencoded = base64.urlsafe_b64decode(str(encoded))
#convert signedRequest into a dictionary
signedRequest = json.loads(unencoded)
try:
#try to find the oauth_token, if it's not there, then
#redirect to the login page
access_token = signedRequest['oauth_token']
print(access_token)
except:
print("Access token not found, redirect user to login")
redirect = "<script type=\"text/javascript\">\ntop.location.href=\"" +_RegURL + "\";\n</script>"
print(redirect)
return redirect
# Do something on the canvas page
returnString = "<html><body>Hello</body></html>"
print(returnString)
class index:
def GET(self):
authorize()
def POST(self):
authorize()
if __name__ == "__main__":
app.run()
For the time being, I want to concentrate on the case where the user is already logged in, so assume that oauth_token is found.
My question is: Why is my "Hello" not being outputted, and instead all I see is "None"?
It appears that I'm missing something very fundamental, because I swear to you, I've scoured the Internet for solutions, and I've read the Facebook pages on this many times. Similarly, I've found many good blogs and stackoverflow questions that document precisely how to use OAuth 2.0 and signed_request. But the fact that I am getting a proper oauth_token, but my only output is "None" makes me think there is something fundamental that I'm doing incorrectly. I realize that "None" is a special word in python, so maybe that's the cause, but I can't pin down exactly what I'm doing wrong.
When I turn off OAuth 2.0, and revert my code to look for the older POST data, I'm able to easily print stuff to the screen.
Any help on this would be greatly appreciated!
How embarrassing!
In my authorize function, I return a string. But since class index is calling authorize, it needs to be returned from the class, not from authorize. If I return the return from authorize, it works.

Find Facebook user (url to profile page) by known email address

I have an email address and want to find out if there is a Facebook user linked to this address. If there is, then I want to retrieve the url to this users profile page and save it somewhere.
I do not have a facebook application, but, if necessary, I would use existing account data to login to facebook and perform the task.
I thought this would be an easy task, but somehow it's not. I read through the Graph API documentation and there you find instructions on how to search public data. It says the format is:
https://graph.facebook.com/search?q=QUERY&type=OBJECT_TYPE
But trying this with an email address in the q param and user in the type param without further information results in an OAuthException saying "An access token is required to request this resource."
However, if you click the example search links Facebook generates a url with the mentioned access token related to the currently logged on user. Performing searches with this token gives the expected results. But i cannot figure out how to get this user session access token after logging in. Every time I search on how to get an access token I only find information regarding Facebook apps and retrieving permissions for basic or specific data access. This is, as I mentioned, not what I am looking for, as I don't have and don't need a facebook app.
Since Facebook gives me the needed token in the example links I thought it shouldn't be a problem to get it too. Or do they only have it because of home advantage?
Also, the Outlook Social Connector Provider for Facebook is able to retrieve Facebook data just via an email address (and the account data provided). So I thought, if Microsoft can do this stuff I should be also possible to do simliar things.
Last but not least this is the more frustrating since I, theoretically and practically, am already able to find users profile url just by searching for the email address. I don't even have to be logged on to Facebook. And it's not the official API way.
If I perform a web request to http://www.facebook.com/search.php?init=s:email&q=example#domain.com&type=users I get the expected search result. The problem is that I have to parse the HTML code and extract the url (that's okay) and that the result page is possibly subject to change and could easily break my method to extract the url (problematic).
So does anybody has an idea what's the best way to accomplish the given task?
The definitive answer to this is from Facebook themselves. In post today at https://developers.facebook.com/bugs/335452696581712 a Facebook dev says
The ability to pass in an e-mail address into the "user" search type was
removed on July 10, 2013. This search type only returns results that match
a user's name (including alternate name).
So, alas, the simple answer is you can no longer search for users by their email address. This sucks, but that's Facebook's new rules.
Simply use the graph API with this url format:
https://graph.facebook.com/search?q=zuck#fb.com&type=user&access_token=... You can easily create an application here and grab an access token for it here. I believe you get an estimated 600 requests per 600 seconds, although this isn't documented.
If you are doing this in bulk, you could use batch requests in batches of 20 email addresses. This may help with rate limits (I am not sure if you get 600 batch requests per 600 seconds or 600 individual requests).
In response to the bug filed here: http://developers.facebook.com/bugs/167188686695750 a Facebook engineer replied:
This is by design, searching for users is intended to be a user to user function only, for use in finding new friends or searching by email to find existing contacts on Facebook. The "scraping" mentioned on StackOverflow is specifically against our Terms of Service https://www.facebook.com/terms.php and in fact the only legitimate way to search for users on Facebook is when you are a user.
Maybe this is a little bit late but I found a web site which gives social media account details by know email addreess. It is https://www.fullcontact.com
You can use Person Api there and get the info.
This is a type of get : https://api.fullcontact.com/v2/person.xml?email=someone#****&apiKey=********
Also there is xml or json choice.
I've captured the communication of Outlook plugin for Facebook and here is the POST request
https://api.facebook.com/method/fql.multiquery
access_token=TOKEN&queries={"USER0":"select '0', uid, name, birthday_date, profile_url, pic, website from user where uid in (select uid from email where email in ('EMAIL_HASH'))","PENDING_OUT":"select uid_to from friend_request where uid_from = MY_ID and (uid_to IN (select uid from #USER0))"}
where
TOKEN - valid access token
EMAIL_HASH - combination of CRC32 and MD5 hash of searched email address in format crc32_md5
MY_ID - ID of facebook profile of access token owner
But when I run this query with different access token (generated for my own application) the server response is: "The table you requested does not exist" I also haven't found the table email in Facebook API documentation. Does Microsoft have some extra rights at Facebook?
Andreas,
I've also been looking for an "email-to-id" ellegant solution and couldn't find one.
However, as you said, screen scraping is not such a bad idea in this case, because emails are unique and you either get a single match or none. As long as Facebook don't change their search page drastically, the following will do the trick:
final static String USER_SEARCH_QUERY = "http://www.facebook.com/search.php?init=s:email&q=%s&type=users";
final static String USER_URL_PREFIX = "http://www.facebook.com/profile.php?id=";
public static String emailToID(String email)
{
try
{
String html = getHTML(String.format(USER_SEARCH_QUERY, email));
if (html != null)
{
int i = html.indexOf(USER_URL_PREFIX) + USER_URL_PREFIX.length();
if (i > 0)
{
StringBuilder sb = new StringBuilder();
char c;
while (Character.isDigit(c = html.charAt(i++)))
sb.append(c);
if (sb.length() > 0)
return sb.toString();
}
}
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
private static String getHTML(String htmlUrl) throws MalformedURLException, IOException
{
StringBuilder response = new StringBuilder();
URL url = new URL(htmlUrl);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK)
{
BufferedReader input = new BufferedReader(new InputStreamReader(httpConn.getInputStream()), 8192);
String strLine = null;
while ((strLine = input.readLine()) != null)
response.append(strLine);
input.close();
}
return (response.length() == 0) ? null : response.toString();
}
This is appeared as pretty easy task, as Facebook don't hiding user emails or phones from me. So here is html parsing function on PHP with cURL
/*
Search Facebook without authorization
Query
user name, e-mail, phone, page etc
Types of search
all, people, pages, places, groups, apps, events
Result
Array with facebook page names ( facebook.com/{page} )
By 57ar7up
Date 2016
*/
function facebook_search($query, $type = 'all'){
$url = 'http://www.facebook.com/search/'.$type.'/?q='.$query;
$user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36';
$c = curl_init();
curl_setopt_array($c, array(
CURLOPT_URL => $url,
CURLOPT_USERAGENT => $user_agent,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_FOLLOWLOCATION => TRUE,
CURLOPT_SSL_VERIFYPEER => FALSE
));
$data = curl_exec($c);
preg_match_all('/href=\"https:\/\/www.facebook.com\/(([^\"\/]+)|people\/([^\"]+\/\d+))[\/]?\"/', $data, $matches);
if($matches[3][0] != FALSE){ // facebook.com/people/name/id
$pages = array_map(function($el){
return explode('/', $el)[0];
}, $matches[3]);
} else // facebook.com/name
$pages = $matches[2];
return array_filter(array_unique($pages)); // Removing duplicates and empty values
}
Facebook has a strict policy on sharing only the content which a profile makes public to the end user.. Still what you want is possible if the user has actually left the email id open to public domain..
A wild try u can do is send batch requests for the maximum possible batch size to ids..."http://graph.facebook.com/ .. and parse the result to check if email exists and if it does then it matches to the one you want.. you don't need any access_token for the public information ..
in case you want email id of a FB user only possible way is that they authorize ur app and then you can use the access_token thus generated for the required task.
Maybe things changed, but I recall rapleaf had a service where you enter an email address and you could receive a facebook id.
https://www.rapleaf.com/
If something was not in there, one could "sign up" with the email, and it should have a chance to get the data after a while.
I came across this when using a search tool called Maltego a few years back.
The app uses many types of "transforms", and a few where related to facebook and twitter etc..
..or find some new sqli's on fb and fb apps, hehe. :)
WARNING: Old and outdated answer. Do not use
I think that you will have to go for your last solution, scraping the result page of the search, because you can only search by email with the API into those users that have authorized your APP (and you will need one because the token that FB provides in the examples has an expiry date and you need extended permissions to access the user's email).
The only approach that I have not tried, but I think it's limited in the same way, is FQL. Something like
SELECT * FROM user WHERE email 'your#email.com'
First I thank you. # 57ar7up and I will add the following code it helps in finding the return phone number.
function index(){
// $keyword = "0946664869";
$sql = "SELECT * FROM phone_find LIMIT 10";
$result = $this->GlobalMD->query_global($sql);
$fb = array();
foreach($result as $value){
$keyword = $value['phone'];
$fb[] = $this->facebook_search($keyword);
}
var_dump($fb);
}
function facebook_search($query, $type = 'all'){
$url = 'http://www.facebook.com/search/'.$type.'/?q='.$query;
$user_agent = $this->loaduserAgent();
$c = curl_init();
curl_setopt_array($c, array(
CURLOPT_URL => $url,
CURLOPT_USERAGENT => $user_agent,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_FOLLOWLOCATION => TRUE,
CURLOPT_SSL_VERIFYPEER => FALSE
));
$data = curl_exec($c);
preg_match('/\{"id":(?P<fbUserId>\d+)\,/', $data, $matches);
if(isset($matches["fbUserId"]) && $matches["fbUserId"] != ""){
$fbUserId = $matches["fbUserId"];
$params = array($query,$fbUserId);
}else{
$fbUserId = "";
$params = array($query,$fbUserId);
}
return $params;
}

Paypal sandbox IPN return INVALID

I am trying IPN callback, using servlet. The code I am using is provided by paypal for verifying the ipn data. But every time i getting a INVALID response.
Here is the code:
Enumeration en = req.getParameterNames();
String str = "cmd=_notify-validate";
while (en.hasMoreElements()) {
String paramName = (String) en.nextElement();
String paramValue = req.getParameter(paramName);
//str = str + "&" + paramName + "=" + URLEncoder.encode(paramValue,"UTF-8"); // for UTF-8 i set the encode format in my account as UTF-8
//str = str + "&" + paramName + "=" + URLEncoder.encode(paramValue,"ISO-8859-1");// for ISO-8859-1 i set the encode format in my account as ISO-8859-1
str = str + "&" + paramName + "=" + URLEncoder.encode(paramValue); //default as provided by paypal
}
URL u = new URL("http://www.sandbox.paypal.com/cgi-bin/webscr");
URLConnection uc = u.openConnection();
uc.setDoOutput(true);
uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
PrintWriter pw = new PrintWriter(uc.getOutputStream());
pw.println(str);
pw.close();
BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream()));
String res = in.readLine();
in.close();
if (res.equals("VERIFIED") || !res.equals("VERIFIED")) {
//Update database...
} else if (res.equals("INVALID")) {
//INVALID
}
I have checked all three possibilities provided by paypal in case paypal return INVALID as follow:
1) Missing Parameters - As I am send all the parameters no issue of missing parameters
2) Invalid URL. - I am using sandbox so URL is : http://www.sandbox.paypal.com/cgi-bin/webscr
3) Character encoding. - Tried with character encoding same as paypal account setting parameter encoding.
the request I am sending back to paypal using following parameters:
cmd=_notify-validate&last_name=User&test_ipn=1&address_name=Test+User&txn_type=web_accept&receiver_email=sellr1_1252495907_biz%40gmail.com&residence_country=US&address_city=San+Jose&payment_gross=&payment_date=01%3A55%3A04+Sep+26%2C+2009+PDT&address_zip=95131&payment_status=Completed&address_street=1+Main+St&first_name=Test&payer_email=buyer1_1252495751_per%40gmail.com&protection_eligibility=Eligible&payer_id=BXBKS22JQCUWL&verify_sign=AOMkeg7ofCL7FJfioyWA19uCxD4XAgZirsjiGh8cUy1fd2YAqBwOkkst&payment_type=instant&business=sellr1_1252495907_biz%40gmail.com&address_country_code=US&mc_fee=0.64&address_status=confirmed&transaction_subject=True+Up&quantity=1&notify_version=2.8&mc_currency=EUR&custom=&address_state=CA&payment_fee=&handling_amount=0.00&payer_status=verified&shipping=0.00&item_name=True+Up&tax=0.00&username=hannonj&charset=windows-1252&item_number=567&mc_gross=10.00&txn_id=7F456350BS7942738&receiver_id=MASSU6BSR9SC2&address_country=United+States
Please , can any one direct me to proper direction? I am not getting what is wrong the code or the URL or anything else. I tried all the possibilities. Please help me.
An “INVALID” message is due to the following reasons:
Check that your are posting your response to the correct URL, which is https://www.sandbox.paypal.com/cgi-bin/webscr or https://www.paypal.com/cgi-bin/webscr, depending on whether you are testing in the Sandbox or you are live, respectively.
Verify that your response to the test IPN message contains exactly the same variables and values as the test message and that they are in the same order as in the test message. Finally, verify that the original variables are preceded by a cmd=_notify-validate variable.
Ensure that you are encoding your response string and are using the same character encoding as used by the test IPN message. (for example, I can see that he is using letters with umlaut and other symbols like “/”, etc).
With regard to the last point, the merchant can try to change the encoding language in use in his PayPal account, following the steps below:
Login on you PayPal account
Click on Profile
Click on “My Selling Preferences” tab
Click on “PayPal Button Language Encoding” (at the end of the page)
Click on "Other Options"
Select from the drop down menu: UTF-8
Choose the same charset also for the second option, which is related to IPN
Click “Save”
If the issue persists, we recommend to review the script in use, PayPal has some IPN code samples available at: https://github.com/paypal/ipn-code-samples
For additional information I include the link: https://developer.paypal.com/webapps/developer/docs/classic/ipn/integration-guide/IPNTesting/#id091GFE00WY4
I'm pretty sure the URL to send to is just "www.sandbox.paypal.com", see chapter 4 of Sandbox User Guide, and well, this is what I put for my own code (incidentally, for live, it is also just "www.paypal.com", for their sample code)
Thank you guys for your reply.
ohhh I solved it at last.
Actually in notify URL I also added a username parameter. Paypal want the parameter values for IPN same as it return to the servlet.(You can get it as req.getParameterNames()). As I have username parameter extra, which is not known to paypal. Paypal was returning INVALID.
Remember paypal's sandbox has completely different credentials. You must have development account and be logged into development panel to use sandbox.
If you're testing Paypal IPN over SSL, you will have to use ssl://www.sandbox.paypal.com on the port 443
I ran into multiple problems layered on top of each other before I could get Paypal IPN working - it kept returning INVALID but was not specific about which part I was getting wrong, unfortunately.
Things I got wrong:
Sandbox - if you use the Sandbox you need to use the entire Sandbox environment. It requires creating a new, separate account on the Paypal Sandbox website. The Sandbox API credentials it sets up under your regular account are not enough. You then use that separate Paypal account to file fake transactions on the Paypal Sandbox website, and watch them come across IPN on the Sandbox endpoint. The need for this second account is not obvious or clear at all in setting up API access. Also, switching between Sandbox and Live requires more than switching the URL, you need to switch the credentials. So a simple compile flag alternating a string isn't going to cut it.
Live - if you use the Live environment a number of things will get in your way. For us, it took a long time for Paypal to open up "Business" access to us. It wouldn't provide us anything over the API until that was enabled. When we initially applied we were flatly denied with no explanation or timeline to resolve it. A month later ish of taking payments (with no API to keep us up to date with those payments) it seemed to just magically start working.
Code example - the code example provided by Paypal is outdated, and has some clear issues. Here's an example that uses modern TPL/async:
// Send the verification back to Paypal in the format Paypal requested
var verif = (HttpWebRequest)WebRequest.Create(ipnVerifyUrl);
verif.Method = "POST";
verif.ContentType = "application/x-www-form-urlencoded";
var param = req.BinaryRead(req.TotalBytes);
var sRequest = Encoding.ASCII.GetString(param);
sRequest = "cmd=_notify-validate&" + sRequest;
verif.ContentLength = sRequest.Length;
using (var streamOut = new StreamWriter(verif.GetRequestStream(), Encoding.ASCII))
{
await streamOut.WriteAsync(sRequest);
}
// Send it
using (var re = await verif.GetResponseAsync())
{
var s = await HttpWebRequestAsync.GetFullResponseStringAsync((HttpWebResponse)re);
// Log the response (s)
}
Besides this code actually working (This is exactly what we have in Production, with some of our logging library calls stripped out), this code won't freeze a thread while waiting on network.
The awaits allow the thread to step away while the network does its thing, both in writing the verification request to Paypal, and in receiving the response back, both of which could be a long time.