candy_check 0.2.0 → 0.2.1

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 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