passbookpgh 0.4.5
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 +7 -0
- data/.travis.yml +7 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +130 -0
- data/LICENSE +22 -0
- data/README.md +294 -0
- data/Rakefile +36 -0
- data/VERSION +1 -0
- data/bin/pk +22 -0
- data/lib/commands/build.rb +62 -0
- data/lib/commands/commands.rb +31 -0
- data/lib/commands/generate.rb +44 -0
- data/lib/commands/templates/boarding-pass.json +56 -0
- data/lib/commands/templates/coupon.json +33 -0
- data/lib/commands/templates/event-ticket.json +33 -0
- data/lib/commands/templates/generic.json +33 -0
- data/lib/commands/templates/store-card.json +33 -0
- data/lib/passbook/pkpass.rb +121 -0
- data/lib/passbook/push_notification.rb +19 -0
- data/lib/passbook/signer.rb +40 -0
- data/lib/passbook/version.rb +3 -0
- data/lib/passbook.rb +15 -0
- data/lib/rack/passbook_rack.rb +98 -0
- data/lib/rails/generators/passbook/config/config_generator.rb +16 -0
- data/lib/rails/generators/passbook/config/templates/initializer.rb +13 -0
- data/lib/utils/command_utils.rb +12 -0
- data/passbookpgh.gemspec +110 -0
- data/spec/data/icon.png +0 -0
- data/spec/data/icon@2x.png +0 -0
- data/spec/data/logo.png +0 -0
- data/spec/data/logo@2x.png +0 -0
- data/spec/lib/commands/build_spec.rb +92 -0
- data/spec/lib/commands/commands_spec.rb +102 -0
- data/spec/lib/commands/commands_spec_helper.rb +69 -0
- data/spec/lib/commands/generate_spec.rb +72 -0
- data/spec/lib/passbook/pkpass_spec.rb +108 -0
- data/spec/lib/passbook/push_notification_spec.rb +23 -0
- data/spec/lib/passbook/signer_spec.rb +84 -0
- data/spec/lib/rack/passbook_rack_spec.rb +233 -0
- data/spec/spec_helper.rb +9 -0
- metadata +244 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatVersion" : 1,
|
|
3
|
+
"passTypeIdentifier" : "pass.com.example.boarding-pass",
|
|
4
|
+
"description" : "Example Boarding Pass",
|
|
5
|
+
"teamIdentifier": "Example",
|
|
6
|
+
"organizationName": "Example",
|
|
7
|
+
"serialNumber" : "123456",
|
|
8
|
+
"foregroundColor": "#866B23",
|
|
9
|
+
"backgroundColor": "#FFD248",
|
|
10
|
+
"boardingPass" : {
|
|
11
|
+
"primaryFields" : [
|
|
12
|
+
{
|
|
13
|
+
"key" : "origin",
|
|
14
|
+
"label" : "Atlanta",
|
|
15
|
+
"value" : "ATL"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"key" : "destination",
|
|
19
|
+
"label" : "Johannesburg",
|
|
20
|
+
"value" : "JNB"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"secondaryFields" : [
|
|
24
|
+
{
|
|
25
|
+
"key" : "boarding-gate",
|
|
26
|
+
"label" : "Gate",
|
|
27
|
+
"value" : "F12"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"auxiliaryFields" : [
|
|
31
|
+
{
|
|
32
|
+
"key" : "seat",
|
|
33
|
+
"label" : "Seat",
|
|
34
|
+
"value" : "7A"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"key" : "passenger-name",
|
|
38
|
+
"label" : "Passenger",
|
|
39
|
+
"value" : "Honey Badger"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"transitType" : "PKTransitTypeAir",
|
|
43
|
+
"barcode" : {
|
|
44
|
+
"message" : "DL123",
|
|
45
|
+
"format" : "PKBarcodeFormatQR",
|
|
46
|
+
"messageEncoding" : "iso-8859-1"
|
|
47
|
+
},
|
|
48
|
+
"backFields" : [
|
|
49
|
+
{
|
|
50
|
+
"key" : "terms",
|
|
51
|
+
"label" : "Terms and Conditions",
|
|
52
|
+
"value" : "Valid for date of travel only"
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatVersion" : 1,
|
|
3
|
+
"passTypeIdentifier" : "pass.com.example.coupon",
|
|
4
|
+
"description" : "Example Coupon",
|
|
5
|
+
"teamIdentifier": "Example",
|
|
6
|
+
"organizationName": "Example",
|
|
7
|
+
"serialNumber" : "123456",
|
|
8
|
+
"foregroundColor": "#FFFFFF",
|
|
9
|
+
"backgroundColor": "#C799FF",
|
|
10
|
+
"generic" : {
|
|
11
|
+
"primaryFields" : [
|
|
12
|
+
|
|
13
|
+
],
|
|
14
|
+
"secondaryFields" : [
|
|
15
|
+
|
|
16
|
+
],
|
|
17
|
+
"auxiliaryFields" : [
|
|
18
|
+
|
|
19
|
+
],
|
|
20
|
+
"barcode" : {
|
|
21
|
+
"message" : "ABCD 123 EFGH 456 IJKL 789 MNOP",
|
|
22
|
+
"format" : "PKBarcodeFormatPDF417",
|
|
23
|
+
"messageEncoding" : "iso-8859-1"
|
|
24
|
+
},
|
|
25
|
+
"backFields" : [
|
|
26
|
+
{
|
|
27
|
+
"key" : "terms",
|
|
28
|
+
"label" : "Terms and Conditions",
|
|
29
|
+
"value" : "T's and C's Apply"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatVersion" : 1,
|
|
3
|
+
"passTypeIdentifier" : "pass.com.example.event-ticket",
|
|
4
|
+
"description" : "Example Event Ticket",
|
|
5
|
+
"teamIdentifier": "Example",
|
|
6
|
+
"organizationName": "Example",
|
|
7
|
+
"serialNumber" : "123456",
|
|
8
|
+
"foregroundColor": "#FFFFFF",
|
|
9
|
+
"backgroundColor": "#FF5453",
|
|
10
|
+
"generic" : {
|
|
11
|
+
"primaryFields" : [
|
|
12
|
+
|
|
13
|
+
],
|
|
14
|
+
"secondaryFields" : [
|
|
15
|
+
|
|
16
|
+
],
|
|
17
|
+
"auxiliaryFields" : [
|
|
18
|
+
|
|
19
|
+
],
|
|
20
|
+
"barcode" : {
|
|
21
|
+
"message" : "ABCD 123 EFGH 456 IJKL 789 MNOP",
|
|
22
|
+
"format" : "PKBarcodeFormatPDF417",
|
|
23
|
+
"messageEncoding" : "iso-8859-1"
|
|
24
|
+
},
|
|
25
|
+
"backFields" : [
|
|
26
|
+
{
|
|
27
|
+
"key" : "terms",
|
|
28
|
+
"label" : "Terms and Conditions",
|
|
29
|
+
"value" : "T's and C's apply"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatVersion" : 1,
|
|
3
|
+
"passTypeIdentifier" : "pass.com.example.generic",
|
|
4
|
+
"description" : "Example Generic Pass",
|
|
5
|
+
"teamIdentifier": "Example",
|
|
6
|
+
"organizationName": "Example",
|
|
7
|
+
"serialNumber" : "123456",
|
|
8
|
+
"foregroundColor": "#FFFFFF",
|
|
9
|
+
"backgroundColor": "#444444",
|
|
10
|
+
"generic" : {
|
|
11
|
+
"primaryFields" : [
|
|
12
|
+
|
|
13
|
+
],
|
|
14
|
+
"secondaryFields" : [
|
|
15
|
+
|
|
16
|
+
],
|
|
17
|
+
"auxiliaryFields" : [
|
|
18
|
+
|
|
19
|
+
],
|
|
20
|
+
"barcode" : {
|
|
21
|
+
"message" : "ABCD 123 EFGH 456 IJKL 789 MNOP",
|
|
22
|
+
"format" : "PKBarcodeFormatPDF417",
|
|
23
|
+
"messageEncoding" : "iso-8859-1"
|
|
24
|
+
},
|
|
25
|
+
"backFields" : [
|
|
26
|
+
{
|
|
27
|
+
"key" : "terms",
|
|
28
|
+
"label" : "Terms and Conditions",
|
|
29
|
+
"value" : "Put your terms here"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatVersion" : 1,
|
|
3
|
+
"passTypeIdentifier" : "pass.com.example.store-card",
|
|
4
|
+
"description" : "Example Store Card",
|
|
5
|
+
"teamIdentifier": "Example",
|
|
6
|
+
"organizationName": "Example",
|
|
7
|
+
"serialNumber" : "123456",
|
|
8
|
+
"foregroundColor": "#FFFFFF",
|
|
9
|
+
"backgroundColor": "#AFC1E3",
|
|
10
|
+
"generic" : {
|
|
11
|
+
"primaryFields" : [
|
|
12
|
+
|
|
13
|
+
],
|
|
14
|
+
"secondaryFields" : [
|
|
15
|
+
|
|
16
|
+
],
|
|
17
|
+
"auxiliaryFields" : [
|
|
18
|
+
|
|
19
|
+
],
|
|
20
|
+
"barcode" : {
|
|
21
|
+
"message" : "ABCD 123 EFGH 456 IJKL 789 MNOP",
|
|
22
|
+
"format" : "PKBarcodeFormatPDF417",
|
|
23
|
+
"messageEncoding" : "iso-8859-1"
|
|
24
|
+
},
|
|
25
|
+
"backFields" : [
|
|
26
|
+
{
|
|
27
|
+
"key" : "terms",
|
|
28
|
+
"label" : "Terms and Conditions",
|
|
29
|
+
"value" : "T's and C's apply"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
require 'digest/sha1'
|
|
2
|
+
require 'openssl'
|
|
3
|
+
require 'zip'
|
|
4
|
+
require 'base64'
|
|
5
|
+
|
|
6
|
+
module Passbook
|
|
7
|
+
class PKPass
|
|
8
|
+
attr_accessor :pass, :manifest_files, :signer
|
|
9
|
+
|
|
10
|
+
TYPES = ['boarding-pass', 'coupon', 'event-ticket', 'store-card', 'generic']
|
|
11
|
+
|
|
12
|
+
def initialize(pass, init_signer = nil)
|
|
13
|
+
@pass = pass
|
|
14
|
+
@manifest_files = []
|
|
15
|
+
@signer = init_signer || Passbook::Signer.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def addFile(file)
|
|
19
|
+
@manifest_files << file
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def addFiles(files)
|
|
23
|
+
@manifest_files += files
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# for backwards compatibility
|
|
27
|
+
def json=(json)
|
|
28
|
+
@pass = json
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def build
|
|
32
|
+
manifest = createManifest
|
|
33
|
+
|
|
34
|
+
# Check pass for necessary files and fields
|
|
35
|
+
checkPass manifest
|
|
36
|
+
|
|
37
|
+
# Create pass signature
|
|
38
|
+
signature = @signer.sign manifest
|
|
39
|
+
|
|
40
|
+
[manifest, signature]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Backward compatibility
|
|
44
|
+
def create
|
|
45
|
+
self.file.path
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Return a Tempfile containing our ZipStream
|
|
49
|
+
def file(options = {})
|
|
50
|
+
options[:file_name] ||= 'pass.pkpass'
|
|
51
|
+
|
|
52
|
+
temp_file = Tempfile.new(options[:file_name])
|
|
53
|
+
temp_file.binmode
|
|
54
|
+
temp_file.write self.stream.string
|
|
55
|
+
temp_file.close
|
|
56
|
+
|
|
57
|
+
temp_file
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Return a ZipOutputStream
|
|
61
|
+
def stream
|
|
62
|
+
manifest, signature = build
|
|
63
|
+
|
|
64
|
+
outputZip manifest, signature
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def checkPass(manifest)
|
|
70
|
+
# Check for default images
|
|
71
|
+
raise 'Icon missing' unless manifest.include?('icon.png')
|
|
72
|
+
raise 'Icon@2x missing' unless manifest.include?('icon@2x.png')
|
|
73
|
+
|
|
74
|
+
# Check for developer field in JSON
|
|
75
|
+
raise 'Pass Type Identifier missing' unless @pass.include?('passTypeIdentifier')
|
|
76
|
+
raise 'Team Identifier missing' unless @pass.include?('teamIdentifier')
|
|
77
|
+
raise 'Serial Number missing' unless @pass.include?('serialNumber')
|
|
78
|
+
raise 'Organization Name Identifier missing' unless @pass.include?('organizationName')
|
|
79
|
+
raise 'Format Version' unless @pass.include?('formatVersion')
|
|
80
|
+
raise 'Format Version should be a numeric' unless JSON.parse(@pass)['formatVersion'].is_a?(Numeric)
|
|
81
|
+
raise 'Description' unless @pass.include?('description')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def createManifest
|
|
85
|
+
sha1s = {}
|
|
86
|
+
sha1s['pass.json'] = Digest::SHA1.hexdigest @pass
|
|
87
|
+
|
|
88
|
+
@manifest_files.each do |file|
|
|
89
|
+
if file.class == Hash
|
|
90
|
+
sha1s[file[:name]] = Digest::SHA1.hexdigest file[:content]
|
|
91
|
+
else
|
|
92
|
+
sha1s[File.basename(file)] = Digest::SHA1.file(file).hexdigest
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
sha1s.to_json
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def outputZip(manifest, signature)
|
|
100
|
+
|
|
101
|
+
Zip::OutputStream.write_buffer do |zip|
|
|
102
|
+
zip.put_next_entry 'pass.json'
|
|
103
|
+
zip.write @pass
|
|
104
|
+
zip.put_next_entry 'manifest.json'
|
|
105
|
+
zip.write manifest
|
|
106
|
+
zip.put_next_entry 'signature'
|
|
107
|
+
zip.write signature
|
|
108
|
+
|
|
109
|
+
@manifest_files.each do |file|
|
|
110
|
+
if file.class == Hash
|
|
111
|
+
zip.put_next_entry file[:name]
|
|
112
|
+
zip.print file[:content]
|
|
113
|
+
else
|
|
114
|
+
zip.put_next_entry File.basename(file)
|
|
115
|
+
zip.print IO.read(file)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Passbook
|
|
2
|
+
class PushNotification
|
|
3
|
+
class << self
|
|
4
|
+
def pusher
|
|
5
|
+
@pusher ||= Grocer.pusher(
|
|
6
|
+
:certificate => Passbook.notification_cert,
|
|
7
|
+
:passphrase => Passbook.notification_passphrase || "",
|
|
8
|
+
:gateway => Passbook.notification_gateway
|
|
9
|
+
)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def send_notification(device_token)
|
|
13
|
+
notification = Grocer::PassbookNotification.new(:device_token => device_token)
|
|
14
|
+
|
|
15
|
+
pusher.push notification
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'base64'
|
|
3
|
+
|
|
4
|
+
module Passbook
|
|
5
|
+
class Signer
|
|
6
|
+
attr_accessor :certificate, :password, :key, :wwdc_cert, :key_hash, :p12_cert
|
|
7
|
+
|
|
8
|
+
def initialize(params = {})
|
|
9
|
+
@certificate = params[:certificate] || Passbook.p12_certificate
|
|
10
|
+
@password = params[:password] || Passbook.p12_password
|
|
11
|
+
@key = params[:key] || (params.empty? ? Passbook.p12_key : nil)
|
|
12
|
+
@wwdc_cert = params[:wwdc_cert] || Passbook.wwdc_cert
|
|
13
|
+
compute_cert
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def sign(data)
|
|
17
|
+
wwdc = OpenSSL::X509::Certificate.new File.read(wwdc_cert)
|
|
18
|
+
pk7 = OpenSSL::PKCS7.sign key_hash[:cert], key_hash[:key], data.to_s, [wwdc], OpenSSL::PKCS7::BINARY | OpenSSL::PKCS7::DETACHED
|
|
19
|
+
data = OpenSSL::PKCS7.write_smime pk7
|
|
20
|
+
|
|
21
|
+
str_debut = "filename=\"smime.p7s\"\n\n"
|
|
22
|
+
data = data[data.index(str_debut)+str_debut.length..data.length-1]
|
|
23
|
+
str_end = "\n\n------"
|
|
24
|
+
data = data[0..data.index(str_end)-1]
|
|
25
|
+
|
|
26
|
+
Base64.decode64(data)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def compute_cert
|
|
30
|
+
@key_hash = {}
|
|
31
|
+
if key
|
|
32
|
+
@key_hash[:key] = OpenSSL::PKey::RSA.new File.read(key), password
|
|
33
|
+
@key_hash[:cert] = OpenSSL::X509::Certificate.new File.read(certificate)
|
|
34
|
+
else
|
|
35
|
+
p12 = OpenSSL::PKCS12.new File.read(certificate), password
|
|
36
|
+
@key_hash[:key], @key_hash[:cert] = p12.key, p12.certificate
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/passbook.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require "passbook/version"
|
|
2
|
+
require "passbook/pkpass"
|
|
3
|
+
require "passbook/signer"
|
|
4
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
|
5
|
+
require 'passbook/push_notification'
|
|
6
|
+
require 'grocer'
|
|
7
|
+
require 'rack/passbook_rack'
|
|
8
|
+
|
|
9
|
+
module Passbook
|
|
10
|
+
mattr_accessor :p12_certificate, :p12_password, :wwdc_cert, :p12_key, :notification_cert, :notification_gateway, :notification_passphrase
|
|
11
|
+
|
|
12
|
+
def self.configure
|
|
13
|
+
yield self
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module Rack
|
|
2
|
+
class PassbookRack
|
|
3
|
+
|
|
4
|
+
def initialize(app)
|
|
5
|
+
@app = app
|
|
6
|
+
@parameters = {}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(env)
|
|
10
|
+
@parameters['authToken'] = env['HTTP_AUTHORIZATION'].gsub(/ApplePass /,'') if env['HTTP_AUTHORIZATION']
|
|
11
|
+
@parameters.merge!(Rack::Utils.parse_nested_query(env['QUERY_STRING']))
|
|
12
|
+
method_and_params = find_method env['PATH_INFO']
|
|
13
|
+
if method_and_params
|
|
14
|
+
case method_and_params[:method]
|
|
15
|
+
when 'device_register_delete'
|
|
16
|
+
if env['REQUEST_METHOD'] == 'POST'
|
|
17
|
+
[Passbook::PassbookNotification.
|
|
18
|
+
register_pass(method_and_params[:params].merge! JSON.parse(env['rack.input'].read 1000))[:status],
|
|
19
|
+
{}, ['']]
|
|
20
|
+
elsif env['REQUEST_METHOD'] == 'DELETE'
|
|
21
|
+
[Passbook::PassbookNotification.unregister_pass(method_and_params[:params])[:status], {}, {}]
|
|
22
|
+
end
|
|
23
|
+
when 'passes_for_device'
|
|
24
|
+
response = Passbook::PassbookNotification.passes_for_device(method_and_params[:params])
|
|
25
|
+
[response ? 200 : 204, {}, [response.to_json]]
|
|
26
|
+
when 'latest_pass'
|
|
27
|
+
response = Passbook::PassbookNotification.latest_pass(method_and_params[:params])
|
|
28
|
+
if response[:status] == 200
|
|
29
|
+
[200, {'Content-Type' => 'application/vnd.apple.pkpass',
|
|
30
|
+
'Content-Disposition' => 'attachment',
|
|
31
|
+
'filename' => "#{method_and_params[:params]['serialNumber']}.pkpass","last-modified" => response[:last_modified]}, [response[:latest_pass]]]
|
|
32
|
+
else
|
|
33
|
+
[response[:status], {}, {}]
|
|
34
|
+
end
|
|
35
|
+
when 'log'
|
|
36
|
+
Passbook::PassbookNotification.passbook_log JSON.parse(env['rack.input'].read 10000)
|
|
37
|
+
[200, {}, {}]
|
|
38
|
+
else
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
@app.call env
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def append_parameter_separator url
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def each(&block)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def find_method(path)
|
|
53
|
+
parsed_path = path.split '/'
|
|
54
|
+
url_beginning = parsed_path.index 'v1'
|
|
55
|
+
if url_beginning
|
|
56
|
+
args_length = parsed_path.size - url_beginning
|
|
57
|
+
|
|
58
|
+
if (parsed_path[url_beginning + 1 ] == 'devices') and
|
|
59
|
+
(parsed_path[url_beginning + 3 ] == 'registrations')
|
|
60
|
+
if args_length == 6
|
|
61
|
+
return method_and_params_hash 'device_register_delete', path
|
|
62
|
+
elsif args_length == 5
|
|
63
|
+
return method_and_params_hash 'passes_for_device', path
|
|
64
|
+
end
|
|
65
|
+
elsif parsed_path[url_beginning + 1] == 'passes' and args_length == 4
|
|
66
|
+
return method_and_params_hash 'latest_pass', path
|
|
67
|
+
elsif parsed_path[url_beginning + 1] == 'log' and args_length == 2
|
|
68
|
+
return {:method => 'log'}
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
return nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def method_and_params_hash(method, path)
|
|
78
|
+
parsed_path = path.split '/'
|
|
79
|
+
url_beginning = parsed_path.index 'v1'
|
|
80
|
+
if method == 'latest_pass'
|
|
81
|
+
{:method => 'latest_pass',
|
|
82
|
+
:params => @parameters.merge!({'passTypeIdentifier' => parsed_path[url_beginning + 2],
|
|
83
|
+
'serialNumber' => parsed_path[url_beginning + 3]})}
|
|
84
|
+
else
|
|
85
|
+
return_hash = {:method => method, :params =>
|
|
86
|
+
@parameters.merge!({'deviceLibraryIdentifier' => parsed_path[url_beginning + 2],
|
|
87
|
+
'passTypeIdentifier' => parsed_path[url_beginning + 4]})}
|
|
88
|
+
if method == 'device_register_delete'
|
|
89
|
+
return_hash[:params]['serialNumber'] = parsed_path[url_beginning + 5]
|
|
90
|
+
end
|
|
91
|
+
return_hash
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Passbook
|
|
2
|
+
module Generators
|
|
3
|
+
class ConfigGenerator < Rails::Generators::Base
|
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
5
|
+
|
|
6
|
+
argument :wwdc_cert_path, type: :string, default: '', optional: true, banner: "Absolute path to your wwdc cert file"
|
|
7
|
+
argument :p12_cert_path, type: :string, default: '', optional: true, banner: "Absolute path to your cert.p12 file"
|
|
8
|
+
argument :p12_password, type: :string, default: '', optional: true, banner: "Password for your certificate"
|
|
9
|
+
|
|
10
|
+
desc 'Create passbook initializer'
|
|
11
|
+
def create_initializer_file
|
|
12
|
+
template 'initializer.rb', File.join('config', 'initializers', 'passbook.rb')
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'passbook'
|
|
2
|
+
|
|
3
|
+
Passbook.configure do |passbook|
|
|
4
|
+
|
|
5
|
+
# Path to your wwdc cert file
|
|
6
|
+
passbook.wwdc_cert = '<%= wwdc_cert_path %>'
|
|
7
|
+
|
|
8
|
+
# Path to your cert.p12 file
|
|
9
|
+
passbook.p12_certificate = '<%= p12_cert_path %>'
|
|
10
|
+
|
|
11
|
+
# Password for your certificate
|
|
12
|
+
passbook.p12_password = '<%= p12_password %>'
|
|
13
|
+
end
|
data/passbookpgh.gemspec
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
# stub: passbook 0.4.4 ruby lib
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |s|
|
|
8
|
+
s.name = "passbookpgh"
|
|
9
|
+
s.version = "0.4.5"
|
|
10
|
+
|
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
12
|
+
s.require_paths = ["lib"]
|
|
13
|
+
s.authors = ["Thomas Lauro", "Lance Gleason", "Danielle Greaves"]
|
|
14
|
+
s.date = "2022-01-26"
|
|
15
|
+
s.description = "This gem allows you to create IOS Passbooks. Unlike some, this works with Rails but does not require it."
|
|
16
|
+
s.email = ["thomas@lauro.fr", "lgleason@polyglotprogramminginc.com", "greaves@trustarts.org"]
|
|
17
|
+
s.executables = ["pk"]
|
|
18
|
+
s.extra_rdoc_files = [
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"README.md"
|
|
21
|
+
]
|
|
22
|
+
s.files = [
|
|
23
|
+
".travis.yml",
|
|
24
|
+
"Gemfile",
|
|
25
|
+
"Gemfile.lock",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md",
|
|
28
|
+
"Rakefile",
|
|
29
|
+
"VERSION",
|
|
30
|
+
"bin/pk",
|
|
31
|
+
"lib/commands/build.rb",
|
|
32
|
+
"lib/commands/commands.rb",
|
|
33
|
+
"lib/commands/generate.rb",
|
|
34
|
+
"lib/commands/templates/boarding-pass.json",
|
|
35
|
+
"lib/commands/templates/coupon.json",
|
|
36
|
+
"lib/commands/templates/event-ticket.json",
|
|
37
|
+
"lib/commands/templates/generic.json",
|
|
38
|
+
"lib/commands/templates/store-card.json",
|
|
39
|
+
"lib/passbook.rb",
|
|
40
|
+
"lib/passbook/pkpass.rb",
|
|
41
|
+
"lib/passbook/push_notification.rb",
|
|
42
|
+
"lib/passbook/signer.rb",
|
|
43
|
+
"lib/passbook/version.rb",
|
|
44
|
+
"lib/rack/passbook_rack.rb",
|
|
45
|
+
"lib/rails/generators/passbook/config/config_generator.rb",
|
|
46
|
+
"lib/rails/generators/passbook/config/templates/initializer.rb",
|
|
47
|
+
"lib/utils/command_utils.rb",
|
|
48
|
+
"passbookpgh.gemspec",
|
|
49
|
+
"spec/data/icon.png",
|
|
50
|
+
"spec/data/icon@2x.png",
|
|
51
|
+
"spec/data/logo.png",
|
|
52
|
+
"spec/data/logo@2x.png",
|
|
53
|
+
"spec/lib/commands/build_spec.rb",
|
|
54
|
+
"spec/lib/commands/commands_spec.rb",
|
|
55
|
+
"spec/lib/commands/commands_spec_helper.rb",
|
|
56
|
+
"spec/lib/commands/generate_spec.rb",
|
|
57
|
+
"spec/lib/passbook/pkpass_spec.rb",
|
|
58
|
+
"spec/lib/passbook/push_notification_spec.rb",
|
|
59
|
+
"spec/lib/passbook/signer_spec.rb",
|
|
60
|
+
"spec/lib/rack/passbook_rack_spec.rb",
|
|
61
|
+
"spec/spec_helper.rb"
|
|
62
|
+
]
|
|
63
|
+
s.homepage = "https://github.com/pgharts/passbookpgh"
|
|
64
|
+
s.licenses = ["MIT"]
|
|
65
|
+
s.rubygems_version = "2.4.8"
|
|
66
|
+
s.summary = "A IOS Passbook generator."
|
|
67
|
+
|
|
68
|
+
if s.respond_to? :specification_version then
|
|
69
|
+
s.specification_version = 4
|
|
70
|
+
|
|
71
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
72
|
+
s.add_runtime_dependency(%q<rubyzip>, [">= 1.0.0"])
|
|
73
|
+
s.add_runtime_dependency(%q<grocer>, [">= 0"])
|
|
74
|
+
s.add_runtime_dependency(%q<commander>, [">= 0"])
|
|
75
|
+
s.add_runtime_dependency(%q<terminal-table>, [">= 0"])
|
|
76
|
+
s.add_development_dependency(%q<rack-test>, [">= 0"])
|
|
77
|
+
s.add_development_dependency(%q<activesupport>, [">= 0"])
|
|
78
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
|
79
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
|
80
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
|
81
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
|
82
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
|
83
|
+
else
|
|
84
|
+
s.add_dependency(%q<rubyzip>, [">= 1.0.0"])
|
|
85
|
+
s.add_dependency(%q<grocer>, [">= 0"])
|
|
86
|
+
s.add_dependency(%q<commander>, [">= 0"])
|
|
87
|
+
s.add_dependency(%q<terminal-table>, [">= 0"])
|
|
88
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
|
89
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
|
90
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
|
91
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
|
92
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
|
93
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
|
94
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
|
95
|
+
end
|
|
96
|
+
else
|
|
97
|
+
s.add_dependency(%q<rubyzip>, [">= 1.0.0"])
|
|
98
|
+
s.add_dependency(%q<grocer>, [">= 0"])
|
|
99
|
+
s.add_dependency(%q<commander>, [">= 0"])
|
|
100
|
+
s.add_dependency(%q<terminal-table>, [">= 0"])
|
|
101
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
|
102
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
|
103
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
|
104
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
|
105
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
|
106
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
|
107
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
data/spec/data/icon.png
ADDED
|
Binary file
|
|
Binary file
|
data/spec/data/logo.png
ADDED
|
Binary file
|
|
Binary file
|