candy_check 0.0.1 → 0.0.2
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/README.md +15 -14
- data/bin/candy_check +7 -0
- data/candy_check.gemspec +1 -0
- data/lib/candy_check/cli/app.rb +62 -0
- data/lib/candy_check/cli/commands/app_store.rb +34 -0
- data/lib/candy_check/cli/commands/base.rb +31 -0
- data/lib/candy_check/cli/commands/play_store.rb +38 -0
- data/lib/candy_check/cli/commands/version.rb +13 -0
- data/lib/candy_check/cli/commands.rb +12 -0
- data/lib/candy_check/cli/out.rb +32 -0
- data/lib/candy_check/cli.rb +10 -0
- data/lib/candy_check/version.rb +1 -1
- data/spec/cli/app_spec.rb +38 -0
- data/spec/cli/commands/app_store_spec.rb +56 -0
- data/spec/cli/commands/play_store_spec.rb +51 -0
- data/spec/cli/commands/version_spec.rb +11 -0
- data/spec/cli/out_spec.rb +41 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/with_command.rb +33 -0
- metadata +37 -5
- data/bin/cc_appstore +0 -90
- data/bin/cc_playstore +0 -119
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41d1f7faf73085f4926d3067f7a8fee741130dcf
|
4
|
+
data.tar.gz: b24506e17a0cf66931b1000514bc341766cc9cb4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5509c6dc5e3530da73b7770690f520efe5d480c1ac5f2771404af38754088a9e289e36acebdaa2c425d246b26fc305e46e27dfdb3589b9ed52f1bbafaff74adf
|
7
|
+
data.tar.gz: 2c92efbe856d8b2f7039429eeedd4d8c5b46a5ddfd860549bcbee8aaebd5a1d406aada06a242dd0c24c86098f8a7c44b0a279858ac5dd29e0ba5e64197c8bf93
|
data/README.md
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
[](http://badge.fury.io/rb/candy_check)
|
4
4
|
[](https://travis-ci.org/jnbt/candy_check)
|
5
5
|
[](https://coveralls.io/r/jnbt/candy_check?branch=master)
|
6
|
+
[](https://codeclimate.com/github/jnbt/candy_check)
|
7
|
+
[](https://gemnasium.com/jnbt/candy_check)
|
6
8
|
[](http://inch-ci.org/github/jnbt/candy_check)
|
7
9
|
[](http://www.rubydoc.info/github/jnbt/candy_check/master)
|
8
10
|
|
@@ -22,12 +24,12 @@ Google's PlayStore.
|
|
22
24
|
### AppStore
|
23
25
|
|
24
26
|
If you have set up an iOS app and it's in-app items correctly and the in-app store is working your app should receive a
|
25
|
-
`SKPaymentTransaction`. Currently this gem assumes that you use the old [`transactionReceipt`](https://developer.apple.com/library/ios/documentation/StoreKit/Reference/SKPaymentTransaction_Class/index.html#//apple_ref/occ/instp/SKPaymentTransaction/transactionReceipt)
|
26
|
-
which is returned per transaction. The `transactionReceipt` is a base64 encoded binary blob which you should send to your
|
27
|
+
`SKPaymentTransaction`. Currently this gem assumes that you use the old [`transactionReceipt`](https://developer.apple.com/library/ios/documentation/StoreKit/Reference/SKPaymentTransaction_Class/index.html#//apple_ref/occ/instp/SKPaymentTransaction/transactionReceipt)
|
28
|
+
which is returned per transaction. The `transactionReceipt` is a base64 encoded binary blob which you should send to your
|
27
29
|
server for the validation process.
|
28
30
|
|
29
|
-
To validate a receipt one normally has to choose between the two different endpoints "production" and "sandbox" which are provided from
|
30
|
-
[Apple](https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1).
|
31
|
+
To validate a receipt one normally has to choose between the two different endpoints "production" and "sandbox" which are provided from
|
32
|
+
[Apple](https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1).
|
31
33
|
During development your app gets receipts from the sandbox while when released from the production system. A special case is the
|
32
34
|
review process because the review team uses the release version of your app but processes payment against the sandbox.
|
33
35
|
Only for receipts that contain auto-renewable subscriptions you need your app's shared secret (a hexadecimal string),
|
@@ -38,7 +40,7 @@ your app's development.
|
|
38
40
|
### PlayStore
|
39
41
|
|
40
42
|
Google's PlayStore has different kind of server-to-server API to check purchases and requires that you register a so
|
41
|
-
called "[service account](https://developers.google.com/accounts/docs/OAuth2ServiceAccount)". You have to register a
|
43
|
+
called "[service account](https://developers.google.com/accounts/docs/OAuth2ServiceAccount)". You have to register a
|
42
44
|
new account by yourself, export the generated certificate file and grant the correct permissions to the account for
|
43
45
|
your app using the [Google Developer Console](https://console.developers.google.com).
|
44
46
|
|
@@ -75,8 +77,8 @@ Please see the class documenations [`CandyCheck::AppStore::Receipt`](http://www.
|
|
75
77
|
|
76
78
|
### PlayStore
|
77
79
|
|
78
|
-
First initialize and **boot** a verifier instance for your application. This loads the API discovery and
|
79
|
-
fetches the needed OAuth access token. When configuring a `cache_file` the discovery is loaded (or dumped) to
|
80
|
+
First initialize and **boot** a verifier instance for your application. This loads the API discovery and
|
81
|
+
fetches the needed OAuth access token. When configuring a `cache_file` the discovery is loaded (or dumped) to
|
80
82
|
this file:
|
81
83
|
|
82
84
|
```ruby
|
@@ -102,20 +104,20 @@ Please see the class documenations [`CandyCheck::PlayStore::Receipt`](http://www
|
|
102
104
|
|
103
105
|
## CLI
|
104
106
|
|
105
|
-
This gem ships with
|
107
|
+
This gem ships with an executable to verify in-app purchases directly from your terminal:
|
106
108
|
|
107
109
|
### AppStore
|
108
110
|
|
109
111
|
You only need to specify the base64 encoded receipt:
|
110
112
|
|
111
113
|
```bash
|
112
|
-
$
|
114
|
+
$ candy_check app_store RECEIPT_DATA
|
113
115
|
```
|
114
116
|
|
115
117
|
See all options:
|
116
118
|
|
117
119
|
```bash
|
118
|
-
$
|
120
|
+
$ candy_check help app_store
|
119
121
|
```
|
120
122
|
|
121
123
|
### PlayStore
|
@@ -124,14 +126,13 @@ For the PlayStore you need to specify at least the issuer, the key file, your pa
|
|
124
126
|
purchase token:
|
125
127
|
|
126
128
|
```bash
|
127
|
-
$
|
128
|
-
--package PACKAGE_NAME --product-id PRODUCT_ID --token PURCHASE_TOKEN
|
129
|
+
$ candy_check play_store PACKAGE PRODUCT_ID TOKEN --issuer=ISSUER --key-file=KEY_FILE
|
129
130
|
```
|
130
131
|
|
131
132
|
See all options:
|
132
133
|
|
133
134
|
```bash
|
134
|
-
$
|
135
|
+
$ candy_check help play_store
|
135
136
|
```
|
136
137
|
|
137
138
|
|
@@ -154,4 +155,4 @@ rake
|
|
154
155
|
|
155
156
|
## Copyright
|
156
157
|
|
157
|
-
Copyright © 2015 Jonas Thiel. See LICENSE.txt for details.
|
158
|
+
Copyright © 2015 Jonas Thiel. See LICENSE.txt for details.
|
data/bin/candy_check
ADDED
data/candy_check.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
spec.add_dependency 'multi_json', '~> 1.10'
|
21
21
|
spec.add_dependency 'google-api-client', '~> 0.8'
|
22
|
+
spec.add_dependency 'thor', '~> 0.19'
|
22
23
|
|
23
24
|
spec.add_development_dependency 'rubocop', '~> 0.28'
|
24
25
|
spec.add_development_dependency 'inch', '~> 0.5'
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module CandyCheck
|
4
|
+
module CLI
|
5
|
+
# Main class for the executable 'candy_check'
|
6
|
+
# @example
|
7
|
+
# $ candy_check help
|
8
|
+
class App < Thor
|
9
|
+
package_name 'CandyCheck'
|
10
|
+
|
11
|
+
desc 'app_store RECEIPT_DATA', 'Verify a base64 encoded AppStore receipt'
|
12
|
+
method_option :environment,
|
13
|
+
default: 'production',
|
14
|
+
type: :string,
|
15
|
+
enum: %w(production sandbox),
|
16
|
+
aliases: '-e',
|
17
|
+
desc: 'The environment to use for verfication'
|
18
|
+
method_option :secret,
|
19
|
+
aliases: '-s',
|
20
|
+
type: :string,
|
21
|
+
desc: 'The shared secret for auto-renewable subscriptions'
|
22
|
+
def app_store(receipt)
|
23
|
+
Commands::AppStore.run(receipt, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'play_store PACKAGE PRODUCT_ID TOKEN', 'Verify PlayStore purchase'
|
27
|
+
method_option :issuer,
|
28
|
+
required: true,
|
29
|
+
type: :string,
|
30
|
+
aliases: '-i',
|
31
|
+
desc: 'The issuer\'s email address for the API call'
|
32
|
+
method_option :key_file,
|
33
|
+
required: true,
|
34
|
+
type: :string,
|
35
|
+
aliases: '-k',
|
36
|
+
desc: 'The key file to use for API authentication'
|
37
|
+
method_option :key_secret,
|
38
|
+
default: 'notasecret',
|
39
|
+
type: :string,
|
40
|
+
aliases: '-s',
|
41
|
+
desc: 'The secret to decrypt the key_file'
|
42
|
+
method_option :application_name,
|
43
|
+
default: 'CandyCheck',
|
44
|
+
type: :string,
|
45
|
+
aliases: '-a',
|
46
|
+
desc: 'Your application\'s name'
|
47
|
+
method_option :application_version,
|
48
|
+
default: CandyCheck::VERSION,
|
49
|
+
type: :string,
|
50
|
+
aliases: '-v',
|
51
|
+
desc: 'Your application\'s version'
|
52
|
+
def play_store(package, product_id, token)
|
53
|
+
Commands::PlayStore.run(package, product_id, token, options)
|
54
|
+
end
|
55
|
+
|
56
|
+
desc 'version', 'Print the gem\'s version'
|
57
|
+
def version
|
58
|
+
Commands::Version.run
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module CandyCheck
|
2
|
+
module CLI
|
3
|
+
module Commands
|
4
|
+
# Command to verify an AppStore receipt token
|
5
|
+
class AppStore < Base
|
6
|
+
# Prepare a verification run from the terminal
|
7
|
+
# @param receipt [String]
|
8
|
+
# @param options [Hash]
|
9
|
+
# @option options [String] :secret A shared secret to use
|
10
|
+
# @option options [String] :environment The environment to use
|
11
|
+
def initialize(receipt, options)
|
12
|
+
@receipt = receipt
|
13
|
+
super(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Print the result of the verification to the terminal
|
17
|
+
def run
|
18
|
+
verifier = CandyCheck::AppStore::Verifier.new(config)
|
19
|
+
result = verifier.verify(@receipt, options[:secret])
|
20
|
+
out.print "#{result.class}:"
|
21
|
+
out.pretty result
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def config
|
27
|
+
CandyCheck::AppStore::Config.new(
|
28
|
+
environment: options[:environment].to_sym
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module CandyCheck
|
2
|
+
module CLI
|
3
|
+
module Commands
|
4
|
+
# Base for all commands providing simple support for running a single
|
5
|
+
# command and printing to an {Out} instance
|
6
|
+
class Base
|
7
|
+
# Initialize a new command and prepare options for the run
|
8
|
+
# @param options [Object]
|
9
|
+
def initialize(options = nil)
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
# Run a single instance of a command
|
14
|
+
# @param args [Array] arguments for the command
|
15
|
+
# @return [Base] the command after the run
|
16
|
+
def self.run(*args)
|
17
|
+
new(*args).tap(&:run)
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
# @return [Object] configuration for the run
|
23
|
+
attr_reader :options
|
24
|
+
|
25
|
+
def out
|
26
|
+
@out ||= Out.new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module CandyCheck
|
2
|
+
module CLI
|
3
|
+
module Commands
|
4
|
+
# Command to verify an PlayStore purchase
|
5
|
+
class PlayStore < Base
|
6
|
+
# Prepare a verification run from the terminal
|
7
|
+
# @param package [String]
|
8
|
+
# @param product_id [String]
|
9
|
+
# @param token [String]
|
10
|
+
# @param options [Hash]
|
11
|
+
# @option options [String] :issuer to use for API access
|
12
|
+
# @option options [String] :key_file to use for API access
|
13
|
+
# @option options [String] :key_secret to decrypt the key file
|
14
|
+
# @option options [String] :application_name for the API call
|
15
|
+
# @option options [String] :application_version for the API call
|
16
|
+
def initialize(package, product_id, token, options)
|
17
|
+
@package, @product_id, @token = package, product_id, token
|
18
|
+
super(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Print the result of the verification to the terminal
|
22
|
+
def run
|
23
|
+
verifier = CandyCheck::PlayStore::Verifier.new(config)
|
24
|
+
verifier.boot!
|
25
|
+
result = verifier.verify(@package, @product_id, @token)
|
26
|
+
out.print "#{result.class}:"
|
27
|
+
out.pretty result
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def config
|
33
|
+
CandyCheck::PlayStore::Config.new(options)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'candy_check/cli/commands/base'
|
2
|
+
require 'candy_check/cli/commands/app_store'
|
3
|
+
require 'candy_check/cli/commands/play_store'
|
4
|
+
require 'candy_check/cli/commands/version'
|
5
|
+
|
6
|
+
module CandyCheck
|
7
|
+
module CLI
|
8
|
+
# Module for actual commands which can be invoked from the terminal
|
9
|
+
module Commands
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
module CandyCheck
|
4
|
+
module CLI
|
5
|
+
# A wrapper to output text information to any kind of buffer
|
6
|
+
# @example
|
7
|
+
# out = Out.new(std_buffer)
|
8
|
+
# out.print('something') # => appends 'something' to std_buffer
|
9
|
+
class Out
|
10
|
+
# @return [Object] buffer used as default outlet
|
11
|
+
attr_reader :out
|
12
|
+
|
13
|
+
# Bind a new out instance to two buffers
|
14
|
+
# @param out [Object] STDOUT is default
|
15
|
+
def initialize(out = $stdout)
|
16
|
+
@out = out
|
17
|
+
end
|
18
|
+
|
19
|
+
# Prints to +out+
|
20
|
+
# @param text [String]
|
21
|
+
def print(text = '')
|
22
|
+
out.puts text
|
23
|
+
end
|
24
|
+
|
25
|
+
# Pretty print an object to +out+
|
26
|
+
# @param object [Object]
|
27
|
+
def pretty(object)
|
28
|
+
PP.pp(object, out)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/candy_check/version.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CandyCheck::CLI::App do
|
4
|
+
subject { CandyCheck::CLI::App.new }
|
5
|
+
|
6
|
+
it 'supports the version command' do
|
7
|
+
stub_command(CandyCheck::CLI::Commands::Version) do
|
8
|
+
subject.version.must_equal :stubbed
|
9
|
+
@arguments.must_be_empty
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'supports the app_store command' do
|
14
|
+
stub_command(CandyCheck::CLI::Commands::AppStore) do
|
15
|
+
subject.app_store('receipt').must_equal :stubbed
|
16
|
+
@arguments.must_equal ['receipt', {}]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'supports the play_store command' do
|
21
|
+
stub_command(CandyCheck::CLI::Commands::PlayStore) do
|
22
|
+
subject.play_store('package', 'id', 'token').must_equal :stubbed
|
23
|
+
@arguments.must_equal ['package', 'id', 'token', {}]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def stub_command(target)
|
30
|
+
stub = proc do |*args|
|
31
|
+
@arguments = args
|
32
|
+
:stubbed
|
33
|
+
end
|
34
|
+
target.stub :run, stub do
|
35
|
+
yield
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CandyCheck::CLI::Commands::AppStore do
|
4
|
+
include WithCommand
|
5
|
+
subject { CandyCheck::CLI::Commands::AppStore }
|
6
|
+
let(:arguments) { [receipt, options] }
|
7
|
+
let(:receipt) { 'data' }
|
8
|
+
let(:options) do
|
9
|
+
{
|
10
|
+
environment: :sandbox
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
stub = proc do |*args|
|
16
|
+
@verifier = DummyAppStoreVerifier.new(*args)
|
17
|
+
end
|
18
|
+
CandyCheck::AppStore::Verifier.stub :new, stub do
|
19
|
+
run_command!
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'default' do
|
24
|
+
it 'uses the receipt and the options' do
|
25
|
+
@verifier.config.environment.must_equal :sandbox
|
26
|
+
@verifier.arguments.must_equal [receipt, nil]
|
27
|
+
out.must_be 'Hash:', result: :stubbed
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'with secret' do
|
32
|
+
let(:options) do
|
33
|
+
{
|
34
|
+
environment: :production,
|
35
|
+
secret: 'notasecret'
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'uses the secret for verification' do
|
40
|
+
@verifier.config.environment.must_equal :production
|
41
|
+
@verifier.arguments.must_equal [receipt, 'notasecret']
|
42
|
+
out.must_be 'Hash:', result: :stubbed
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
class DummyAppStoreVerifier < Struct.new(:config)
|
49
|
+
attr_reader :arguments
|
50
|
+
|
51
|
+
def verify(*arguments)
|
52
|
+
@arguments = arguments
|
53
|
+
{ result: :stubbed }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CandyCheck::CLI::Commands::PlayStore do
|
4
|
+
include WithCommand
|
5
|
+
subject { CandyCheck::CLI::Commands::PlayStore }
|
6
|
+
let(:arguments) { [package, product_id, token, options] }
|
7
|
+
let(:package) { 'the_package' }
|
8
|
+
let(:product_id) { 'the_product' }
|
9
|
+
let(:token) { 'the_token' }
|
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
|
19
|
+
|
20
|
+
before do
|
21
|
+
stub = proc do |*args|
|
22
|
+
@verifier = DummyPlayStoreVerifier.new(*args)
|
23
|
+
end
|
24
|
+
CandyCheck::PlayStore::Verifier.stub :new, stub do
|
25
|
+
run_command!
|
26
|
+
end
|
27
|
+
end
|
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
|
+
class DummyPlayStoreVerifier < Struct.new(:config)
|
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 }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CandyCheck::CLI::Out do
|
4
|
+
subject { CandyCheck::CLI::Out.new(out) }
|
5
|
+
let(:out) { StringIO.new }
|
6
|
+
|
7
|
+
it 'defaults to use STDOUT' do
|
8
|
+
CandyCheck::CLI::Out.new.out.must_be_same_as $stdout
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'holds the outlet' do
|
12
|
+
subject.out.must_be_same_as out
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'prints to outlet' do
|
16
|
+
subject.print 'some text'
|
17
|
+
subject.print 'another line'
|
18
|
+
close
|
19
|
+
out.readlines.must_equal [
|
20
|
+
"some text\n",
|
21
|
+
"another line\n"
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'pretty prints to outlet' do
|
26
|
+
subject.pretty dummy: 1
|
27
|
+
subject.pretty [1, 2, 3]
|
28
|
+
close
|
29
|
+
out.readlines.must_equal [
|
30
|
+
"{:dummy=>1}\n",
|
31
|
+
"[1, 2, 3]\n"
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def close
|
38
|
+
out.flush
|
39
|
+
out.rewind
|
40
|
+
end
|
41
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,6 +2,7 @@ require 'coveralls'
|
|
2
2
|
Coveralls.wear!
|
3
3
|
|
4
4
|
require 'candy_check'
|
5
|
+
require 'candy_check/cli'
|
5
6
|
|
6
7
|
require 'minitest/autorun'
|
7
8
|
require 'minitest/around/spec'
|
@@ -10,6 +11,7 @@ require 'webmock/minitest'
|
|
10
11
|
|
11
12
|
require_relative 'support/with_fixtures'
|
12
13
|
require_relative 'support/with_temp_file'
|
14
|
+
require_relative 'support/with_command'
|
13
15
|
|
14
16
|
ENV['DEBUG'] && Google::APIClient.logger.level = Logger::DEBUG
|
15
17
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module WithCommand
|
2
|
+
def out
|
3
|
+
@out ||= OutRecorder.new
|
4
|
+
end
|
5
|
+
|
6
|
+
def run_command!
|
7
|
+
CandyCheck::CLI::Out.stub :new, out do
|
8
|
+
subject.run(*arguments)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def arguments
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
class OutRecorder
|
17
|
+
def lines
|
18
|
+
@lines ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
def print(text = '')
|
22
|
+
lines << text
|
23
|
+
end
|
24
|
+
|
25
|
+
def pretty(object)
|
26
|
+
lines << object
|
27
|
+
end
|
28
|
+
|
29
|
+
def must_be(*expected)
|
30
|
+
lines.must_equal expected
|
31
|
+
end
|
32
|
+
end
|
33
|
+
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.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Thiel
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: thor
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.19'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.19'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rubocop
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -154,8 +168,7 @@ description:
|
|
154
168
|
email:
|
155
169
|
- jonas@thiel.io
|
156
170
|
executables:
|
157
|
-
-
|
158
|
-
- cc_playstore
|
171
|
+
- candy_check
|
159
172
|
extensions: []
|
160
173
|
extra_rdoc_files: []
|
161
174
|
files:
|
@@ -167,8 +180,7 @@ files:
|
|
167
180
|
- LICENSE.txt
|
168
181
|
- README.md
|
169
182
|
- Rakefile
|
170
|
-
- bin/
|
171
|
-
- bin/cc_playstore
|
183
|
+
- bin/candy_check
|
172
184
|
- candy_check.gemspec
|
173
185
|
- lib/candy_check.rb
|
174
186
|
- lib/candy_check/app_store.rb
|
@@ -178,6 +190,14 @@ files:
|
|
178
190
|
- lib/candy_check/app_store/verification.rb
|
179
191
|
- lib/candy_check/app_store/verification_failure.rb
|
180
192
|
- lib/candy_check/app_store/verifier.rb
|
193
|
+
- lib/candy_check/cli.rb
|
194
|
+
- lib/candy_check/cli/app.rb
|
195
|
+
- lib/candy_check/cli/commands.rb
|
196
|
+
- lib/candy_check/cli/commands/app_store.rb
|
197
|
+
- lib/candy_check/cli/commands/base.rb
|
198
|
+
- lib/candy_check/cli/commands/play_store.rb
|
199
|
+
- lib/candy_check/cli/commands/version.rb
|
200
|
+
- lib/candy_check/cli/out.rb
|
181
201
|
- lib/candy_check/play_store.rb
|
182
202
|
- lib/candy_check/play_store/client.rb
|
183
203
|
- lib/candy_check/play_store/config.rb
|
@@ -197,6 +217,11 @@ files:
|
|
197
217
|
- spec/app_store/verification_spec.rb
|
198
218
|
- spec/app_store/verifier_spec.rb
|
199
219
|
- spec/candy_check_spec.rb
|
220
|
+
- spec/cli/app_spec.rb
|
221
|
+
- spec/cli/commands/app_store_spec.rb
|
222
|
+
- spec/cli/commands/play_store_spec.rb
|
223
|
+
- spec/cli/commands/version_spec.rb
|
224
|
+
- spec/cli/out_spec.rb
|
200
225
|
- spec/fixtures/api_cache.dump
|
201
226
|
- spec/fixtures/play_store/api_cache.dump
|
202
227
|
- spec/fixtures/play_store/auth_failure.txt
|
@@ -214,6 +239,7 @@ files:
|
|
214
239
|
- spec/play_store/verification_spec.rb
|
215
240
|
- spec/play_store/verifier_spec.rb
|
216
241
|
- spec/spec_helper.rb
|
242
|
+
- spec/support/with_command.rb
|
217
243
|
- spec/support/with_fixtures.rb
|
218
244
|
- spec/support/with_temp_file.rb
|
219
245
|
homepage: https://github.com/jnbt/candy_check
|
@@ -248,6 +274,11 @@ test_files:
|
|
248
274
|
- spec/app_store/verification_spec.rb
|
249
275
|
- spec/app_store/verifier_spec.rb
|
250
276
|
- spec/candy_check_spec.rb
|
277
|
+
- spec/cli/app_spec.rb
|
278
|
+
- spec/cli/commands/app_store_spec.rb
|
279
|
+
- spec/cli/commands/play_store_spec.rb
|
280
|
+
- spec/cli/commands/version_spec.rb
|
281
|
+
- spec/cli/out_spec.rb
|
251
282
|
- spec/fixtures/api_cache.dump
|
252
283
|
- spec/fixtures/play_store/api_cache.dump
|
253
284
|
- spec/fixtures/play_store/auth_failure.txt
|
@@ -265,6 +296,7 @@ test_files:
|
|
265
296
|
- spec/play_store/verification_spec.rb
|
266
297
|
- spec/play_store/verifier_spec.rb
|
267
298
|
- spec/spec_helper.rb
|
299
|
+
- spec/support/with_command.rb
|
268
300
|
- spec/support/with_fixtures.rb
|
269
301
|
- spec/support/with_temp_file.rb
|
270
302
|
has_rdoc:
|
data/bin/cc_appstore
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'bundler/setup'
|
4
|
-
require 'optparse'
|
5
|
-
require 'ostruct'
|
6
|
-
require 'pp'
|
7
|
-
require 'candy_check'
|
8
|
-
|
9
|
-
class InputParser
|
10
|
-
attr_reader :config, :secret, :receipt
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
@config = {
|
14
|
-
environment: :production
|
15
|
-
}
|
16
|
-
configure!
|
17
|
-
end
|
18
|
-
|
19
|
-
def parse!(args)
|
20
|
-
@parser.parse!(args)
|
21
|
-
help! unless @receipt
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def configure!
|
27
|
-
@parser = OptionParser.new do |opts|
|
28
|
-
@opts = opts
|
29
|
-
opts.banner = 'Usage cc_appstore [options]'
|
30
|
-
opts.separator ''
|
31
|
-
opts.separator 'Required options:'
|
32
|
-
|
33
|
-
opts.on('-r', '--receipt RECEIPT', 'a base64 encoded receipt data string') do |v|
|
34
|
-
@receipt = v
|
35
|
-
end
|
36
|
-
opts.separator ''
|
37
|
-
opts.separator 'Optional options:'
|
38
|
-
opts.on('-e', '--environment ENVIRONMENT', 'production (default) or sandbox') do |v|
|
39
|
-
config[:environment] = v.to_sym
|
40
|
-
end
|
41
|
-
opts.on('-s', '--secret SECRET', 'shared secret for auto-renewable subscriptions') do |v|
|
42
|
-
@secret = v
|
43
|
-
end
|
44
|
-
opts.separator ''
|
45
|
-
opts.separator 'Further functions:'
|
46
|
-
opts.on_tail('-v', '--version', 'Show version') do
|
47
|
-
puts CandyCheck::VERSION
|
48
|
-
exit
|
49
|
-
end
|
50
|
-
opts.on_tail('-h', '--help', 'Show help') do
|
51
|
-
help!
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def help!
|
57
|
-
puts @opts
|
58
|
-
exit
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
class App
|
63
|
-
attr_reader :input
|
64
|
-
|
65
|
-
def initialize
|
66
|
-
@input = InputParser.new
|
67
|
-
load_config
|
68
|
-
perform
|
69
|
-
end
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
def load_config
|
74
|
-
input.parse!(ARGV)
|
75
|
-
end
|
76
|
-
|
77
|
-
def perform
|
78
|
-
config = CandyCheck::AppStore::Config.new(input.config)
|
79
|
-
puts 'Configuration'
|
80
|
-
pp config
|
81
|
-
puts
|
82
|
-
verifier = CandyCheck::AppStore::Verifier.new(config)
|
83
|
-
result = verifier.verify(input.receipt, input.secret)
|
84
|
-
puts
|
85
|
-
puts "#{result.class}:"
|
86
|
-
pp result
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
App.new
|
data/bin/cc_playstore
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'bundler/setup'
|
4
|
-
require 'optparse'
|
5
|
-
require 'ostruct'
|
6
|
-
require 'pp'
|
7
|
-
require 'candy_check'
|
8
|
-
|
9
|
-
class InputParser
|
10
|
-
attr_reader :config, :package, :product_id, :token
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
@config = {
|
14
|
-
application_name: 'CandyCheck',
|
15
|
-
application_version: version,
|
16
|
-
key_secret: 'notasecret'
|
17
|
-
}
|
18
|
-
configure!
|
19
|
-
end
|
20
|
-
|
21
|
-
def parse!(args)
|
22
|
-
@parser.parse!(args)
|
23
|
-
help! unless @package && @product_id && @token
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def configure!
|
29
|
-
@parser = OptionParser.new do |opts|
|
30
|
-
@opts = opts
|
31
|
-
opts.banner = 'Usage cc_playstore [options]'
|
32
|
-
opts.separator ''
|
33
|
-
opts.separator 'Required options:'
|
34
|
-
|
35
|
-
opts.on('-e', '--issuer ISSUER', 'your service account\'s email') do |v|
|
36
|
-
config[:issuer] = v
|
37
|
-
end
|
38
|
-
opts.on('-k', '--key-file KEY_FILE', 'the key file to use') do |v|
|
39
|
-
config[:key_file] = v
|
40
|
-
end
|
41
|
-
opts.on('-p', '--package NAME', 'your app\'s package name') do |v|
|
42
|
-
@package = v
|
43
|
-
end
|
44
|
-
opts.on('-i', '--product-id ID', 'your product\'s id') do |v|
|
45
|
-
@product_id = v
|
46
|
-
end
|
47
|
-
opts.on('-t', '--token TOKEN', 'your the purchase token') do |v|
|
48
|
-
@token = v
|
49
|
-
end
|
50
|
-
opts.separator ''
|
51
|
-
opts.separator 'Optional options:'
|
52
|
-
opts.on('-s', '--secret SECRET', 'the secret to decrypt the key file, defaults to \'notasecret\'') do |v|
|
53
|
-
config[:key_secret] = v
|
54
|
-
end
|
55
|
-
opts.on('-a', '--app-name NAME', 'your application\'s name, default to \'CandyCheck\'') do |v|
|
56
|
-
config[:application_name] = v
|
57
|
-
end
|
58
|
-
opts.on('-r', '--app-version VERSION', "your application's version, default to '#{version}'") do |v|
|
59
|
-
config[:application_version] = v
|
60
|
-
end
|
61
|
-
opts.separator ''
|
62
|
-
opts.separator 'Further functions:'
|
63
|
-
opts.on_tail('-v', '--version', 'Show version') do
|
64
|
-
puts version
|
65
|
-
exit
|
66
|
-
end
|
67
|
-
opts.on_tail('-h', '--help', 'Show help') do
|
68
|
-
help!
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def help!
|
74
|
-
puts @opts
|
75
|
-
exit
|
76
|
-
end
|
77
|
-
|
78
|
-
def version
|
79
|
-
CandyCheck::VERSION
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
class App
|
84
|
-
attr_reader :input
|
85
|
-
|
86
|
-
def initialize
|
87
|
-
@input = InputParser.new
|
88
|
-
load_config
|
89
|
-
perform
|
90
|
-
end
|
91
|
-
|
92
|
-
private
|
93
|
-
|
94
|
-
def load_config
|
95
|
-
input.parse!(ARGV)
|
96
|
-
end
|
97
|
-
|
98
|
-
def perform
|
99
|
-
puts "Package: #{input.package}"
|
100
|
-
puts "Product: #{input.product_id}"
|
101
|
-
puts "Token: #{input.token}"
|
102
|
-
puts
|
103
|
-
config = CandyCheck::PlayStore::Config.new(input.config)
|
104
|
-
puts 'Configuration'
|
105
|
-
pp config
|
106
|
-
puts
|
107
|
-
verifier = CandyCheck::PlayStore::Verifier.new(config)
|
108
|
-
puts 'Booting'
|
109
|
-
verifier.boot!
|
110
|
-
puts '-> done'
|
111
|
-
puts
|
112
|
-
result = verifier.verify(input.package, input.product_id, input.token)
|
113
|
-
puts
|
114
|
-
puts "#{result.class}:"
|
115
|
-
pp result
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
App.new
|