rm_vendor 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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YzI2MTI0ZmVkMTIzYjk2Y2IwODg5ZjA0MjhhNDJiNjNiMWI2N2JhMg==
5
+ data.tar.gz: !binary |-
6
+ YmUxYzM1YjhmOWUyZjYxNTNlOGNjNjk3ZTRhYzE0ODM2NGRjMTFkZg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ OGE5NDAxMzZjNDVlYmUzYjA1MTZiOWFlZmVhYzg4Y2IzMzM1ZjdiMTU2ODE2
10
+ MmQyNjdlMGFhMjM0ZGMzYzEyNDQ1MzVhNzY3ZTJmMzcwYzFlYTRiOTQzZjY2
11
+ NWEzN2VhYTFlNWJmMzhhZmRjNGZhNTEyOWJiOWExODlhMTlmMDA=
12
+ data.tar.gz: !binary |-
13
+ MTcyZWVkNjc3MGNkN2I1OTUzOWZjMWI5NmFmYmUxMzQ0MWYzOGI2NGNmMzU4
14
+ M2QyOTkwMjYyZjZmYTA5ZjQwNGVmMTAyN2JhM2QzNWVhYTQ2NmU2NjA2Y2U2
15
+ YTBiZDgwYmI0Y2JhYmNjZTNlOWIxMGY5OWVkMmZjNGUyMWE4YmQ=
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # Vendor
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ gem 'rm_vendor'
8
+
9
+ And then execute:
10
+
11
+ $ bundle
12
+
13
+ ## USAGE
14
+
15
+ #### Initialize.
16
+
17
+ Initialize your product with your In App Purchase ID. Vendor will check if the product exists and return it's attributes:
18
+
19
+ ```Ruby
20
+ @product = Vendor::Product.new(:id => "com.your.item.id") do |product|
21
+ p "Product exists: #{product.success}"
22
+ p "Product response: #{product.response}"
23
+ end
24
+ ```
25
+
26
+ You don't need to pass along a block though. If you're sure your product exists, then you can just do this:
27
+
28
+ ```Ruby
29
+ @product = Vendor::Product.new(:id => "com.your.item.id")
30
+ ```
31
+
32
+ #### Get product info.
33
+
34
+ After you've initialized your product, you get its information:
35
+
36
+ ```Ruby
37
+ @product.price
38
+ @product.title
39
+ @product.description
40
+ @product.bought?
41
+ ```
42
+
43
+ #### Purchase product.
44
+
45
+ To purchase a product, simply do:
46
+
47
+ ```Ruby
48
+ @product.purchase do |product|
49
+ p "Purchase successful: #{product.success}"
50
+ p "Purchase transaction: #{product.transaction}"
51
+ end
52
+ ```
53
+
54
+ #### Subscriptions.
55
+
56
+ Vendor also works with subscriptions. To initialize a subscription:
57
+
58
+ ```Ruby
59
+ @subscription = Vendor::Product.new(:id => "com.your.subscription.id", :secret => "abcdefg12345", :subscription => true) do |subscription|
60
+ p "Subscription exists: #{subscription.success}"
61
+ p "Subscription response: #{subscription.response}"
62
+ end
63
+ ```
64
+
65
+ To purchase a subscription:
66
+
67
+ ```Ruby
68
+ @subscription.purchase do |subscription|
69
+ p "Subscription successful: #{subscription.success}"
70
+ p "Subscription transaction: #{subscription.transaction}"
71
+ end
72
+ ```
73
+
74
+ To see wether product is registered as a subscription and if user is currently subscribed:
75
+
76
+ ```Ruby
77
+ @subscription.subscribed?
78
+ @subscription.subscription?
79
+ ```
80
+
81
+ ## Example App.
82
+
83
+ You can find an example app inside this gem (inside the app folder).
data/lib/Vendor.rb ADDED
@@ -0,0 +1,27 @@
1
+ #encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bubble-wrap/core'
5
+ require 'sugarcube-anonymous'
6
+ require 'sugarcube-legacy'
7
+ require 'sugarcube-numbers'
8
+ require 'sugarcube-nsuserdefaults'
9
+ require 'sugarcube-files'
10
+ require 'motion-cocoapods'
11
+
12
+ unless defined?(Motion::Project::Config)
13
+ raise "This file must be required within a RubyMotion project Rakefile."
14
+ end
15
+
16
+ require 'motion/project/template/ios'
17
+
18
+ lib_dir_path = File.dirname(File.expand_path(__FILE__))
19
+ Motion::Project::App.setup do |app|
20
+ app.files.unshift(Dir.glob(File.join(lib_dir_path, "project/**/*.rb")))
21
+
22
+ app.frameworks += ['StoreKit']
23
+
24
+ app.pods ||= Motion::Project::CocoaPods.new(app)
25
+ app.pods.pod 'CocoaSecurity', '~> 1.2.1'
26
+ app.pods.pod 'AFNetworking'
27
+ end
@@ -0,0 +1,73 @@
1
+ module Vendor
2
+ class Buy
3
+ attr_accessor :params, :block, :request_operation_queue
4
+
5
+ def initialize(params)
6
+ @params = params
7
+ SKPaymentQueue.defaultQueue.addTransactionObserver(self)
8
+ end
9
+
10
+ def close
11
+ SKPaymentQueue.defaultQueue.removeTransactionObserver(self)
12
+ end
13
+
14
+
15
+
16
+ # PUBLIC METHODS
17
+ def purchase(&block)
18
+ @block = block
19
+ SKPaymentQueue.defaultQueue.addPayment(SKPayment.paymentWithProductIdentifier(@params.id))
20
+ end
21
+
22
+ def restore(&block)
23
+ @block = block
24
+ SKPaymentQueue.defaultQueue.restoreCompletedTransactions
25
+ end
26
+
27
+
28
+
29
+ # DELEGATE METHODS
30
+ def finishTransaction(transaction, success:success)
31
+ SKPaymentQueue.defaultQueue.finishTransaction(transaction)
32
+ SKPaymentQueue.defaultQueue.removeTransactionObserver(self)
33
+
34
+ if success
35
+ Vendor::Receipt.new(transaction.transactionReceipt, @params) do |block|
36
+ result_object = BW::JSON.parse(block.object).to_object
37
+ valid_receipt = block.success && result_object.status.to_i == 0
38
+
39
+ @block.call({success: valid_receipt, transaction: transaction}.to_object) unless @block.blank?
40
+ end
41
+ else
42
+ @block.call({success: success, transaction: transaction}.to_object) unless @block.blank?
43
+ end
44
+ end
45
+
46
+ def paymentQueue(queue, updatedTransactions:transactions)
47
+ transactions.each do |transaction|
48
+ case transaction.transactionState
49
+ when SKPaymentTransactionStatePurchased
50
+ completeTransaction(transaction)
51
+ when SKPaymentTransactionStateFailed
52
+ failedTransaction(transaction)
53
+ when SKPaymentTransactionStateRestored
54
+ restoreTransaction(transaction)
55
+ else
56
+ end
57
+ end
58
+ end
59
+
60
+ def completeTransaction(transaction)
61
+ finishTransaction(transaction, success:true)
62
+ end
63
+
64
+ def restoreTransaction(transaction)
65
+ finishTransaction(transaction, success:true)
66
+ end
67
+
68
+ def failedTransaction(transaction)
69
+ finishTransaction(transaction, success:false)
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,84 @@
1
+ module Vendor
2
+ class Info
3
+ attr_accessor :params, :block
4
+
5
+ def initialize(params, &block)
6
+ @params = params
7
+ @block = block
8
+
9
+ # Start product request
10
+ productsRequest = SKProductsRequest.alloc.initWithProductIdentifiers(NSSet.setWithObject(@params.id))
11
+ productsRequest.delegate = self
12
+ productsRequest.start
13
+
14
+ # Update receipt if bought and subscription
15
+ Vendor::Receipt.new(NSUserDefaults["#{@params.id}.receipt_data"], @params) if bought? && subscription?
16
+ end
17
+
18
+
19
+
20
+ # INFO METHODS
21
+ def price
22
+ price = NSUserDefaults["#{@params.id}.price"] || @params.price
23
+ price.to_f.string_with_style(NSNumberFormatterCurrencyStyle)
24
+ end
25
+
26
+ def title
27
+ NSUserDefaults["#{@params.id}.localizedTitle"] || @params.title
28
+ end
29
+
30
+ def description
31
+ NSUserDefaults["#{@params.id}.localizedDescription"] || @params.desc
32
+ end
33
+
34
+ def bought?
35
+ NSUserDefaults["#{@params.id}.receipt"] != nil
36
+ end
37
+
38
+ def subscription?
39
+ @params.subscription
40
+ end
41
+
42
+ def subscribed?
43
+ return false if !subscription?
44
+ receipt_object = BW::JSON.parse(NSUserDefaults["#{@params.id}.receipt"]).to_object
45
+ return false if receipt_object.blank? || receipt_object.status!=0
46
+
47
+ decoder = CocoaSecurityDecoder.new
48
+ latest_receipt_data = decoder.base64(receipt_object.latest_receipt)
49
+ latest_receipt_plist = NSPropertyListSerialization.propertyListWithData(latest_receipt_data, options:NSPropertyListImmutable, format:nil, error:nil)
50
+
51
+ purchase_info_data = decoder.base64(latest_receipt_plist.objectForKey("purchase-info"))
52
+ purchase_info_plist = NSPropertyListSerialization.propertyListWithData(purchase_info_data, options:NSPropertyListImmutable, format:nil, error:nil)
53
+ purchase_info_plist = NSPropertyListSerialization.propertyListWithData(purchase_info_data, options:NSPropertyListImmutable, format:nil, error:nil)
54
+
55
+ expires_date = purchase_info_plist.objectForKey("expires-date")
56
+ # ap "expires_date: #{expires_date}"
57
+ expires_calc = expires_date.to_i/1000
58
+
59
+ return expires_calc > NSDate.date.timeIntervalSince1970
60
+ end
61
+
62
+
63
+
64
+ # DELEGATE METHODS
65
+ def productsRequest(request, didReceiveResponse:response)
66
+ exists = response.invalidProductIdentifiers.count==0
67
+
68
+ # Save needed product info
69
+ if exists
70
+ product = response.products.first
71
+ NSUserDefaults["#{@params.id}.price"] = product.price
72
+ NSUserDefaults["#{@params.id}.localizedTitle"] = product.localizedTitle
73
+ NSUserDefaults["#{@params.id}.localizedDescription"] = product.localizedDescription
74
+ end
75
+
76
+ @block.call({success: exists, response: response}.to_object)
77
+ end
78
+
79
+ def request(request, didFailWithError:error)
80
+ @block.call({success: false, error: error}.to_object)
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,78 @@
1
+ module Vendor
2
+ class Product
3
+ attr_reader :params, :block, :exists, :info, :buy
4
+
5
+ def initialize(options={}, &block)
6
+ # Set default options
7
+ default_options = {
8
+ :id => "no_id",
9
+ :secret => "no_secret",
10
+ :subscription => false,
11
+ :price => "0.99",
12
+ :title => "No Title",
13
+ :desc => "No Description."
14
+ }
15
+
16
+ # Set global params and block
17
+ options = default_options.merge(options)
18
+ @params = options.to_object
19
+ @block = block
20
+
21
+ # Raise argument error if id is not included
22
+ raise ArgumentError, "VENDOR WARNING: You forgot to write in your item id. You can't sell item without an id." if @params.id == "no_id"
23
+ raise ArgumentError, "VENDOR WARNING: You're missing your subscriptions shared secret. Subscriptions must have a shared secret." if @params.subscription && @params.secret == "no_secret"
24
+
25
+ # Update product and set exists variable
26
+ @info = Vendor::Info.new(@params) do |block|
27
+ @exists = block.success
28
+ @block.call(block) unless @block.nil?
29
+ end
30
+
31
+ # Initialize purchase
32
+ @buy = Vendor::Buy.new(@params)
33
+ end
34
+
35
+
36
+
37
+ # PURCHASE METHODS
38
+ def purchase(&block)
39
+ @buy.purchase { |result| block.call(result)}
40
+ end
41
+
42
+ def restore(&block)
43
+ @buy.restore { |result| block.call(result)}
44
+ end
45
+
46
+
47
+
48
+ # INFO METHODS
49
+ def price
50
+ @info.price
51
+ end
52
+
53
+ def title
54
+ @info.title
55
+ end
56
+
57
+ def description
58
+ @info.description
59
+ end
60
+
61
+ def exists?
62
+ @exists || false
63
+ end
64
+
65
+ def bought?
66
+ @info.bought?
67
+ end
68
+
69
+ def subscription?
70
+ @info.subscription?
71
+ end
72
+
73
+ def subscribed?
74
+ @info.subscribed?
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,42 @@
1
+ module Vendor
2
+ class Receipt
3
+
4
+ attr_reader :receipt_data, :params, :request_operation_queue, :block
5
+
6
+ def initialize(receipt_data, params, &block)
7
+ @receipt_data = receipt_data
8
+ @params = params
9
+ @block = block
10
+ @request_operation_queue = NSOperationQueue.alloc.init
11
+
12
+ check_receipt(receipt_data)
13
+ end
14
+
15
+ def check_receipt(receipt_data)
16
+ encoder = CocoaSecurityEncoder.new
17
+ json_data = {"receipt-data" => encoder.base64(receipt_data)}
18
+ json_data[:password] = @params.secret if @params.secret != "no_secret"
19
+ server = App.development? ? "sandbox" : "buy"
20
+ base_url = NSURL.URLWithString("https://#{server}.itunes.apple.com")
21
+
22
+ # TODO - Could look nicer. Dunno if AFMotion could be used instead.
23
+ client = AFHTTPClient.alloc.initWithBaseURL(base_url)
24
+ client.setDefaultHeader("Accept", value:"application/json")
25
+ client.registerHTTPOperationClass(AFJSONRequestOperation.class)
26
+ client.setParameterEncoding(AFJSONParameterEncoding)
27
+ AFJSONRequestOperation.addAcceptableContentTypes(NSSet.setWithObject("text/plain"))
28
+
29
+ request = client.requestWithMethod("POST", path:"verifyReceipt", parameters:json_data)
30
+ request_operation = client.HTTPRequestOperationWithRequest(request, success: lambda { |operation, response_object|
31
+ NSUserDefaults["#{@params.id}.receipt_data"] = receipt_data
32
+ NSUserDefaults["#{@params.id}.receipt"] = response_object
33
+ @block.call({success: true, object: response_object}.to_object) unless @block.blank?
34
+ }, failure: lambda {|operation, error|
35
+ @block.call({success: false, error: error}.to_object) unless @block.blank?
36
+ })
37
+
38
+ @request_operation_queue.addOperation(request_operation)
39
+ end
40
+
41
+ end
42
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rm_vendor
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Holger Sindbaek
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: sugarcube
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bubble-wrap
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: motion-cocoapods
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: A RubyMotion StoreKit Wrapper that allows you to buy, restore and get
70
+ product info on your in app purchases and subscriptions
71
+ email:
72
+ - HolgerSindbaek@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - README.md
78
+ - lib/project/buy.rb
79
+ - lib/project/info.rb
80
+ - lib/project/product.rb
81
+ - lib/project/receipt.rb
82
+ - lib/Vendor.rb
83
+ homepage: https://github.com/holgersindbaek/Vendor
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.1.10
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: RubyMotion StoreKit Wrapper
107
+ test_files: []