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

Quick Run

  1. To run the example, run:

    (env)$ python example/__init__.py
    
  2. And browse on http://apps.facebook.com/pyramid_bacefook/?

Important

Facebook requires usage of https. Example uses a self signed certificate (certificate.pem) which your browser might refuse.

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}/
    

    Important

    In facebook app settings, enable sandbox mode. The two reason for this are: You do not need your application to be accessible publicly when developping. When sandbox mode is disabled, it is required to provide a secure canvas URL (https).

  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):
    config.add_opengraph_collection(CurrencyCollection,
                                    'store/currencies')


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. The route name for this collection is 'opengraph_currencycollection' (i.e. lower-cased class name). 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}/store/currencies/rainbows. Note that namespace is added by pyramid facebook.

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 an OrderReceived event, which has request and payment (dict) attributes.

Real-Time Update fullfilment

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.

To subscribe to real-time update, you can use the pfacebook-real-time command documented in Real-Time Update.

If the application has subscribed to real-time update on payments object and actions and/or disputes fields, an OrderReceived which has request and payment (dict) attributes is notified.

Important

When subscribing to real-time payment updates, OrderReceived can be sent multiple times for the same order id and status if you use both a frontend call and real-time update for payment.

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

class pyramid_facebook.real_time.ManageSubscriptions(argv)

Manage real-time subscriptions.

The easiest way to setup real time subscription is to define the facebook.rt-subscriptions setting in the ini file:

facebook.rt-subscriptions =
    user = name, friends
    payments = actions, disputes

Then run the setup command:

$ pfacebook-real-time env.ini setup

To list subscriptions:

$ pfacebook-real-time env.ini list

To update or create subscriptions without changing the config file:

$ pfacebook-real-time env.ini update user=friends,name payments=actions

To delete all subscriptions:

$ pfacebook-real-time env.ini delete

Resources added on configuration

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.OrderReceived(request, payment)

An order for a local currency payment was received

class pyramid_facebook.events.OrderProcessingError(request, payment)

An exception happened when notifying OrderReceived.

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.

Changelog

Development

2.0.2 (2014-12-17)

  • Add a test configuration in pyramid_facebook.tests which mocks facepy graph api object. To use it in functionnal tests: config.include('pyramid_facebook.tests') when configuring test app.

2.0.1 (2014-11-25)

  • Upgrade to facebook graph api v2

1.0.1 (2014-11-25)

  • Add Facebook payment event type for refunded orders.

0.6.7 (2014-08-26)

  • Remove constraint on pyramid_contextauth version requirement.
  • Add pyramid_mako as required dependency.

0.6.6 (2014-05-05)

  • Improve request property request.graph_api
  • Add request property request.fb_app_token

0.6.5 (2014-04-23)

  • 2014-04-23 - Facebook real time payment fails and logging code raises a KeyError.

0.6.4 (2014-03-26)

0.6.3 (2014-03-25)

  • Fix doc and rst file for pypi.

0.6.2 (2014-03-25)

  • Remove authentication_policy decorator in favor of config.register_authentication_policy.

0.6.1 (2014-03-24)

  • Update dependency to pyramid_contextauth >= 0.5

0.5.324

  • Add a GraphAPI utility lazily instantiated with the application token.
  • Add attribute ChangeNotification.object event.
  • Event OrderReceived is notified when receiving a real-time payment update
  • Add pyramid_facebook.tests.integration.test_payments used in pyramid_facebook.tests.functional.test_payments

Breaking Changes

  • Rename OrderCreated for OrderReceived

    • Can be sent multiple times for the same order with same or different status
  • Rename OrderCreationError for OrderProcessingError

0.4.317

  • Added pfacebook-real-time command to update real time subscriptions.
  • Breaking changes: namespace is now added by the framework in opengraph URLs.

0.2.246

  • Reusable view and template for OpenGraph objects.
  • Support for Facebook local currency payments.

This release is backward-compatible for apps that use the app currency and Facebook credits decorators. A future version will remove support for credits (Facebook will remove them) and app currencies (which can now use the generic OpenGraph view).

0.2.235

  • Packaging fix-ups.

0.2.220

  • Fix bug where permissions defined in facebook.scope setting were ignored by prompt_authorize.

0.2.217

  • Add view to redirect from GET canvas to the Facebook application page.

0.2.207

  • Move predicates from lib to predicates.
  • Add PermissionEventPredicate for filtering event subscriber with permission.

0.2.2

  • Include pyramid_contextauth for dealing with context-based authentication.

0.1.194

  • Added CanceledOrder when any payment update fail during event notification.
  • add includeme for any sub module to uniform configuration
  • facebook auth policy does not rely anymore on context for authentication.
  • add an CanvasRequested event triggered when a identified user request canvas.

0.1.127

  • In credits: Check item title not being an empty string to avoid FB failing with no explicit message. “Fail early.”
  • Fixed bug which raised configuration conflict because pyramid_facebook was not commiting config via config.commit.

0.0

  • Initial version