candy_check 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f276e0409f0b3ee54bd5543e939d7635c95059dfc5ae77d9b071d5715d1e291f
4
- data.tar.gz: 56858af56be773214faae7e848e4aa9ae5a30b8cba2da782aca8554da7895c94
3
+ metadata.gz: 0ce2db5208c39a8d9cda9eb6308fae0ae95b34460a053036b16cf2496c5fdffb
4
+ data.tar.gz: 1eba58dfa69e6c8732ae1f2e33553d2386e85653c5ba867603253d531c2e0891
5
5
  SHA512:
6
- metadata.gz: 6eb5506e96216125602b47994b99f8038aa20aecf17cf2515ba5ab7578f06415735301f793aa64786bddad59a4c0296bdf5f6f3545151ee0b1f104b7a5eef262
7
- data.tar.gz: a6084226820a90997d852976659841930fbeaf5d79507fe392d8551447f0000a3016636f317f79f1af50cb9501f355905a96203751162153ac210ac97bcca519
6
+ metadata.gz: 0f58665b4df59f402b0e43f6f9a09a74b40caf8ab7fd6511732b7ff742b35f8a1a32f53821a777a423effdc386757aaec88965aa1a63686ca463b9e98f7fc54d
7
+ data.tar.gz: 245dad3d70c0eccc33c992a5420f2ac2ddb8521a524a06b0361418516d35ee9471f71c12bff4771ce6a00390ba16858d12b836060e463524cd2c3b0310ab59d8
data/README.md CHANGED
@@ -4,7 +4,6 @@
4
4
  [![Build Status](https://travis-ci.org/jnbt/candy_check.svg?branch=master)](https://travis-ci.org/jnbt/candy_check)
5
5
  [![Coverage Status](https://coveralls.io/repos/jnbt/candy_check/badge.svg?branch=master)](https://coveralls.io/r/jnbt/candy_check?branch=master)
6
6
  [![Code Climate](https://codeclimate.com/github/jnbt/candy_check/badges/gpa.svg)](https://codeclimate.com/github/jnbt/candy_check)
7
- [![Gemnasium](https://img.shields.io/gemnasium/jnbt/candy_check.svg?style=flat)](https://gemnasium.com/jnbt/candy_check)
8
7
  [![Inline docs](http://inch-ci.org/github/jnbt/candy_check.svg?branch=master)](http://inch-ci.org/github/jnbt/candy_check)
9
8
  [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg?style=flat)](http://www.rubydoc.info/github/jnbt/candy_check/master)
10
9
 
@@ -76,6 +75,14 @@ For **subscription verification**, Apple also returns a list of the user's purch
76
75
  verifier.verify_subscription(your_receipt_data, your_secret) # => ReceiptCollection or VerificationFailure
77
76
  ```
78
77
 
78
+ If you want to restrict the subscription verification to some specific products, pass their ids as an array:
79
+
80
+ ```ruby
81
+ # ... create your verifier as above
82
+ product_ids = ['sk_product_id_1', 'sk_product_id_2'...]
83
+ verifier.verify_subscription(your_receipt_data, your_secret, product_ids) # => ReceiptCollection or VerificationFailure
84
+ ```
85
+
79
86
  Please see the class documentation for [`CandyCheck::AppStore::ReceiptCollection`](http://www.rubydoc.info/github/jnbt/candy_check/master/CandyCheck/AppStore/ReceiptCollection) for further details.
80
87
 
81
88
  ### PlayStore
@@ -8,9 +8,11 @@ module CandyCheck
8
8
 
9
9
  # Initializes a new instance which bases on a JSON result
10
10
  # from Apple's verification server
11
- # @param attributes [Array<Hash>]
11
+ # @param attributes [Array<Hash>] raw data from Apple's server
12
12
  def initialize(attributes)
13
- @receipts = attributes.map { |r| Receipt.new(r) }
13
+ @receipts = attributes.map {|r| Receipt.new(r) }.sort{ |a, b|
14
+ a.purchase_date - b.purchase_date
15
+ }
14
16
  end
15
17
 
16
18
  # Check if the latest expiration date is passed
@@ -3,13 +3,28 @@ module CandyCheck
3
3
  # Verifies a latest_receipt_info block against a verification server.
4
4
  # The call return either an {ReceiptCollection} or a {VerificationFailure}
5
5
  class SubscriptionVerification < CandyCheck::AppStore::Verification
6
+ # Builds a fresh verification run
7
+ # @param endpoint_url [String] the verification URL to use
8
+ # @param receipt_data [String] the raw data to be verified
9
+ # @param secret [String] optional: shared secret
10
+ # @param product_ids [Array<String>] optional: select specific products
11
+ def initialize(
12
+ endpoint_url,
13
+ receipt_data,
14
+ secret = nil,
15
+ product_ids = nil
16
+ )
17
+ super(endpoint_url, receipt_data, secret)
18
+ @product_ids = product_ids
19
+ end
20
+
6
21
  # Performs the verification against the remote server
7
22
  # @return [ReceiptCollection] if successful
8
23
  # @return [VerificationFailure] otherwise
9
24
  def call!
10
25
  verify!
11
26
  if valid?
12
- ReceiptCollection.new(@response['latest_receipt_info'])
27
+ build_collection(@response['latest_receipt_info'])
13
28
  else
14
29
  VerificationFailure.fetch(@response['status'])
15
30
  end
@@ -17,6 +32,15 @@ module CandyCheck
17
32
 
18
33
  private
19
34
 
35
+ def build_collection(latest_receipt_info)
36
+ unless @product_ids.nil?
37
+ latest_receipt_info = latest_receipt_info.select do |info|
38
+ @product_ids.include?(info['product_id'])
39
+ end
40
+ end
41
+ ReceiptCollection.new(latest_receipt_info)
42
+ end
43
+
20
44
  def valid?
21
45
  status_is_ok = @response['status'] == STATUS_OK
22
46
  @response && status_is_ok && @response['latest_receipt_info']
@@ -16,7 +16,7 @@ module CandyCheck
16
16
  # Builds a fresh verification run
17
17
  # @param endpoint_url [String] the verification URL to use
18
18
  # @param receipt_data [String] the raw data to be verified
19
- # @param secret [String] the optional shared secret
19
+ # @param secret [String] optional: shared secret
20
20
  def initialize(endpoint_url, receipt_data, secret = nil)
21
21
  @endpoint_url = endpoint_url
22
22
  @receipt_data = receipt_data
@@ -30,33 +30,33 @@ module CandyCheck
30
30
  # @return [Receipt] if successful
31
31
  # @return [VerificationFailure] otherwise
32
32
  def verify(receipt_data, secret = nil)
33
- @verifier = Verification
34
- fetch_receipt_information(receipt_data, secret)
33
+ fetch_receipt_information(Verification, [receipt_data, secret])
35
34
  end
36
35
 
37
36
  # Calls a subscription verification for the given input
38
37
  # @param receipt_data [String] the raw data to be verified
39
- # @param secret [string] the optional shared secret
38
+ # @param secret [String] optional: shared secret
39
+ # @param product_ids [Array<String>] optional: products to filter
40
40
  # @return [ReceiptCollection] if successful
41
41
  # @return [Verification] otherwise
42
- def verify_subscription(receipt_data, secret = nil)
43
- @verifier = SubscriptionVerification
44
- fetch_receipt_information(receipt_data, secret)
42
+ def verify_subscription(receipt_data, secret = nil, product_ids = nil)
43
+ args = [receipt_data, secret, product_ids]
44
+ fetch_receipt_information(SubscriptionVerification, args)
45
45
  end
46
46
 
47
47
  private
48
48
 
49
- def fetch_receipt_information(receipt_data, secret = nil)
49
+ def fetch_receipt_information(verifier_class, args)
50
50
  default_endpoint, opposite_endpoint = endpoints
51
- result = call_for(default_endpoint, receipt_data, secret)
51
+ result = call_for(verifier_class, args.dup.unshift(default_endpoint))
52
52
  if should_retry?(result)
53
- return call_for(opposite_endpoint, receipt_data, secret)
53
+ return call_for(verifier_class, args.dup.unshift(opposite_endpoint))
54
54
  end
55
55
  result
56
56
  end
57
57
 
58
- def call_for(endpoint_url, receipt_data, secret)
59
- @verifier.new(endpoint_url, receipt_data, secret).call!
58
+ def call_for(verifier_class, args)
59
+ verifier_class.new(*args).call!
60
60
  end
61
61
 
62
62
  def should_retry?(result)
@@ -66,7 +66,7 @@ module CandyCheck
66
66
  # Get number of overdue days. If this is negative, it is not overdue.
67
67
  # @return [Integer]
68
68
  def overdue_days
69
- (Date.today - expires_at.to_date).to_i
69
+ (Time.now.utc.to_date - expires_at.to_date).to_i
70
70
  end
71
71
 
72
72
  # Get the auto renewal status as given by Google
@@ -1,4 +1,4 @@
1
1
  module CandyCheck
2
2
  # The current gem's version
3
- VERSION = "0.2.0".freeze
3
+ VERSION = "0.2.1".freeze
4
4
  end
@@ -8,10 +8,12 @@ describe CandyCheck::AppStore::ReceiptCollection do
8
8
  [{
9
9
  'expires_date' => '2014-04-15 12:52:40 Etc/GMT',
10
10
  'expires_date_pst' => '2014-04-15 05:52:40 America/Los_Angeles',
11
+ 'purchase_date' => '2014-04-14 12:52:40 Etc/GMT',
11
12
  'is_trial_period' => 'false'
12
13
  }, {
13
14
  'expires_date' => '2015-04-15 12:52:40 Etc/GMT',
14
15
  'expires_date_pst' => '2015-04-15 05:52:40 America/Los_Angeles',
16
+ 'purchase_date' => '2015-04-14 12:52:40 Etc/GMT',
15
17
  'is_trial_period' => 'false'
16
18
  }]
17
19
  end
@@ -42,16 +44,40 @@ describe CandyCheck::AppStore::ReceiptCollection do
42
44
  end
43
45
  end
44
46
 
47
+ describe 'unordered receipts' do
48
+ let(:attributes) do
49
+ [{
50
+ 'expires_date' => '2015-04-15 12:52:40 Etc/GMT',
51
+ 'expires_date_pst' => '2015-04-15 05:52:40 America/Los_Angeles',
52
+ 'purchase_date' => '2015-04-14 12:52:40 Etc/GMT',
53
+ 'is_trial_period' => 'false'
54
+ }, {
55
+ 'expires_date' => '2014-04-15 12:52:40 Etc/GMT',
56
+ 'expires_date_pst' => '2014-04-15 05:52:40 America/Los_Angeles',
57
+ 'purchase_date' => '2014-04-14 12:52:40 Etc/GMT',
58
+ 'is_trial_period' => 'false'
59
+ }]
60
+ end
61
+
62
+ it 'the expires date is the latest one in time' do
63
+ expected = DateTime.new(2015, 4, 15, 12, 52, 40)
64
+ subject.expires_at.must_equal expected
65
+ end
66
+
67
+ end
68
+
45
69
  describe 'unexpired trial subscription' do
46
70
  two_days_from_now = DateTime.now + 2
47
71
 
48
72
  let(:attributes) do
49
73
  [{
50
74
  'expires_date' => '2016-04-15 12:52:40 Etc/GMT',
75
+ 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT',
51
76
  'is_trial_period' => 'true'
52
77
  }, {
53
78
  'expires_date' =>
54
79
  two_days_from_now.strftime('%Y-%m-%d %H:%M:%S Etc/GMT'),
80
+ 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT',
55
81
  'is_trial_period' => 'true'
56
82
  }]
57
83
  end
@@ -68,4 +94,5 @@ describe CandyCheck::AppStore::ReceiptCollection do
68
94
  subject.overdue_days.must_equal(-2)
69
95
  end
70
96
  end
97
+
71
98
  end
@@ -38,20 +38,53 @@ describe CandyCheck::AppStore::SubscriptionVerification do
38
38
  response = {
39
39
  'status' => 0,
40
40
  'latest_receipt_info' => [
41
- { 'item_id' => 'some_id' },
42
- { 'item_id' => 'some_other_id' }
41
+ { 'item_id' => 'some_id', 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT' },
42
+ { 'item_id' => 'some_other_id', 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT' }
43
43
  ]
44
44
  }
45
45
  with_mocked_response(response) do
46
46
  result = subject.call!
47
47
  result.must_be_instance_of CandyCheck::AppStore::ReceiptCollection
48
48
  result.receipts.must_be_instance_of Array
49
+ result.receipts.size.must_equal(2)
49
50
  last = result.receipts.last
50
51
  last.must_be_instance_of CandyCheck::AppStore::Receipt
51
52
  last.item_id.must_equal('some_other_id')
52
53
  end
53
54
  end
54
55
 
56
+ describe 'filtered product_ids' do
57
+ subject do
58
+ CandyCheck::AppStore::SubscriptionVerification.new(
59
+ endpoint,
60
+ data,
61
+ secret,
62
+ product_ids
63
+ )
64
+ end
65
+ let(:product_ids) { ['product_1'] }
66
+
67
+ it 'returns only filtered reciepts when specifc product_ids are reqested' do
68
+ response = {
69
+ 'status' => 0,
70
+ 'latest_receipt_info' => [
71
+ { 'item_id' => 'some_id', 'product_id' => 'product_1', 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT' },
72
+ { 'item_id' => 'some_other_id', 'product_id' => 'product_1', 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT' },
73
+ { 'item_id' => 'some_id', 'product_id' => 'product_2', 'purchase_date' => '2016-04-15 12:52:40 Etc/GMT' }
74
+ ]
75
+ }
76
+ with_mocked_response(response) do
77
+ result = subject.call!
78
+ result.must_be_instance_of CandyCheck::AppStore::ReceiptCollection
79
+ result.receipts.must_be_instance_of Array
80
+ result.receipts.size.must_equal(2)
81
+ last = result.receipts.last
82
+ last.must_be_instance_of CandyCheck::AppStore::Receipt
83
+ last.item_id.must_equal('some_other_id')
84
+ end
85
+ end
86
+ end
87
+
55
88
  private
56
89
 
57
90
  DummyClient = Struct.new(:response) do
@@ -89,7 +89,7 @@ describe CandyCheck::AppStore::Verifier do
89
89
  subject.verify_subscription(
90
90
  data, secret
91
91
  ).must_be_same_as receipt_collection
92
- assert_recorded([production_endpoint, data, secret])
92
+ assert_recorded([production_endpoint, data, secret, nil])
93
93
  end
94
94
  end
95
95
 
@@ -97,7 +97,7 @@ describe CandyCheck::AppStore::Verifier do
97
97
  failure = get_failure(21_000)
98
98
  with_mocked_verifier(failure) do
99
99
  subject.verify_subscription(data, secret).must_be_same_as failure
100
- assert_recorded([production_endpoint, data, secret])
100
+ assert_recorded([production_endpoint, data, secret, nil])
101
101
  end
102
102
  end
103
103
 
@@ -106,11 +106,21 @@ describe CandyCheck::AppStore::Verifier do
106
106
  with_mocked_verifier(failure, receipt) do
107
107
  subject.verify_subscription(data, secret).must_be_same_as receipt
108
108
  assert_recorded(
109
- [production_endpoint, data, secret],
110
- [sandbox_endpoint, data, secret]
109
+ [production_endpoint, data, secret, nil],
110
+ [sandbox_endpoint, data, secret, nil]
111
111
  )
112
112
  end
113
113
  end
114
+
115
+ it 'passed the product_ids' do
116
+ product_ids = ['product_1']
117
+ with_mocked_verifier(receipt_collection) do
118
+ subject.verify_subscription(
119
+ data, secret, product_ids
120
+ ).must_be_same_as receipt_collection
121
+ assert_recorded([production_endpoint, data, secret, product_ids])
122
+ end
123
+ end
114
124
  end
115
125
 
116
126
  private
@@ -134,8 +144,17 @@ describe CandyCheck::AppStore::Verifier do
134
144
  CandyCheck::AppStore::VerificationFailure.fetch(code)
135
145
  end
136
146
 
137
- DummyAppStoreVerification = Struct.new(:endpoint, :data, :secret) do
147
+ class DummyAppStoreVerification
148
+ attr_reader :endpoint, :data, :secret, :product_ids
138
149
  attr_accessor :results
150
+
151
+ def initialize(endpoint, data, secret, product_ids = nil)
152
+ @endpoint = endpoint
153
+ @data = data
154
+ @secret = secret
155
+ @product_ids = product_ids
156
+ end
157
+
139
158
  def call!
140
159
  results.shift
141
160
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: candy_check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Thiel
@@ -362,8 +362,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
362
362
  - !ruby/object:Gem::Version
363
363
  version: '0'
364
364
  requirements: []
365
- rubyforge_project:
366
- rubygems_version: 2.7.9
365
+ rubygems_version: 3.0.3
367
366
  signing_key:
368
367
  specification_version: 4
369
368
  summary: Check and verify in-app receipts