candy_check 0.1.0.pre → 0.3.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 +5 -5
- data/.rubocop.yml +23 -0
- data/.ruby-version +1 -1
- data/.travis.yml +7 -8
- data/Guardfile +42 -0
- data/MIGRATION_GUIDE_0_2_0.md +141 -0
- data/README.md +86 -26
- data/Rakefile +1 -1
- data/candy_check.gemspec +33 -25
- data/lib/candy_check/app_store/receipt_collection.rb +5 -3
- data/lib/candy_check/app_store/subscription_verification.rb +25 -1
- data/lib/candy_check/app_store/verification.rb +1 -1
- data/lib/candy_check/app_store/verifier.rb +11 -11
- 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 +20 -10
- data/lib/candy_check/play_store/acknowledger.rb +19 -0
- data/lib/candy_check/play_store/android_publisher_service.rb +6 -0
- data/lib/candy_check/play_store/product_acknowledgements/acknowledgement.rb +45 -0
- data/lib/candy_check/play_store/product_acknowledgements/response.rb +24 -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 +154 -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/utils/config.rb +5 -3
- data/lib/candy_check/version.rb +1 -1
- data/spec/app_store/receipt_collection_spec.rb +33 -0
- data/spec/app_store/subscription_verification_spec.rb +35 -2
- data/spec/app_store/verifier_spec.rb +24 -5
- 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_acknowledgements/acknowledged.yml +105 -0
- data/spec/fixtures/vcr_cassettes/play_store/product_acknowledgements/already_acknowledged.yml +124 -0
- data/spec/fixtures/vcr_cassettes/play_store/product_acknowledgements/refunded.yml +122 -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/acknowledger_spec.rb +48 -0
- data/spec/play_store/product_acknowledgements/acknowledgement_spec.rb +54 -0
- data/spec/play_store/product_acknowledgements/response_spec.rb +66 -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 +237 -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 +32 -10
- metadata +175 -75
- data/lib/candy_check/play_store/client.rb +0 -126
- 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 -138
- 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 -98
- data/spec/play_store/verification_spec.rb +0 -82
@@ -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
|
+
- Thu, 10 Oct 2019 14:07:52 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": "my_token",
|
51
|
+
"expires_in": 3600,
|
52
|
+
"token_type": "Bearer"
|
53
|
+
}
|
54
|
+
http_version:
|
55
|
+
recorded_at: Thu, 10 Oct 2019 14:07:52 GMT
|
56
|
+
- request:
|
57
|
+
method: get
|
58
|
+
uri: https://www.googleapis.com/androidpublisher/v3/applications/my_package_name/purchases/subscriptions/my_subscription_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-31-generic (gzip)
|
65
|
+
Accept:
|
66
|
+
- "*/*"
|
67
|
+
Accept-Encoding:
|
68
|
+
- gzip,deflate
|
69
|
+
Date:
|
70
|
+
- Thu, 10 Oct 2019 14:07:52 GMT
|
71
|
+
Authorization:
|
72
|
+
- Bearer my_bearer
|
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
|
+
- Thu, 10 Oct 2019 14:07:52 GMT
|
91
|
+
Expires:
|
92
|
+
- Thu, 10 Oct 2019 14:07:52 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: Thu, 10 Oct 2019 14:07:52 GMT
|
126
|
+
- request:
|
127
|
+
method: get
|
128
|
+
uri: https://www.googleapis.com/androidpublisher/v3/applications/my_package_name/purchases/subscriptions/my_subscription_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-31-generic (gzip)
|
135
|
+
Accept:
|
136
|
+
- "*/*"
|
137
|
+
Accept-Encoding:
|
138
|
+
- gzip,deflate
|
139
|
+
Date:
|
140
|
+
- Thu, 10 Oct 2019 14:07:53 GMT
|
141
|
+
Authorization:
|
142
|
+
- Bearer my_bearer
|
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
|
+
- Thu, 10 Oct 2019 14:07:53 GMT
|
161
|
+
Expires:
|
162
|
+
- Thu, 10 Oct 2019 14:07:53 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: Thu, 10 Oct 2019 14:07:53 GMT
|
196
|
+
recorded_with: VCR 5.0.0
|
@@ -0,0 +1,127 @@
|
|
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: some_grant
|
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
|
+
- Thu, 24 Oct 2019 10:43:13 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: Thu, 24 Oct 2019 10:43:13 GMT
|
56
|
+
- request:
|
57
|
+
method: get
|
58
|
+
uri: https://www.googleapis.com/androidpublisher/v3/applications/my_package_name/purchases/subscriptions/my_subscription_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-32-generic (gzip)
|
65
|
+
Accept:
|
66
|
+
- "*/*"
|
67
|
+
Accept-Encoding:
|
68
|
+
- gzip,deflate
|
69
|
+
Date:
|
70
|
+
- Thu, 24 Oct 2019 10:43:13 GMT
|
71
|
+
Authorization:
|
72
|
+
- Bearer some_token
|
73
|
+
Content-Type:
|
74
|
+
- application/x-www-form-urlencoded
|
75
|
+
response:
|
76
|
+
status:
|
77
|
+
code: 200
|
78
|
+
message: OK
|
79
|
+
headers:
|
80
|
+
Expires:
|
81
|
+
- Thu, 24 Oct 2019 10:43:13 GMT
|
82
|
+
Date:
|
83
|
+
- Thu, 24 Oct 2019 10:43:13 GMT
|
84
|
+
Cache-Control:
|
85
|
+
- private, max-age=0, must-revalidate, no-transform
|
86
|
+
Etag:
|
87
|
+
- ''
|
88
|
+
Vary:
|
89
|
+
- Origin
|
90
|
+
- X-Origin
|
91
|
+
Content-Type:
|
92
|
+
- application/json; charset=UTF-8
|
93
|
+
Content-Encoding:
|
94
|
+
- gzip
|
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
|
+
"kind": "androidpublisher#subscriptionPurchase",
|
113
|
+
"startTimeMillis": "123456789",
|
114
|
+
"expiryTimeMillis": "123456789",
|
115
|
+
"autoRenewing": false,
|
116
|
+
"priceCurrencyCode": "EUR",
|
117
|
+
"priceAmountMicros": "123",
|
118
|
+
"countryCode": "DE",
|
119
|
+
"developerPayload": "",
|
120
|
+
"cancelReason": 1,
|
121
|
+
"orderId": "123",
|
122
|
+
"purchaseType": 0,
|
123
|
+
"acknowledgementState": 1
|
124
|
+
}
|
125
|
+
http_version:
|
126
|
+
recorded_at: Thu, 24 Oct 2019 10:43:13 GMT
|
127
|
+
recorded_with: VCR 5.0.0
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CandyCheck::PlayStore::Acknowledger do
|
4
|
+
let(:json_key_file) { File.expand_path("../fixtures/play_store/random_dummy_key.json", __dir__) }
|
5
|
+
subject { CandyCheck::PlayStore::Acknowledger.new(authorization: authorization) }
|
6
|
+
|
7
|
+
let(:package_name) { "fake_package_name" }
|
8
|
+
let(:product_id) { "fake_product_id" }
|
9
|
+
let(:token) { "fake_token" }
|
10
|
+
|
11
|
+
let(:authorization) { CandyCheck::PlayStore.authorization(json_key_file) }
|
12
|
+
|
13
|
+
describe "#acknowledge_product_purchase" do
|
14
|
+
it "when acknowledgement succeeds" do
|
15
|
+
VCR.use_cassette("play_store/product_acknowledgements/acknowledged") do
|
16
|
+
result = subject.acknowledge_product_purchase(package_name: package_name, product_id: product_id, token: token)
|
17
|
+
|
18
|
+
result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
|
19
|
+
result.acknowledged?.must_be_true
|
20
|
+
result.error.must_be_nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
it "when already acknowledged" do
|
24
|
+
error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"errors\": [\n {\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"invalidPurchaseState\",\n \"location\": \"token\",\n \"locationType\": \"parameter\"\n }\n ]\n }\n}\n"
|
25
|
+
|
26
|
+
VCR.use_cassette("play_store/product_acknowledgements/already_acknowledged") do
|
27
|
+
result = subject.acknowledge_product_purchase(package_name: package_name, product_id: product_id, token: token)
|
28
|
+
|
29
|
+
result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
|
30
|
+
result.acknowledged?.must_be_false
|
31
|
+
result.error[:body].must_equal(error_body)
|
32
|
+
result.error[:status_code].must_equal(400)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
it "when it has been refunded" do
|
36
|
+
error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The product purchase is not owned by the user.\",\n \"errors\": [\n {\n \"message\": \"The product purchase is not owned by the user.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"productNotOwnedByUser\"\n }\n ]\n }\n}\n"
|
37
|
+
|
38
|
+
VCR.use_cassette("play_store/product_acknowledgements/refunded") do
|
39
|
+
result = subject.acknowledge_product_purchase(package_name: package_name, product_id: product_id, token: token)
|
40
|
+
|
41
|
+
result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
|
42
|
+
result.acknowledged?.must_be_false
|
43
|
+
result.error[:body].must_equal(error_body)
|
44
|
+
result.error[:status_code].must_equal(400)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CandyCheck::PlayStore::ProductAcknowledgements::Acknowledgement do
|
4
|
+
subject do
|
5
|
+
CandyCheck::PlayStore::ProductAcknowledgements::Acknowledgement.new(
|
6
|
+
package_name: package_name,
|
7
|
+
product_id: product_id,
|
8
|
+
token: token,
|
9
|
+
authorization: authorization,
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:package_name) { "fake_package_name" }
|
14
|
+
let(:product_id) { "fake_product_id" }
|
15
|
+
let(:token) { "fake_token" }
|
16
|
+
let(:json_key_file) { File.expand_path("../../fixtures/play_store/random_dummy_key.json", __dir__) }
|
17
|
+
let(:authorization) { CandyCheck::PlayStore.authorization(json_key_file) }
|
18
|
+
|
19
|
+
describe "#call!" do
|
20
|
+
it "when acknowledgement succeeds" do
|
21
|
+
VCR.use_cassette("play_store/product_acknowledgements/acknowledged") do
|
22
|
+
result = subject.call!
|
23
|
+
|
24
|
+
result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
|
25
|
+
result.acknowledged?.must_be_true
|
26
|
+
result.error.must_be_nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
it "when already acknowledged" do
|
30
|
+
error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"errors\": [\n {\n \"message\": \"The purchase is not in a valid state to perform the desired operation.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"invalidPurchaseState\",\n \"location\": \"token\",\n \"locationType\": \"parameter\"\n }\n ]\n }\n}\n"
|
31
|
+
|
32
|
+
VCR.use_cassette("play_store/product_acknowledgements/already_acknowledged") do
|
33
|
+
result = subject.call!
|
34
|
+
|
35
|
+
result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
|
36
|
+
result.acknowledged?.must_be_false
|
37
|
+
result.error[:body].must_equal(error_body)
|
38
|
+
result.error[:status_code].must_equal(400)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
it "when it has been refunded" do
|
42
|
+
error_body = "{\n \"error\": {\n \"code\": 400,\n \"message\": \"The product purchase is not owned by the user.\",\n \"errors\": [\n {\n \"message\": \"The product purchase is not owned by the user.\",\n \"domain\": \"androidpublisher\",\n \"reason\": \"productNotOwnedByUser\"\n }\n ]\n }\n}\n"
|
43
|
+
|
44
|
+
VCR.use_cassette("play_store/product_acknowledgements/refunded") do
|
45
|
+
result = subject.call!
|
46
|
+
|
47
|
+
result.must_be_instance_of CandyCheck::PlayStore::ProductAcknowledgements::Response
|
48
|
+
result.acknowledged?.must_be_false
|
49
|
+
result.error[:body].must_equal(error_body)
|
50
|
+
result.error[:status_code].must_equal(400)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CandyCheck::PlayStore::ProductAcknowledgements::Response do
|
4
|
+
subject do
|
5
|
+
CandyCheck::PlayStore::ProductAcknowledgements::Response.new(result: result, error_data: error_data)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#acknowledged?' do
|
9
|
+
context 'when result present' do
|
10
|
+
let(:result) { '' }
|
11
|
+
let(:error_data) { nil }
|
12
|
+
|
13
|
+
it 'returns true' do
|
14
|
+
result = subject.acknowledged?
|
15
|
+
|
16
|
+
result.must_be_true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when result is not present' do
|
21
|
+
let(:result) { nil }
|
22
|
+
let(:error_data) { nil }
|
23
|
+
|
24
|
+
it 'returns false' do
|
25
|
+
result = subject.acknowledged?
|
26
|
+
|
27
|
+
result.must_be_false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#error' do
|
33
|
+
context 'when error present' do
|
34
|
+
let(:result) { nil }
|
35
|
+
let(:error_data) do
|
36
|
+
Module.new do
|
37
|
+
def status_code
|
38
|
+
400
|
39
|
+
end
|
40
|
+
def body
|
41
|
+
'A String describing the issue'
|
42
|
+
end
|
43
|
+
module_function :status_code, :body
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns the expected data' do
|
48
|
+
result = subject.error
|
49
|
+
|
50
|
+
result[:status_code].must_equal(400)
|
51
|
+
result[:body].must_equal('A String describing the issue')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when error is not present' do
|
56
|
+
let(:result) { '' }
|
57
|
+
let(:error_data) { nil }
|
58
|
+
|
59
|
+
it 'returns false' do
|
60
|
+
result = subject.error
|
61
|
+
|
62
|
+
result.must_be_nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|