helu 0.5 → 0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +57 -8
- data/lib/helu.rb +1 -0
- data/lib/project/fake_helu.rb +35 -0
- data/lib/project/helu.rb +10 -8
- data/lib/project/product_info_fetcher.rb +115 -0
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53fb21dab5afe5f99d560e4326b61e8222040089
|
4
|
+
data.tar.gz: 743acc254c9f25923817058f0028d1ddbf35fc2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45cae952adc479e019b24bce56d7c64e7a5ab68aa5d4545b3736e0220099b8848ca167e16e3df84d72f67f0c53ed8a1880d010497dd5778630511e374e4bf7cf
|
7
|
+
data.tar.gz: 41dff4c9c13efa64328622dfa0543996fb0eefbd8c5ec0078bed9c5cfbb655b61685986efab632adbb4b2b5bacc5ad46a8b10e64d74c7b12b39aba142f7437b5
|
data/README.md
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# Helu
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/ivanacostarubio/helu.png)](https://travis-ci.org/ivanacostarubio/helu)
|
4
4
|
|
5
|
+
In-App purchases for RubyMotion.
|
6
|
+
|
7
|
+
![Money](https://raw.github.com/ivanacostarubio/helu/readme/resources/money.jpg)
|
5
8
|
|
6
9
|
## Installation
|
7
10
|
|
@@ -16,6 +19,7 @@ And then execute:
|
|
16
19
|
Or install it yourself as:
|
17
20
|
|
18
21
|
$ gem install helu
|
22
|
+
|
19
23
|
|
20
24
|
## USAGE
|
21
25
|
|
@@ -47,17 +51,62 @@ The transaction object on the lambda is the one we get from Apple; Therefore, it
|
|
47
51
|
@helu.restore
|
48
52
|
|
49
53
|
|
50
|
-
|
54
|
+
#### Make sure that if your code ever throws out the Helu object, it better also close the store before doing so.
|
51
55
|
|
52
56
|
@helu.close
|
53
57
|
|
58
|
+
### Supported types of In App Purchases
|
59
|
+
|
60
|
+
+ Consumables and Non-Consumables are supported.
|
61
|
+
+ Auto-Renewable subscriptions and Non-Renewing Subscriptions are not supported yet. However, we would love some help making it happen.
|
62
|
+
|
63
|
+
### Requesting product information:
|
64
|
+
|
65
|
+
Asynchronous, raw:
|
66
|
+
|
67
|
+
inapps = %w[first second third]
|
68
|
+
@x = Helu::ProductInfoFetcher.new(inapps) do |pi|
|
69
|
+
p pi
|
70
|
+
end
|
71
|
+
|
72
|
+
Asynchronous, wrapped in method call:
|
73
|
+
|
74
|
+
Helu::ProductInfoFetcher.fetch(inapps) do |pi|
|
75
|
+
p pi
|
76
|
+
end
|
77
|
+
|
78
|
+
Synchronous:
|
79
|
+
|
80
|
+
pi = Helu::ProductInfoFetcher.fetch(inapps)
|
81
|
+
p pi
|
82
|
+
|
83
|
+
|
84
|
+
All three calls return hash of the following form:
|
85
|
+
|
86
|
+
{
|
87
|
+
"inapp_id": {
|
88
|
+
id: "inapp_id",
|
89
|
+
title: "inapp_localized_title",
|
90
|
+
description: "inapp_localized_description",
|
91
|
+
price: "0.89", # float
|
92
|
+
currency: "EUR",
|
93
|
+
price_str: "\u20AC0.89",
|
94
|
+
}, # ....
|
95
|
+
}
|
96
|
+
|
54
97
|
|
55
98
|
## Example App:
|
56
99
|
|
57
|
-
[You can find an example app here](https://github.com/ivanacostarubio/helu-example).
|
58
|
-
|
59
|
-
|
60
|
-
## Supported types of In App Purchases
|
100
|
+
[You can find an example app here](https://github.com/ivanacostarubio/helu-example).
|
101
|
+
Remember that for this to work properly, you must add your app identifier to the Rakefile.
|
102
|
+
|
61
103
|
|
62
|
-
|
63
|
-
|
104
|
+
## License
|
105
|
+
|
106
|
+
Helu is relase under the [MIT license](http://opensource.org/licenses/MIT).
|
107
|
+
|
108
|
+
## Credits
|
109
|
+
|
110
|
+
Ivan Acosta-Rubio | Initial work
|
111
|
+
Michal J. | Product Info Fetcher
|
112
|
+
Simon Traels Ravn | Removing Observer and Bug fixes.
|
data/lib/helu.rb
CHANGED
@@ -0,0 +1,35 @@
|
|
1
|
+
class FakeHelu
|
2
|
+
attr_reader :product_id
|
3
|
+
attr_accessor :storage, :winning, :restore, :fail
|
4
|
+
|
5
|
+
def initialize(product_id)
|
6
|
+
@produc_id = product_id
|
7
|
+
end
|
8
|
+
|
9
|
+
def bought?
|
10
|
+
#TODO. Be able to switch this
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def close
|
15
|
+
# just implementing Helu's interface
|
16
|
+
end
|
17
|
+
|
18
|
+
def buy
|
19
|
+
winning.call(fake_transaction)
|
20
|
+
end
|
21
|
+
|
22
|
+
def fake_fail
|
23
|
+
# TODO: We need to implement a way to fake a fail
|
24
|
+
end
|
25
|
+
|
26
|
+
def restore
|
27
|
+
winning.call(fake_transaction)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def fake_transaction
|
33
|
+
""
|
34
|
+
end
|
35
|
+
end
|
data/lib/project/helu.rb
CHANGED
@@ -3,6 +3,13 @@ class Helu
|
|
3
3
|
attr_reader :product_id
|
4
4
|
attr_accessor :storage, :winning, :restore, :fail
|
5
5
|
|
6
|
+
class <<self
|
7
|
+
# see product_info_fetcher.rb for info
|
8
|
+
def fetch_product_info(*products, &b)
|
9
|
+
ProductInfoFetcher.fetch(*products, &b)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
6
13
|
def initialize(product_id)
|
7
14
|
@product_id = product_id
|
8
15
|
SKPaymentQueue.defaultQueue.addTransactionObserver(self)
|
@@ -26,11 +33,8 @@ class Helu
|
|
26
33
|
@storage.all.include?(product_id)
|
27
34
|
end
|
28
35
|
|
29
|
-
# private
|
30
|
-
|
31
36
|
def finishTransaction(transaction, wasSuccessful:wasSuccessful)
|
32
37
|
SKPaymentQueue.defaultQueue.finishTransaction(transaction)
|
33
|
-
produt_id = transaction.payment.productIdentifier
|
34
38
|
if wasSuccessful
|
35
39
|
@winning.call(transaction)
|
36
40
|
storage.add(product_id)
|
@@ -48,11 +52,9 @@ class Helu
|
|
48
52
|
end
|
49
53
|
|
50
54
|
def failedTransaction(transaction)
|
51
|
-
|
52
|
-
|
53
|
-
if (transaction.error.code != SKErrorPaymentCancelled)
|
55
|
+
if transaction.error && (transaction.error.code != SKErrorPaymentCancelled)
|
54
56
|
finishTransaction(transaction, wasSuccessful:false)
|
55
|
-
elsif transaction.error.code == SKErrorPaymentCancelled
|
57
|
+
elsif transaction.error && (transaction.error.code == SKErrorPaymentCancelled)
|
56
58
|
@fail.call(transaction)
|
57
59
|
else
|
58
60
|
SKPaymentQueue.defaultQueue.finishTransaction(transaction)
|
@@ -119,4 +121,4 @@ class Helu
|
|
119
121
|
|
120
122
|
end
|
121
123
|
|
122
|
-
end
|
124
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
=begin
|
2
|
+
# Sample use:
|
3
|
+
## Asynchronous, raw:
|
4
|
+
inapps = %w[first second third]
|
5
|
+
@x = Helu::ProductInfoFetcher.new(inapps) do |pi|
|
6
|
+
p pi
|
7
|
+
end
|
8
|
+
|
9
|
+
## Asynchronous, wrapped in method call:
|
10
|
+
Helu::ProductInfoFetcher.fetch(inapps) do |pi|
|
11
|
+
p pi
|
12
|
+
end
|
13
|
+
|
14
|
+
## Synchronous:
|
15
|
+
pi = Helu::ProductInfoFetcher.fetch(inapps)
|
16
|
+
p pi
|
17
|
+
|
18
|
+
|
19
|
+
# All three calls return hash of the following form:
|
20
|
+
# {
|
21
|
+
# "inapp_id": {
|
22
|
+
# id: "inapp_id",
|
23
|
+
# title: "inapp_localized_title",
|
24
|
+
# description: "inapp_localized_description",
|
25
|
+
# price: "0.89", # float
|
26
|
+
# currency: "EUR",
|
27
|
+
# price_str: "\u20AC0.89",
|
28
|
+
# },
|
29
|
+
# # ...
|
30
|
+
# }
|
31
|
+
=end
|
32
|
+
class Helu
|
33
|
+
class ProductInfoFetcher
|
34
|
+
@@mutex = Mutex.new
|
35
|
+
@@cache = {}
|
36
|
+
def initialize(*products, &b)
|
37
|
+
raise LocalJumpError, "block expected" if b.nil?
|
38
|
+
@callback = b
|
39
|
+
@products = products.flatten
|
40
|
+
|
41
|
+
# all cached? skip the call...
|
42
|
+
if (@@cache.keys & @products).sort == @products.sort
|
43
|
+
h = @products.inject({}) { |m, prod| m[prod] = @@cache[prod]; m }
|
44
|
+
@callback.call(h)
|
45
|
+
else
|
46
|
+
@sr = SKProductsRequest.alloc.initWithProductIdentifiers(@products)
|
47
|
+
@sr.delegate = self
|
48
|
+
@sr.start
|
49
|
+
end
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def productsRequest(req, didReceiveResponse: resp)
|
55
|
+
if resp.nil?
|
56
|
+
@callback.call(nil)
|
57
|
+
else
|
58
|
+
h = resp.products.inject({}) { |m, prod|
|
59
|
+
m.merge(sk_to_hash(prod))
|
60
|
+
}
|
61
|
+
@@mutex.synchronize { @@cache.merge!(h) }
|
62
|
+
@callback.call(h)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def sk_to_hash(sk)
|
69
|
+
nf = NSNumberFormatter.alloc.init
|
70
|
+
nf.setFormatterBehavior(NSNumberFormatterBehavior10_4)
|
71
|
+
nf.setNumberStyle(NSNumberFormatterCurrencyStyle)
|
72
|
+
nf.setLocale(sk.priceLocale)
|
73
|
+
price_str = nf.stringFromNumber(sk.price)
|
74
|
+
|
75
|
+
{
|
76
|
+
sk.productIdentifier => {
|
77
|
+
id: sk.productIdentifier,
|
78
|
+
title: sk.localizedTitle,
|
79
|
+
description: sk.localizedDescription,
|
80
|
+
price: sk.price,
|
81
|
+
currency: sk.priceLocale.objectForKey(NSLocaleCurrencyCode),
|
82
|
+
price_str: price_str,
|
83
|
+
}
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
class <<self
|
88
|
+
# make a sync call out of an async one
|
89
|
+
def call_synchronized(method, *args)
|
90
|
+
finished = false
|
91
|
+
result = nil
|
92
|
+
send(method, *args) do |res|
|
93
|
+
result = res
|
94
|
+
finished = true
|
95
|
+
end
|
96
|
+
sleep 0.1 until finished
|
97
|
+
result
|
98
|
+
end
|
99
|
+
|
100
|
+
def fetch(*products, &b)
|
101
|
+
products.flatten!
|
102
|
+
if b.nil?
|
103
|
+
call_synchronized(:fetch, *products)
|
104
|
+
else
|
105
|
+
new(*products, &b)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def clear_cache
|
110
|
+
@@mutex.synchronize { @@cache = {} }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: helu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.6'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Acosta-Rubio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
description: 'RubyMotion :: StoreKit Wrapper :: Allows In App Purchases '
|
@@ -33,7 +33,9 @@ extra_rdoc_files: []
|
|
33
33
|
files:
|
34
34
|
- README.md
|
35
35
|
- lib/helu.rb
|
36
|
+
- lib/project/fake_helu.rb
|
36
37
|
- lib/project/helu.rb
|
38
|
+
- lib/project/product_info_fetcher.rb
|
37
39
|
homepage: http://www.ivanacostarubio.com
|
38
40
|
licenses:
|
39
41
|
- MIT
|
@@ -44,17 +46,17 @@ require_paths:
|
|
44
46
|
- lib
|
45
47
|
required_ruby_version: !ruby/object:Gem::Requirement
|
46
48
|
requirements:
|
47
|
-
- -
|
49
|
+
- - ">="
|
48
50
|
- !ruby/object:Gem::Version
|
49
51
|
version: '0'
|
50
52
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
53
|
requirements:
|
52
|
-
- -
|
54
|
+
- - ">="
|
53
55
|
- !ruby/object:Gem::Version
|
54
56
|
version: '0'
|
55
57
|
requirements: []
|
56
58
|
rubyforge_project:
|
57
|
-
rubygems_version: 2.
|
59
|
+
rubygems_version: 2.2.2
|
58
60
|
signing_key:
|
59
61
|
specification_version: 4
|
60
62
|
summary: RubyMotion StoreKit Wrapper
|