suhovius-itunes-receipt 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.rdoc +49 -0
- data/Rakefile +19 -0
- data/VERSION +1 -0
- data/itunes-receipt.gemspec +24 -0
- data/lib/itunes.rb +39 -0
- data/lib/itunes/receipt.rb +188 -0
- data/spec/fake_json/array_of_latest_receipt_info.json +135 -0
- data/spec/fake_json/autorenew_subscription.json +2 -0
- data/spec/fake_json/autorenew_subscription_expired.json +2 -0
- data/spec/fake_json/invalid.json +1 -0
- data/spec/fake_json/offline.json +1 -0
- data/spec/fake_json/sandboxed.json +1 -0
- data/spec/fake_json/valid.json +1 -0
- data/spec/fake_json/valid_application.json +38 -0
- data/spec/helpers/fake_json_helper.rb +25 -0
- data/spec/itunes/receipt_spec.rb +252 -0
- data/spec/itunes_spec.rb +30 -0
- data/spec/spec_helper.rb +16 -0
- metadata +191 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8165f8d1e086ee0a062b699545ea24ff2bd3eae6
|
4
|
+
data.tar.gz: f7b34a091438598aee027f650f693079327e7259
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 476aebc331f25fead9423402f9487e20eb99ced306f195d677065d05d9783a2b3d5898217a8746dc63ad06a8c9dddf341bc6f662a686874d70857d696714f2f3
|
7
|
+
data.tar.gz: da1a9177737ab57564ca142a3440d6466c175d8fb32262e31d7b913009fe5620ee282d4608b7207328e54c157b2afab30f778e456f03ec6a5e7af55d1b3ab442
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 nov matake
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
= suhovius-itunes-receipt
|
2
|
+
|
3
|
+
Handle iTunes In App Purchase Receipt Verification.
|
4
|
+
|
5
|
+
== Please Note
|
6
|
+
|
7
|
+
This gem is clone of original itunes-receipt by nov matake https://github.com/nov/itunes-receipt
|
8
|
+
|
9
|
+
There are some changes that were needed for my project. So, I'd recommend using original gem since it might be already updated with fixes that I've added here at this copy.
|
10
|
+
|
11
|
+
== Installation
|
12
|
+
|
13
|
+
gem install suhovius-itunes-receipt
|
14
|
+
|
15
|
+
== Usage
|
16
|
+
|
17
|
+
require 'itunes/receipt'
|
18
|
+
|
19
|
+
# pass Base64 encoded raw receipt data which you received from your iOS app
|
20
|
+
receipt = Itunes::Receipt.verify! 'ewoJInNpZ25hdHVyZSIgPSAi...'
|
21
|
+
receipt.product_id # => 'com.example.products.100gems'
|
22
|
+
receipt.transaction_id # => '1234567890'
|
23
|
+
:
|
24
|
+
|
25
|
+
See lib/itunes/receipt.rb for more attributes.
|
26
|
+
|
27
|
+
If you want to accept iTunes sandbox receipts, do like this.
|
28
|
+
With <code>allow_sandbox_receipt</code> option specified, this gem post given receipt data to iTunes production first, and when iTunes production tells it's sandbox receipt, re-send it to iTunes sandbox again automatically.
|
29
|
+
Without this option, this gem just raises an exception.
|
30
|
+
|
31
|
+
receipt = Itunes::Receipt.verify! 'ewoJInNpZ25hdHVyZSIgPSAi...', :allow_sandbox_receipt
|
32
|
+
receipt.sandbox? # => true/false
|
33
|
+
|
34
|
+
If you want to accept ONLY iTunes sandbox receipts (in your stable server etc.), call <code>Itunes.sandbox!</code> somewhere before you call <code>Itunes::Receipt.verify!</code>.
|
35
|
+
Then all verification call after that goes to iTunes sandbox, until you do <code>Itunes.sandbox = false</code>.
|
36
|
+
|
37
|
+
== Note on Patches/Pull Requests
|
38
|
+
|
39
|
+
* Fork the project.
|
40
|
+
* Make your feature addition or bug fix.
|
41
|
+
* Add tests for it. This is important so I don't break it in a
|
42
|
+
future version unintentionally.
|
43
|
+
* Commit, do not mess with rakefile, version, or history.
|
44
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
45
|
+
* Send me a pull request. Bonus points for topic branches.
|
46
|
+
|
47
|
+
== Copyright
|
48
|
+
|
49
|
+
Copyright (c) 2011 nov matake. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
namespace :coverage do
|
8
|
+
desc "Open coverage report"
|
9
|
+
task :report do
|
10
|
+
require 'simplecov'
|
11
|
+
`open "#{File.join SimpleCov.coverage_path, 'index.html'}"`
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
task :spec do
|
16
|
+
Rake::Task[:'coverage:report'].invoke unless ENV['TRAVIS_RUBY_VERSION']
|
17
|
+
end
|
18
|
+
|
19
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.1.1
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'suhovius-itunes-receipt'
|
6
|
+
s.description = %q{Handle iTunes In App Purchase Receipt Verification}
|
7
|
+
s.summary = %q{Handle iTunes In App Purchase Receipt Verification}
|
8
|
+
s.version = File.read(File.join(File.dirname(__FILE__), 'VERSION'))
|
9
|
+
s.authors = ['nov matake', 'suhovius']
|
10
|
+
s.email = 'nov@matake.jp'
|
11
|
+
s.homepage = 'https://github.com/suhovius/itunes-receipt'
|
12
|
+
s.require_paths = ['lib']
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.add_dependency 'json', '>= 1.4.3'
|
17
|
+
s.add_dependency 'restclient_with_cert'
|
18
|
+
s.add_dependency 'activesupport', '>= 2.3'
|
19
|
+
s.add_dependency 'i18n'
|
20
|
+
s.add_development_dependency 'rake', '>= 0.8'
|
21
|
+
s.add_development_dependency 'rspec', '>= 2'
|
22
|
+
s.add_development_dependency 'fakeweb', '>= 1.3.0'
|
23
|
+
s.add_development_dependency 'simplecov'
|
24
|
+
end
|
data/lib/itunes.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'restclient_with_cert'
|
4
|
+
|
5
|
+
module Itunes
|
6
|
+
|
7
|
+
ENDPOINT = {
|
8
|
+
:production => 'https://buy.itunes.apple.com/verifyReceipt',
|
9
|
+
:sandbox => 'https://sandbox.itunes.apple.com/verifyReceipt'
|
10
|
+
}
|
11
|
+
|
12
|
+
def self.endpoint
|
13
|
+
ENDPOINT[itunes_env]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.itunes_env
|
17
|
+
sandbox? ? :sandbox : :production
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.sandbox?
|
21
|
+
@@sandbox
|
22
|
+
end
|
23
|
+
def self.sandbox!
|
24
|
+
self.sandbox = true
|
25
|
+
end
|
26
|
+
def self.sandbox=(boolean)
|
27
|
+
@@sandbox = boolean
|
28
|
+
end
|
29
|
+
self.sandbox = false
|
30
|
+
|
31
|
+
def self.shared_secret
|
32
|
+
@@shared_secret
|
33
|
+
end
|
34
|
+
def self.shared_secret=(shared_secret)
|
35
|
+
@@shared_secret = shared_secret
|
36
|
+
end
|
37
|
+
self.shared_secret = nil
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'itunes'
|
2
|
+
|
3
|
+
module Itunes
|
4
|
+
class Receipt
|
5
|
+
class VerificationFailed < StandardError
|
6
|
+
attr_reader :status
|
7
|
+
def initialize(attributes = {})
|
8
|
+
@status = attributes[:status]
|
9
|
+
super attributes[:exception]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class SandboxReceiptReceived < VerificationFailed; end;
|
14
|
+
|
15
|
+
class ReceiptServerOffline < VerificationFailed; end;
|
16
|
+
|
17
|
+
class ExpiredReceiptReceived < VerificationFailed
|
18
|
+
attr_reader :receipt
|
19
|
+
def initialize(attributes = {})
|
20
|
+
@receipt = attributes[:receipt]
|
21
|
+
super attributes
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader(
|
26
|
+
:adam_id,
|
27
|
+
:app_item_id,
|
28
|
+
:application_version,
|
29
|
+
:bid,
|
30
|
+
:bundle_id,
|
31
|
+
:bvrs,
|
32
|
+
:download_id,
|
33
|
+
:expires_date,
|
34
|
+
:expires_date_ms,
|
35
|
+
:in_app,
|
36
|
+
:is_trial_period,
|
37
|
+
:itunes_env,
|
38
|
+
:latest,
|
39
|
+
:original,
|
40
|
+
:product_id,
|
41
|
+
:purchase_date,
|
42
|
+
:purchase_date_ms,
|
43
|
+
:purchase_date_pst,
|
44
|
+
:quantity,
|
45
|
+
:receipt_data,
|
46
|
+
:request_date,
|
47
|
+
:request_date_ms,
|
48
|
+
:request_date_pst,
|
49
|
+
:transaction_id,
|
50
|
+
:version_external_identifier,
|
51
|
+
)
|
52
|
+
|
53
|
+
def initialize(attributes = {})
|
54
|
+
receipt_attributes = attributes.with_indifferent_access[:receipt]
|
55
|
+
@adam_id = receipt_attributes[:adam_id]
|
56
|
+
@app_item_id = receipt_attributes[:app_item_id]
|
57
|
+
@application_version = receipt_attributes[:application_version]
|
58
|
+
@bid = receipt_attributes[:bid]
|
59
|
+
@bundle_id = receipt_attributes[:bundle_id]
|
60
|
+
@bvrs = receipt_attributes[:bvrs]
|
61
|
+
@download_id = receipt_attributes[:download_id]
|
62
|
+
@expires_date = if receipt_attributes[:expires_date]
|
63
|
+
Time.parse receipt_attributes[:expires_date].sub('Etc/GMT', 'GMT')
|
64
|
+
end
|
65
|
+
@expires_date_ms = if receipt_attributes[:expires_date_ms]
|
66
|
+
receipt_attributes[:expires_date_ms].to_i
|
67
|
+
end
|
68
|
+
@in_app = if receipt_attributes[:in_app]
|
69
|
+
receipt_attributes[:in_app].map { |ia| self.class.new(:receipt => ia) }
|
70
|
+
end
|
71
|
+
@is_trial_period = if receipt_attributes[:is_trial_period]
|
72
|
+
receipt_attributes[:is_trial_period] == "true"
|
73
|
+
end
|
74
|
+
@itunes_env = attributes[:itunes_env] || Itunes.itunes_env
|
75
|
+
@latest = case attributes[:latest_receipt_info]
|
76
|
+
when Hash
|
77
|
+
self.class.new(
|
78
|
+
:receipt => attributes[:latest_receipt_info],
|
79
|
+
:latest_receipt => attributes[:latest_receipt],
|
80
|
+
:receipt_type => :latest
|
81
|
+
)
|
82
|
+
when Array
|
83
|
+
attributes[:latest_receipt_info].collect do |latest_receipt_info|
|
84
|
+
self.class.new(
|
85
|
+
:receipt => latest_receipt_info,
|
86
|
+
:latest_receipt => attributes[:latest_receipt],
|
87
|
+
:receipt_type => :latest
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
@original = if receipt_attributes[:original_transaction_id] || receipt_attributes[:original_purchase_date]
|
92
|
+
self.class.new(:receipt => {
|
93
|
+
:transaction_id => receipt_attributes[:original_transaction_id],
|
94
|
+
:purchase_date => receipt_attributes[:original_purchase_date],
|
95
|
+
:purchase_date_ms => receipt_attributes[:original_purchase_date_ms],
|
96
|
+
:purchase_date_pst => receipt_attributes[:original_purchase_date_pst],
|
97
|
+
:application_version => receipt_attributes[:original_application_version]
|
98
|
+
})
|
99
|
+
end
|
100
|
+
@product_id = receipt_attributes[:product_id]
|
101
|
+
@purchase_date = if receipt_attributes[:purchase_date]
|
102
|
+
Time.parse receipt_attributes[:purchase_date].sub('Etc/GMT', 'GMT')
|
103
|
+
end
|
104
|
+
@purchase_date_ms = if receipt_attributes[:purchase_date_ms]
|
105
|
+
receipt_attributes[:purchase_date_ms].to_i
|
106
|
+
end
|
107
|
+
@purchase_date_pst = if receipt_attributes[:purchase_date_pst]
|
108
|
+
Time.parse receipt_attributes[:purchase_date_pst].sub('America/Los_Angeles', 'PST')
|
109
|
+
end
|
110
|
+
@quantity = if receipt_attributes[:quantity]
|
111
|
+
receipt_attributes[:quantity].to_i
|
112
|
+
end
|
113
|
+
@receipt_data = if attributes[:receipt_type] == :latest
|
114
|
+
attributes[:latest_receipt]
|
115
|
+
end
|
116
|
+
@request_date = if receipt_attributes[:request_date]
|
117
|
+
Time.parse receipt_attributes[:request_date].sub('Etc/', '')
|
118
|
+
end
|
119
|
+
@request_date_ms = if receipt_attributes[:request_date_ms]
|
120
|
+
receipt_attributes[:request_date_ms].to_i
|
121
|
+
end
|
122
|
+
@request_date_pst = if receipt_attributes[:request_date_pst]
|
123
|
+
Time.parse receipt_attributes[:request_date_pst].sub('America/Los_Angeles', 'PST')
|
124
|
+
end
|
125
|
+
@transaction_id = receipt_attributes[:transaction_id]
|
126
|
+
@version_external_identifier = receipt_attributes[:version_external_identifier]
|
127
|
+
end
|
128
|
+
|
129
|
+
def application_receipt?
|
130
|
+
!@bundle_id.nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
def sandbox?
|
134
|
+
itunes_env == :sandbox
|
135
|
+
end
|
136
|
+
|
137
|
+
def last_in_app_receipt
|
138
|
+
self.in_app.last
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.verify!(receipt_data, allow_sandbox_receipt = false)
|
142
|
+
request_data = {:'receipt-data' => receipt_data}
|
143
|
+
request_data.merge!(:password => Itunes.shared_secret) if Itunes.shared_secret
|
144
|
+
response = post_to_endpoint(request_data)
|
145
|
+
begin
|
146
|
+
successful_response(response)
|
147
|
+
rescue SandboxReceiptReceived => e
|
148
|
+
# Retry with sandbox, as per:
|
149
|
+
# http://developer.apple.com/library/ios/#technotes/tn2259/_index.html
|
150
|
+
# FAQ#16
|
151
|
+
if allow_sandbox_receipt
|
152
|
+
sandbox_response = post_to_endpoint(request_data, Itunes::ENDPOINT[:sandbox])
|
153
|
+
successful_response(
|
154
|
+
sandbox_response.merge(:itunes_env => :sandbox)
|
155
|
+
)
|
156
|
+
else
|
157
|
+
raise e
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def self.post_to_endpoint(request_data, endpoint = Itunes.endpoint)
|
165
|
+
response = RestClient.post(
|
166
|
+
endpoint,
|
167
|
+
request_data.to_json
|
168
|
+
)
|
169
|
+
response = JSON.parse(response).with_indifferent_access
|
170
|
+
end
|
171
|
+
|
172
|
+
def self.successful_response(response)
|
173
|
+
case response[:status]
|
174
|
+
when 0
|
175
|
+
new response
|
176
|
+
when 21005
|
177
|
+
raise ReceiptServerOffline.new(response)
|
178
|
+
when 21006
|
179
|
+
raise ExpiredReceiptReceived.new(response)
|
180
|
+
when 21007
|
181
|
+
raise SandboxReceiptReceived.new(response)
|
182
|
+
else
|
183
|
+
raise VerificationFailed.new(response)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
{
|
2
|
+
"environment": "Sandbox",
|
3
|
+
"latest_receipt": "BASE64ENCODED RECEIPT WAS HERE",
|
4
|
+
"latest_receipt_info": [{
|
5
|
+
"expires_date": "2014-11-12 15:10:33 Etc/GMT",
|
6
|
+
"expires_date_ms": "1415805033000",
|
7
|
+
"expires_date_pst": "2014-11-12 07:10:33 America/Los_Angeles",
|
8
|
+
"is_trial_period": "true",
|
9
|
+
"original_purchase_date": "2014-11-12 15:07:34 Etc/GMT",
|
10
|
+
"original_purchase_date_ms": "1415804854000",
|
11
|
+
"original_purchase_date_pst": "2014-11-12 07:07:34 America/Los_Angeles",
|
12
|
+
"original_transaction_id": "1000000131413546",
|
13
|
+
"product_id": "com.musteus.musteus.subscription.month",
|
14
|
+
"purchase_date": "2014-11-12 15:25:30 Etc/GMT",
|
15
|
+
"purchase_date_ms": "1415805930044",
|
16
|
+
"purchase_date_pst": "2014-11-12 07:25:30 America/Los_Angeles",
|
17
|
+
"quantity": "1",
|
18
|
+
"transaction_id": "1000000131413546",
|
19
|
+
"web_order_line_item_id": "1000000028817186"
|
20
|
+
}, {
|
21
|
+
"expires_date": "2014-11-12 15:15:33 Etc/GMT",
|
22
|
+
"expires_date_ms": "1415805333000",
|
23
|
+
"expires_date_pst": "2014-11-12 07:15:33 America/Los_Angeles",
|
24
|
+
"is_trial_period": "false",
|
25
|
+
"original_purchase_date": "2014-11-12 15:08:34 Etc/GMT",
|
26
|
+
"original_purchase_date_ms": "1415804914000",
|
27
|
+
"original_purchase_date_pst": "2014-11-12 07:08:34 America/Los_Angeles",
|
28
|
+
"original_transaction_id": "1000000131413546",
|
29
|
+
"product_id": "com.musteus.musteus.subscription.month",
|
30
|
+
"purchase_date": "2014-11-12 15:25:30 Etc/GMT",
|
31
|
+
"purchase_date_ms": "1415805930044",
|
32
|
+
"purchase_date_pst": "2014-11-12 07:25:30 America/Los_Angeles",
|
33
|
+
"quantity": "1",
|
34
|
+
"transaction_id": "1000000131413602",
|
35
|
+
"web_order_line_item_id": "1000000028817187"
|
36
|
+
}, {
|
37
|
+
"expires_date": "2014-11-12 15:20:33 Etc/GMT",
|
38
|
+
"expires_date_ms": "1415805633000",
|
39
|
+
"expires_date_pst": "2014-11-12 07:20:33 America/Los_Angeles",
|
40
|
+
"is_trial_period": "false",
|
41
|
+
"original_purchase_date": "2014-11-12 15:14:15 Etc/GMT",
|
42
|
+
"original_purchase_date_ms": "1415805255000",
|
43
|
+
"original_purchase_date_pst": "2014-11-12 07:14:15 America/Los_Angeles",
|
44
|
+
"original_transaction_id": "1000000131413546",
|
45
|
+
"product_id": "com.musteus.musteus.subscription.month",
|
46
|
+
"purchase_date": "2014-11-12 15:25:30 Etc/GMT",
|
47
|
+
"purchase_date_ms": "1415805930044",
|
48
|
+
"purchase_date_pst": "2014-11-12 07:25:30 America/Los_Angeles",
|
49
|
+
"quantity": "1",
|
50
|
+
"transaction_id": "1000000131414118",
|
51
|
+
"web_order_line_item_id": "1000000028817195"
|
52
|
+
}, {
|
53
|
+
"expires_date": "2014-11-12 15:25:33 Etc/GMT",
|
54
|
+
"expires_date_ms": "1415805933000",
|
55
|
+
"expires_date_pst": "2014-11-12 07:25:33 America/Los_Angeles",
|
56
|
+
"is_trial_period": "false",
|
57
|
+
"original_purchase_date": "2014-11-12 15:18:35 Etc/GMT",
|
58
|
+
"original_purchase_date_ms": "1415805515000",
|
59
|
+
"original_purchase_date_pst": "2014-11-12 07:18:35 America/Los_Angeles",
|
60
|
+
"original_transaction_id": "1000000131413546",
|
61
|
+
"product_id": "com.musteus.musteus.subscription.month",
|
62
|
+
"purchase_date": "2014-11-12 15:25:30 Etc/GMT",
|
63
|
+
"purchase_date_ms": "1415805930044",
|
64
|
+
"purchase_date_pst": "2014-11-12 07:25:30 America/Los_Angeles",
|
65
|
+
"quantity": "1",
|
66
|
+
"transaction_id": "1000000131415612",
|
67
|
+
"web_order_line_item_id": "1000000028817228"
|
68
|
+
}, {
|
69
|
+
"expires_date": "2014-11-12 15:30:33 Etc/GMT",
|
70
|
+
"expires_date_ms": "1415806233000",
|
71
|
+
"expires_date_pst": "2014-11-12 07:30:33 America/Los_Angeles",
|
72
|
+
"is_trial_period": "false",
|
73
|
+
"original_purchase_date": "2014-11-12 15:23:35 Etc/GMT",
|
74
|
+
"original_purchase_date_ms": "1415805815000",
|
75
|
+
"original_purchase_date_pst": "2014-11-12 07:23:35 America/Los_Angeles",
|
76
|
+
"original_transaction_id": "1000000131413546",
|
77
|
+
"product_id": "com.musteus.musteus.subscription.month",
|
78
|
+
"purchase_date": "2014-11-12 15:25:30 Etc/GMT",
|
79
|
+
"purchase_date_ms": "1415805930044",
|
80
|
+
"purchase_date_pst": "2014-11-12 07:25:30 America/Los_Angeles",
|
81
|
+
"quantity": "1",
|
82
|
+
"transaction_id": "1000000131415863",
|
83
|
+
"web_order_line_item_id": "1000000028817256"
|
84
|
+
}],
|
85
|
+
"receipt": {
|
86
|
+
"adam_id": 0,
|
87
|
+
"app_item_id": 0,
|
88
|
+
"application_version": "1.0",
|
89
|
+
"bundle_id": "com.musteus.musteus",
|
90
|
+
"download_id": 0,
|
91
|
+
"in_app": [{
|
92
|
+
"expires_date": "2014-11-12 15:15:33 Etc/GMT",
|
93
|
+
"expires_date_ms": "1415805333000",
|
94
|
+
"expires_date_pst": "2014-11-12 07:15:33 America/Los_Angeles",
|
95
|
+
"is_trial_period": "false",
|
96
|
+
"original_purchase_date": "2014-11-12 15:08:34 Etc/GMT",
|
97
|
+
"original_purchase_date_ms": "1415804914000",
|
98
|
+
"original_purchase_date_pst": "2014-11-12 07:08:34 America/Los_Angeles",
|
99
|
+
"original_transaction_id": "1000000131413546",
|
100
|
+
"product_id": "com.musteus.musteus.subscription.month",
|
101
|
+
"purchase_date": "2014-11-12 15:09:10 Etc/GMT",
|
102
|
+
"purchase_date_ms": "1415804950000",
|
103
|
+
"purchase_date_pst": "2014-11-12 07:09:10 America/Los_Angeles",
|
104
|
+
"quantity": "1",
|
105
|
+
"transaction_id": "1000000131413602",
|
106
|
+
"web_order_line_item_id": "1000000028817187"
|
107
|
+
}, {
|
108
|
+
"expires_date": "2014-11-12 15:10:33 Etc/GMT",
|
109
|
+
"expires_date_ms": "1415805033000",
|
110
|
+
"expires_date_pst": "2014-11-12 07:10:33 America/Los_Angeles",
|
111
|
+
"is_trial_period": "false",
|
112
|
+
"original_purchase_date": "2014-11-12 15:07:34 Etc/GMT",
|
113
|
+
"original_purchase_date_ms": "1415804854000",
|
114
|
+
"original_purchase_date_pst": "2014-11-12 07:07:34 America/Los_Angeles",
|
115
|
+
"original_transaction_id": "1000000131413546",
|
116
|
+
"product_id": "com.musteus.musteus.subscription.month",
|
117
|
+
"purchase_date": "2014-11-12 15:09:10 Etc/GMT",
|
118
|
+
"purchase_date_ms": "1415804950000",
|
119
|
+
"purchase_date_pst": "2014-11-12 07:09:10 America/Los_Angeles",
|
120
|
+
"quantity": "1",
|
121
|
+
"transaction_id": "1000000131413546",
|
122
|
+
"web_order_line_item_id": "1000000028817186"
|
123
|
+
}],
|
124
|
+
"original_application_version": "1.0",
|
125
|
+
"original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
|
126
|
+
"original_purchase_date_ms": "1375340400000",
|
127
|
+
"original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles",
|
128
|
+
"receipt_type": "ProductionSandbox",
|
129
|
+
"request_date": "2014-11-12 15:25:30 Etc/GMT",
|
130
|
+
"request_date_ms": "1415805930053",
|
131
|
+
"request_date_pst": "2014-11-12 07:25:30 America/Los_Angeles",
|
132
|
+
"version_external_identifier": 0
|
133
|
+
},
|
134
|
+
"status": 0
|
135
|
+
}
|
@@ -0,0 +1,2 @@
|
|
1
|
+
{"receipt":{"original_purchase_date_pst":"2012-10-11 07:45:40 America/Los_Angeles", "unique_identifier":"4d69cc362c5c58ce62da68ee43721ea670907bad", "original_transaction_id":"1000000057005439", "expires_date":"1350157508197", "transaction_id":"1000000055076747", "quantity":"1", "product_id":"com.notkeepingitreal.fizzbuzz.subscription.autorenew1m", "original_purchase_date_ms":"1349966740000", "bid":"com.notkeepingitreal.fizzbuzz", "web_order_line_item_id":"1000000026553289", "bvrs":"1.0", "expires_date_formatted":"2012-10-13 19:45:08 Etc/GMT", "purchase_date":"2012-10-13 19:40:08 Etc/GMT", "purchase_date_ms":"1350157208197", "expires_date_formatted_pst":"2012-10-13 12:45:08 America/Los_Angeles", "purchase_date_pst":"2012-10-13 12:40:08 America/Los_Angeles", "original_purchase_date":"2012-10-11 14:45:40 Etc/GMT", "item_id":"570504929"}, "latest_receipt_info":{"original_purchase_date_pst":"2012-10-11 07:45:40 America/Los_Angeles", "unique_identifier":"4d79cc562c5c58ce62da68ee43721ea670907bad", "original_transaction_id":"1000000057005439", "expires_date":"1350157808000", "transaction_id":"1000000052076747", "quantity":"1", "product_id":"com.notkeepingitreal.fizzbuzz.subscription.autorenew1m", "original_purchase_date_ms":"1349966740000", "bid":"com.notkeepingitreal.fizzbuzz", "web_order_line_item_id":"1000000027293289", "bvrs":"1.0", "expires_date_formatted":"2012-10-13 19:50:08 Etc/GMT", "purchase_date":"2012-10-13 19:40:08 Etc/GMT", "purchase_date_ms":"1350157208000", "expires_date_formatted_pst":"2012-10-13 12:50:08 America/Los_Angeles", "purchase_date_pst":"2012-10-13 12:40:08 America/Los_Angeles", "original_purchase_date":"2012-10-11 14:45:40 Etc/GMT", "item_id":"570504929"}, "status":0, "latest_receipt":"junk="}
|
2
|
+
|
@@ -0,0 +1,2 @@
|
|
1
|
+
{"receipt":{"original_purchase_date_pst":"2012-10-11 07:45:40 America/Los_Angeles", "unique_identifier":"4d69cc362c5c58ce62da68ee43721ea670907bad", "original_transaction_id":"1000000057005439", "expires_date":"1350157508197", "transaction_id":"1000000055076747", "quantity":"1", "product_id":"com.notkeepingitreal.fizzbuzz.subscription.autorenew1m", "original_purchase_date_ms":"1349966740000", "bid":"com.notkeepingitreal.fizzbuzz", "web_order_line_item_id":"1000000026553289", "bvrs":"1.0", "expires_date_formatted":"2012-10-13 19:45:08 Etc/GMT", "purchase_date":"2012-10-13 19:40:08 Etc/GMT", "purchase_date_ms":"1350157208197", "expires_date_formatted_pst":"2012-10-13 12:45:08 America/Los_Angeles", "purchase_date_pst":"2012-10-13 12:40:08 America/Los_Angeles", "original_purchase_date":"2012-10-11 14:45:40 Etc/GMT", "item_id":"570504929"}, "latest_receipt_info":{"original_purchase_date_pst":"2012-10-11 07:45:40 America/Los_Angeles", "unique_identifier":"4d79cc562c5c58ce62da68ee43721ea670907bad", "original_transaction_id":"1000000057005439", "expires_date":"1350157808000", "transaction_id":"1000000052076747", "quantity":"1", "product_id":"com.notkeepingitreal.fizzbuzz.subscription.autorenew1m", "original_purchase_date_ms":"1349966740000", "bid":"com.notkeepingitreal.fizzbuzz", "web_order_line_item_id":"1000000027293289", "bvrs":"1.0", "expires_date_formatted":"2012-10-13 19:50:08 Etc/GMT", "purchase_date":"2012-10-13 19:40:08 Etc/GMT", "purchase_date_ms":"1350157208000", "expires_date_formatted_pst":"2012-10-13 12:50:08 America/Los_Angeles", "purchase_date_pst":"2012-10-13 12:40:08 America/Los_Angeles", "original_purchase_date":"2012-10-11 14:45:40 Etc/GMT", "item_id":"570504929"}, "status":21006, "latest_receipt":"junk="}
|
2
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status":21002, "exception":"java.lang.IllegalArgumentException: propertyListFromString parsed an object, but there's still more text in the string. A plist should contain only one top-level object. Line number: 6, column: 1."}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"status":21005}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"receipt":{"item_id":"420862234", "original_transaction_id":"1000000001479608", "bvrs":"1.0", "product_id":"com.cerego.iknow.30d", "purchase_date":"2011-02-17 06:20:57 Etc/GMT", "quantity":"1", "bid":"com.cerego.iknow", "original_purchase_date":"2011-02-17 06:20:57 Etc/GMT", "transaction_id":"1000000001500000"}, "status":21007}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"receipt":{"item_id":"420862234", "original_transaction_id":"1000000001479608", "bvrs":"1.0", "product_id":"com.cerego.iknow.30d", "purchase_date":"2011-02-17 06:20:57 Etc/GMT", "quantity":"1", "bid":"com.cerego.iknow", "original_purchase_date":"2011-02-17 06:20:57 Etc/GMT", "transaction_id":"1000000001479608"}, "status":0}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
{"status":0,
|
2
|
+
"environment":"Sandbox",
|
3
|
+
"receipt":{
|
4
|
+
"receipt_type":"ProductionSandbox",
|
5
|
+
"adam_id":0,
|
6
|
+
"bundle_id":"com.tekkinnovations.fars",
|
7
|
+
"application_version":"1.80",
|
8
|
+
"download_id":0,
|
9
|
+
"request_date":"2013-11-26 14:49:09 Etc/GMT",
|
10
|
+
"request_date_ms":"1385477349878",
|
11
|
+
"request_date_pst":"2013-11-26 06:49:09 America/Los_Angeles",
|
12
|
+
"original_purchase_date":"2013-08-01 07:00:00 Etc/GMT",
|
13
|
+
"original_purchase_date_ms":"1375340400000",
|
14
|
+
"original_purchase_date_pst":"2013-08-01 00:00:00 America/Los_Angeles",
|
15
|
+
"original_application_version":"1.0",
|
16
|
+
"in_app":[{"quantity":"1",
|
17
|
+
"product_id":"com.tekkinnovations.fars.subscription.6.months",
|
18
|
+
"transaction_id":"1000000091176126",
|
19
|
+
"original_transaction_id":"1000000091176126",
|
20
|
+
"purchase_date":"2013-11-26 05:58:48 Etc/GMT",
|
21
|
+
"purchase_date_ms":"1385445528000",
|
22
|
+
"purchase_date_pst":"2013-11-25 21:58:48 America/Los_Angeles",
|
23
|
+
"original_purchase_date":"2013-10-24 04:55:56 Etc/GMT",
|
24
|
+
"original_purchase_date_ms":"1382590556000",
|
25
|
+
"original_purchase_date_pst":"2013-10-23 21:55:56 America/Los_Angeles",
|
26
|
+
"is_trial_period":"false"},
|
27
|
+
{"quantity":"1",
|
28
|
+
"product_id":"com.tekkinnovations.fars.subscription.3.months",
|
29
|
+
"transaction_id":"1000000091221097",
|
30
|
+
"original_transaction_id":"1000000091221097",
|
31
|
+
"purchase_date":"2013-11-26 05:58:48 Etc/GMT",
|
32
|
+
"purchase_date_ms":"1385445528000",
|
33
|
+
"purchase_date_pst":"2013-11-25 21:58:48 America/Los_Angeles",
|
34
|
+
"original_purchase_date":"2013-10-24 09:40:22 Etc/GMT",
|
35
|
+
"original_purchase_date_ms":"1382607622000",
|
36
|
+
"original_purchase_date_pst":"2013-10-24 02:40:22 America/Los_Angeles",
|
37
|
+
"is_trial_period":"false"}
|
38
|
+
]}}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'fakeweb'
|
2
|
+
|
3
|
+
module FakeJsonHelper
|
4
|
+
|
5
|
+
def fake_json(expected, options = {})
|
6
|
+
FakeWeb.register_uri(
|
7
|
+
:post,
|
8
|
+
Itunes.endpoint,
|
9
|
+
options.merge(
|
10
|
+
:body => File.read(File.join(File.dirname(__FILE__), '../fake_json', "#{expected}.json"))
|
11
|
+
)
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def post_to(endpoint)
|
16
|
+
raise_error(
|
17
|
+
FakeWeb::NetConnectNotAllowedError,
|
18
|
+
"Real HTTP connections are disabled. Unregistered request: POST #{endpoint}"
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
FakeWeb.allow_net_connect = false
|
25
|
+
include FakeJsonHelper
|
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Itunes::Receipt do
|
4
|
+
|
5
|
+
describe '.verify!' do
|
6
|
+
it 'should support sandbox mode' do
|
7
|
+
sandbox_mode do
|
8
|
+
expect do
|
9
|
+
Itunes::Receipt.verify! 'receipt-data'
|
10
|
+
end.to post_to Itunes::ENDPOINT[:sandbox]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should not pass along shared secret if not set' do
|
15
|
+
fake_json(:invalid)
|
16
|
+
Itunes.shared_secret = nil
|
17
|
+
RestClient.should_receive(:post).with(Itunes.endpoint, {:'receipt-data' => 'receipt-data'}.to_json).and_return("{}")
|
18
|
+
expect do
|
19
|
+
Itunes::Receipt.verify! 'receipt-data'
|
20
|
+
end.to raise_error Itunes::Receipt::VerificationFailed
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should pass along shared secret if set' do
|
24
|
+
fake_json(:invalid)
|
25
|
+
Itunes.shared_secret = 'hey'
|
26
|
+
RestClient.should_receive(:post).with(Itunes.endpoint, {:'receipt-data' => 'receipt-data', :password => 'hey'}.to_json).and_return("{}")
|
27
|
+
expect do
|
28
|
+
Itunes::Receipt.verify! 'receipt-data'
|
29
|
+
end.to raise_error Itunes::Receipt::VerificationFailed
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'when invalid' do
|
33
|
+
before do
|
34
|
+
fake_json :invalid
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should raise VerificationFailed' do
|
38
|
+
expect do
|
39
|
+
Itunes::Receipt.verify! 'invalid'
|
40
|
+
end.to raise_error Itunes::Receipt::VerificationFailed
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'due to a sandbox receipt reply' do
|
44
|
+
before do
|
45
|
+
fake_json :sandboxed
|
46
|
+
sandbox_mode do
|
47
|
+
fake_json :valid
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when sandbox receipt accepted explicitly' do
|
52
|
+
it 'should try and verify the receipt against the sandbox ' do
|
53
|
+
receipt = Itunes::Receipt.verify! 'sandboxed', :allow_sandbox_receipt
|
54
|
+
receipt.should be_instance_of Itunes::Receipt
|
55
|
+
receipt.transaction_id.should == '1000000001479608'
|
56
|
+
receipt.itunes_env.should == :sandbox
|
57
|
+
receipt.sandbox?.should eq true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'otherwise' do
|
62
|
+
it 'should raise SandboxReceiptReceived exception' do
|
63
|
+
expect do
|
64
|
+
Itunes::Receipt.verify! 'sandboxed'
|
65
|
+
end.to raise_error Itunes::Receipt::SandboxReceiptReceived
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'when valid' do
|
72
|
+
before do
|
73
|
+
fake_json :valid
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should not be an application receipt' do
|
77
|
+
receipt = Itunes::Receipt.verify! 'valid_application'
|
78
|
+
receipt.application_receipt?.should == false
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should return valid Receipt instance' do
|
82
|
+
receipt = Itunes::Receipt.verify! 'valid'
|
83
|
+
receipt.should be_instance_of Itunes::Receipt
|
84
|
+
receipt.quantity == 1
|
85
|
+
receipt.product_id.should == 'com.cerego.iknow.30d'
|
86
|
+
receipt.transaction_id.should == '1000000001479608'
|
87
|
+
receipt.purchase_date.should == Time.utc(2011, 2, 17, 6, 20, 57)
|
88
|
+
receipt.bid.should == 'com.cerego.iknow'
|
89
|
+
receipt.bvrs.should == '1.0'
|
90
|
+
receipt.original.quantity.should be_nil
|
91
|
+
receipt.original.transaction_id.should == '1000000001479608'
|
92
|
+
receipt.original.purchase_date.should == Time.utc(2011, 2, 17, 6, 20, 57)
|
93
|
+
receipt.expires_date.should be_nil
|
94
|
+
receipt.receipt_data.should be_nil
|
95
|
+
receipt.itunes_env.should == :production
|
96
|
+
|
97
|
+
# Those attributes are not returned from iTunes Connect Sandbox
|
98
|
+
receipt.app_item_id.should be_nil
|
99
|
+
receipt.version_external_identifier.should be_nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when application receipt' do
|
104
|
+
before do
|
105
|
+
fake_json :valid_application
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should be an application receipt' do
|
109
|
+
receipt = Itunes::Receipt.verify! 'valid_application'
|
110
|
+
receipt.application_receipt?.should == true
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should return valid Receipt instance' do
|
114
|
+
receipt = Itunes::Receipt.verify! 'valid_application'
|
115
|
+
receipt.bundle_id.should == 'com.tekkinnovations.fars'
|
116
|
+
receipt.application_version.should == '1.80'
|
117
|
+
receipt.in_app.should be_instance_of Array
|
118
|
+
|
119
|
+
receipt.in_app[0].should be_instance_of Itunes::Receipt
|
120
|
+
receipt.in_app[0].quantity.should == 1
|
121
|
+
receipt.in_app[0].product_id.should == "com.tekkinnovations.fars.subscription.6.months"
|
122
|
+
receipt.in_app[0].transaction_id.should == "1000000091176126"
|
123
|
+
receipt.in_app[0].purchase_date.should == Time.utc(2013, 11, 26, 5, 58, 48)
|
124
|
+
receipt.in_app[0].original.purchase_date.should == Time.utc(2013, 10, 24, 4, 55, 56)
|
125
|
+
|
126
|
+
receipt.in_app[1].should be_instance_of Itunes::Receipt
|
127
|
+
receipt.in_app[1].quantity.should == 1
|
128
|
+
receipt.in_app[1].product_id.should == "com.tekkinnovations.fars.subscription.3.months"
|
129
|
+
receipt.in_app[1].transaction_id.should == "1000000091221097"
|
130
|
+
receipt.in_app[1].purchase_date.should == Time.utc(2013, 11, 26, 5, 58, 48)
|
131
|
+
receipt.in_app[1].original.purchase_date.should == Time.utc(2013, 10, 24, 9, 40, 22)
|
132
|
+
|
133
|
+
receipt.original.quantity.should be_nil
|
134
|
+
receipt.original.transaction_id.should be_nil
|
135
|
+
receipt.original.purchase_date.should == Time.utc(2013, 8, 1, 7, 00, 00)
|
136
|
+
receipt.original.application_version.should == '1.0'
|
137
|
+
|
138
|
+
receipt.should be_instance_of Itunes::Receipt
|
139
|
+
receipt.quantity.should be_nil
|
140
|
+
receipt.product_id.should be_nil
|
141
|
+
receipt.transaction_id.should be_nil
|
142
|
+
receipt.purchase_date.should be_nil
|
143
|
+
receipt.bid.should be_nil
|
144
|
+
receipt.bvrs.should be_nil
|
145
|
+
receipt.expires_date.should be_nil
|
146
|
+
receipt.receipt_data.should be_nil
|
147
|
+
receipt.itunes_env.should == :production
|
148
|
+
|
149
|
+
# Those attributes are not returned from iTunes Connect Sandbox
|
150
|
+
receipt.app_item_id.should be_nil
|
151
|
+
receipt.version_external_identifier.should be_nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'when autorenew subscription' do
|
156
|
+
before do
|
157
|
+
fake_json :autorenew_subscription
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should return valid Receipt instance for autorenew subscription' do
|
161
|
+
original_transaction_id = '1000000057005439'
|
162
|
+
original_purchase_date = Time.utc(2012, 10, 11, 14, 45, 40)
|
163
|
+
receipt = Itunes::Receipt.verify! 'autorenew_subscription'
|
164
|
+
receipt.should be_instance_of Itunes::Receipt
|
165
|
+
receipt.quantity == 1
|
166
|
+
receipt.product_id.should == 'com.notkeepingitreal.fizzbuzz.subscription.autorenew1m'
|
167
|
+
receipt.transaction_id.should == '1000000055076747'
|
168
|
+
receipt.purchase_date.should == Time.utc(2012, 10, 13, 19, 40, 8)
|
169
|
+
receipt.bid.should == 'com.notkeepingitreal.fizzbuzz'
|
170
|
+
receipt.bvrs.should == '1.0'
|
171
|
+
receipt.original.quantity.should be_nil
|
172
|
+
receipt.original.transaction_id.should == original_transaction_id
|
173
|
+
receipt.original.purchase_date.should == original_purchase_date
|
174
|
+
receipt.expires_date.should == Time.utc(2012, 10, 13, 19, 45, 8)
|
175
|
+
receipt.receipt_data.should be_nil
|
176
|
+
|
177
|
+
# Those attributes are not returned from iTunes Connect Sandbox
|
178
|
+
receipt.app_item_id.should be_nil
|
179
|
+
receipt.version_external_identifier.should be_nil
|
180
|
+
|
181
|
+
latest = receipt.latest
|
182
|
+
latest.should be_instance_of Itunes::Receipt
|
183
|
+
latest.quantity == 1
|
184
|
+
latest.product_id.should == 'com.notkeepingitreal.fizzbuzz.subscription.autorenew1m'
|
185
|
+
latest.transaction_id.should == '1000000052076747'
|
186
|
+
latest.purchase_date.should == Time.utc(2012, 10, 13, 19, 40, 8)
|
187
|
+
latest.expires_date.should == Time.utc(2012, 10, 13, 19, 50, 8) # five minutes after the "old" receipt
|
188
|
+
latest.bid.should == 'com.notkeepingitreal.fizzbuzz'
|
189
|
+
latest.bvrs.should == '1.0'
|
190
|
+
latest.original.quantity.should be_nil
|
191
|
+
latest.original.transaction_id.should == original_transaction_id
|
192
|
+
latest.original.purchase_date.should == original_purchase_date
|
193
|
+
latest.receipt_data.should == 'junk='
|
194
|
+
|
195
|
+
# Those attributes are not returned from iTunes Connect Sandbox
|
196
|
+
latest.app_item_id.should be_nil
|
197
|
+
latest.version_external_identifier.should be_nil
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context 'when expired autorenew subscription' do
|
202
|
+
before do
|
203
|
+
fake_json :autorenew_subscription_expired
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'should raise ExpiredReceiptReceived exception' do
|
207
|
+
expect do
|
208
|
+
Itunes::Receipt.verify! 'autorenew_subscription_expired'
|
209
|
+
end.to raise_error Itunes::Receipt::ExpiredReceiptReceived do |e|
|
210
|
+
e.receipt.should_not be_nil
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'when offline' do
|
217
|
+
before do
|
218
|
+
fake_json :offline
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'should raise ReceiptServerOffline exception' do
|
222
|
+
expect do
|
223
|
+
Itunes::Receipt.verify! 'offline'
|
224
|
+
end.to raise_error Itunes::Receipt::ReceiptServerOffline
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe '#latest' do
|
229
|
+
let(:receipt) { Itunes::Receipt.verify! 'receipt-data' }
|
230
|
+
subject { receipt.latest }
|
231
|
+
|
232
|
+
context 'when latest_receipt_info is a Hash' do
|
233
|
+
before do
|
234
|
+
fake_json :autorenew_subscription
|
235
|
+
end
|
236
|
+
it { should be_a Itunes::Receipt }
|
237
|
+
end
|
238
|
+
|
239
|
+
context 'when latest_receipt_info is an Array' do
|
240
|
+
before do
|
241
|
+
fake_json :array_of_latest_receipt_info
|
242
|
+
end
|
243
|
+
it { should be_a Array }
|
244
|
+
it 'should include only Itunes::Receipt' do
|
245
|
+
receipt.latest.each do |element|
|
246
|
+
element.should be_a Itunes::Receipt
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
data/spec/itunes_spec.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Itunes do
|
4
|
+
|
5
|
+
describe 'the module' do
|
6
|
+
before do
|
7
|
+
Itunes.shared_secret = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'sandbox' do
|
11
|
+
it 'should be production by default' do
|
12
|
+
Itunes.sandbox?.should == false
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should be settable' do
|
16
|
+
Itunes.sandbox!
|
17
|
+
Itunes.sandbox?.should == true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'shared_secret' do
|
22
|
+
it 'should allow setting' do
|
23
|
+
Itunes.shared_secret.should be_nil
|
24
|
+
Itunes.shared_secret = 'hey'
|
25
|
+
Itunes.shared_secret.should == 'hey'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter 'spec'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rspec'
|
8
|
+
require 'itunes/receipt'
|
9
|
+
require 'helpers/fake_json_helper'
|
10
|
+
|
11
|
+
def sandbox_mode(&block)
|
12
|
+
Itunes.sandbox!
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
Itunes.sandbox = false
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: suhovius-itunes-receipt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- nov matake
|
8
|
+
- suhovius
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2016-04-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 1.4.3
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 1.4.3
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: restclient_with_cert
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: activesupport
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '2.3'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '2.3'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: i18n
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :runtime
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rake
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.8'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.8'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rspec
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '2'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '2'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: fakeweb
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 1.3.0
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 1.3.0
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: simplecov
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: Handle iTunes In App Purchase Receipt Verification
|
127
|
+
email: nov@matake.jp
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".gitignore"
|
133
|
+
- ".rspec"
|
134
|
+
- ".travis.yml"
|
135
|
+
- Gemfile
|
136
|
+
- LICENSE
|
137
|
+
- README.rdoc
|
138
|
+
- Rakefile
|
139
|
+
- VERSION
|
140
|
+
- itunes-receipt.gemspec
|
141
|
+
- lib/itunes.rb
|
142
|
+
- lib/itunes/receipt.rb
|
143
|
+
- spec/fake_json/array_of_latest_receipt_info.json
|
144
|
+
- spec/fake_json/autorenew_subscription.json
|
145
|
+
- spec/fake_json/autorenew_subscription_expired.json
|
146
|
+
- spec/fake_json/invalid.json
|
147
|
+
- spec/fake_json/offline.json
|
148
|
+
- spec/fake_json/sandboxed.json
|
149
|
+
- spec/fake_json/valid.json
|
150
|
+
- spec/fake_json/valid_application.json
|
151
|
+
- spec/helpers/fake_json_helper.rb
|
152
|
+
- spec/itunes/receipt_spec.rb
|
153
|
+
- spec/itunes_spec.rb
|
154
|
+
- spec/spec_helper.rb
|
155
|
+
homepage: https://github.com/suhovius/itunes-receipt
|
156
|
+
licenses: []
|
157
|
+
metadata: {}
|
158
|
+
post_install_message:
|
159
|
+
rdoc_options: []
|
160
|
+
require_paths:
|
161
|
+
- lib
|
162
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
168
|
+
requirements:
|
169
|
+
- - ">="
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
version: '0'
|
172
|
+
requirements: []
|
173
|
+
rubyforge_project:
|
174
|
+
rubygems_version: 2.4.3
|
175
|
+
signing_key:
|
176
|
+
specification_version: 4
|
177
|
+
summary: Handle iTunes In App Purchase Receipt Verification
|
178
|
+
test_files:
|
179
|
+
- spec/fake_json/array_of_latest_receipt_info.json
|
180
|
+
- spec/fake_json/autorenew_subscription.json
|
181
|
+
- spec/fake_json/autorenew_subscription_expired.json
|
182
|
+
- spec/fake_json/invalid.json
|
183
|
+
- spec/fake_json/offline.json
|
184
|
+
- spec/fake_json/sandboxed.json
|
185
|
+
- spec/fake_json/valid.json
|
186
|
+
- spec/fake_json/valid_application.json
|
187
|
+
- spec/helpers/fake_json_helper.rb
|
188
|
+
- spec/itunes/receipt_spec.rb
|
189
|
+
- spec/itunes_spec.rb
|
190
|
+
- spec/spec_helper.rb
|
191
|
+
has_rdoc:
|