M-Webs Interactive

ASP.Net and the Facebook Credits API

June 22, 2011

Update: February 16, 2012

    Facebook is now doing away with the third 'settled' callback and directs that developers handle awarding the item on the 'placed' callback instead. My code below has been update to show this. You can read their blog post here.

Update: November 07, 2011

    I had some issues getting credits to work with Dragonville. Facebook kept throwing a '1383046' Error complaining the initial response was invalid. Removing the following lines from the callback.aspx page codebehind fixed the problem, and caused no other issues:

// 1. Make sure facebook is authenticated
//-------------------------------------------
CanvasAuthorizer auth = new CanvasAuthorizer { Permissions = new[] { "user_about_me" } };
if (auth.Authorize()) { sigreq = Request.Params["signed_request"]; }



Intro

    So Facebook is going to start taking a whopping 30% of the developer profits now, and are forcing all games to exclusively use Facebook Credits. Since it needs to be done by July 1st, I've been hard at working trying to figure out how implement a Facebook Credits dialog successfully with ASP.Net. There is woefully little good documentation at all, and even less for doing it in C#. So after about 3 days of digging and trial and error, I've finally come up with a working solution. There are NO complete examples of how to do it in ASP.Net so I'm hoping this will be a big help to anyone trying to do the same.

The Facebook C# SDK can be found at http://facebooksdk.codeplex.com. It is best utilized by ASP.NET 4.0.
The Facebook Credits sample application can be found at https://github.com/facebook/credits-api-sample but it is in php form.

