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
@@ -0,0 +1,49 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CandyCheck::PlayStore::ProductPurchases::ProductVerification do
|
4
|
+
subject do
|
5
|
+
CandyCheck::PlayStore::ProductPurchases::ProductVerification.new(
|
6
|
+
package_name: package_name,
|
7
|
+
product_id: product_id,
|
8
|
+
token: token,
|
9
|
+
authorization: authorization,
|
10
|
+
)
|
11
|
+
end
|
12
|
+
let(:package_name) { "my_package_name" }
|
13
|
+
let(:product_id) { "my_product_id" }
|
14
|
+
let(:token) { "my_token" }
|
15
|
+
let(:json_key_file) { File.expand_path("../../fixtures/play_store/random_dummy_key.json", __dir__) }
|
16
|
+
|
17
|
+
let(:authorization) { CandyCheck::PlayStore.authorization(json_key_file) }
|
18
|
+
|
19
|
+
describe "valid" do
|
20
|
+
it "returns a product purchase" do
|
21
|
+
VCR.use_cassette("play_store/product_purchases/valid_but_not_consumed") do
|
22
|
+
result = subject.call!
|
23
|
+
result.must_be_instance_of CandyCheck::PlayStore::ProductPurchases::ProductPurchase
|
24
|
+
result.valid?.must_be_true
|
25
|
+
result.consumed?.must_be_false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "failure" do
|
31
|
+
it "returns a verification failure" do
|
32
|
+
VCR.use_cassette("play_store/product_purchases/permission_denied") do
|
33
|
+
result = subject.call!
|
34
|
+
result.must_be_instance_of CandyCheck::PlayStore::VerificationFailure
|
35
|
+
result.code.must_equal 401
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "empty" do
|
41
|
+
it "returns a verification failure" do
|
42
|
+
VCR.use_cassette("play_store/product_purchases/response_with_empty_body") do
|
43
|
+
result = subject.call!
|
44
|
+
result.must_be_instance_of CandyCheck::PlayStore::VerificationFailure
|
45
|
+
result.code.must_equal(-1)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CandyCheck::PlayStore::SubscriptionPurchases::SubscriptionPurchase do
|
4
|
+
subject { CandyCheck::PlayStore::SubscriptionPurchases::SubscriptionPurchase.new(fake_subscription_purchase) }
|
5
|
+
|
6
|
+
describe "expired and canceled subscription" do
|
7
|
+
let(:fake_subscription_purchase) do
|
8
|
+
FakeSubscriptionPurchase.new(
|
9
|
+
kind: "androidpublisher#subscriptionPurchase",
|
10
|
+
start_time_millis: 1459540113244,
|
11
|
+
expiry_time_millis: 1462132088610,
|
12
|
+
auto_renewing: false,
|
13
|
+
developer_payload: "payload that gets stored and returned",
|
14
|
+
cancel_reason: 0,
|
15
|
+
payment_state: 1,
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "is expired?" do
|
20
|
+
subject.expired?.must_be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
it "is canceled by user" do
|
24
|
+
subject.canceled_by_user?.must_be_true
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns the payment_state" do
|
28
|
+
subject.payment_state.must_equal 1
|
29
|
+
end
|
30
|
+
|
31
|
+
it "considers a payment as valid" do
|
32
|
+
subject.payment_received?.must_be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "checks that auto renewal status is false" do
|
36
|
+
subject.auto_renewing?.must_be_false
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns the developer_payload" do
|
40
|
+
subject.developer_payload.must_equal \
|
41
|
+
"payload that gets stored and returned"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns the kind" do
|
45
|
+
subject.kind.must_equal "androidpublisher#subscriptionPurchase"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "returns the start_time_millis" do
|
49
|
+
subject.start_time_millis.must_equal 145_954_011_324_4
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns the expiry_time_millis" do
|
53
|
+
subject.expiry_time_millis.must_equal 146_213_208_861_0
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns the starts_at" do
|
57
|
+
expected = DateTime.new(2016, 4, 1, 19, 48, 33)
|
58
|
+
subject.starts_at.must_equal expected
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns the expires_at" do
|
62
|
+
expected = DateTime.new(2016, 5, 1, 19, 48, 8)
|
63
|
+
subject.expires_at.must_equal expected
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "unexpired and renewing subscription" do
|
68
|
+
two_days_from_now = DateTime.now + 2
|
69
|
+
let(:fake_subscription_purchase) do
|
70
|
+
FakeSubscriptionPurchase.new(
|
71
|
+
kind: "androidpublisher#subscriptionPurchase",
|
72
|
+
start_time_millis: 1459540113244,
|
73
|
+
expiry_time_millis: (two_days_from_now.to_time.to_i * 1000),
|
74
|
+
auto_renewing: true,
|
75
|
+
developer_payload: "payload that gets stored and returned",
|
76
|
+
cancel_reason: 0,
|
77
|
+
payment_state: 1,
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "is expired?" do
|
82
|
+
subject.expired?.must_be_false
|
83
|
+
end
|
84
|
+
|
85
|
+
it "is two days left until it is overdue" do
|
86
|
+
subject.overdue_days.must_equal(-2)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "expired due to payment failure" do
|
91
|
+
let(:fake_subscription_purchase) do
|
92
|
+
FakeSubscriptionPurchase.new(
|
93
|
+
kind: "androidpublisher#subscriptionPurchase",
|
94
|
+
start_time_millis: 1459540113244,
|
95
|
+
expiry_time_millis: 1462132088610,
|
96
|
+
auto_renewing: true,
|
97
|
+
developer_payload: "payload that gets stored and returned",
|
98
|
+
cancel_reason: 1,
|
99
|
+
payment_state: 1,
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "is expired?" do
|
104
|
+
subject.expired?.must_be_true
|
105
|
+
end
|
106
|
+
|
107
|
+
it "is payment_failed?" do
|
108
|
+
subject.payment_failed?.must_be_true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "expired with pending payment" do
|
113
|
+
let(:fake_subscription_purchase) do
|
114
|
+
FakeSubscriptionPurchase.new(
|
115
|
+
kind: "androidpublisher#subscriptionPurchase",
|
116
|
+
start_time_millis: 1459540113244,
|
117
|
+
expiry_time_millis: 1462132088610,
|
118
|
+
auto_renewing: true,
|
119
|
+
developer_payload: "payload that gets stored and returned",
|
120
|
+
cancel_reason: 0,
|
121
|
+
payment_state: 0,
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "is expired?" do
|
126
|
+
subject.expired?.must_be_true
|
127
|
+
end
|
128
|
+
|
129
|
+
it "is payment_pending?" do
|
130
|
+
subject.payment_pending?.must_be_true
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "trial" do
|
135
|
+
let(:fake_subscription_purchase) do
|
136
|
+
FakeSubscriptionPurchase.new(
|
137
|
+
kind: "androidpublisher#subscriptionPurchase",
|
138
|
+
start_time_millis: 1459540113244,
|
139
|
+
expiry_time_millis: 1462132088610,
|
140
|
+
auto_renewing: false,
|
141
|
+
developer_payload: "payload that gets stored and returned",
|
142
|
+
cancel_reason: 0,
|
143
|
+
payment_state: 1,
|
144
|
+
price_currency_code: "SOMECODE",
|
145
|
+
price_amount_micros: 0,
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "is trial?" do
|
150
|
+
subject.trial?.must_be_true
|
151
|
+
end
|
152
|
+
|
153
|
+
it "returns the price_currency_code" do
|
154
|
+
subject.price_currency_code.must_equal "SOMECODE"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
class FakeSubscriptionPurchase
|
161
|
+
FIELDS = [
|
162
|
+
:kind,
|
163
|
+
:start_time_millis,
|
164
|
+
:expiry_time_millis,
|
165
|
+
:auto_renewing,
|
166
|
+
:developer_payload,
|
167
|
+
:cancel_reason,
|
168
|
+
:payment_state,
|
169
|
+
:price_amount_micros,
|
170
|
+
:price_currency_code,
|
171
|
+
].freeze
|
172
|
+
|
173
|
+
attr_accessor *FIELDS
|
174
|
+
|
175
|
+
def initialize(hash)
|
176
|
+
FIELDS.each do |key|
|
177
|
+
self.public_send("#{key}=", hash[key])
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe CandyCheck::PlayStore::SubscriptionPurchases::SubscriptionVerification do
|
4
|
+
subject do
|
5
|
+
CandyCheck::PlayStore::SubscriptionPurchases::SubscriptionVerification.new(
|
6
|
+
package_name: package_name,
|
7
|
+
subscription_id: subscription_id,
|
8
|
+
token: token,
|
9
|
+
authorization: authorization,
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:json_key_file) { File.expand_path("../../fixtures/play_store/random_dummy_key.json", __dir__) }
|
14
|
+
let(:authorization) { CandyCheck::PlayStore.authorization(json_key_file) }
|
15
|
+
|
16
|
+
let(:package_name) { "my_package_name" }
|
17
|
+
let(:subscription_id) { "my_subscription_id" }
|
18
|
+
let(:token) { "my_token" }
|
19
|
+
|
20
|
+
describe "valid" do
|
21
|
+
it "returns a subscription" do
|
22
|
+
VCR.use_cassette("play_store/subscription_purchases/valid_but_expired") do
|
23
|
+
result = subject.call!
|
24
|
+
|
25
|
+
result.must_be_instance_of CandyCheck::PlayStore::SubscriptionPurchases::SubscriptionPurchase
|
26
|
+
result.expired?.must_be_true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "failure" do
|
32
|
+
it "returns a verification failure" do
|
33
|
+
VCR.use_cassette("play_store/subscription_purchases/permission_denied") do
|
34
|
+
result = subject.call!
|
35
|
+
result.must_be_instance_of CandyCheck::PlayStore::VerificationFailure
|
36
|
+
result.code.must_equal 401
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "empty" do
|
42
|
+
let(:response) do
|
43
|
+
{}
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns a verification failure" do
|
47
|
+
result = subject.call!
|
48
|
+
result.must_be_instance_of CandyCheck::PlayStore::VerificationFailure
|
49
|
+
result.code.must_equal(-1)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "invalid response kind" do
|
54
|
+
let(:response) do
|
55
|
+
{
|
56
|
+
"kind" => "something weird",
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
it "returns a verification failure" do
|
61
|
+
result = subject.call!
|
62
|
+
result.must_be_instance_of CandyCheck::PlayStore::VerificationFailure
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -1,35 +1,35 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
describe CandyCheck::PlayStore::VerificationFailure do
|
4
|
-
subject { CandyCheck::PlayStore::VerificationFailure.new(
|
5
|
-
|
6
|
-
describe
|
7
|
-
let(:
|
8
|
-
|
9
|
-
'errors' => [],
|
10
|
-
'code' => 401,
|
11
|
-
'message' => 'The current user has insufficient permissions'
|
12
|
-
}
|
4
|
+
subject { CandyCheck::PlayStore::VerificationFailure.new(fake_error) }
|
5
|
+
|
6
|
+
describe "denied" do
|
7
|
+
let(:fake_error) do
|
8
|
+
FakeError.new("401", "The current user has insufficient permissions")
|
13
9
|
end
|
14
10
|
|
15
|
-
it
|
11
|
+
it "returns the code" do
|
16
12
|
subject.code.must_equal 401
|
17
13
|
end
|
18
14
|
|
19
|
-
it
|
20
|
-
subject.message.must_equal
|
15
|
+
it "returns the message" do
|
16
|
+
subject.message.must_equal "The current user has insufficient permissions"
|
21
17
|
end
|
22
18
|
end
|
23
19
|
|
24
|
-
describe
|
25
|
-
let(:
|
20
|
+
describe "empty" do
|
21
|
+
let(:fake_error) do
|
22
|
+
FakeError.new(nil, nil)
|
23
|
+
end
|
26
24
|
|
27
|
-
it
|
25
|
+
it "returns an unknown code" do
|
28
26
|
subject.code.must_equal(-1)
|
29
27
|
end
|
30
28
|
|
31
|
-
it
|
32
|
-
subject.message.must_equal
|
29
|
+
it "returns an unknown message" do
|
30
|
+
subject.message.must_equal "Unknown error"
|
33
31
|
end
|
34
32
|
end
|
33
|
+
|
34
|
+
FakeError = Struct.new(:status_code, :message)
|
35
35
|
end
|
@@ -1,111 +1,47 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
describe CandyCheck::PlayStore::Verifier do
|
4
|
-
subject { CandyCheck::PlayStore::Verifier.new(
|
5
|
-
let(:config) do
|
6
|
-
CandyCheck::PlayStore::Config
|
7
|
-
.new(
|
8
|
-
application_name: 'YourApplication',
|
9
|
-
application_version: '1.0',
|
10
|
-
issuer: 'abcdefg@developer.gserviceaccount.com',
|
11
|
-
key_file: 'local/google.p12',
|
12
|
-
key_secret: 'notasecret'
|
13
|
-
)
|
14
|
-
end
|
15
|
-
let(:package) { 'the_package' }
|
16
|
-
let(:product_id) { 'the_product' }
|
17
|
-
let(:token) { 'the_token' }
|
18
|
-
|
19
|
-
it 'holds the config' do
|
20
|
-
subject.config.must_be_same_as config
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'requires a boot before verification' do
|
24
|
-
proc do
|
25
|
-
subject.verify(package, product_id, token)
|
26
|
-
end.must_raise CandyCheck::PlayStore::Verifier::BootRequiredError
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'it configures and boots a client but raises on second boot!' do
|
30
|
-
with_mocked_client do
|
31
|
-
subject.boot!
|
32
|
-
@client.config.must_be_same_as config
|
33
|
-
@client.booted.must_be_true
|
34
|
-
end
|
35
|
-
|
36
|
-
proc do
|
37
|
-
subject.boot!
|
38
|
-
end.must_raise CandyCheck::PlayStore::Verifier::BootRequiredError
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'uses a verifier when booted' do
|
42
|
-
result = :stubbed
|
43
|
-
with_mocked_client do
|
44
|
-
subject.boot!
|
45
|
-
end
|
46
|
-
with_mocked_verifier(result) do
|
47
|
-
subject.verify(package, product_id, token).must_be_same_as result
|
48
|
-
|
49
|
-
assert_recorded(
|
50
|
-
[@client, package, product_id, token]
|
51
|
-
)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'uses a subscription verifier when booted' do
|
56
|
-
result = :stubbed
|
57
|
-
with_mocked_client do
|
58
|
-
subject.boot!
|
59
|
-
end
|
60
|
-
with_mocked_verifier(result) do
|
61
|
-
subject.verify_subscription(
|
62
|
-
package, product_id, token
|
63
|
-
).must_be_same_as result
|
4
|
+
subject { CandyCheck::PlayStore::Verifier.new(authorization: authorization) }
|
64
5
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
6
|
+
let(:package_name) { "my_package_name" }
|
7
|
+
let(:product_id) { "my_product_id" }
|
8
|
+
let(:subscription_id) { "my_subscription_id" }
|
9
|
+
let(:token) { "my_token" }
|
70
10
|
|
71
|
-
|
11
|
+
let(:json_key_file) { File.expand_path("../fixtures/play_store/random_dummy_key.json", __dir__) }
|
12
|
+
let(:authorization) { CandyCheck::PlayStore.authorization(json_key_file) }
|
72
13
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
14
|
+
describe "product purchases" do
|
15
|
+
it "verifies a product purchase" do
|
16
|
+
VCR.use_cassette("play_store/product_purchases/valid_but_not_consumed") do
|
17
|
+
result = subject.verify_product_purchase(package_name: package_name, product_id: product_id, token: token)
|
18
|
+
result.must_be_instance_of CandyCheck::PlayStore::ProductPurchases::ProductPurchase
|
19
|
+
result.valid?.must_be_true
|
20
|
+
result.consumed?.must_be_false
|
21
|
+
end
|
78
22
|
end
|
79
|
-
CandyCheck::PlayStore::Verification.stub :new, stub do
|
80
|
-
yield
|
81
|
-
end
|
82
|
-
end
|
83
23
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
yield
|
24
|
+
it "can return a product purchase verification failure" do
|
25
|
+
VCR.use_cassette("play_store/product_purchases/permission_denied") do
|
26
|
+
result = subject.verify_product_purchase(package_name: package_name, product_id: product_id, token: token)
|
27
|
+
result.must_be_instance_of CandyCheck::PlayStore::VerificationFailure
|
28
|
+
end
|
90
29
|
end
|
91
30
|
end
|
92
31
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
attr_accessor :results
|
100
|
-
def call!
|
101
|
-
results.shift
|
32
|
+
describe "subscription purchases" do
|
33
|
+
it "verifies a subscription purchase" do
|
34
|
+
VCR.use_cassette("play_store/subscription_purchases/valid_but_expired") do
|
35
|
+
result = subject.verify_subscription_purchase(package_name: package_name, subscription_id: subscription_id, token: token)
|
36
|
+
result.must_be_instance_of CandyCheck::PlayStore::SubscriptionPurchases::SubscriptionPurchase
|
37
|
+
end
|
102
38
|
end
|
103
|
-
end
|
104
39
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
40
|
+
it "can return a subscription purchase verification failure" do
|
41
|
+
VCR.use_cassette("play_store/subscription_purchases/permission_denied") do
|
42
|
+
result = subject.verify_subscription_purchase(package_name: package_name, subscription_id: subscription_id, token: token)
|
43
|
+
result.must_be_instance_of CandyCheck::PlayStore::VerificationFailure
|
44
|
+
end
|
109
45
|
end
|
110
46
|
end
|
111
47
|
end
|