candy_check 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -12
  3. data/Guardfile +42 -0
  4. data/MIGRATION_GUIDE_0_2_0.md +141 -0
  5. data/README.md +49 -26
  6. data/candy_check.gemspec +32 -26
  7. data/lib/candy_check/cli/app.rb +16 -33
  8. data/lib/candy_check/cli/commands/play_store.rb +12 -13
  9. data/lib/candy_check/play_store.rb +17 -10
  10. data/lib/candy_check/play_store/android_publisher_service.rb +6 -0
  11. data/lib/candy_check/play_store/product_purchases/product_purchase.rb +90 -0
  12. data/lib/candy_check/play_store/product_purchases/product_verification.rb +53 -0
  13. data/lib/candy_check/play_store/subscription_purchases/subscription_purchase.rb +141 -0
  14. data/lib/candy_check/play_store/subscription_purchases/subscription_verification.rb +55 -0
  15. data/lib/candy_check/play_store/verification_failure.rb +8 -6
  16. data/lib/candy_check/play_store/verifier.rb +24 -49
  17. data/lib/candy_check/version.rb +1 -1
  18. data/spec/candy_check_spec.rb +2 -2
  19. data/spec/cli/commands/play_store_spec.rb +10 -43
  20. data/spec/fixtures/play_store/random_dummy_key.json +12 -0
  21. data/spec/fixtures/vcr_cassettes/play_store/product_purchases/permission_denied.yml +196 -0
  22. data/spec/fixtures/vcr_cassettes/play_store/product_purchases/response_with_empty_body.yml +183 -0
  23. data/spec/fixtures/vcr_cassettes/play_store/product_purchases/valid_but_not_consumed.yml +122 -0
  24. data/spec/fixtures/vcr_cassettes/play_store/subscription_purchases/permission_denied.yml +196 -0
  25. data/spec/fixtures/vcr_cassettes/play_store/subscription_purchases/valid_but_expired.yml +127 -0
  26. data/spec/play_store/product_purchases/product_purchase_spec.rb +110 -0
  27. data/spec/play_store/product_purchases/product_verification_spec.rb +49 -0
  28. data/spec/play_store/subscription_purchases/subscription_purchase_spec.rb +181 -0
  29. data/spec/play_store/subscription_purchases/subscription_verification_spec.rb +65 -0
  30. data/spec/play_store/verification_failure_spec.rb +18 -18
  31. data/spec/play_store/verifier_spec.rb +32 -96
  32. data/spec/spec_helper.rb +24 -11
  33. metadata +120 -47
  34. data/lib/candy_check/play_store/client.rb +0 -139
  35. data/lib/candy_check/play_store/config.rb +0 -51
  36. data/lib/candy_check/play_store/discovery_repository.rb +0 -33
  37. data/lib/candy_check/play_store/receipt.rb +0 -81
  38. data/lib/candy_check/play_store/subscription.rb +0 -139
  39. data/lib/candy_check/play_store/subscription_verification.rb +0 -30
  40. data/lib/candy_check/play_store/verification.rb +0 -48
  41. data/spec/fixtures/api_cache.dump +0 -1
  42. data/spec/fixtures/play_store/api_cache.dump +0 -1
  43. data/spec/fixtures/play_store/auth_failure.txt +0 -18
  44. data/spec/fixtures/play_store/auth_success.txt +0 -20
  45. data/spec/fixtures/play_store/discovery.txt +0 -2841
  46. data/spec/fixtures/play_store/dummy.p12 +0 -0
  47. data/spec/fixtures/play_store/empty.txt +0 -17
  48. data/spec/fixtures/play_store/products_failure.txt +0 -29
  49. data/spec/fixtures/play_store/products_success.txt +0 -22
  50. data/spec/play_store/client_spec.rb +0 -125
  51. data/spec/play_store/config_spec.rb +0 -96
  52. data/spec/play_store/discovery_respository_spec.rb +0 -31
  53. data/spec/play_store/receipt_spec.rb +0 -88
  54. data/spec/play_store/subscription_spec.rb +0 -138
  55. data/spec/play_store/subscription_verification_spec.rb +0 -97
  56. data/spec/play_store/verification_spec.rb +0 -81
