candy_check 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/candy_check.svg)](http://badge.fury.io/rb/candy_check)
|
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
|
+
[![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)
|
6
8
|
[![Inline docs](http://inch-ci.org/github/jnbt/candy_check.svg?branch=master)](http://inch-ci.org/github/jnbt/candy_check)
|
7
9
|
[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg?style=flat)](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
|