Yesterday we announced significant improvements to Apps on Facebook to drive distribution and engagement to high quality games and apps. As a follow up to these new features, this post outlines how to get started with publishing scores and achievements in your app.

The Graph API for achievements and scores enable you to publish stories for user’s achievements, passing friends’ scores, or leaderboard movement to make game play more competitive, social and exciting. These stories can only be published by apps categorized as ‘Games’ and are only shown to users when playing a game. We intend to broaden the types of apps that can use these new APIs in the near future.

Requesting the publish_actions permission

In order to start publishing scores and achievements, a user needs to grant your app the publish_actions permission. The example below shows how to ask for the publish_actions permission:

https://www.facebook.com/dialog/oauth?
  client_id=YOUR_APP_ID&
  redirect_uri=YOUR_CANVAS_PAGE&
  scope=publish_actions

The following PHP example demonstrates how to access the signed_request parameter, app access token and prompt the user to authorize your app the publish_actions permission:

<?php
  $app_id = 'YOUR_APP_ID';
  $app_secret = 'YOUR_APP_SECRET';
  $canvas_page_url = 'YOUR_CANVAS_PAGE_URL';

  // Authenticate the user
  session_start();
  if(isset($_REQUEST["code"])) {
     $code = $_REQUEST["code"];
  }

  if(empty($code) && !isset($_REQUEST['error'])) {
    $_SESSION['state'] = md5(uniqid(rand(), TRUE)); //CSRF protection
    $dialog_url = 'https://www.facebook.com/dialog/oauth?' 
      . 'client_id=' . $app_id
      . '&redirect_uri=' . urlencode($canvas_page_url)
      . '&state=' . $_SESSION['state']
      . '&scope=publish_actions';

    print('<script> top.location.href=\'' . $dialog_url . '\'</script>');
    exit;
  } else if(isset($_REQUEST['error'])) { 
    // The user did not authorize the app
    print($_REQUEST['error_description']);
    exit;
  };

  // Get the User ID
  $signed_request = parse_signed_request($_POST['signed_request'],
    $app_secret);

  $uid = $signed_request['user_id'];
  echo 'Welcome User: ' . $uid;

  // Get an App Access Token
  $token_url = 'https://graph.facebook.com/oauth/access_token?'
    . 'client_id=' . $app_id
    . '&client_secret=' . $app_secret
    . '&grant_type=client_credentials';

  $token_response = file_get_contents($token_url);
  $params = null;
  parse_str($token_response, $params);
  $app_access_token = $params['access_token'];

  function parse_signed_request($signed_request, $secret) {
    list($encoded_sig, $payload) = explode('.', $signed_request, 2); 

    // decode the data
    $sig = base64_url_decode($encoded_sig);
    $data = json_decode(base64_url_decode($payload), true);

    if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
      error_log('Unknown algorithm. Expected HMAC-SHA256');
      return null;
    }

    // check sig
    $expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
    if ($sig !== $expected_sig) {
      error_log('Bad Signed JSON signature!');
      return null;
    }

    return $data;
  }

  function base64_url_decode($input) {
    return base64_decode(strtr($input, '-_', '+/'));
  }
?>

Publishing User Scores

You can publish stories about a user’s score by issuing an HTTP POST to /USER_ID/scores and specifying a user’s score in the score parameter. An app access token (shown in the example above) is required to make this API call.

https://graph.facebook.com/USER_ID/scores?
  score=USER_SCORE&access_token=APP_ACCESS_TOKEN

Scores show up in the app ticker in various story types that help drive re-engagement back to your app:

Publishing User Achievements

You can define a set of achievements for your app using the game.achievement Open Graph type.

  • Each achievement has points associated with it
  • Each game gets a total of 1000 points to distribute across all their achievements
  • Each game gets a maximum of 1000 achievements
  • One user can achieve a particular achievement for a game only once

Achievements get displayed in the app ticker as follows:

Start by defining achievements for your app. When a user clicks on an achievement in the app ticker, they will be sent to the Achievement URL you registered. The recommended user experience is that the landing page explains what the achievement is.

Below is a simple HTML code snippet that you can use to set up your achievement:

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:og="http://ogp.me/ns#">
  <head>
    <title>ACHIEVEMENT_TITLE
    <meta property="og:type" content="game.achievement"/>
    <meta property="og:url" content="URL_FOR_THIS_PAGE"/>
    <meta property="og:title" content="ACHIEVEMENT_TITLE"/>
    <meta property="og:description" content="ACHIEVEMENT_DESCRIPTON"/>
    <meta property="og:image" content="URL_FOR_ACHIEVEMENT_IMAGE"/>
    <meta property="game:points" content="POINTS_FOR_ACHIEVEMENT"/>
    <meta property="fb:app_id" content="YOUR_APP_ID"/>
  </head>
  <body>
    Promotional content for the Achievement.  
    This is the landing page where a user will be directed after
    clicking on the achievement story.
  </body>
