imobile 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/CHANGELOG +1 -0
- data/LICENSE +21 -0
- data/Manifest +10 -0
- data/README +6 -0
- data/Rakefile +22 -0
- data/imobile.gemspec +40 -0
- data/lib/imobile/validate_receipt.rb +104 -0
- data/lib/imobile.rb +7 -0
- data/test/validate_receipt_test.rb +50 -0
- data/testdata/forged_sandbox_receipt +6 -0
- data/testdata/valid_sandbox_receipt +6 -0
- metadata +103 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v0.0.1. Initial release. In-App Purchase receipt validation.
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2009 Zergling.Net
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/Manifest
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'echoe'
|
3
|
+
|
4
|
+
Echoe.new('imobile') do |p|
|
5
|
+
p.project = 'zerglings' # rubyforge project
|
6
|
+
|
7
|
+
p.author = 'Victor Costan'
|
8
|
+
p.email = 'victor@zergling.net'
|
9
|
+
p.summary = 'Library for servers backing iPhone applications.'
|
10
|
+
p.url = 'http://github.com/costan/imobile'
|
11
|
+
p.dependencies = ['json >=1.1.7']
|
12
|
+
p.development_dependencies = ["echoe >=3.1.1", "flexmock >=0.8.6"]
|
13
|
+
|
14
|
+
p.need_tar_gz = true
|
15
|
+
p.need_zip = true
|
16
|
+
p.rdoc_pattern = /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
|
17
|
+
end
|
18
|
+
|
19
|
+
if $0 == __FILE__
|
20
|
+
Rake.application = Rake::Application.new
|
21
|
+
Rake.application.run
|
22
|
+
end
|
data/imobile.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{imobile}
|
5
|
+
s.version = "0.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Victor Costan"]
|
9
|
+
s.date = %q{2009-07-23}
|
10
|
+
s.description = %q{Library for servers backing iPhone applications.}
|
11
|
+
s.email = %q{victor@zergling.net}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "lib/imobile/validate_receipt.rb", "lib/imobile.rb", "LICENSE", "README"]
|
13
|
+
s.files = ["CHANGELOG", "lib/imobile/validate_receipt.rb", "lib/imobile.rb", "LICENSE", "Manifest", "Rakefile", "README", "test/validate_receipt_test.rb", "testdata/forged_sandbox_receipt", "testdata/valid_sandbox_receipt", "imobile.gemspec"]
|
14
|
+
s.homepage = %q{http://github.com/costan/imobile}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Imobile", "--main", "README"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{zerglings}
|
18
|
+
s.rubygems_version = %q{1.3.5}
|
19
|
+
s.summary = %q{Library for servers backing iPhone applications.}
|
20
|
+
s.test_files = ["test/validate_receipt_test.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<json>, [">= 1.1.7"])
|
28
|
+
s.add_development_dependency(%q<echoe>, [">= 3.1.1"])
|
29
|
+
s.add_development_dependency(%q<flexmock>, [">= 0.8.6"])
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<json>, [">= 1.1.7"])
|
32
|
+
s.add_dependency(%q<echoe>, [">= 3.1.1"])
|
33
|
+
s.add_dependency(%q<flexmock>, [">= 0.8.6"])
|
34
|
+
end
|
35
|
+
else
|
36
|
+
s.add_dependency(%q<json>, [">= 1.1.7"])
|
37
|
+
s.add_dependency(%q<echoe>, [">= 3.1.1"])
|
38
|
+
s.add_dependency(%q<flexmock>, [">= 0.8.6"])
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# Online validation for receipt blobs generated by the In-App Purchase process.
|
2
|
+
#
|
3
|
+
# Author:: Victor Costan
|
4
|
+
# Copyright:: Copyright (C) 2009 Zergling.Net
|
5
|
+
# License:: MIT
|
6
|
+
|
7
|
+
require 'base64'
|
8
|
+
require 'date'
|
9
|
+
require 'net/http'
|
10
|
+
require 'net/https'
|
11
|
+
require 'openssl'
|
12
|
+
require 'time'
|
13
|
+
|
14
|
+
require 'rubygems'
|
15
|
+
require 'json'
|
16
|
+
|
17
|
+
|
18
|
+
# :nodoc: namespace
|
19
|
+
module Imobile
|
20
|
+
|
21
|
+
# Decodes and validates an In-App Purchase receipt from the App Store.
|
22
|
+
#
|
23
|
+
# Args:
|
24
|
+
# receipt_blob:: raw receipt in SKPaymentTransaction.transactionReceipt
|
25
|
+
# server_type:: production or sandbox (the API accepts symbols and strings)
|
26
|
+
#
|
27
|
+
# The decoded receipt is returned as a Ruby-friendly hash. Keys are converted to
|
28
|
+
# snake_case symbols (e.g. 'purchase-date' becomes :purchase_date). Dates and
|
29
|
+
# relevant numbers are parsed out of the JSON strings.
|
30
|
+
#
|
31
|
+
# Returns +false+ if validation fails (the receipt was tampered with). Raises a
|
32
|
+
# RuntimeException if Apple's Web service returns a HTTP error code.
|
33
|
+
def self.validate_receipt(receipt_blob, server_type = :sandbox)
|
34
|
+
AppStoreReceiptValidation.validate_receipt receipt_blob
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# Implementation details for validate_receipt.
|
39
|
+
module AppStoreReceiptValidation
|
40
|
+
# An URI object pointing to the App Store receipt validating server.
|
41
|
+
#
|
42
|
+
# The server type is production or sandbox (strings and symbols work).
|
43
|
+
def self.store_uri(server_type)
|
44
|
+
uri_string = {
|
45
|
+
:production => "https://buy.itunes.apple.com/verifyReceipt",
|
46
|
+
:sandbox => "https://sandbox.itunes.apple.com/verifyReceipt"
|
47
|
+
}[server_type.to_sym]
|
48
|
+
|
49
|
+
uri_string and URI.parse(uri_string)
|
50
|
+
end
|
51
|
+
|
52
|
+
# A Net:HTTP request for validating a receipt.
|
53
|
+
def self.request(receipt_blob, store_uri)
|
54
|
+
request = Net::HTTP::Post.new store_uri.path
|
55
|
+
request.set_content_type 'application/json'
|
56
|
+
request.body = {'receipt-data' => Base64.encode64(receipt_blob) }.to_json
|
57
|
+
request
|
58
|
+
end
|
59
|
+
|
60
|
+
# Issues a HTTP request to Apple's receipt validating server.
|
61
|
+
def self.issue_request(http_request, store_uri)
|
62
|
+
http = Net::HTTP.new store_uri.host, store_uri.port
|
63
|
+
http.use_ssl = (store_uri.scheme == 'https')
|
64
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
65
|
+
http.request http_request
|
66
|
+
end
|
67
|
+
|
68
|
+
# Turns JSON receipt information into a nice Ruby-like receipt.
|
69
|
+
#
|
70
|
+
# String keys are mapped to Ruby symbols (e.g. 'purchase-date' becomes
|
71
|
+
# :purchase_date) and date strings are parsed to Ruby dates.
|
72
|
+
def self.nice_receipt(json_receipt)
|
73
|
+
nice = Hash[*json_receipt.map { |key, value|
|
74
|
+
[key.gsub('-', '_').to_sym, value]
|
75
|
+
}.flatten]
|
76
|
+
|
77
|
+
[:purchase_date, :original_purchase_date].each do |key|
|
78
|
+
nice[key] = DateTime.parse(nice[key]) if nice[key]
|
79
|
+
end
|
80
|
+
nice[:quantity] = nice[:quantity].to_i if nice[:quantity]
|
81
|
+
|
82
|
+
nice
|
83
|
+
end
|
84
|
+
|
85
|
+
# Processes a HTTP response into a receipt.
|
86
|
+
def self.process_response(http_response)
|
87
|
+
unless http_response.kind_of? Net::HTTPSuccess
|
88
|
+
raise "Internal error in Apple's Web service -- #{http_response.inspect}"
|
89
|
+
end
|
90
|
+
|
91
|
+
response = JSON.parse http_response.body
|
92
|
+
return false unless response['status'] == 0
|
93
|
+
nice_receipt response['receipt']
|
94
|
+
end
|
95
|
+
|
96
|
+
# Real implementation of Imobile.validate_receipt
|
97
|
+
def self.validate_receipt(receipt_blob, server_type = :production)
|
98
|
+
uri = store_uri server_type
|
99
|
+
|
100
|
+
process_response issue_request(request(receipt_blob, uri), uri)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end # namespace Imobile
|
data/lib/imobile.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Author:: Victor Costan
|
2
|
+
# Copyright:: Copyright (C) 2009 Zergling.Net
|
3
|
+
# License:: MIT
|
4
|
+
|
5
|
+
require 'imobile'
|
6
|
+
|
7
|
+
require 'time'
|
8
|
+
require 'test/unit'
|
9
|
+
|
10
|
+
|
11
|
+
class ValidateReceiptTest < Test::Unit::TestCase
|
12
|
+
def setup
|
13
|
+
testdata_path = File.join(File.dirname(__FILE__), '..', 'testdata')
|
14
|
+
|
15
|
+
@forged_sandbox_blob = File.read File.join(testdata_path,
|
16
|
+
'forged_sandbox_receipt')
|
17
|
+
@valid_sandbox_blob = File.read File.join(testdata_path,
|
18
|
+
'valid_sandbox_receipt')
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_valid_sandbox_receipt
|
22
|
+
golden_date = Time.parse('2009-07-22 18:52:46 EEST')
|
23
|
+
|
24
|
+
receipt = Imobile.validate_receipt @valid_sandbox_blob, :sandbox
|
25
|
+
assert receipt, "Genuine receipt failed validation"
|
26
|
+
|
27
|
+
assert_equal 1, receipt[:quantity], 'Wrong quantity'
|
28
|
+
assert_equal 'us.costan.ZergSupportTests', receipt[:bid], 'Wrong bundle ID'
|
29
|
+
assert_equal '1.9.8.3', receipt[:bvrs], 'Wrong bundle version'
|
30
|
+
assert_equal 'net.zergling.ZergSupport.sub_cheap', receipt[:product_id],
|
31
|
+
'Wrong product ID'
|
32
|
+
assert_equal '324740515', receipt[:item_id], 'Wrong item (iTunes app) ID'
|
33
|
+
assert_equal '1000000000016600', receipt[:transaction_id],
|
34
|
+
'Wrong transaction ID'
|
35
|
+
assert_equal '1000000000016600', receipt[:original_transaction_id],
|
36
|
+
'Wrong original transaction ID'
|
37
|
+
assert_equal golden_date.to_f,
|
38
|
+
Time.parse(receipt[:purchase_date].to_s).to_f,
|
39
|
+
'Wrong purchase date'
|
40
|
+
assert_equal golden_date.to_f,
|
41
|
+
Time.parse(receipt[:original_purchase_date].to_s).to_f,
|
42
|
+
'Wrong original purchase date'
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_forged_sandbox_receipt
|
46
|
+
assert_equal false,
|
47
|
+
Imobile.validate_receipt(@forged_sandbox_blob, :sandbox),
|
48
|
+
"Forged receipt passed validation"
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
{
|
2
|
+
"signature" = "AUXu7Ofn0I4Zl7kghQOLSDB17PzQT0lscGJfFiFsJFT0q9BFGG0ynHJAOARZMXdUFnb87YxrhqD1pm7KPxqgs18mndMRkw/fcRDgMIRmp4nsQlISF8mRqrkYqehHPTXwd75YxpBRkKil8qYciQRMuQEAhL8C5M+ycXzIpTgF2l1pMIIDUzCCAjugAwIBAgIIZRSRTdlYBLUwDQYJKoZIhvcNAQEFBQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTMwMQYDVQQDDCpBcHBsZSBpVHVuZXMgU3RvcmUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDkwNjE1MjIwNTU2WhcNMTQwNjE0MjIwNTU2WjBkMSMwIQYDVQQDDBpQdXJjaGFzZVJlY2VpcHRDZXJ0aWZpY2F0ZTEbMBkGA1UECwwSQXBwbGUgaVR1bmVzIFN0b3JlMRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAytGMXZy3gitJ2JMKFojSDynC/9yYezyn9HBX+u3/3VcpWE2XhcgGKYqNBA1+AewOzrKO774OsokTu4qymEx10ph8UTmsZewB0ESMHBEjF7FN6/HccsQUYC3WagrHnT12HG2Ih0OAm/ZhpWzj0HS4m813LpIyo00sewMvMNL2hkcCAwEAAaNyMHAwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQ2HejinYLSARi1MmsO10MLkVhDOjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFKmDg/IZSMU+ElcIFMzNo36ZXyT1MBAGCiqGSIb3Y2QGBQEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQARpJs+O2Y3gL8gHdASkrfZHFpwINd1VcB5VF5LkVpnFz63zylA/3cGIDG91b/d5NIwZjkVt4Bgvd62o/mCbzCsWiNfSKTJVFK1D78BDQoSO2oHTuQuz1BR7xzNHxQZ90zUS6ZX9SC8N3g3A1jEtAyDhZNB+CRBBXLwZdnBUeBsT9QLpjvTnekZcGTnU08zfCjGF3eBJEu9eP6WgexK1xMSp72kEOmYbn6yTi3D4YrcYx4Q3n/57VBP2en8qXWeP5oHDsLTGzLRsWdoB3VxJLrF2ivL8JS8zqC0qyac452pN6xunRuzyyfpaqzQL12BzFEe44xna2byektSbtquA5LNAAAAAA==";
|
3
|
+
"purchase-info" = "ewoJIml0ZW0taWQiID0gIjMyNDc0MDUxNSI7Cgkib3JpZ2luYWwtdHJhbnNhY3Rpb24taWQiID0gIjEwMDAwMDAwMDAwMTY2MDAiOwoJInB1cmNoYXNlLWRhdGUiID0gIjIwMDktMDctMjIgMjI6NTI6NDYgRXRjL0dNVCI7CgkicHJvZHVjdC1pZCIgPSAibmV0LnplcmdsaW5nLlplcmdTdXBwb3J0LnN1Yl9jaGVhcCI7CgkidHJhbnNhY3Rpb24taWQiID0gIjEwMDAwMDAwMDAwMTY2MDAiOwoJInF1YW50aXR5IiA9ICIxIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlIiA9ICIyMDA5LTA3LTIyIDIyOjUyOjQ2IEV0Yy9HTVQiOwoJImJpZCIgPSAidXMuY29zdGFuLlplcmdTdXBwb3J0VGVzdHMiOwoJImJ2cnMiID0gIjEuOS44LjMiOwpA";
|
4
|
+
"pod" = "100";
|
5
|
+
"signing-status" = "0";
|
6
|
+
}
|
@@ -0,0 +1,6 @@
|
|
1
|
+
{
|
2
|
+
"signature" = "AUXu7Ofn0I4Zl7kghQOLSDB17PzQT0lscGJfFiFsJFT0q9BFGG0ynHJAOARZMXdUFnb87YxrhqD1pm7KPxqgs18mndMRkw/fcRDgMIRmp4nsQlISF8mRqrkYqehHPTXwd75YxpBRkKil8qYciQRMuQEAhL8C5M+ycXzIpTgF2l1pMIIDUzCCAjugAwIBAgIIZRSRTdlYBLUwDQYJKoZIhvcNAQEFBQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTMwMQYDVQQDDCpBcHBsZSBpVHVuZXMgU3RvcmUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDkwNjE1MjIwNTU2WhcNMTQwNjE0MjIwNTU2WjBkMSMwIQYDVQQDDBpQdXJjaGFzZVJlY2VpcHRDZXJ0aWZpY2F0ZTEbMBkGA1UECwwSQXBwbGUgaVR1bmVzIFN0b3JlMRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAytGMXZy3gitJ2JMKFojSDynC/9yYezyn9HBX+u3/3VcpWE2XhcgGKYqNBA1+AewOzrKO774OsokTu4qymEx10ph8UTmsZewB0ESMHBEjF7FN6/HccsQUYC3WagrHnT12HG2Ih0OAm/ZhpWzj0HS4m813LpIyo00sewMvMNL2hkcCAwEAAaNyMHAwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQ2HejinYLSARi1MmsO10MLkVhDOjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFKmDg/IZSMU+ElcIFMzNo36ZXyT1MBAGCiqGSIb3Y2QGBQEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQARpJs+O2Y3gL8gHdASkrfZHFpwINd1VcB5VF5LkVpnFz63zylA/3cGIDG91b/d5NIwZjkVt4Bgvd62o/mCbzCsWiNfSKTJVFK1D78BDQoSO2oHTuQuz1BR7xzNHxQZ90zUS6ZX9SC8N3g3A1jEtAyDhZNB+CRBBXLwZdnBUeBsT9QLpjvTnekZcGTnU08zfCjGF3eBJEu9eP6WgexK1xMSp72kEOmYbn6yTi3D4YrcYx4Q3n/57VBP2en8qXWeP5oHDsLTGzLRsWdoB3VxJLrF2ivL8JS8zqC0qyac452pN6xunRuzyyfpaqzQL12BzFEe44xna2byektSbtquA5LNAAAAAA==";
|
3
|
+
"purchase-info" = "ewoJIml0ZW0taWQiID0gIjMyNDc0MDUxNSI7Cgkib3JpZ2luYWwtdHJhbnNhY3Rpb24taWQiID0gIjEwMDAwMDAwMDAwMTY2MDAiOwoJInB1cmNoYXNlLWRhdGUiID0gIjIwMDktMDctMjIgMjI6NTI6NDYgRXRjL0dNVCI7CgkicHJvZHVjdC1pZCIgPSAibmV0LnplcmdsaW5nLlplcmdTdXBwb3J0LnN1Yl9jaGVhcCI7CgkidHJhbnNhY3Rpb24taWQiID0gIjEwMDAwMDAwMDAwMTY2MDAiOwoJInF1YW50aXR5IiA9ICIxIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlIiA9ICIyMDA5LTA3LTIyIDIyOjUyOjQ2IEV0Yy9HTVQiOwoJImJpZCIgPSAidXMuY29zdGFuLlplcmdTdXBwb3J0VGVzdHMiOwoJImJ2cnMiID0gIjEuOS44LjMiOwp9";
|
4
|
+
"pod" = "100";
|
5
|
+
"signing-status" = "0";
|
6
|
+
}
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: imobile
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Victor Costan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-23 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.1.7
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: echoe
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.1.1
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: flexmock
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.8.6
|
44
|
+
version:
|
45
|
+
description: Library for servers backing iPhone applications.
|
46
|
+
email: victor@zergling.net
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- CHANGELOG
|
53
|
+
- lib/imobile/validate_receipt.rb
|
54
|
+
- lib/imobile.rb
|
55
|
+
- LICENSE
|
56
|
+
- README
|
57
|
+
files:
|
58
|
+
- CHANGELOG
|
59
|
+
- lib/imobile/validate_receipt.rb
|
60
|
+
- lib/imobile.rb
|
61
|
+
- LICENSE
|
62
|
+
- Manifest
|
63
|
+
- Rakefile
|
64
|
+
- README
|
65
|
+
- test/validate_receipt_test.rb
|
66
|
+
- testdata/forged_sandbox_receipt
|
67
|
+
- testdata/valid_sandbox_receipt
|
68
|
+
- imobile.gemspec
|
69
|
+
has_rdoc: true
|
70
|
+
homepage: http://github.com/costan/imobile
|
71
|
+
licenses: []
|
72
|
+
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options:
|
75
|
+
- --line-numbers
|
76
|
+
- --inline-source
|
77
|
+
- --title
|
78
|
+
- Imobile
|
79
|
+
- --main
|
80
|
+
- README
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: "0"
|
88
|
+
version:
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: "1.2"
|
94
|
+
version:
|
95
|
+
requirements: []
|
96
|
+
|
97
|
+
rubyforge_project: zerglings
|
98
|
+
rubygems_version: 1.3.5
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Library for servers backing iPhone applications.
|
102
|
+
test_files:
|
103
|
+
- test/validate_receipt_test.rb
|