candy_check 0.1.2 → 0.2.0

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.
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