@@ -1,21 +1,29 @@
1
- require 'coveralls'
1
+ require "coveralls"
2
2
  Coveralls.wear!
3
3
 
4
- require 'candy_check'
5
- require 'candy_check/cli'
4
+ require "candy_check"
5
+ require "candy_check/cli"
6
6
 
7
- require 'minitest/autorun'
8
- require 'minitest/around/spec'
7
+ def in_continuous_integration_environment?
8
+ ENV["CI"] || ENV["TRAVIS"] || ENV["CONTINUOUS_INTEGRATION"]
9
+ end
10
+
11
+ require "minitest/autorun"
12
+ require "minitest/around/spec"
13
+ require "minitest/focus" unless in_continuous_integration_environment?
14
+
15
+ require "webmock/minitest"
16
+ require "vcr"
9
17
 
10
- require 'webmock/minitest'
18
+ require "timecop"
11
19
 
12
- require 'timecop'
20
+ require "pry"
13
21
 
14
- require_relative 'support/with_fixtures'
15
- require_relative 'support/with_temp_file'
16
- require_relative 'support/with_command'
22
+ require_relative "support/with_fixtures"
23
+ require_relative "support/with_temp_file"
24
+ require_relative "support/with_command"
17
25
 
18
- ENV['DEBUG'] && Google::APIClient.logger.level = Logger::DEBUG
26
+ ENV["DEBUG"] && Google::APIClient.logger.level = Logger::DEBUG
19
27
 
20
28
  module MiniTest
21
29
  module Assertions
@@ -37,3 +45,8 @@ module MiniTest
37
45
  infect_an_assertion :assert_false, :must_be_false, :unary
38
46
  end
39
47
  end
48
+
49
+ VCR.configure do |config|
50
+ config.cassette_library_dir = "spec/fixtures/vcr_cassettes"
51
+ config.hook_into :webmock
52
+ end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: candy_check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Thiel
8
+ - Christoph Weegen
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2018-06-25 00:00:00.000000000 Z
12
+ date: 2019-11-07 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: google-api-client
@@ -16,14 +17,14 @@ dependencies:
16
17
  requirements:
17
18
  - - "~>"
18
19
  - !ruby/object:Gem::Version
19
- version: 0.8.6
20
+ version: 0.34.0
20
21
  type: :runtime
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
25
  - - "~>"
25
26
  - !ruby/object:Gem::Version
26
- version: 0.8.6
27
+ version: 0.34.0
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: multi_json
29
30
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +59,14 @@ dependencies:
58
59
  requirements:
59
60
  - - "~>"
60
61
  - !ruby/object:Gem::Version
61
- version: '1.14'
62
+ version: '2.0'
62
63
  type: :development
63
64
  prerelease: false
64
65
  version_requirements: !ruby/object:Gem::Requirement
65
66
  requirements:
66
67
  - - "~>"
67
68
  - !ruby/object:Gem::Version
68
- version: '1.14'
69
+ version: '2.0'
69
70
  - !ruby/object:Gem::Dependency
70
71
  name: coveralls
71
72
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +123,20 @@ dependencies:
122
123
  - - "~>"
123
124
  - !ruby/object:Gem::Version
124
125
  version: '0.4'
126
+ - !ruby/object:Gem::Dependency
127
+ name: minitest-focus
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
125
140
  - !ruby/object:Gem::Dependency
126
141
  name: rake
127
142
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +193,76 @@ dependencies:
178
193
  - - "~>"
179
194
  - !ruby/object:Gem::Version
180
195
  version: '3.0'
