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,69 +1,44 @@
1
1
  module CandyCheck
2
2
  module PlayStore
3
3
  # Verifies purchase tokens against the Google API.
4
- # The call return either an {Receipt} or a {VerificationFailure}
4
+ # The call return either a {SubscriptionPurchases::SubscriptionPurchase} or a {VerificationFailure}
5
5
  class Verifier
6
- # Error thrown when the verifier isn't booted before the first
7
- # verification check or on double invocation
8
- class BootRequiredError < RuntimeError; end
9
-
10
- # @return [Config] the current configuration
11
- attr_reader :config
12
-
13
- # Initializes a new verifier for the application which is bound
14
- # to a configuration
15
- # @param config [Config]
16
- def initialize(config)
17
- @config = config
18
- end
19
-
20
- # Boot the module
21
- def boot!
22
- boot_error('You\'re only allowed to boot the verifier once') if booted?
23
- @client = Client.new(config)
24
- @client.boot!
6
+ # Initializes a new verifier which is bound to an authorization
7
+ # @param authorization [Google::Auth::ServiceAccountCredentials] to use against the PlayStore API
8
+ def initialize(authorization:)
9
+ @authorization = authorization
25
10
  end
26
11
 
27
12
  # Contacts the Google API and requests the product state
28
- # @param package [String] to query
13
+ # @param package_name [String] to query
29
14
  # @param product_id [String] to query
30
15
  # @param token [String] to use for authentication
31
- # @return [Receipt] if successful
16
+ # @return [ProductPurchases::ProductPurchase] if successful
32
17
  # @return [VerificationFailure] otherwise
33
- def verify(package, product_id, token)
34
- check_boot!
35
- verification = Verification.new(@client, package, product_id, token)
36
- verification.call!
18
+ def verify_product_purchase(package_name:, product_id:, token:)
19
+ verifier = CandyCheck::PlayStore::ProductPurchases::ProductVerification.new(
20
+ package_name: package_name,
21
+ product_id: product_id,
22
+ token: token,
23
+ authorization: @authorization,
24
+ )
25
+ verifier.call!
37
26
  end
38
27
 
39
28
  # Contacts the Google API and requests the product state
40
- # @param package [String] to query
29
+ # @param package_name [String] to query
41
30
  # @param subscription_id [String] to query
42
31
  # @param token [String] to use for authentication
43
- # @return [Receipt] if successful
32
+ # @return [SubscriptionPurchases::SubscriptionPurchase] if successful
44
33
  # @return [VerificationFailure] otherwise