Setup and Notes

  • Make sure you are using OAuth2.0 (inherent in the new C# Facebook SDK, see my previous post for how to implement)
  • No Facebook Dialog will work inside a nested iFrame inside the Facebook iFrame. You must call the javascript from the main iFrame.

The front end page for purchasing items

The front end page is fairly simplistic, and can be copied almost verbatum from the examples. All you need is a link for each item you want to buy connected to a Facebook Javascript function that calls the PAY API. Your order info can be a simple string like mine, or you can get all complicated and make it a JSON array with lots of information. All the magic happens in the callback page, where you can do lookups of your itemid and return more detailed information. This is the functional part of my purchase.aspx page.

<!-- Button for Buying the Item -->
<b>100x</b> <img src="images/fbcredit.png" style="width: 30px;" /> <a onclick="placeOrder('itemid'); return false;">

<script>
  FB.init({appId: '<%= ConfigurationManager.AppSettings["AppID"] %>', status: true, cookie: true});

  function placeOrder(theitemid) {
    var order_info = theitemid; // Can be a JSON array if needed

    // calling the Facebook API ...
    var obj = {
      method: 'pay',
      order_info: order_info,
      purchase_type: 'item',
    };

    FB.ui(obj, callback);
  }

  var callback = function(data) {
    if (data['order_id']) {
      writeback("SUCCESS - " + data);
      return true;
    } else {
      //handle errors here
      writeback("Callback FAILED - " + data['error_code']);
      return false;
    }
  };

  function writeback(str) {
      document.getElementById('output').innerHTML=str;
  }
</script>

The callback.aspx page Part 1: Initial Callback

The callback page is where all the magic (and frustration) happen. Facebook pings this internally with the information you sent. Where all the php code has the credits information pulled from a JSON array in the decoded signed request, I was able to pull it directly from form posts and act accordingly. The .aspx front end doesn't need anything, it's all ignored anyway. The code behind is tricky though. I will post my working code and explain it in more detail as we go.

First up, Facebook calls your callback.aspx page (specified in the Credits section of the application setup through Facebook Developer) and passes three fields: 'order_info', 'order_id', and 'method'. You grab these via Request.Form and then respond with more detailed information about the item. The following is the start of my Page_Load() method. After grabbing the sent information, I construct an item and package it in a JSON Array which is then packed in another JSON array and sent as a reply to facebook. The entire reply looks something like this:

    {'method':'payments_get_items', 'order_id':12345678, 'content': { 'title':'yourtitle', 'description' : 'desc', etc... } }

Tip: you can check backend variables in this part by passing them as strings to the description or title objects being sent back to facebook, and they will show up in the dialog

protected void Page_Load(object sender, EventArgs e) {

// 1. Make sure facebook is authenticated
//-------------------------------------------
CanvasAuthorizer auth = new CanvasAuthorizer { Permissions = new[] { "user_about_me" } };
if (auth.Authorize()) { sigreq = Request.Params["signed_request"]; }

// 2. Grab the relevant CREDITS info from page POST form
//------------------------------------------------------
string order_info = "no info"; try { order_info = Request.Form["order_info"]; } catch { }
string order_id = "no order id"; try { order_id = Request.Form["order_id"]; } catch { }
string method = "no method"; try { method = Request.Form["method"]; } catch { }

// 3. Initial Buy request - return item info
// ------------------------------------------------------
if (method == "payments_get_items") {
  // Look up the item named 'order_info' and act accordingly
  //------------------------------------------------------------
  order_info = order_info.Substring(1, (order_info.Length - 2)); // remove the quotes
  // Code to handle your order here
  ulong credscost = {your item cost}; // Price of purchase in facebook credits

  // Construct the return item
  //------------------------------------------
  var theItem = new FacebookBuyItem() {
    item_id = youritemid,
    description = "Description of your item",
    price = credscost,
    title = "Item Name",
    product_url = "http://apps.facebook.com/yourapp/buy.aspx",
    image_url = "http://yoururl.com/image.jpg"
  };
  // Return the initial response to FB
  //------------------------------------------
  var res = new Dictionary<string, object>();
  res["method"] = method;
  res["order_id"] = order_id;
  res["content"] = new object[] { theItem };
  JavaScriptSerializer jss = new JavaScriptSerializer();
  var ob = jss.Serialize(res);
  ob = ob.Replace("#$", @"\/");
  Response.ContentType = "application/json";
  Response.Write(ob);
  Response.End();
}

Finally, you will need to paste this class in the code-behind of your callback page to utilize the FacebookBuyItem object used above:


// custom Facebook Item object (for credit callback returns)
//------------------------------------------------------------------
public class FacebookBuyItem {
  public int item_id { get; set; }
  public string title { get; set; }
  public string description { get; set; }
  public string image_url { get; set; }
  public string product_url { get; set; }
  public ulong price { get; set; }
  public string data { get; set; }
}

The callback.aspx page Part 2: Confirming Purchase

Whew, ok that last part was a doozie. It can only get easier from here, right? Ya, right. If everything is correct above, Facebook should pop up a BUY ITEM dialog with the information you sent. When you click confirm, it calls the callback.aspx page again, this time with a 'status' field and a 'order_details' field along side the previous fields. The order_details is actually a JSON array, and you can work with it after deserializing and grabbing the dictionary object out of resulting ArrayList. Then you have to return a reply including the 'oder_id', 'status', and 'content'. The second reply to Facebook should look like this:

    {'method':'payments_status_update', 'content': [{'order_id':1234567, 'status': 'settled' }] }

And here is the code to handle the second callback (directly following the code above, still inside the Page_Load() method)

// 4. Secondary confirmation dialog reply - handle all internal work and then return a canceled or settled status
// ------------------------------------------------------
else if (method == "payments_status_update") {

  string status = Request.Form["status"];

  // 4a. If order is PLACED then confirm and respond with settled so FB completes transaction
  //----------------------------------------------------------------------------------------
  if (status == "placed") {

    // Get Order information
    var order_details_array = Request.Form["order_details"];
    JavaScriptSerializer jss = new JavaScriptSerializer();
    Dictionary<string, object> order_details = jss.Deserialize<Dictionary<string, object>>(order_details_array);
    string fb_id = order_details["buyer"].ToString();
    string credsspent = order_details["amount"].ToString();

    // Parse Item information
    ArrayList arrlist = (ArrayList)order_details["items"];
    Dictionary<string, object> item_details = (Dictionary<string, object>)arrlist[0];
    string itemid = item_details["item_id"].ToString();

    // Create PENDING entry in your logs
    createLogEntry(fb_id, orderid, itemid, status); }

    // 16-FEB-12 - CODE BELOW WAS MOVED FROM SETTLED CALLBACK DUE TO FACEBOOK UPDATES.
    // Handle app-side functions of awarding the item
    bool wasAwarded = AwardTheItem(fb_id, itemid);
    if (wasAwarded == true) { updateLogEntry(fb_id, orderid, status); } // update PENDING entry to to SETTLED / COMPLETED
    else { updateLogEntry(fb_id, orderid, "Error, Item not awarded properly!"); } // Else Create ERROR log entry

    // mark new status as settled
    string newstatus = "settled";

    // Return the response
    var content = new Dictionary<string, object>();
    content["order_id"] = order_id;
    content["status"] = newstatus;
    var res = new Dictionary<string, object>();
    res["method"] = method;
    res["content"] = content;
    var ob = jss.Serialize(res);
    ob = ob.Replace("#$", @"\/");
    Response.ContentType = "application/json";
    Response.Write(ob);
    Response.End();

  } // End 'status = placed' block

} // End 'method = payments_status_updates' block

NOTE: The package Facebook sends that includes your order details (Request.Form["order_details"] in the code) looks like the following:

{"order_id":1234567, "buyer":2345566667, "app":9988776655, "receiver":2345566667, "amount":100, "update_time":1308603480, "time_placed":1308603479, "data":"", "items":[{"item_id":"0", "title":"Cool Item", "description":"You are purchasing a Cool Item for 100 Facebook Credits.", "image_url":"http:\/\/yourdomain\/image.jpg", "product_url":"http:\/\/apps.facebook.com\/yourapp\/buy.aspx", "price":100,"data":""}], "status":"placed"}

The callback.aspx page Part 3: Settled

NOTE: This callback has been removed by Facebook and is no longer valid as of March 1, 2012. Handle everything in the 'status=placed' tier.


Peter

Blog by: Peter Mooney

Peter is a veteran ASP.NET programmer currently in the process of growing his start-up social media game development company: M-Webs Interactive.

If you wish to contact him to leave a comment or suggest corrections to the blog, please send an email to pete (at) m-webs (dot) com.

© 2012 M-Webs Interactive LLC     Home