196
+ - !ruby/object:Gem::Dependency
197
+ name: vcr
198
+ requirement: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ type: :development
204
+ prerelease: false
205
+ version_requirements: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - ">="
208
+ - !ruby/object:Gem::Version
209
+ version: '0'
210
+ - !ruby/object:Gem::Dependency
211
+ name: pry
212
+ requirement: !ruby/object:Gem::Requirement
213
+ requirements:
214
+ - - ">="
215
+ - !ruby/object:Gem::Version
216
+ version: '0'
217
+ type: :development
218
+ prerelease: false
219
+ version_requirements: !ruby/object:Gem::Requirement
220
+ requirements:
221
+ - - ">="
222
+ - !ruby/object:Gem::Version
223
+ version: '0'
224
+ - !ruby/object:Gem::Dependency
225
+ name: awesome_print
226
+ requirement: !ruby/object:Gem::Requirement
227
+ requirements:
228
+ - - ">="
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ type: :development
232
+ prerelease: false
233
+ version_requirements: !ruby/object:Gem::Requirement
234
+ requirements:
235
+ - - ">="
236
+ - !ruby/object:Gem::Version
237
+ version: '0'
238
+ - !ruby/object:Gem::Dependency
239
+ name: guard
240
+ requirement: !ruby/object:Gem::Requirement
241
+ requirements:
242
+ - - ">="
243
+ - !ruby/object:Gem::Version
244
+ version: '0'
245
+ type: :development
246
+ prerelease: false
247
+ version_requirements: !ruby/object:Gem::Requirement
248
+ requirements:
249
+ - - ">="
250
+ - !ruby/object:Gem::Version
251
+ version: '0'
252
+ - !ruby/object:Gem::Dependency
253
+ name: guard-minitest
254
+ requirement: !ruby/object:Gem::Requirement
255
+ requirements:
256
+ - - ">="
257
+ - !ruby/object:Gem::Version
258
+ version: '0'
259
+ type: :development
260
+ prerelease: false
261
+ version_requirements: !ruby/object:Gem::Requirement
262
+ requirements:
263
+ - - ">="
264
+ - !ruby/object:Gem::Version
265
+ version: '0'
181
266
  description:
182
267
  email:
183
268
  - jonas@thiel.io
@@ -191,7 +276,9 @@ files:
191
276
  - ".ruby-version"
192
277
  - ".travis.yml"
193
278
  - Gemfile
279
+ - Guardfile
194
280
  - LICENSE.txt
281
+ - MIGRATION_GUIDE_0_2_0.md
195
282
  - README.md
196
283
  - Rakefile
197
284
  - bin/candy_check
@@ -215,13 +302,11 @@ files:
215
302
  - lib/candy_check/cli/commands/version.rb
216
303
  - lib/candy_check/cli/out.rb
217
304
  - lib/candy_check/play_store.rb
218
- - lib/candy_check/play_store/client.rb
219
- - lib/candy_check/play_store/config.rb
220
- - lib/candy_check/play_store/discovery_repository.rb
221
- - lib/candy_check/play_store/receipt.rb
222
- - lib/candy_check/play_store/subscription.rb
223
- - lib/candy_check/play_store/subscription_verification.rb
224
- - lib/candy_check/play_store/verification.rb
305
+ - lib/candy_check/play_store/android_publisher_service.rb
306
+ - lib/candy_check/play_store/product_purchases/product_purchase.rb
307
+ - lib/candy_check/play_store/product_purchases/product_verification.rb
308
+ - lib/candy_check/play_store/subscription_purchases/subscription_purchase.rb
309
+ - lib/candy_check/play_store/subscription_purchases/subscription_verification.rb
225
310
  - lib/candy_check/play_store/verification_failure.rb
226
311
  - lib/candy_check/play_store/verifier.rb
227
312
  - lib/candy_check/utils.rb
@@ -242,23 +327,17 @@ files:
242
327
  - spec/cli/commands/play_store_spec.rb
243
328
  - spec/cli/commands/version_spec.rb
