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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -12
- data/Guardfile +42 -0
- data/MIGRATION_GUIDE_0_2_0.md +141 -0
- data/README.md +49 -26
- data/candy_check.gemspec +32 -26
- data/lib/candy_check/cli/app.rb +16 -33
- data/lib/candy_check/cli/commands/play_store.rb +12 -13
- data/lib/candy_check/play_store.rb +17 -10
- data/lib/candy_check/play_store/android_publisher_service.rb +6 -0
- data/lib/candy_check/play_store/product_purchases/product_purchase.rb +90 -0
- data/lib/candy_check/play_store/product_purchases/product_verification.rb +53 -0
- data/lib/candy_check/play_store/subscription_purchases/subscription_purchase.rb +141 -0
- data/lib/candy_check/play_store/subscription_purchases/subscription_verification.rb +55 -0
- data/lib/candy_check/play_store/verification_failure.rb +8 -6
- data/lib/candy_check/play_store/verifier.rb +24 -49
- data/lib/candy_check/version.rb +1 -1
- data/spec/candy_check_spec.rb +2 -2
- data/spec/cli/commands/play_store_spec.rb +10 -43
- data/spec/fixtures/play_store/random_dummy_key.json +12 -0
- data/spec/fixtures/vcr_cassettes/play_store/product_purchases/permission_denied.yml +196 -0
- data/spec/fixtures/vcr_cassettes/play_store/product_purchases/response_with_empty_body.yml +183 -0
- data/spec/fixtures/vcr_cassettes/play_store/product_purchases/valid_but_not_consumed.yml +122 -0
- data/spec/fixtures/vcr_cassettes/play_store/subscription_purchases/permission_denied.yml +196 -0
- data/spec/fixtures/vcr_cassettes/play_store/subscription_purchases/valid_but_expired.yml +127 -0
- data/spec/play_store/product_purchases/product_purchase_spec.rb +110 -0
- data/spec/play_store/product_purchases/product_verification_spec.rb +49 -0
- data/spec/play_store/subscription_purchases/subscription_purchase_spec.rb +181 -0
- data/spec/play_store/subscription_purchases/subscription_verification_spec.rb +65 -0
- data/spec/play_store/verification_failure_spec.rb +18 -18
- data/spec/play_store/verifier_spec.rb +32 -96
- data/spec/spec_helper.rb +24 -11
- metadata +120 -47
- data/lib/candy_check/play_store/client.rb +0 -139
- data/lib/candy_check/play_store/config.rb +0 -51
- data/lib/candy_check/play_store/discovery_repository.rb +0 -33
- data/lib/candy_check/play_store/receipt.rb +0 -81
- data/lib/candy_check/play_store/subscription.rb +0 -139
- data/lib/candy_check/play_store/subscription_verification.rb +0 -30
- data/lib/candy_check/play_store/verification.rb +0 -48
- data/spec/fixtures/api_cache.dump +0 -1
- data/spec/fixtures/play_store/api_cache.dump +0 -1
- data/spec/fixtures/play_store/auth_failure.txt +0 -18
- data/spec/fixtures/play_store/auth_success.txt +0 -20
- data/spec/fixtures/play_store/discovery.txt +0 -2841
- data/spec/fixtures/play_store/dummy.p12 +0 -0
- data/spec/fixtures/play_store/empty.txt +0 -17
- data/spec/fixtures/play_store/products_failure.txt +0 -29
- data/spec/fixtures/play_store/products_success.txt +0 -22
- data/spec/play_store/client_spec.rb +0 -125
- data/spec/play_store/config_spec.rb +0 -96
- data/spec/play_store/discovery_respository_spec.rb +0 -31
- data/spec/play_store/receipt_spec.rb +0 -88
- data/spec/play_store/subscription_spec.rb +0 -138
- data/spec/play_store/subscription_verification_spec.rb +0 -97
- 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
|
4
|
+
# The call return either a {SubscriptionPurchases::SubscriptionPurchase} or a {VerificationFailure}
|
5
5
|
class Verifier
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
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
|
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 [
|
16
|
+
# @return [ProductPurchases::ProductPurchase] if successful
|
32
17
|
# @return [VerificationFailure] otherwise
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
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 [
|
32
|
+
# @return [SubscriptionPurchases::SubscriptionPurchase] if successful
|
44
33
|
# @return [VerificationFailure] otherwise
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
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
|
data/lib/candy_check/version.rb
CHANGED
data/spec/candy_check_spec.rb
CHANGED
@@ -1,51 +1,18 @@
|
|
1
|
-
require
|
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(:
|
7
|
-
let(:
|
8
|
-
let(:
|
9
|
-
let(:
|
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
|
-
|
21
|
-
|
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
|
-
|
27
|
-
|
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
|