obi-wan 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+