244
329
  - spec/cli/out_spec.rb
245
- - spec/fixtures/api_cache.dump
246
- - spec/fixtures/play_store/api_cache.dump
247
- - spec/fixtures/play_store/auth_failure.txt
248
- - spec/fixtures/play_store/auth_success.txt
249
- - spec/fixtures/play_store/discovery.txt
250
- - spec/fixtures/play_store/dummy.p12
251
- - spec/fixtures/play_store/empty.txt
252
- - spec/fixtures/play_store/products_failure.txt
253
- - spec/fixtures/play_store/products_success.txt
254
- - spec/play_store/client_spec.rb
255
- - spec/play_store/config_spec.rb
256
- - spec/play_store/discovery_respository_spec.rb
257
- - spec/play_store/receipt_spec.rb
258
- - spec/play_store/subscription_spec.rb
259
- - spec/play_store/subscription_verification_spec.rb
330
+ - spec/fixtures/play_store/random_dummy_key.json
331
+ - spec/fixtures/vcr_cassettes/play_store/product_purchases/permission_denied.yml
332
+ - spec/fixtures/vcr_cassettes/play_store/product_purchases/response_with_empty_body.yml
333
+ - spec/fixtures/vcr_cassettes/play_store/product_purchases/valid_but_not_consumed.yml
334
+ - spec/fixtures/vcr_cassettes/play_store/subscription_purchases/permission_denied.yml
335
+ - spec/fixtures/vcr_cassettes/play_store/subscription_purchases/valid_but_expired.yml
336
+ - spec/play_store/product_purchases/product_purchase_spec.rb
337
+ - spec/play_store/product_purchases/product_verification_spec.rb
338
+ - spec/play_store/subscription_purchases/subscription_purchase_spec.rb
339
+ - spec/play_store/subscription_purchases/subscription_verification_spec.rb
260
340
  - spec/play_store/verification_failure_spec.rb
261
- - spec/play_store/verification_spec.rb
262
341
  - spec/play_store/verifier_spec.rb
263
342
  - spec/spec_helper.rb
264
343
  - spec/support/with_command.rb
@@ -276,7 +355,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
276
355
  requirements:
277
356
  - - ">="
278
357
  - !ruby/object:Gem::Version
279
- version: '2.0'
358
+ version: '2.4'
280
359
  required_rubygems_version: !ruby/object:Gem::Requirement
281
360
  requirements:
282
361
  - - ">="
@@ -284,7 +363,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
284
363
  version: '0'
285
364
  requirements: []
286
365
  rubyforge_project:
287
- rubygems_version: 2.7.6
366
+ rubygems_version: 2.7.9
288
367
  signing_key:
289
368
  specification_version: 4
290
369
  summary: Check and verify in-app receipts
@@ -303,23 +382,17 @@ test_files:
303
382
  - spec/cli/commands/play_store_spec.rb
304
383
  - spec/cli/commands/version_spec.rb
305
384
  - spec/cli/out_spec.rb
306
- - spec/fixtures/api_cache.dump
307
- - spec/fixtures/play_store/api_cache.dump
308
- - spec/fixtures/play_store/auth_failure.txt
309
- - spec/fixtures/play_store/auth_success.txt
310
- - spec/fixtures/play_store/discovery.txt
311
- - spec/fixtures/play_store/dummy.p12
312
- - spec/fixtures/play_store/empty.txt
313
- - spec/fixtures/play_store/products_failure.txt
314
- - spec/fixtures/play_store/products_success.txt
315
- - spec/play_store/client_spec.rb
316
- - spec/play_store/config_spec.rb
317
- - spec/play_store/discovery_respository_spec.rb
318
- - spec/play_store/receipt_spec.rb
319
- - spec/play_store/subscription_spec.rb
320
- - spec/play_store/subscription_verification_spec.rb
385
+ - spec/fixtures/play_store/random_dummy_key.json
386
+ - spec/fixtures/vcr_cassettes/play_store/product_purchases/permission_denied.yml
387
+ - spec/fixtures/vcr_cassettes/play_store/product_purchases/response_with_empty_body.yml
388
+ - spec/fixtures/vcr_cassettes/play_store/product_purchases/valid_but_not_consumed.yml
389
+ - spec/fixtures/vcr_cassettes/play_store/subscription_purchases/permission_denied.yml
390
+ - spec/fixtures/vcr_cassettes/play_store/subscription_purchases/valid_but_expired.yml
391
+ - spec/play_store/product_purchases/product_purchase_spec.rb
392
+ - spec/play_store/product_purchases/product_verification_spec.rb
393
+ - spec/play_store/subscription_purchases/subscription_purchase_spec.rb
394
+ - spec/play_store/subscription_purchases/subscription_verification_spec.rb
321
395
  - spec/play_store/verification_failure_spec.rb
