pyramid_facebook

Overview

A flexible Pyramid extension that provides default routes and views for Facebook canvas application.

Installation

Install using pip, e.g. (within a virtualenv):

$ pip install pyramid_facebook

Setup

  1. Once pyramid_facebook is installed, you can use the config.include mechanism to include it into your Pyramid project’s configuration. In your Pyramid project’s __init__.py:

    config = Configurator(...)
    config.include('pyramid_facebook')
    

    If you want to use only some features, you can include selected sub-modules:

    config.include('pyramid_facebook.opengraph')
    config.include('pyramid_facebook.payments')
    
  2. Create a Facebook application on https://developers.facebook.com/apps

  3. pyramid_facebook obtains Facebook application information from the **settings dictionary passed to the Configurator. It assumes that you’ve placed some of your Facebook application configuration parameters prefixed with facebook. in your Pyramid application’s .ini file:

    [app:myapp]
    facebook.app_id = 123456789
    facebook.secret_key = 5fbf6252b38eec5d7f8a6962c8a00556
    facebook.namespace = myfacebookapp
    facebook.scope = user_events
    

Canvas page

  1. In the app settings on https://developers.facebook.com/apps, set canvas URL to point to your server. For development, it is handy to run a lightweight server like waitress and create a Facebook app pointing to localhost:

    http://127.0.0.1:6543/{facebook application namespace}/
    
  2. Define your Facebook canvas view:

    from pyramid_facebook.canvas import facebook_canvas
    
    @facebook_canvas()
    def canvas(context, request):
        # 1 canvas is available only to users who grant application all
        #   permissions defined in setting['facebook.scope'].
        # 2 context.facebook_data dict contains signed_request content.
        #   e.g. context.facebook_data["user_id"]
        return Response('Hello Facebok World')
    

    You will probably want to use a template to return HTML. facebook_canvas supports the same arguments as pyramid.views.view_config.

  3. Register event handlers on Oauth accept or deny by subscribing to OauthAccept and OauthDeny:

    from pyramid.events import subscriber
    
    from pyramid_facebook.events import OauthAccept, OauthDeny
    
    
    @subscriber(OauthAccept)
    def user_accept(context, request):
        # save user info in app database
        pass
    
    @subscriber(OauthDeny)
    def user_deny(context, request):
        # allow guest usage, or display error message
        pass
    
  4. Visit your app on http://apps.facebook.com/[facebook app namespace]!

Defining OpenGraph objects

Many Facebook features make use of OpenGraph objects, which are HTML pages with special metadata. These objects are rendered as rich posts and pop-ups on users’ timelines.

Other sub-modules of pyramid_facebook use URL dispatch to register routes and views, but this module uses traversal, which proves simple and flexible to use.

pyramid_facebook.opengraph defines a new configuration directive when included, config.add_opengraph_collection, which you can use to register a class representing a collection of OpenGraph objects, for example your application currencies:

def includeme(config):
    namespace = config.registry.settings['facebook.namespace']
    config.add_opengraph_collection(CurrencyCollection,
                                    '%s/store/currencies' % namespace)


class CurrencyCollection(object):

    __name__ = None
    __parent__ = None

    def __init__(self, request):
        self.children = {
            'rainbows': RainbowsCurrency(self),
        }

    def __getitem__(self, key):
        return self.children[key]

This example shows the minimum interface for an OpenGraph collection: __name__ and __parent__ set to None (needed for URL generation), __init__ that accepts a request object, and __getitem__ called when traversing the collection to a child resource. If the objects returned from __getitem__ implement the interface defined by pyramid_facebook.opengraph.IResource, a generic view and template will be automatically configured for GET requests on that resource:

from zope.interface import implementer
from pyramid_facebook import opengraph

@implementer(opengraph.IResource)
class RainbowsCurrency(object):

    def __init__(self, parent):
        self.__name__ = 'rainbows'
        self.__parent__ = parent

        ## extra OpenGraph namespaces
        self.prefixes = {
            'product': 'http://ogp.me/ns/product#',
        }

        ## the pyramid_facebook view will add fb:app_id and og:url
        self.properties = {
            'og:type': 'og:product',
            'og:title': 'Rainbows',
            'og:description': 'Use Rainbows to play more',

            ## use a list to generate multiple values:
            #    <meta name="product:price:amount" value="0.10">
            #    <meta name="product:price:currency" value="CAD">
            #    <meta name="product:price:amount" value="0.15">
            #    <meta name="product:price:currency" value="USD">
            'product:price': [
                {'amount': '0.10', 'currency': 'CAD'},
                {'amount': '0.15', 'currency': 'USD'},
            ],
        }

The path of this example OpenGraph object will be /{namespace}/opengraph/store/currencies/rainbows.

If there is a query string in the OpenGraph object URI, it will be preserved in the og:url property. This gives flexibility similar to the order_info field used with the old credits system, and can be simpler to implement than dynamic pricing.

Enabling payments

Payments are implemented by a combination of OpenGraph objects defining the currencies and virtual goods you wish to sell, client-side code used as callback to the FB.ui call, and a server-side view to verify and process payments.

