Skip to content

Instantly share code, notes, and snippets.

@t2-support-gists
Created May 3, 2012 20:59
Show Gist options
  • Select an option

  • Save t2-support-gists/2589447 to your computer and use it in GitHub Desktop.

Select an option

Save t2-support-gists/2589447 to your computer and use it in GitHub Desktop.
Payment Ruby app2
AT&T API Samples - Payment app 2
----------------------------------
This file describes how to set up, configure and run the ruby versions of the AT&T HTML5 Program sample applications.
It covers all steps required to register the application on DevConnect and, based on the generated API keys and secrets,
create and run one's own full-fledged sample applications.
1. Configuration
2. Installation
3. Parameters
4. Running the application
1. Configuration
Configuration consists of a few steps necessary to get an application registered on DevConnect with the proper services and
endpoints, depending on the type of client-side application (autonomous/non-autonomous).
To register an application, go to https://devconnect-api.att.com/ and login with your valid username and password.
Next, choose "My Page" from the bar at the top of the page and click the "Setup a New Application" button.
Fill in the form, in particular all fields marked as "required".
Be careful while filling in the "OAuth Redirect URL" field. It should contain the URL that the oAuth provider will redirect
users to when he/she successfully authenticates and authorizes your application.
Having your application registered, you will get back an important pair of data: an API key and Secret key. They are
necessary to get your applications working with the AT&T HTML5 APIs. See 'Adjusting parameters' below to learn how to use
these keys.
Initially your newly registered application is restricted to the "Sandbox" environment only. To move it to production,
you may promote it by clicking the "Promote to production" button. Notice that you will get a different API key and secret,
so these values in your application should be adjusted accordingly.
Depending on the kind of authentication used, an application may be based on either the Autonomous Client or the Web-Server
Client OAuth flow (see https://devconnect-api.att.com/docs/oauth20/autonomous-client-application-oauth-flow or
https://devconnect-api.att.com/docs/oauth20/web-server-client-application-oauth-flow respectively).
2. Installation
** Requirements
To run the examples you need ruby 1.8+ and a few gems that the applications are heavily based on:
- sinatra (http://www.sinatrarb.com/)
used to construct a simple web application and manage URLs within.
- sinatra-contrib
additional set of useful helpers, including ones used to read settings from external files.
- also make sure you have rest-client and json gems installed
To install these gems open up a terminal window and invoke:
gem install sinatra sinatra-contrib rest-client json
Having them installed, you are almost ready to run the sample applications.
** Setting up on a different port number
In case multiple applications need to be run at the same time, you need to consider using different
port numbers.
By default sinatra uses port number 4567 and only one running application may use this port. In case
you want to run one more sinatra-based application, you need to change its port number, eg. to 4568. This way you
may have each application running on a unique port.
3. Parameters
Each application contains a config.yml file. It holds configurable parameters
described in an easy to read YAML format which you are free to adjust to your needs. Following are short descriptions
of parameters used by this application:
1) FQDN : https://api.att.com
2) port : port number that application binds to (default: 4567)
3) api_key : set the value as per your registered appliaction 'API key' field value
4) secret_key : set the value as per your registered appliaction 'Secret key' field value
5) tokens_file : file in which OAuth tokens are stored
4. Running the application
To run the application, first make sure you have Notary up and running. From Notary/Ruby/app1
directory, type in terminal window:
ruby ./notary.rb
Next, change directory back to the payment application and run it:
ruby ./app2.rb
or
./app2.rb
Your application becomes available in a web browser, so you may visit: http://localhost:4567/ to see it working.
You may interrupt the application at any time by pressing ctrl-C.
#!/usr/bin/ruby
require 'rubygems'
require 'json'
require 'rest_client'
require 'sinatra'
require 'sinatra/config_file'
require File.join(File.dirname(__FILE__), 'common.rb')
enable :sessions
config_file 'config.yml'
set :port, settings.port
SCOPE = 'PAYMENT'
# setup filter fired before reaching our urls
# this is to ensure we are o-authenticated before actual action
# autonomous version
['/','/createSubscription','/getSubscriptionStatus','/getSubscriptionDetails','/refundSubscription','/refreshNotifications', '/callbackSubscription'].each do |path|
before path do
@subscriptions = []
@merchant_subscription_id ||= session[:merchant_subscription_id]
@subscription_auth_code ||= session[:subscription_auth_code]
@consumer_id ||= session[:consumer_id]
read_recent_items(settings.subscriptions_file).map {|item| a,b,c=item.split; @subscriptions.push({:merchant_subscription_id => a, :subscription_id => b, :consumer_id => c}) }
obtain_tokens(settings.FQDN, settings.api_key, settings.secret_key, SCOPE, settings.tokens_file)
end
end
get '/' do
erb :app2
end
post '/createSubscription' do
create_subscription
end
get '/callbackSubscription' do
callback_subscription
end
post '/getSubscriptionStatus' do
get_subscription_status
end
post '/getSubscriptionDetails' do
get_subscription_details
end
post '/refundSubscription' do
refund_subscription
end
post '/refreshNotifications' do
refresh_notifications
end
# URL handlers go here
def create_subscription
session[:merchant_subscription_id] = 'User' + sprintf('%03d', rand(1000)) + 'Subscription' + sprintf('%04d', rand(10000))
# prepare payload
data = {
:Amount => params[:product] == '1' ? 1.99 : 3.99,
:Category => 1,
:Channel => 'MOBILE_WEB',
:Description => 'Word game 1',
:MerchantTransactionId => session[:merchant_subscription_id],
:MerchantProductId => 'wordGame1',
:MerchantPaymentRedirectUrl => settings.subscription_redirect_url,
:MerchantSubscriptionIdList => 'sampleSubscription1',
:IsPurchaseOnNoActiveSubscription => 'false',
:SubscriptionRecurringNumber => '99999',
:SubscriptionRecurringPeriod => 'MONTHLY',
:SubscriptionRecurringPeriodAmount => 1
}
response = RestClient.post settings.notary_app_sign_url, :payload => data.to_json
from_json = JSON.parse response
redirect settings.FQDN + "/Commerce/Payment/Rest/2/Subscriptions?clientid=#{settings.api_key}&SignedPaymentDetail=#{from_json['signed_payload']}&Signature=#{from_json['signature']}"
end
def callback_subscription
@subscription_auth_code = session[:subscription_auth_code] = params['SubscriptionAuthCode']
@new_subscription = {
:merchant_subscription_id => session[:merchant_subscription_id],
:subscription_auth_code => @subscription_auth_code
}
items = @subscriptions.unshift(@new_subscription).map {|s| s[:merchant_subscription_id] + " " + (s[:subscription_id] || "") + " " + (s[:consumer_id] || "")}
write_recent_items(settings.subscriptions_file, settings.recent_subscriptions_stored, items);
rescue => e
@create_error = true
ensure
return erb :app2
end
def get_subscription_status
u = settings.FQDN + "/Commerce/Payment/Rest/2/Subscriptions/SubscriptionAuthCode/#{@subscription_auth_code}?access_token=#{@access_token}"
response = RestClient.get u
@subscription_status = JSON.parse response
@subscriptions.first[:subscription_id] = @subscription_status["SubscriptionId"]
@subscriptions.first[:consumer_id] = @subscription_status["ConsumerId"]
items = @subscriptions.map {|s| s[:merchant_subscription_id] + " " + (s[:subscription_id] || "") + " " + (s[:consumer_id] || "")}
write_recent_items(settings.subscriptions_file, settings.recent_subscriptions_stored, items);
rescue => e
@status_error = true
ensure
return erb :app2
end
def get_subscription_details
redirect '/' if params[:consumer_id].nil? || params[:consumer_id].empty?
u = settings.FQDN + "/Commerce/Payment/Rest/2/Subscriptions/sampleSubscription1/Detail/#{params[:consumer_id]}?access_token=#{@access_token}"
response = RestClient.get u
@consumer_id = session[:consumer_id] = params[:consumer_id]
@subscription_details = JSON.parse response
rescue => e
@details_error = true
ensure
return erb :app2
end
def refund_subscription
redirect '/' if params[:subscription_id].nil? || params[:subscription_id].empty?
u = settings.FQDN + "/Commerce/Payment/Rest/2/Transactions/#{params[:subscription_id]}?Action=refund&access_token=#{@access_token}"
payload = { :RefundReasonCode => 1, :RefundReasonText => 'User did not like product'}
RestClient.put u, payload.to_json, :content_type => 'application/json', :accept => 'application/json' do |response, request, code, &block|
@refund_details = JSON.parse response
end
rescue => e
@refund_error = true
ensure
return erb :app2
end
def refresh_notifications
rescue => e
@refresh_error = true
ensure
return erb :app2
end
# Tries to parse supplied address using one of known formats. Returns false on failure.
def parse_address(address)
address.strip!
if (address.match('^\d{10}$'))
elsif (m = address.match('^1(\d{10})$'))
address = m[1].to_s
elsif (m = address.match('^\+1(\d{10})$'))
address = m[1].to_s
elsif (m = address.match('^tel:(\d{10})$'))
address = m[1].to_s
elsif (address.match('^\d{3}-\d{3}-\d{4}$'))
address.gsub! '-', ''
else
return false
end
address
end
# Makes sure that valid access_token is stored in the session. Retrieves new tokens if needed.
def obtain_tokens(fqdn, client_id, client_secret, scope, tokens_file)
read_tokens(tokens_file)
if !@access_token.nil? and @access_token_expires > Time.now
return
elsif !@refresh_token.nil? and @refresh_token_expires > Time.now
url = "#{fqdn}/oauth/token?grant_type=refresh_token&client_id=" + client_id + "&client_secret=" + client_secret + "&refresh_token=" + @refresh_token
else
url = "#{fqdn}/oauth/token?grant_type=client_credentials&client_id=" + client_id + "&client_secret=" + client_secret + "&scope=" + scope
end
response = RestClient.get url
from_json = JSON.parse(response.to_str)
@access_token = from_json['access_token']
@refresh_token = from_json['refresh_token']
@access_token_expires = Time.now + (from_json['expires_in'].to_i)/1000
@refresh_token_expires = Time.now + (60*60*24)
write_tokens(tokens_file)
end
def write_tokens(tokens_file)
File.open(tokens_file, 'w+') { |f| f.puts @access_token, @access_token_expires, @refresh_token, @refresh_token_expires }
end
def read_tokens(tokens_file)
@access_token, access_expiration, @refresh_token, refresh_expiration = File.foreach(tokens_file).first(4).map! &:strip!
@access_token_expires = Time.parse access_expiration
@refresh_token_expires = Time.parse refresh_expiration
rescue
return
end
# general helper functions to read and write recently generated items
def read_recent_items file_name
items = []
File.open file_name, 'r' do |f|
while line = f.gets
items.push line
end
end
ensure
return items
end
def write_recent_items file_name, number, items
items = items.first(number-1)
File.open file_name, 'w+' do |f|
items.each do |t|
f.puts t
end
end
end
port: 4567
api_key:
secret_key:
tokens_file: tokens
subscriptions_file: subscriptions
recent_subscriptions_stored: 5
notary_app_sign_url: http://localhost:4568/signPayload
notary_app_view_payload_url: http://localhost:4568/
subscription_redirect_url: http://localhost:4567/callbackSubscription
FQDN: https://api.att.com
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment