13

I am wanting to implement Facebook Credits in my Facebook app. Does anyone know of a version of the Facebook Credits sample app available in Ruby on Rails? Has anybody made a gem for this yet? If I find one I'll link below...

Richard Jordan
  • 8,066
  • 3
  • 39
  • 45
  • 2
    Okay so I have my Ruby On Rails version working, so I'll post a copy of it in here shortly if anyone's interested. Then maybe I'll look to gemify it, if that seems to make sense. – Richard Jordan Feb 04 '11 at 00:33
  • 1
    Hey Richard, did you ever post a copy of your working Rails Facebook credits app? I'm interested in looking it over. Thanks! – Tim Feb 12 '11 at 19:00
  • No Tim, sorry, completely forgot. I'll get to it this week. If you don't see it here shortly just message me directly. – Richard Jordan Mar 28 '11 at 02:00

1 Answers1

14

Here is my code taking the FB example and RORing it:

require 'facebook_signed_request.rb'

class FacebookCreditsController < ApplicationController

skip_before_filter :verify_authenticity_token
layout nil

  def index
    facebook_request = FacebookSignedRequest.new(request.params['signed_request'])
    data = { "content" => {} }
    # handle invalid request
    if !facebook_request.valid?
      return
    end

    signed_request = facebook_request.signed_request

    # handle invalid signed request
    if signed_request.nil?
      return
    end

    method = request.params['method']

    order_id = request.params['order_id']

    # second state response from facebook
    if method == 'payments_status_update'
      status = request.params['status']

      if status == 'placed'
        next_state = 'settled'
        data['content']['status'] = next_state
      elsif status == 'settled'
        redirect_to '/lots'
        return
      end

      # compose returning data array_change_key_case
      data['content']['order_id'] = order_id

    # first stage response from facebook
    elsif method == 'payments_get_items'

      order_info = request.params['order_info']

      item = JSON.parse(order_info)
      item['price'] = item['price'].to_i

      # for url fields, if not prefixed by http://, prefix them
      url_key = [ 'product_url', 'image_url' ]
      url_key.each do |key|
        if item[key][0..6] != 'http://'
          item[key] = "http://#{item[key]}"
        end
      end

      # if payload['test_mode']
      if request.params['test_mode']
        update_keys = ['title', 'description']
        update_keys.each do |key|
            item[key] = '[Test Mode] ' + item[key]
        end
      end

      data['content'] = [item]  
    end

    data['method'] = method

    render :json => data
  end

end

Then in addition to this there is:

require 'base64'
require 'json'
require 'openssl'

class FacebookSignedRequest

  attr_reader :signed_request

  def initialize(signed_request)
    @signed_request = signed_request
  end

  def base64_url_decode str
    encoded_str = str.gsub('-','+').gsub('_','/')
    encoded_str += '=' while !(encoded_str.size % 4).zero?
    Base64.decode64(encoded_str)
  end

  def valid?
    # TODO: move it to some configuration
    secret = " << my secret is here >>"

    # decode data
    encoded_sig, payload = signed_request.split('.')
    sig = base64_url_decode(encoded_sig).unpack("H*")[0]
    data = JSON.parse base64_url_decode(payload)

    if data['algorithm'].to_s.upcase != 'HMAC-SHA256'
    # Rails.logger.error 'Unknown algorithm. Expected HMAC-SHA256'
      return false
    end

    #check sig
    expected_sig = OpenSSL::HMAC.hexdigest('sha256', secret, payload)
    if expected_sig != sig
    # Rails.logger.error 'Bad Signed JSON signature!'
      return false
    end

    data
  end

I don't know if this helps anyone else but it's all working for me. Sorry for taking so long to remember to come back and post my working code...

View related code as requested:

#view
<%= javascript_include_tag "premium_signup" %>
<script type="text/javascript">
    $(document).ready(function() {
        $('#premium_signup_button').click(function() {
            signupAsPremiumMember('Premium Membership', 'Create unlimited auctions with no extra charge at all for 1 year.', "1", '', '');
        });
    });
</script>
...
<button id="premium_signup_button">Signup as a premium member</button>

#premium_signup.js
function signupAsPremiumMember(title, desc, price, imageURL, productURL) {
var order_info = {
    "title": title,
    "description": desc,
    "price": price,
    "image_url": imageURL,
    "product_url": productURL
};

var payload = {
    "method": 'pay',
    "order_info": order_info,
    "purchase_type": 'item'
};

console.log(FB.ui(payload, facebookPurchaseCompletionCallback));
}

function facebookPurchaseCompletionCallback(data) {
    if (data['order_id']) {
        console.log(data['order_id']);
    }
    else if (data['error_code']) {
        console.log(data['error_code']);
    }
    else {
        console.log("failed");
    }
}

#in the layout
#in head
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="http://connect.facebook.net/en_US/all.js"></script>
<%= javascript_include_tag 'application' %>
#in body
  <div id="fb-root"></div>
    <script>
      FB.init({
        appId  : '############',
        status : true, // check login status
        cookie : true, // enable cookies to allow the server to access the session
        xfbml  : true  // parse XFBML
      });

Now all of this is coming from a guy who sat down and learned to program in Ruby On Rails with as little supporting Javascript knowledge as I can get away with, in about 12 weeks last year with a few more weeks thrown in this year for good measure. I say that by way of caution - the code I've posted might be garbage... but it works :-)

And if anyone actually finds any of this useful a VOTE UP on the answer would be appreciated - just sayin' :-)

Richard Jordan
  • 8,066
  • 3
  • 39
  • 45
  • hai.. I like to use facebook credits api for my rails 3 application. Can share the code of the views as well. So that I can understand how flow begins. – Amal Kumar S Jul 15 '11 at 13:31
  • Hi - I'll be happy to share any of it :-) ...I'll have to dig out what I did in the views (not working on this application at the moment) so please be patient and I'll post... alternatively feel free to message me directly on here and chase me for the code :-) ...R – Richard Jordan Jul 19 '11 at 19:59
  • sure... waiting for your code. Thanks in advance for your help – Amal Kumar S Jul 19 '11 at 20:15
  • done... also you probably don't need openssl any more because with Rails 3.1 you can get ssl baked in :-) ...oh and if these answers are helpful to you Amal please vote the answer up - it means something to me to get an up arrow click :-) – Richard Jordan Jul 26 '11 at 22:02
  • Hey, I'm at the point of the elsif status == 'settled' and the redirect to '/lots', which sends "order_details" parameters to a #create method (post). After it creates a new @lots object, it redirects ANOTHER post to the lots/:ID path. Did u run into this problem and if so How did you solve this? – JZ. Aug 19 '11 at 01:24
  • Hi... sorry for not responding sooner - I hadn't noticed your comment... I don't have this code live anywhere right now, as I built a prototype of something which I decided not to go live with. I am going to build the whole thing from scratch again (hopefully better and worthy of launch) this weekend at the TechCrunch Disrupt hackathon, so I'll almost certainly see the problem you mention there and if/when I do I'll ping you my answer. – Richard Jordan Sep 07 '11 at 23:18