45
- def verify_subscription(package, subscription_id, token)
46
- check_boot!
47
- v = SubscriptionVerification.new(
48
- @client, package, subscription_id, token
34
+ def verify_subscription_purchase(package_name:, subscription_id:, token:)
35
+ verifier = CandyCheck::PlayStore::SubscriptionPurchases::SubscriptionVerification.new(
36
+ package_name: package_name,
37
+ subscription_id: subscription_id,
38
+ token: token,
39
+ authorization: @authorization,
49
40
  )
50
- v.call!
51
- end
52
-
53
- private
54
-
55
- def booted?
56
- instance_variable_defined?(:@client)
57
- end
58
-
59
- def check_boot!
60
- return if booted?
61
- boot_error 'You need to boot the verifier service first: '\
62
- 'CandyCheck::PlayStore::Verifier#boot!'
63
- end
64
-
65
- def boot_error(message)
66
- raise BootRequiredError, message
41
+ verifier.call!
67
42
  end
68
43
  end
69
44
  end
@@ -1,4 +1,4 @@
1
1
  module CandyCheck
2
2
  # The current gem's version
3
- VERSION = '0.1.2'.freeze
3
+ VERSION = "0.2.0".freeze
4
4
  end
@@ -1,9 +1,9 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe CandyCheck do
4
4
  subject { CandyCheck }
5
5
 
6
- it 'has a version' do
6
+ it "has a version" do
7
7
  subject::VERSION.wont_be_nil
8
8
  end
9
9
  end
@@ -1,51 +1,18 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe CandyCheck::CLI::Commands::PlayStore do
4
4
  include WithCommand
5
- subject { CandyCheck::CLI::Commands::PlayStore }
6
- let(:arguments) { [package, product_id, token, options] }
7
- let(:package) { 'the_package' }
8
- let(:product_id) { 'the_product' }
9
- let(:token) { 'the_token' }
10
- let(:options) do
11
- {
12
- application_name: 'YourApplication',
13
- application_version: '1.0',
14
- issuer: 'abcdefg@developer.gserviceaccount.com',
15
- key_file: 'local/google.p12',
16
- key_secret: 'notasecret'
17
- }
18
- end
5
+ subject { CandyCheck::CLI::Commands::PlayStore.new(package_name, product_id, token, "json_key_file" => json_key_file) }
6
+ let(:package_name) { "my_package_name" }
7
+ let(:product_id) { "my_product_id" }
8
+ let(:token) { "my_token" }
9
+ let(:json_key_file) { File.expand_path("../../fixtures/play_store/random_dummy_key.json", __dir__) }
19
10
 
20
- before do
21
- stub = proc do |*args|
22
- @verifier = DummyPlayStoreVerifier.new(*args)
23
- end
24
- CandyCheck::PlayStore::Verifier.stub :new, stub do
11
+ it "calls and outputs the verifier" do
12
+ VCR.use_cassette("play_store/product_purchases/valid_but_not_consumed") do
25
13
  run_command!
26
- end
27
- end
28
-
29
- it 'calls and outputs the verifier' do
30
- options.each do |k, v|
31
- @verifier.config.public_send(k).must_equal v
32
- end
33
- @verifier.arguments.must_equal [package, product_id, token]
34
- out.must_be 'Hash:', result: :stubbed
35
- end
36
-
37
- private
38
-
39
- DummyPlayStoreVerifier = Struct.new(:config) do
40
- attr_reader :arguments, :booted
41
-
42
- def boot!
43
- @booted = true
44
- end
45
-
46
- def verify(*arguments)
47
- @arguments = arguments
48
- { result: :stubbed }
14
+ assert_equal "CandyCheck::PlayStore::ProductPurchases::ProductPurchase:", out.lines.first
15
+ assert_equal CandyCheck::PlayStore::ProductPurchases::ProductPurchase, out.lines[1].class
49
16
  end
50
17
  end
51
18
  end
@@ -0,0 +1,12 @@
1
+ {
2
+ "type": "service_account",
3
+ "project_id": "example",
4
+ "private_key_id": "123456789",
5
+ "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEoAIBAAKCAQEAvR7l72CT7UBP6P+02Iut8gKKbKyekz/pQxnckPp1VafuaIwC\nMvYfP4ffVJTcY5IhU9mISNxZf6YDQ0TuD1aOrZYG9wsIgGY0nXhOUZxe/Q5I+V7D\nOI/hSzKF7W0cNCvaJPUSo8+soCLNSQ5mjnV3sRZ6uJwGFN30i1GulqHHKkx3vGxb\niaAL9YG58dPSbPGHFTA/epqUyd1fzCuWHyL9dHW7aw4RroNyEtVdiftAQfaK20I2\nueeDfuEtCPaxQYFQqbz5kKnXQx3fwHRpC7/84xHxsrY576evGxHw4p5EJD37scNN\ncneTG3Ly79/VVSAlrSm6ltutx0+S70scCqK0ewIDAQABAoH/MjwC15LPuDVdBIbn\ngp2XlrEWE8fGV1ainzA/ZkMg55+ztBF8hAzcQAPXTqA76jbmo18k1DWzkDSIqVWl\n5m0XeQRg1T4ZBAIh97H9G7BtispAl/yT3nJZZaAF8wsIctMzHp36VYjUUbTs0nsA\nwtZw9JkEAAVxmBlc26TWuyw9uv4fYXuR+uOsWH8jTTVPvxM9FaCCdK+dOMnswm7Y\nlOAlJj5dANkB2KPwIeE461ThyMo9GHEjpsvciMhKLuBoTSucNkhdgapAmYTSI+/1\nf1cA/KEdCMs9ANr1HFujeS01+N1Xrw/yW6EazaDN1oFHCVORtlB295Eac0Wq6y/P\npf1BAoGBAPIw4HQWsolU3f4FdIvc2POAcSJDRgt++I9Qt/QXq1SJ2dGKIveFiJgo\nZjCfHQFVZ8xl64cLzQ1WagZA1JBbbk9g5RxHDxRv7q+Kn3ogugDo9GUoQvpuuAU6\nXHoR/mLinDorJUnttL3U49xTMfrrut4qkUg+daBVptPtylpio6EDAoGBAMfnYq08\nfd/cPEQ2XPeswgtzXsKNLqA6UXBM7ZauKaFLByjy8peMMF6JPOYlBKQif5k+Egmu\nWIe8oTm8Nn5Ymt32bEd+MkHUC7kFzQeiXnM3u0oKzJMXLAvjSTs296g50YM5zJTC\nl64ACQmQOLZ9tdKorl52ZcmdbBEcZ2uwRvkpAoGAKhs5SrWPgLTSi5FjO9W/mkYg\nZTaQ/PqsOC5ubO+Yh/AXgIiln6cFon6Tlax0HIE+tJibpDT3B3SYplGrIxXiTcao\nzovEIWd8deSB6Xe7HuFhbBzd2DBbqf0FiuuJ8KM5ShuqNfovzDkxDGMic198c5eu\n/oJtbNy3Tm0vGxu/GwUCgYAgmRPXShkAq0pMmUzZups+AMdAFIO47ymelXzc6HOz\ncKevPsbefabZk6mRohG6rkF+fMe2Om8HW3QzFQUR32MJtQh9NA//+hMbTd3cU9bx\nFPJ+pXostkehfKPReyoxjZQjwQYicAUKA8l1fMYyxBclTgp5Lvd0RC5+L9KRlgJM\n2QKBgGVIWRNVpGg38dDqdq/4ue1BoTFhqoMGi6WQm3xa+NH+lyJGacdUhGRz8PxN\nhVKpIj8ljg2Rq/CA9qSgL/Z9rhn8QUMWULuAroCp0S2pMBtZ2RB+Mg2FdVFR9/Ft\nfG7co6mKUGkFPtr48EMfeKY88BRsp3yGOsROGdDsCHItjOVH\n-----END RSA PRIVATE KEY-----\n",
6
+ "client_email": "info@example.com",
7
+ "client_id": "123456789",
8
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
9
+ "token_uri": "https://oauth2.googleapis.com/token",
10
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11
+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/example.iam.gserviceaccount.com"
12
+ }
@@ -0,0 +1,196 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://www.googleapis.com/oauth2/v4/token
6
+ body:
7
+ encoding: ASCII-8BIT
8
+ string: params
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.15.4
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - "*/*"
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Content-Type:
24
+ - application/json; charset=utf-8
25
+ Vary:
26
+ - Origin
27
+ - Referer
28
+ - X-Origin
29
+ Date:
30
+ - Fri, 04 Oct 2019 13:11:23 GMT
31
+ Server:
32
+ - scaffolding on HTTPServer2
33
+ Cache-Control:
34
+ - private
35
+ X-Xss-Protection:
36
+ - '0'
37
+ X-Frame-Options:
38
+ - SAMEORIGIN
39
+ X-Content-Type-Options:
40
+ - nosniff
41
+ Alt-Svc:
42
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
43
+ ma=2592000,h3-Q043=":443"; ma=2592000
44
+ Transfer-Encoding:
45
+ - chunked
46
+ body:
47
+ encoding: ASCII-8BIT
48
+ string: |-
49
+ {
50
+ "access_token": "some token",
51
+ "expires_in": 3600,
52
+ "token_type": "Bearer"
53
+ }
54
+ http_version:
55
+ recorded_at: Fri, 04 Oct 2019 13:11:23 GMT
56
+ - request:
57
+ method: get
58
+ uri: https://www.googleapis.com/androidpublisher/v3/applications/my_package_name/purchases/products/my_product_id/tokens/my_token
59
+ body:
60
+ encoding: UTF-8
61
+ string: ''
62
+ headers:
63
+ User-Agent:
64
+ - unknown/0.0.0 google-api-ruby-client/0.28.7 Linux/5.0.0-29-generic (gzip)
65
+ Accept:
66
+ - "*/*"
67
+ Accept-Encoding:
68
+ - gzip,deflate
69
+ Date:
70
+ - Fri, 04 Oct 2019 13:11:23 GMT
71
+ Authorization:
72
+ - Bearer some_token
73
+ Content-Type:
74
+ - application/x-www-form-urlencoded
75
+ response:
76
+ status:
77
+ code: 401
78
+ message: Unauthorized
79
+ headers:
80
+ Vary:
81
+ - Origin
82
+ - X-Origin
83
+ Www-Authenticate:
84
+ - Bearer realm="https://accounts.google.com/", error=invalid_token
85
+ Content-Type:
86
+ - application/json; charset=UTF-8
87
+ Content-Encoding:
88
+ - gzip
89
+ Date:
90
+ - Fri, 04 Oct 2019 13:11:24 GMT
91
+ Expires:
92
+ - Fri, 04 Oct 2019 13:11:24 GMT
93
+ Cache-Control:
94
+ - private, max-age=0
95
+ X-Content-Type-Options:
96
+ - nosniff
97
+ X-Frame-Options:
98
+ - SAMEORIGIN
99
+ X-Xss-Protection:
100
+ - 1; mode=block
101
+ Server:
102
+ - GSE
103
+ Alt-Svc:
104
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
105
+ ma=2592000,h3-Q043=":443"; ma=2592000
106
+ Transfer-Encoding:
107
+ - chunked
108
+ body:
109
+ encoding: UTF-8
110
+ string: |
111
+ {
112
+ "error": {
113
+ "errors": [
114
+ {
115
+ "domain": "androidpublisher",
116
+ "reason": "permissionDenied",
117
+ "message": "The current user has insufficient permissions to perform the requested operation."
118
+ }
119
+ ],
120
+ "code": 401,
121
+ "message": "The current user has insufficient permissions to perform the requested operation."
122
+ }
123
+ }
124
+ http_version:
125
+ recorded_at: Fri, 04 Oct 2019 13:11:24 GMT
126
+ - request:
127
+ method: get
128
+ uri: https://www.googleapis.com/androidpublisher/v3/applications/my_package_name/purchases/products/my_product_id/tokens/my_token
129
+ body:
130
+ encoding: UTF-8
131
+ string: ''
132
+ headers:
133
+ User-Agent:
134
+ - unknown/0.0.0 google-api-ruby-client/0.28.7 Linux/5.0.0-29-generic (gzip)
135
+ Accept:
136
+ - "*/*"
137
+ Accept-Encoding:
138
+ - gzip,deflate
139
+ Date:
140
+ - Fri, 04 Oct 2019 13:11:24 GMT
141
+ Authorization:
142
+ - Bearer some_token
143
+ Content-Type:
144
+ - application/x-www-form-urlencoded
145
+ response:
146
+ status:
147
+ code: 401
148
+ message: Unauthorized
149
+ headers:
150
+ Vary:
151
+ - Origin
152
+ - X-Origin
153
+ Www-Authenticate:
154
+ - Bearer realm="https://accounts.google.com/", error=invalid_token
155
+ Content-Type:
156
+ - application/json; charset=UTF-8
157
+ Content-Encoding:
158
+ - gzip
159
+ Date:
160
+ - Fri, 04 Oct 2019 13:11:24 GMT
161
+ Expires:
162
+ - Fri, 04 Oct 2019 13:11:24 GMT
163
+ Cache-Control:
164
+ - private, max-age=0
165
+ X-Content-Type-Options:
166
+ - nosniff
167
+ X-Frame-Options:
168
+ - SAMEORIGIN
169
+ X-Xss-Protection:
170
+ - 1; mode=block
171
+ Server:
172
+ - GSE
173
+ Alt-Svc:
174
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
175
+ ma=2592000,h3-Q043=":443"; ma=2592000
176
+ Transfer-Encoding:
177
+ - chunked
178
+ body:
179
+ encoding: UTF-8
180
+ string: |
181
+ {
182
+ "error": {
183
+ "errors": [
184
+ {
185
+ "domain": "androidpublisher",
186
+ "reason": "permissionDenied",
187
+ "message": "The current user has insufficient permissions to perform the requested operation."
188
+ }
189
+ ],
190
+ "code": 401,
191
+ "message": "The current user has insufficient permissions to perform the requested operation."
192
+ }
193
+ }
194
+ http_version:
195
+ recorded_at: Fri, 04 Oct 2019 13:11:24 GMT
196
+ recorded_with: VCR 5.0.0
@@ -0,0 +1,183 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://www.googleapis.com/oauth2/v4/token
6
+ body:
7
+ encoding: ASCII-8BIT
8
+ string: params
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.15.4
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - "*/*"
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Content-Type:
24
+ - application/json; charset=utf-8
25
+ Vary:
26
+ - Origin
27
+ - Referer
28
+ - X-Origin
29
+ Date:
30
+ - Fri, 04 Oct 2019 13:11:23 GMT
31
+ Server:
32
+ - scaffolding on HTTPServer2
33
+ Cache-Control:
34
+ - private
35
+ X-Xss-Protection:
36
+ - '0'
37
+ X-Frame-Options:
38
+ - SAMEORIGIN
39
+ X-Content-Type-Options:
40
+ - nosniff
41
+ Alt-Svc:
42
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
43
+ ma=2592000,h3-Q043=":443"; ma=2592000
44
+ Transfer-Encoding:
45
+ - chunked
46
+ body:
47
+ encoding: ASCII-8BIT
48
+ string: |-
49
+ {
50
+ "access_token": "some token",
51
+ "expires_in": 3600,
52
+ "token_type": "Bearer"
53
+ }
54
+ http_version:
55
+ recorded_at: Fri, 04 Oct 2019 13:11:23 GMT
56
+ - request:
57
+ method: get
58
+ uri: https://www.googleapis.com/androidpublisher/v3/applications/my_package_name/purchases/products/my_product_id/tokens/my_token
59
+ body:
60
+ encoding: UTF-8
61
+ string: ''
62
+ headers:
63
+ User-Agent:
64
+ - unknown/0.0.0 google-api-ruby-client/0.28.7 Linux/5.0.0-29-generic (gzip)
65
+ Accept:
66
+ - "*/*"
67
+ Accept-Encoding:
68
+ - gzip,deflate
69
+ Date:
70
+ - Fri, 04 Oct 2019 13:11:23 GMT
71
+ Authorization:
72
+ - Bearer some_token
73
+ Content-Type:
74
+ - application/x-www-form-urlencoded
75
+ response:
76
+ status:
77
+ code: 401
78
+ message: Unauthorized
79
+ headers:
80
+ Vary:
81
+ - Origin
82
+ - X-Origin
83
+ Www-Authenticate:
84
+ - Bearer realm="https://accounts.google.com/", error=invalid_token
85
+ Content-Type:
86
+ - application/json; charset=UTF-8
87
+ Content-Encoding:
88
+ - gzip
89
+ Date:
90
+ - Fri, 04 Oct 2019 13:11:24 GMT
91
+ Expires:
92
+ - Fri, 04 Oct 2019 13:11:24 GMT
93
+ Cache-Control:
94
+ - private, max-age=0
95
+ X-Content-Type-Options:
96
+ - nosniff
97
+ X-Frame-Options:
98
+ - SAMEORIGIN
99
+ X-Xss-Protection:
100
+ - 1; mode=block
101
+ Server:
102
+ - GSE
103
+ Alt-Svc:
104
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
105
+ ma=2592000,h3-Q043=":443"; ma=2592000
106
+ Transfer-Encoding:
107
+ - chunked
108
+ body:
109
+ encoding: UTF-8
110
+ string: |
111
+ {
112
+ "error": {
113
+ "errors": [
114
+ {
115
+ "domain": "androidpublisher",
116
+ "reason": "permissionDenied",
117
+ "message": "The current user has insufficient permissions to perform the requested operation."
118
+ }
119
+ ],
120
+ "code": 401,
121
+ "message": "The current user has insufficient permissions to perform the requested operation."
122
+ }
123
+ }
124
+ http_version:
125
+ recorded_at: Fri, 04 Oct 2019 13:11:24 GMT
126
+ - request:
127
+ method: get
128
+ uri: https://www.googleapis.com/androidpublisher/v3/applications/my_package_name/purchases/products/my_product_id/tokens/my_token
129
+ body:
130
+ encoding: UTF-8
131
+ string: ''
132
+ headers:
133
+ User-Agent:
134
+ - unknown/0.0.0 google-api-ruby-client/0.28.7 Linux/5.0.0-29-generic (gzip)
135
+ Accept:
136
+ - "*/*"
137
+ Accept-Encoding:
138
+ - gzip,deflate
139
+ Date:
140
+ - Fri, 04 Oct 2019 13:11:24 GMT
141
+ Authorization:
142
+ - Bearer some_token
143
+ Content-Type:
144
+ - application/x-www-form-urlencoded
145
+ response:
146
+ status:
147
+ code: 401
148
+ message: Unauthorized
149
+ headers:
150
+ Vary:
151
+ - Origin
152
+ - X-Origin
153
+ Www-Authenticate:
154
+ - Bearer realm="https://accounts.google.com/", error=invalid_token
155
+ Content-Type:
156
+ - application/json; charset=UTF-8
157
+ Content-Encoding:
158
+ - gzip
159
+ Date:
160
+ - Fri, 04 Oct 2019 13:11:24 GMT
161
+ Expires:
162
+ - Fri, 04 Oct 2019 13:11:24 GMT
163
+ Cache-Control:
164
+ - private, max-age=0
165
+ X-Content-Type-Options:
166
+ - nosniff
167
+ X-Frame-Options:
168
+ - SAMEORIGIN
169
+ X-Xss-Protection:
170
+ - 1; mode=block
171
+ Server:
172
+ - GSE
173
+ Alt-Svc:
174
+ - quic=":443"; ma=2592000; v="46,43",h3-Q048=":443"; ma=2592000,h3-Q046=":443";
175
+ ma=2592000,h3-Q043=":443"; ma=2592000
176
+ Transfer-Encoding:
177
+ - chunked
178
+ body:
179
+ encoding: UTF-8
180
+ string: {}
181
+ http_version:
182
+ recorded_at: Fri, 04 Oct 2019 13:11:24 GMT
183
+ recorded_with: VCR 5.0.0