3

I would like to use the purchase token from the in app purchases in an android app to validate it to the google server on my own server.

With the following code I can validate a token, but I have to authenticate myself with my OAuth credentials every time:

class GooglePlayVerification
  require 'google/api_client'

  # Refer:
  # https://code.google.com/p/google-api-ruby-client/issues/detail?id=72
  # and
  # http://jonathanotto.com/blog/google_oauth2_api_quick_tutorial.html
  # and
  # http://milancermak.wordpress.com/2012/08/24/server-side-verification-of-google-play-subsc/
  GOOGLE_KEY = 'xxx.apps.googleusercontent.com'
  GOOGLE_SECRET = 'xxxx'
  APP_NAME = 'xx.xx.xx'
  SCOPE = "https://www.googleapis.com/auth/androidpublisher"

  def self.token
    @@token ||= begin
      require 'oauth2'
      raise "Missing client_id variable" if GOOGLE_KEY.to_s.empty?
      raise "Missing client_secret variable" if GOOGLE_SECRET.to_s.empty?
      raise "Missing scope variable" if SCOPE.to_s.empty?

      redirect_uri = 'https://localhost/oauth2callback'

      auth_client_obj = OAuth2::Client.new(GOOGLE_KEY, GOOGLE_SECRET, {:site => 'https://accounts.google.com', :authorize_url => "/o/oauth2/auth", :token_url => "/o/oauth2/token"})

      puts "1) Paste this URL into your browser where you are logged in to the relevant Google account\n\n"
      puts auth_client_obj.auth_code.authorize_url(:scope => SCOPE, :access_type => "offline", :redirect_uri => redirect_uri, :approval_prompt => 'force')

      puts "\n\n\n2) Accept the authorization request from Google in your browser:"

      puts "\n\n\n3) Google will redirect you to localhost, but just copy the code parameter out of the URL they redirect you to, paste it here and hit enter:\n"
      code = gets.chomp.strip

      access_token_obj = auth_client_obj.auth_code.get_token(code, {:redirect_uri => redirect_uri, :token_method => :post})
      puts "Result: #{access_token_obj.inspect}\n\n"
      puts "Token is: #{access_token_obj.token}"
      puts "Refresh token is: #{access_token_obj.refresh_token}"

      {
          :access_token => access_token_obj.token,
          :refresh_token => access_token_obj.refresh_token,
          :expires_in => access_token_obj.expires_in,
          :expires_at => access_token_obj.expires_at
      }
    end
  end

  def self.refresh_token
    refresh_client_obj = OAuth2::Client.new(GOOGLE_KEY, GOOGLE_SECRET, {:site => 'https://accounts.google.com', :authorize_url => '/o/oauth2/auth', :token_url => '/o/oauth2/token'})
    refresh_access_token_obj = OAuth2::AccessToken.new(refresh_client_obj, token[:access_token], {refresh_token: token[:refresh_token]})
    refresh_access_token_obj.refresh!
    puts "refresh token: #{refresh_access_token_obj.inspect}"
    @@token = {
        :access_token => refresh_access_token_obj.token,
        :refresh_token => refresh_access_token_obj.refresh_token,
        :expires_in => refresh_access_token_obj.expires_in,
        :expires_at => refresh_access_token_obj.expires_at
    }
  end


  # ie. https://developers.google.com/android-publisher/v1/
  # eg.
  # @subscription_id com.stocklight.stocklight.standardsubscription
  # @purchase_token  xxx
  def self.verify_subscription(subscription_id, purchase_token)
    response = RestClient.get "https://www.googleapis.com/androidpublisher/v1.1/applications/#{APP_NAME}/inapp/#{subscription_id}/purchases/#{purchase_token}?access_token=#{token[:access_token]}"
    puts "Respnse \n #{response.inspect}"
    puts response.code == 200
    puts JSON.parse(response)

    return response.code == 200 && JSON.parse(response)['kind'] =='androidpublisher#inappPurchase'
  rescue
    return false
  end

end

Has anyone an idea how to authenticate a server without such things like OAuth on the server? Is there another authentification possibility?

Thanks!

Sebastian
  • 1,873
  • 4
  • 25
  • 57
  • Ok I know now, that this might not be the best strategy to validate a purchase. Has anyone an idea how to validate a purchase using the private key? I get the response from the consume call: `{"purchaseToken":"xx.yy-xx-aa--bb-aa","packageName":"de.aa.app","signature":"xxxx","purchaseState":0,"orderId":"xxx.xxxx","purchaseTime":xxxx,"productId":"product_test"}` How can I validate this request with the public key from the server? Thanks for your help! – Sebastian Aug 15 '13 at 16:47
  • Similar question: http://stackoverflow.com/questions/5971031/how-do-i-verify-android-in-app-billing-with-a-server-with-ruby – John Naegle Nov 27 '13 at 15:37

1 Answers1

1

Here is my ruby code:

def self.verify_subscription(subscription_id, transaction_id)
    json = JSON.parse(transaction_id)
    order = ["orderId", "packageName", "productId", "purchaseTime", "purchaseState", "purchaseToken"]
    signature = json["signature"]
    data = {}
    order.each do |o|
      data[o] = json[o]
    end
    key = OpenSSL::PKey::RSA.new(Base64.decode64(GOOGLE_PUBLIC_KEY))
    verified = key.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(signature), data.to_json)
    verified
end
Sebastian
  • 1,873
  • 4
  • 25
  • 57