obi-wan 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'nokogiri', '~> 1.4.4'
6
+ gem "httpclient", '~> 2.2.1'
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,47 @@
1
+ require 'obi-wan'
2
+ require 'securerandom'
3
+
4
+ include ObiWan
5
+ include ObiWan::XmlClient::Functions
6
+
7
+ # setup
8
+ client = XmlClient.new({
9
+ :base_url => 'https://aol.xbus.qat.ps.aol.com/biz/api',
10
+ :dev_id => 'pb19dgbLSGltGDiW',
11
+ :cipherdata => 'hello',
12
+ :hexresult => 'e1c9e47be92b358510029b76aea4c5e9',
13
+ :sg => 'patchselfserviceads'
14
+ })
15
+
16
+ # tg is static for this example.
17
+ tg = 1
18
+
19
+
20
+ client.create_instrument(
21
+ tg,
22
+ Type::InstrumentInfo.new( :email => 'spencer@corp.aol.com', :nick_name => 'SP', :card_on_file_flag => true, :default_flag => false ),
23
+ Type::BillingAddress.new( :street1 => '1201 dean st', :city => 'nyc', :state => 'ny', :zip => 11206, :country => 'US'),
24
+ Type::Instrument.new( :account_number => 4444424444444440, :expiry_date => '2012-12-31T09:46:25.705Z', :last_four_digits => 5674, :payment_type => 'VISA_DEBIT' ),
25
+ Type::Name.new(:first_name => 'bob', :last_name => 'hope', :middle_name => 'robert', :honorific => 'mr'),
26
+ Type::Phone.new( :type => 'HOME', :number => 7032641272, :area_code => 703, :country_code => 001 ) )
27
+
28
+ payment_id = xml_to_hash( client.list_instruments(tg, "DETAIL") )[:response][:data][:listInstrumentsResponse][:result][:instruments][:Instrument][:id]
29
+
30
+ # I LOVE this offer!
31
+ 1.upto(1) do
32
+ client.register_offer_subscription(
33
+ tg,
34
+ Type::RegisterOfferSubscription.new( :offer_id => 1003421, :event_source => rand(), :payment_instrument_id => payment_id, :contact_email => 'pants@example.com' )
35
+ )
36
+ end
37
+
38
+ sub = client.list_subscriptions(tg, nil, false)
39
+ offer_sub_ids = offer_subscription_id(sub)
40
+
41
+ # i've been scammed! refund me!
42
+ responses = []
43
+ offer_sub_ids.each do |o|
44
+ puts "Cancelling sub.. #{o}"
45
+ resp = client.cancel_offer(tg, o, 'CANCEL_OFFER_SUBSCRIPTION')
46
+ puts "Transaction failed: #{resp}" unless success? resp
47
+ end
@@ -0,0 +1,57 @@
1
+ require 'obi-wan'
2
+ require 'securerandom'
3
+
4
+ include ObiWan
5
+ include ObiWan::XmlClient::Functions
6
+
7
+ # setup
8
+ client = XmlClient.new({
9
+ :debug => STDERR,
10
+ :base_url => 'https://aol.xbus.qat.ps.aol.com/biz/api',
11
+ :dev_id => 'pb19dgbLSGltGDiW',
12
+ :cipherdata => 'hello',
13
+ :hexresult => 'e1c9e47be92b358510029b76aea4c5e9',
14
+ :sg => 'patchselfserviceads'
15
+ })
16
+
17
+ # let's go asynchonous for only cancel_offer for some mass cancellation fun
18
+ client.set_asynchronous(:cancel_offer)
19
+
20
+ # tg is static for this example.
21
+ tg = 1
22
+
23
+
24
+ client.create_instrument(
25
+ tg,
26
+ Type::InstrumentInfo.new( :email => 'spencer@corp.aol.com', :nick_name => 'SP', :card_on_file_flag => true, :default_flag => false ),
27
+ Type::BillingAddress.new( :street1 => '1201 dean st', :city => 'nyc', :state => 'ny', :zip => 11206, :country => 'US'),
28
+ Type::Instrument.new( :account_number => 4444424444444440, :expiry_date => '2012-12-31T09:46:25.705Z', :last_four_digits => 5674, :payment_type => 'VISA_DEBIT' ),
29
+ Type::Name.new(:first_name => 'bob', :last_name => 'hope', :middle_name => 'robert', :honorific => 'mr'),
30
+ Type::Phone.new( :type => 'HOME', :number => 7032641272, :area_code => 703, :country_code => 001 ) )
31
+
32
+ payment_id = xml_to_hash( client.list_instruments(tg, "DETAIL") )[:response][:data][:listInstrumentsResponse][:result][:instruments][:Instrument][:id]
33
+
34
+ # I LOVE this offer!
35
+ 1.upto(20) do
36
+ client.register_offer_subscription(
37
+ tg,
38
+ Type::RegisterOfferSubscription.new( :offer_id => 1003421, :event_source => rand(), :payment_instrument_id => payment_id, :contact_email => 'pants@example.com' )
39
+ )
40
+ end
41
+
42
+ sub = client.list_subscriptions(tg, nil, false)
43
+ offer_sub_ids = offer_subscription_id(sub)
44
+
45
+ # i've been scammed! refund me!
46
+ # since cancel is now async, do a time on ruby to see how much faster this goes
47
+ responses = []
48
+ offer_sub_ids.each do |o|
49
+ puts "Cancelling sub.. #{o}"
50
+ responses << client.cancel_offer(tg, o, 'CANCEL_OFFER_SUBSCRIPTION')
51
+ end
52
+
53
+ responses.each do |r|
54
+ r.join
55
+ resp = resp_to_xml(r.pop)
56
+ puts "Transaction failed: #{resp}" unless success? resp
57
+ end
@@ -0,0 +1,173 @@
1
+ require 'nokogiri'
2
+
3
+ module ObiWan
4
+ class XmlClient
5
+ module Functions
6
+ def self.included(base)
7
+ base.module_eval { extend(Functions) }
8
+ end
9
+
10
+ def xml_to_hash(node)
11
+
12
+ # this doesn't cover <a>bcdefg<h>pants</h></a>
13
+ # luckily, obi doesn't do insane things like this
14
+
15
+ if node.elements.empty?
16
+ result = node.text
17
+ else
18
+ result = {}
19
+ node.elements.each do |c|
20
+ key = c.name.to_sym
21
+ val = xml_to_hash(c)
22
+
23
+ if result[key].nil?
24
+ result[key] = val
25
+ elsif result[key].is_a? Array
26
+ result[key] << val
27
+ else
28
+ result[key] = [ result[key], val ]
29
+ end
30
+ end
31
+ end
32
+ result
33
+ end
34
+
35
+ def resp_to_xml(resp)
36
+ Nokogiri::XML(resp.body)
37
+ end
38
+
39
+ def success?(response)
40
+ result = response.search("statusCode")
41
+ !result.nil? and result.text.to_i / 100 === 2
42
+ end
43
+
44
+ def offer_subscription_id(response)
45
+ puts response.xpath("/")
46
+ response.xpath("//offerSubscriptionId/value")
47
+ end
48
+ end
49
+
50
+ module CancelOffer
51
+ def self.obi_request(offer_subscription_id, type, reason_text=nil, code=nil, language_code=nil)
52
+ builder = Nokogiri::XML::Builder.new do
53
+ request('xmlns' => 'http://biz.aol.com/schema/2006-12-18') {
54
+ cancelOffer {
55
+ cancelOfferDetail {
56
+ offerSubscriptionId offer_subscription_id
57
+ reason {
58
+ text reason_text
59
+ languageCode code
60
+ }
61
+ actionType type
62
+ }
63
+ }
64
+ }
65
+ end.doc.to_xml
66
+ end
67
+ end
68
+
69
+ module ListSubscriptions
70
+ def self.obi_request(product_class_name, active_only)
71
+ builder = Nokogiri::XML::Builder.new do
72
+ request('xmlns' => 'http://biz.aol.com/schema/2006-12-18') {
73
+ listSubscriptions {
74
+ productClassName product_class_name
75
+ active_only active_only
76
+ }
77
+ }
78
+ end.doc.to_xml
79
+ end
80
+ end
81
+
82
+ module RegisterOfferSubscription
83
+ def self.obi_request(offer_subscription)
84
+ os = offer_subscription
85
+ builder = Nokogiri::XML::Builder.new do
86
+ request('xmlns' => 'http://biz.aol.com/schema/2006-12-18') {
87
+ registerOfferSubscription {
88
+ orderRequests {
89
+ OrderRequest {
90
+ offerId os.offer_id
91
+
92
+ # event_source is a unique order id, non numeric if desired
93
+ # can be something like timestamp-userid-random()
94
+ eventSource os.event_source
95
+ paymentInstrumentId os.payment_instrument_id
96
+ contactEmail os.contact_email
97
+ promoCode os.promo_code
98
+ tosAcceptedDate os.tos_accepted_date
99
+ tosName os.tos_name
100
+ }
101
+ }
102
+ }
103
+ }
104
+ end.doc.to_xml
105
+ end
106
+ end
107
+
108
+ module ListInstruments
109
+ def self.obi_request(detail_level)
110
+ builder = Nokogiri::XML::Builder.new do
111
+ request('xmlns' => 'http://biz.aol.com/schema/2006-12-18') {
112
+ listInstruments {
113
+ paymentDetailLevel detail_level
114
+ }
115
+ }
116
+ end.doc.to_xml
117
+ end
118
+ end
119
+
120
+ module Instrument
121
+ def self.obi_request(info, billing_address, instrument, name, phone)
122
+ builder = Nokogiri::XML::Builder.new do
123
+ request('xmlns' => 'http://biz.aol.com/schema/2006-12-18') {
124
+ createInstrument {
125
+ email {
126
+ value info.email
127
+ }
128
+ instrument {
129
+ id_ info.id
130
+ nickName info.nick_name
131
+ defaultFlag info.default_flag
132
+ cardOnFileFlag info.card_on_file_flag
133
+
134
+ userInformation {
135
+ firstUserName {
136
+ firstName name.first_name
137
+ lastName name.last_name
138
+ middleName name.middle_name
139
+ honorific name.honorific
140
+ }
141
+
142
+ billingAddress {
143
+ street1 billing_address.street1
144
+ street2 billing_address.street2
145
+ city billing_address.city
146
+ state billing_address.state
147
+ zip billing_address.zip
148
+ country billing_address.country
149
+ }
150
+
151
+ eveningPhone {
152
+ countryCode phone.country_code
153
+ areaCode phone.area_code
154
+ number phone.number
155
+ type phone.type
156
+ }
157
+ }
158
+ paymentInstrument {
159
+ accountNumber instrument.account_number
160
+ routingNumber instrument.routing_number
161
+ paymentType instrument.payment_type
162
+ expiryDate instrument.expiry_date
163
+ lastFourDigits instrument.last_four_digits
164
+ pinNumber instrument.pin_number
165
+ }
166
+ }
167
+ }
168
+ }
169
+ end.doc.to_xml
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,68 @@
1
+ module ObiWan
2
+ module Type
3
+ class Builder
4
+ def initialize(init={})
5
+ init.keys.each do |attr|
6
+ self.send("#{attr}=".to_sym, init[attr])
7
+ end
8
+ end
9
+ end
10
+
11
+ # some api calls have misc data. it's easier to group them as single classes
12
+
13
+ class RegisterOfferSubscription < Builder
14
+ attr_accessor :offer_id
15
+ attr_accessor :event_source
16
+ attr_accessor :payment_instrument_id
17
+ attr_accessor :contact_email
18
+ attr_accessor :discount_amount
19
+ attr_accessor :promo_code
20
+ attr_accessor :tos_accepted_date
21
+ attr_accessor :tos_name
22
+ end
23
+
24
+ class InstrumentInfo < Builder
25
+ attr_accessor :email
26
+ attr_accessor :id
27
+ attr_accessor :nick_name
28
+ attr_accessor :default_flag
29
+ attr_accessor :card_on_file_flag
30
+ end
31
+
32
+ # obi data types
33
+
34
+ class BillingAddress < Builder
35
+ attr_accessor :street1
36
+ attr_accessor :street2
37
+ attr_accessor :city
38
+ attr_accessor :state
39
+ attr_accessor :zip
40
+ attr_accessor :country
41
+ end
42
+
43
+ class Name < Builder
44
+ attr_accessor :first_name
45
+ attr_accessor :middle_name
46
+ attr_accessor :last_name
47
+ attr_accessor :honorific
48
+ end
49
+
50
+ class Phone < Builder
51
+ attr_accessor :country_code
52
+ attr_accessor :area_code
53
+ attr_accessor :number
54
+ attr_accessor :type
55
+ end
56
+
57
+ class Instrument < Builder
58
+ attr_accessor :account_number
59
+ attr_accessor :routing_number
60
+ attr_accessor :payment_type
61
+ attr_accessor :expiry_date
62
+ attr_accessor :last_four_digits
63
+ attr_accessor :pin_number
64
+ attr_accessor :authorize_email_address
65
+ attr_accessor :authorize_ip_address
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ module ObiWan
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,64 @@
1
+ require 'obi-wan/messages'
2
+ require 'httpclient'
3
+
4
+ #
5
+ # XmlClient has methods and modules registered to it. Messages are in the messages/ dir.
6
+ # The first parameter to each call must be the target id for use in the url. The rest of the
7
+ # parameters are in the def per message.
8
+ #
9
+ # If you don't like the XML response, you can peform a to_hash on it.
10
+ #
11
+ module ObiWan
12
+ class XmlClient
13
+
14
+ @@DEFAULT_CALLS = {
15
+ :register_offer_subscription => RegisterOfferSubscription,
16
+ :create_instrument => Instrument,
17
+ :edit_instrument => Instrument,
18
+ :list_instruments => ListInstruments,
19
+ :list_subscriptions => ListSubscriptions,
20
+ :cancel_offer => CancelOffer
21
+ }
22
+
23
+ def register(method, message_handler, synchronous = true)
24
+ method = method.to_sym
25
+
26
+ self.send(:define_singleton_method, method) do |tg, *args|
27
+ query = {
28
+ :devId => @config[:dev_id],
29
+ :cipherdata => @config[:cipherdata],
30
+ :hexresult => @config[:hexresult],
31
+ :tg => tg,
32
+ :sg => @config[:sg]
33
+ }
34
+
35
+ header = [['Content-Type', 'text/xml'], ['Accept','*/*'], ['User-Agent', 'obi-wan-0.0.1']]
36
+
37
+ req = HTTPClient.new
38
+ req.debug_dev = @config[:debug] if @config[:debug]
39
+
40
+ if synchronous
41
+ res = req.request(:POST, @config[:base_url], query, message_handler.obi_request(*args), header)
42
+ return resp_to_xml(res)
43
+ else
44
+ return req.request_async(:POST, @config[:base_url], query, message_handler.obi_request(*args), header)
45
+ end
46
+ end
47
+ end
48
+
49
+ def set_asynchronous(method, target = @@DEFAULT_CALLS[method])
50
+ register method, @@DEFAULT_CALLS[method], false
51
+ end
52
+
53
+ def set_synchronous(method, target = @@DEFAULT_CALLS[method])
54
+ register method, @@DEFAULT_CALLS[method], true
55
+ end
56
+
57
+ def initialize(options={})
58
+ @config = options
59
+ @@DEFAULT_CALLS.each do |k,v|
60
+ register k, v, true
61
+ end
62
+ end
63
+ end
64
+ end
data/lib/obi-wan.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'obi-wan/xml-client'
2
+ require 'obi-wan/type'
3
+
4
+ module ObiWan
5
+ end
data/obi-wan.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "obi-wan/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "obi-wan"
7
+ s.version = ObiWan::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["spencer.portee@patch.com"]
10
+ s.email = ["spencer.portee@patch.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{ruby wrapper to obi's xml api}
13
+ s.description = %q{ruby wrapper to obi's xml api}
14
+
15
+ s.rubyforge_project = "obi-wan"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: obi-wan
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - spencer.portee@patch.com
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-06-28 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: ruby wrapper to obi's xml api
18
+ email:
19
+ - spencer.portee@patch.com
20
+ executables: []
21
+
22
+ extensions: []
23
+
24
+ extra_rdoc_files: []
25
+
26
+ files:
27
+ - .gitignore
28
+ - Gemfile
29
+ - Rakefile
30
+ - example/subscription_example.rb
31
+ - example/subscription_example_async.rb
32
+ - lib/obi-wan.rb
33
+ - lib/obi-wan/messages.rb
34
+ - lib/obi-wan/type.rb
35
+ - lib/obi-wan/version.rb
36
+ - lib/obi-wan/xml-client.rb
37
+ - obi-wan.gemspec
38
+ has_rdoc: true
39
+ homepage: ""
40
+ licenses: []
41
+
42
+ post_install_message:
43
+ rdoc_options: []
44
+
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ requirements: []
60
+
61
+ rubyforge_project: obi-wan
62
+ rubygems_version: 1.6.2
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: ruby wrapper to obi's xml api
66
+ test_files: []
67
+