</html>

Now that you have defined your achievements, you need to register these achievements with your app by issuing an HTTP POST to /APP_ID/achievements with an app access token. You also need to specify the achievement parameter. You should also specify a display_order parameter. The achievement parameter is the URL where you define the achievements in your app. The display_order parameter indicates the order of this achievement as it shows up in the UI (low to high). For example, a display_order of 100 will be displayed in the UI before 200.

https://graph.facebook.com/YOUR_APP_ID/achievements?
  achievement=YOUR_ACHIEVEMENT_URL&
  display_order=YOUR_ACHIEVEMENT_DISPLAY_ORDER&
  access_token=APP_ACCESS_TOKEN

Once you register achievements with your app, you can publish them. Publish achievements by issuing an HTTP POST to /USER_ID/achievements and specifying the achievement parameter and the app access token:

https://graph.facebook.com/USER_ID/achievements?
  achievement=YOUR_ACHIEVEMENT_URL&access_token=APP_ACCESS_TOKEN

Putting It Together

The PHP example below builds upon the initial example that requests the publish_actions permission and publishes user scores and achievements:

<?php
  $app_id = 'YOUR_APP_ID';
  $app_secret = 'YOUR_APP_SECRET';
  $canvas_page_url = 'YOUR_CANVAS_PAGE_URL';

  // The Achievement URL
  $achievement = 'YOUR_ACHIEVEMENT_URL';
  $achievement_display_order = 1;

  // The Score
  $score = 'USER_SCORE';

  // Authenticate the user
  session_start();
  if(isset($_REQUEST["code"])) {
     $code = $_REQUEST["code"];
  }

  if(empty($code) && !isset($_REQUEST['error'])) {
    $_SESSION['state'] = md5(uniqid(rand(), TRUE)); //CSRF protection
    $dialog_url = 'https://www.facebook.com/dialog/oauth?' 
      . 'client_id=' . $app_id
      . '&redirect_uri=' . urlencode($canvas_page_url)
      . '&state=' . $_SESSION['state']
      . '&scope=publish_actions';

    print('<script> top.location.href=\'' . $dialog_url . '\'</script>');
    exit;
  } else if(isset($_REQUEST['error'])) { 
    // The user did not authorize the app
    print($_REQUEST['error_description']);
    exit;
  };

  // Get the User ID
  $signed_request = parse_signed_request($_POST['signed_request'],
    $app_secret);
  $uid = $signed_request['user_id'];

  // Get an App Access Token
  $token_url = 'https://graph.facebook.com/oauth/access_token?'
    . 'client_id=' . $app_id
    . '&client_secret=' . $app_secret
    . '&grant_type=client_credentials';

  $token_response = file_get_contents($token_url);
  $params = null;
  parse_str($token_response, $params);
  $app_access_token = $params['access_token'];

  // Register an Achievement for the app
  print('Register Achievement:<br/>');
  $achievement_registration_URL = 'https://graph.facebook.com/' 
    . $app_id . '/achievements';
  $achievement_registration_result=https_post($achievement_registration_URL,
    'achievement=' . $achievement
      . '&display_order=' . $achievement_display_order
      . '&access_token=' . $app_access_token
  );
  print('<br/><br/>');

  // POST a user achievement
  print('Publish a User Achievement<br/>');
  $achievement_URL = 'https://graph.facebook.com/' . $uid . '/achievements';
  $achievement_result = https_post($achievement_URL,
    'achievement=' . $achievement
    . '&access_token=' . $app_access_token
  );
  print('<br/><br/>');

  // POST a user score
  print('Publish a User Score<br/>');
  $score_URL = 'https://graph.facebook.com/' . $uid . '/scores';
  $score_result = https_post($score_URL,
    'score=' . $score
    . '&access_token=' . $app_access_token
  );
  print('<br/><br/>');

  function https_post($uri, $postdata) {
    $ch = curl_init($uri);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
    $result = curl_exec($ch);
    curl_close($ch);

    return $result;
  }

  function parse_signed_request($signed_request, $secret) {
    list($encoded_sig, $payload) = explode('.', $signed_request, 2); 

    // decode the data
    $sig = base64_url_decode($encoded_sig);
    $data = json_decode(base64_url_decode($payload), true);

    if (strtoupper($data['algorithm']) !== 'HMAC-SHA256') {
      error_log('Unknown algorithm. Expected HMAC-SHA256');
      return null;
    }

    // check sig
    $expected_sig = hash_hmac('sha256', $payload, $secret, $raw = true);
    if ($sig !== $expected_sig) {
      error_log('Bad Signed JSON signature!');
      return null;
    }

    return $data;
  }

  function base64_url_decode($input) {
    return base64_decode(strtr($input, '-_', '+/'));
  }

?>

For more information, please read the Graph API documentation for achievements and scores.

We look forward to your feedback and questions in the comments below.