The section above provides an example of an OpenGraph object for a currency definition; you can also create currency packages (similar to virtual goods in the old credits system) or in-game items (freemium goods that were not integrated in the credits system).

On the client side, buying currency can invoke code like this

var params = {method: 'pay',
              action: 'purchaseitem',
              product: rainbows_opengraph_url,
              quantity_min: 10}

FB.ui(params, function(response) {
    $.ajax({
        url: '/{namespace}/orders/' + response.payment_id,
        type: 'PUT',
        data: response,
        success: function(response2) {
            // refresh balances in game, etc
        },
        error: function(response2) {
            // tell the user something went wrong and they
            // can dispute the order to get refunded
        }
    });
}

The view defined by pyramid_facebook.payments for the PUT /{namespace}/orders/{order_id} request will parse the Facebook signed request passed by the jQuery call, get information about the payment using the Graph API, and send a pyramid_facebook.events.OrderCreated event, which has request and payment (dict) attributes.

Facebook documentation explains how to use real-time updates instead of or in addition to a blocking callback to store orders and make sure the user always gets what they paid for; a view to receive these updates and send an OrderCreated event will be added in a future release.

Credits-based payments

To get Facebook credits running, set credits callback in your Facebook application settings to point to http://yourserver.com/[facebook app namespace]/credits

  1. Define your payments_get_items method using the facebook_payments_get_items decorator.
  2. Subscribe to payments update events:
    1. DisputedOrder
    2. RefundedOrder
    3. PlacedItemOrder
    4. EarnedCurrencyOrder

Note that credits are deprecated and slated for removal in September 2013.

Real-Time Update

For detailed info, refer to facebook documentation.

GET /namespace/real-time/subscriptions?access_token=access_token

Get application real-time subscriptions list.

namespace:Application’s namespace.

Required Parameter:

access_token:Access token of a user with administrator role on facebook application.
POST /namespace/real-time/subscriptions

Create or update subscriptions for the application.

namespace:Application’s namespace.

Required Parameters:

access_token:Access token of a user with administrator role on facebook application.
object:The type of the object you want to receive updates about which should be one of user, permissions, page, errors, payment_subscriptions, payments.
fields:A comma-separated list the properties of the object type that you would like to be updated about changes to. For example, to subscribe to changes to users’ hometown or friends, you would specify “hometown,friends” as the value.
DELETE /namespace/real-time/subscriptions

Delete a subscription for the application.

namespace:Application’s namespace.

Required Parameters:

access_token:Access token of a user with administrator role on facebook application.
object:The type of the object you want to stop receiving updates about.

Under The Hood

Decorators

class pyramid_facebook.canvas.facebook_canvas(**kwargs)

Decorator that registers a view for the facebook_canvas route with the view_canvas permission.

Accepts same arguments as view_config:

@facebook_canvas(renderer='canvas.mako')
def canvas(context, request):
    return {
        'title': 'A great Facebook Game'
        }
class pyramid_facebook.credits.facebook_payments_get_items

Decorator to register the function to process facebook credits payments_get_items.

Decorated function receives 2 positional parameters:

  • context: The FacebookCreditsContext the request is associated with. context.facebook_data["user"] gives information about user’s locale which would permit to return different languages.
  • request: The request itself.

It is possible to access order_info via context.order_info:

Decorated function must return a dictionary structured as:

{
    # Required:
    "title":       "100 diamonds",
    "description": "100 shiny diamonds!",
    "price":       1000,
    "image_url":   "http://hadriendavid.com/images/100dimonds.png",

    # Optional (according to facebook doc):
    "item_id": "123",
    "data":    "whatever"
}

Example:

@facebook_payments_get_items()
def get_item(context, request):
    ...
    return {
        "title": a_title,
        "description": a_description,
        "price": a_price_in_facebook_credits,
        "image_url": an_image_url
        }

Contexts

class pyramid_facebook.security.SignedRequestContext(request)

Security context for facebook signed request routes.

Attributes: facebook_data is information decoded from the signed request; custom_data can be set by authentication policies that build on top of pyramid-facebook to use a central database for users, for example.

The properties user and user_country are shortcuts for fields in facebook_data

Views

Events

class pyramid_facebook.events.OauthAccept(context, request)

Event sent when a user accepts app authentication.

class pyramid_facebook.events.OauthDeny(context, request)

Event sent when a user denies app authentication.

class pyramid_facebook.events.OrderCreated(request, payment)

An order for a local currency payment was verified.

class pyramid_facebook.events.OrderCreationError(request, payment)

An exception happened when OrderCreated was sent.

class pyramid_facebook.events.DisputedOrder(context, request)

Event sent when a user disputes an order.

class pyramid_facebook.events.RefundedOrder(context, request)

Event sent when a user got refunded for an order.

class pyramid_facebook.events.PlacedItemOrder(context, request)

Event sent when a user placed an item order.

class pyramid_facebook.events.EarnedCurrencyOrder(context, request)

Event sent when a user placed an currency order.