322
- - spec/play_store/verification_spec.rb
323
396
  - spec/play_store/verifier_spec.rb
324
397
  - spec/spec_helper.rb
325
398
  - spec/support/with_command.rb
@@ -1,139 +0,0 @@
1
- module CandyCheck
2
- module PlayStore
3
- # A client which uses the official Google API SDK to authenticate
4
- # and request product information from Google's API.
5
- #
6
- # @example Usage
7
- # config = ClientConfig.new({...})
8
- # client = Client.new(config)
9
- # client.boot! # a single time
10
- # client.verify('my.bundle', 'product_1', 'a-very-long-secure-token')
11
- # # ... multiple calls from now on
12
- # client.verify('my.bundle', 'product_1', 'another-long-token')
13
- class Client
14
- # Error thrown if the discovery of the API wasn't successful
15
- class DiscoveryError < RuntimeError; end
16
-
17
- # API endpoint
18
- API_URL = 'https://accounts.google.com/o/oauth2/token'.freeze
19
- # API scope for Android services
20
- API_SCOPE = 'https://www.googleapis.com/auth/androidpublisher'.freeze
21
- # API discovery namespace
22
- API_DISCOVER = 'androidpublisher'.freeze
23
- # API version
24
- API_VERSION = 'v2'.freeze
25
-
26
- # Initializes a client using a configuration.
27
- # @param config [ClientConfig]
28
- def initialize(config)
29
- @config = config
30
- end
31
-
32
- # Boots a client by discovering the API's services and then authorizes
33
- # by fetching an access token.
34
- # If the config has a cache_file the client tries to load discovery
35
- def boot!
36
- @api_client = Google::APIClient.new(
37
- application_name: config.application_name,
38
- application_version: config.application_version,
39
- user_agent: user_agent
40
- )
41
- discover!
42
- authorize!
43
- end
44
-
45
- # Calls the remote API to load the product information for a specific
46
- # combination of parameter which should be loaded from the client.
47
- # @param package [String] the app's package name
48
- # @param product_id [String] the app's item id
49
- # @param token [String] the purchase token
50
- # @return [Hash] result of the API call
51
- def verify(package, product_id, token)
52
- parameters = {
53
- 'packageName' => package,
54
- 'productId' => product_id,
55
- 'token' => token
56
- }
57
- execute(parameters, rpc.purchases.products.get)
58
- end
59
-
60
- # Calls the remote API to load the product information for a specific
61
- # combination of parameter which should be loaded from the client.
62
- # @param package [String] the app's package name
63
- # @param subscription_id [String] the app's item id
64
- # @param token [String] the purchase token
65
- # @return [Hash] result of the API call
66
- def verify_subscription(package, subscription_id, token)
67
- parameters = {
68
- 'packageName' => package,
69
- 'subscriptionId' => subscription_id,
70
- 'token' => token
71
- }
72
- execute(parameters, rpc.purchases.subscriptions.get)
73
- end
74
-
75
- private
76
-
77
- attr_reader :config, :api_client, :rpc
78
-
79
- # Execute api call through the API Client's HTTP command class
80
- # @param parameters [hash] the parameters to send to the command
81
- # @param api_method [Method] which api method to call
82
- # @return [hash] the data response, as a hash
83
- def execute(parameters, api_method)
84
- api_client.execute(
85
- api_method: api_method,
86
- parameters: parameters
87
- ).data.to_hash
88
- end
89
-
90
- # Builds a custom user agent to prevent Google::APIClient to
91
- # use an invalid auto-generated one
92
- # @see https://github.com/google/google-api-ruby-client/blob/15853007bf1fc8ad000bb35dafdd3ca6bfa8ae26/lib/google/api_client.rb#L112
93
- def user_agent
94
- [
95
- "#{config.application_name}/#{config.application_version}",
96
- "google-api-ruby-client/#{Google::APIClient::VERSION::STRING}",
97
- Google::APIClient::ENV::OS_VERSION,
98
- '(gzip)'
99
- ].join(' ').delete("\n")
100
- end
101
-
102
- def discover!
103
- @rpc = load_discover_dump || request_discover
104
- validate_rpc!
105
- write_discover_dump
106
- end
107
-
108
- def request_discover
109
- api_client.discovered_api(API_DISCOVER, API_VERSION)
110
- end
111
-
112
- def authorize!
113
- api_client.authorization = Signet::OAuth2::Client.new(
114
- token_credential_uri: API_URL,
115
- audience: API_URL,
116
- scope: API_SCOPE,
117
- issuer: config.issuer,
118
- signing_key: config.api_key
119
- )
120
- api_client.authorization.fetch_access_token!
121
- end
122
-
123
- def validate_rpc!
124
- return if rpc.purchases.products.get
125
- raise DiscoveryError, 'Unable to get the API discovery'
126
- rescue NoMethodError
127
- raise DiscoveryError, 'Unable to get the API discovery'
128
- end
129
-
130
- def load_discover_dump
131
- DiscoveryRepository.new(config.cache_file).load
132
- end
133
-
134
- def write_discover_dump
135
- DiscoveryRepository.new(config.cache_file).save(rpc)
136
- end
137
- end
138
- end
139
- end
@@ -1,51 +0,0 @@
1
- module CandyCheck
2
- module PlayStore
3
- # Configure the usage of the official Google API SDK client
4
- class Config < Utils::Config
5
- # @return [String] your application name
6
- attr_reader :application_name
7
- # @return [String] your application's version
8
- attr_reader :application_version
9
- # @return [String] an optional file to cache the discovery API result
10
- attr_reader :cache_file
11
- # @return [String] your issuer's service account e-mail
12
- attr_reader :issuer
13
- # @return [String] the path to your local *.p12 certificate file
14
- attr_reader :key_file
15
- # @return [String] the secret to load your certificate file
16
- attr_reader :key_secret
17
-
18
- # Initializes a new configuration from a hash
19
- # @param attributes [Hash]
20
- # @example Initialize with a discovery cache file
21
- # ClientConfig.new(
22
- # application_name: 'YourApplication',
23
- # application_version: '1.0',
24
- # cache_file: 'tmp/google_api_cache',
25
- # issuer: 'abcdefg@developer.gserviceaccount.com',
26
- # key_file: 'local/google.p12',
27
- # key_secret: 'notasecret'
28
- # )
29
- def initialize(attributes)
30
- super
31
- end
32
-
33
- # @return [String] the decrypted API key from Google
34
- def api_key
35
- @api_key ||= begin
36
- Google::APIClient::KeyUtils.load_from_pkcs12(key_file, key_secret)
37
- end
38
- end
39
-
40
- private
41
-
42
- def validate!
43
- validates_presence(:application_name)
44
- validates_presence(:application_version)
45
- validates_presence(:issuer)
46
- validates_presence(:key_file)
47
- validates_presence(:key_secret)
48
- end
49
- end
50
- end
51
- end