passkit 0.7.0 → 0.8.0
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/.example.env +1 -1
- data/CHANGELOG.md +4 -0
- data/README.md +37 -4
- data/app/controllers/passkit/api/v1/passes_controller.rb +19 -8
- data/app/models/passkit/pass.rb +1 -4
- data/app/views/passkit/dashboard/passes/index.html.erb +17 -18
- data/app/views/passkit/example_mailer/example_email.html.erb +4 -4
- data/docs/passkit_environment_variables.md +5 -2
- data/lib/passkit/base_pass.rb +28 -29
- data/lib/passkit/example_store_card.rb +2 -6
- data/lib/passkit/factory.rb +1 -0
- data/lib/passkit/generator.rb +25 -13
- data/lib/passkit/payload_generator.rb +5 -4
- data/lib/passkit/url_generator.rb +3 -3
- data/lib/passkit/version.rb +1 -1
- data/lib/passkit.rb +48 -10
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 759d9f1d6698e06c21d68d76e55edfa52f4e6e687148b2e553709471158597db
|
|
4
|
+
data.tar.gz: 90453db3b78892ab6ec2df6466288242a23b782b7524734c92bf08f057fbc032
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f3fa0b1df59c67f5ea94426c9c18bfcfd89aa90ed02a1499fc0e429ad666160a231b6a6b413c8a4399bcc5a8e0285d72d34d4a41e8aca433ca1ff8eb02e5963b
|
|
7
|
+
data.tar.gz: 17f3a2d0abd18a192f52757a1c256ea9d8645dbdba16bdc7aa921e9575194a1ce3c188f2ee698888697a374e9c672bf73c0305c242689897810cd7332aaf51ad
|
data/.example.env
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
PASSKIT_WEB_SERVICE_HOST=https://localhost:3000
|
|
2
|
-
PASSKIT_CERTIFICATE_KEY=
|
|
2
|
+
PASSKIT_CERTIFICATE_KEY=theCertPassword
|
|
3
3
|
PASSKIT_PRIVATE_P12_CERTIFICATE=path/to/certificate.p12
|
|
4
4
|
PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE=path/to/AppleWWDRCA.cer
|
|
5
5
|
PASSKIT_APPLE_TEAM_IDENTIFIER=XXXXXXXXXX
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## [0.8.0]
|
|
2
|
+
- [#36](https://github.com/coorasse/passkit/pull/36): More flexible configuration parameters.
|
|
3
|
+
- [#44](https://github.com/coorasse/passkit/pull/44): Add `stripColor` option.
|
|
4
|
+
|
|
1
5
|
## [0.7.0]
|
|
2
6
|
- [#25](https://github.com/coorasse/passkit/pull/25): Change the label default color to black.
|
|
3
7
|
|
data/README.md
CHANGED
|
@@ -58,6 +58,7 @@ If you followed the installation steps, you already saw that Passkit provides
|
|
|
58
58
|
you the tables and ActiveRecord models, and also an engine with the necessary APIs already implemented.
|
|
59
59
|
|
|
60
60
|
Now is your turn. Before proceeding, you need to set these ENV variables:
|
|
61
|
+
|
|
61
62
|
* `PASSKIT_WEB_SERVICE_HOST`
|
|
62
63
|
* `PASSKIT_CERTIFICATE_KEY`
|
|
63
64
|
* `PASSKIT_PRIVATE_P12_CERTIFICATE`
|
|
@@ -68,6 +69,26 @@ Now is your turn. Before proceeding, you need to set these ENV variables:
|
|
|
68
69
|
We have a [specific guide on how to get all these](docs/passkit_environment_variables.md), please follow it.
|
|
69
70
|
You cannot start using this library without these variables set, and we cannot do the work for you.
|
|
70
71
|
|
|
72
|
+
Alternatively, you can configure passkit with an initializer, where you can use environment variables, Rails secrets,
|
|
73
|
+
or any other source for the required credentials:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
Passkit.configure do |config|
|
|
77
|
+
# Required, no defaults
|
|
78
|
+
config.apple_team_identifier = "dummy ID"
|
|
79
|
+
config.certificate_key = "dummy key"
|
|
80
|
+
config.private_p12_certificate = "path/to/file"
|
|
81
|
+
config.apple_intermediate_certificate = "path/to/file"
|
|
82
|
+
config.pass_type_identifier = "pass.com.some.id"
|
|
83
|
+
|
|
84
|
+
# Optional, defaults shown
|
|
85
|
+
config.dashboard_username = nil
|
|
86
|
+
config.dashboard_password = nil
|
|
87
|
+
config.skip_verification = false # Unless true, throws exceptions on startup when a required configuration is missing
|
|
88
|
+
config.web_service_host = "https://localhost:3000"
|
|
89
|
+
config.available_passes = { "Passkit::ExampleStoreCard" => -> {} }
|
|
90
|
+
end
|
|
91
|
+
|
|
71
92
|
## Usage
|
|
72
93
|
|
|
73
94
|
If you followed the installation steps and you have the ENV variables set, we can start looking at what is provided for you.
|
|
@@ -128,22 +149,34 @@ You can define colors, fields and texts. You can also define the logo and the ba
|
|
|
128
149
|
You should place the images in a 'private/passkit/<your_downcased_passname>' folder.
|
|
129
150
|
There is a [dummy app in the gem](test/dummy) that you can use to check how to create your own Wallet Passes.
|
|
130
151
|
|
|
152
|
+
Full documentation for image specifications is on Apple's
|
|
153
|
+
[Pass Design and Creation](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/PassKit_PG/Creating.html)
|
|
154
|
+
page. Naming the file according to convetion and putting it in 'private/passkit/<your_downcased_passname>' is all that's needed for it
|
|
155
|
+
to be included in the pass.
|
|
156
|
+
|
|
131
157
|
### Serve your Wallet Pass
|
|
132
158
|
|
|
133
|
-
Use the [Passkit::UrlGenerator](lib/passkit/url_generator.rb) to generate the URL to serve your Wallet Pass.
|
|
134
|
-
|
|
159
|
+
Use the [Passkit::UrlGenerator](lib/passkit/url_generator.rb) to generate the URL to serve your Wallet Pass.
|
|
160
|
+
For one pass, you can initialize it with:
|
|
135
161
|
|
|
136
162
|
```ruby
|
|
137
163
|
Passkit::UrlGenerator.new(Passkit::MyPass, User.find(1))
|
|
138
164
|
```
|
|
139
165
|
|
|
166
|
+
For one passes, you can initialize it with:
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
Passkit::UrlGenerator.new(Passkit::UserTicket, User.find(1), :tickets)
|
|
170
|
+
```
|
|
171
|
+
(this presumes you have `User.find(1).tickets` would return the ticket records)
|
|
172
|
+
|
|
140
173
|
and then use `.android` or `.ios` to get the URL to serve the Wallet Pass.
|
|
141
174
|
Again, check the example mailer included in the gem to see how to use it.
|
|
142
175
|
|
|
143
176
|
## Debug issues
|
|
144
177
|
|
|
145
|
-
* On Mac, open the `Console.app` to
|
|
146
|
-
* Check the logs on http://localhost:3000/passkit/logs
|
|
178
|
+
* On Mac, you can open the *.pkpass files with "Pass Viewer". Open the `Console.app` to log possible error messages and filter by "Pass Viewer" process.
|
|
179
|
+
* Check the logs on http://localhost:3000/passkit/dashboard/logs
|
|
147
180
|
* In case of error "The passTypeIdentifier or teamIdentifier provided may not match your certificate,
|
|
148
181
|
or the certificate trust chain could not be verified." the certificate (p12) might be expired.
|
|
149
182
|
|
|
@@ -5,7 +5,18 @@ module Passkit
|
|
|
5
5
|
before_action :decrypt_payload, only: :create
|
|
6
6
|
|
|
7
7
|
def create
|
|
8
|
-
|
|
8
|
+
set_generator
|
|
9
|
+
|
|
10
|
+
if @generator && @payload[:collection_name].present?
|
|
11
|
+
files = @generator.public_send(@payload[:collection_name]).collect do |collection_item|
|
|
12
|
+
Passkit::Factory.create_pass(@payload[:pass_class], collection_item)
|
|
13
|
+
end
|
|
14
|
+
file = Passkit::Generator.compress_passes_files(files)
|
|
15
|
+
send_file(file, type: 'application/vnd.apple.pkpasses', disposition: 'attachment')
|
|
16
|
+
else
|
|
17
|
+
file = Passkit::Factory.create_pass(@payload[:pass_class], @generator)
|
|
18
|
+
send_file(file, type: 'application/vnd.apple.pkpass', disposition: 'attachment')
|
|
19
|
+
end
|
|
9
20
|
end
|
|
10
21
|
|
|
11
22
|
# @return If request is authorized, returns HTTP status 200 with a payload of the pass data.
|
|
@@ -44,13 +55,13 @@ module Passkit
|
|
|
44
55
|
end
|
|
45
56
|
end
|
|
46
57
|
|
|
47
|
-
def
|
|
48
|
-
generator = nil
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
def set_generator
|
|
59
|
+
@generator = nil
|
|
60
|
+
|
|
61
|
+
return unless @payload[:generator_class].present? && @payload[:generator_id].present?
|
|
62
|
+
|
|
63
|
+
generator_class = @payload[:generator_class].constantize
|
|
64
|
+
@generator = generator_class.find(@payload[:generator_id])
|
|
54
65
|
end
|
|
55
66
|
end
|
|
56
67
|
end
|
data/app/models/passkit/pass.rb
CHANGED
|
@@ -17,14 +17,11 @@ module Passkit
|
|
|
17
17
|
:barcodes,
|
|
18
18
|
:beacons,
|
|
19
19
|
:boarding_pass,
|
|
20
|
-
:coupon,
|
|
21
20
|
:description,
|
|
22
|
-
:event_ticket,
|
|
23
21
|
:expiration_date,
|
|
24
22
|
:file_name,
|
|
25
23
|
:foreground_color,
|
|
26
24
|
:format_version,
|
|
27
|
-
:generic,
|
|
28
25
|
:grouping_identifier,
|
|
29
26
|
:header_fields,
|
|
30
27
|
:label_color,
|
|
@@ -42,7 +39,7 @@ module Passkit
|
|
|
42
39
|
:secondary_fields,
|
|
43
40
|
:semantics,
|
|
44
41
|
:sharing_prohibited,
|
|
45
|
-
:
|
|
42
|
+
:strip_color
|
|
46
43
|
:suppress_strip_shine,
|
|
47
44
|
:user_info,
|
|
48
45
|
:voided,
|
|
@@ -2,31 +2,30 @@
|
|
|
2
2
|
<h1>Passkit Passes</h1>
|
|
3
3
|
<%= render 'shared/passkit/navigation' %>
|
|
4
4
|
</header>
|
|
5
|
-
|
|
6
5
|
<main>
|
|
7
6
|
<h2>Last 100 passes and registered devices</h2>
|
|
8
7
|
<table>
|
|
9
8
|
<thead>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
<th>Created at</th>
|
|
10
|
+
<th>Generator</th>
|
|
11
|
+
<th>Pass Class</th>
|
|
12
|
+
<th>Devices</th>
|
|
14
13
|
</thead>
|
|
15
14
|
<tbody>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
<% @passes.each do |pass| %>
|
|
16
|
+
<tr>
|
|
17
|
+
<td><%= I18n.l(pass.created_at) %></td>
|
|
18
|
+
<td><%= pass.generator.class.name %> - <%= pass.generator.id %></td>
|
|
19
|
+
<td><%= pass.klass %></td>
|
|
20
|
+
<td>
|
|
21
|
+
<% pass.devices.each do |device| %>
|
|
22
|
+
<div>
|
|
24
23
|
<%= device.push_token %>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
</div>
|
|
25
|
+
<% end %>
|
|
26
|
+
</td>
|
|
27
|
+
</tr>
|
|
28
|
+
<% end %>
|
|
30
29
|
</tbody>
|
|
31
30
|
</table>
|
|
32
31
|
</main>
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
Here is an example of an email message that contains a link to Download a Wallet Pass.
|
|
3
3
|
</p>
|
|
4
4
|
<p>
|
|
5
|
-
<a href="
|
|
6
|
-
<%=image_tag 'passkit/add_to_apple_wallet_en.png', style: 'width: 150px;' %>
|
|
7
|
-
Download
|
|
5
|
+
<a href="<%= @passkit_url_generator.ios %>">
|
|
6
|
+
<%= image_tag 'passkit/add_to_apple_wallet_en.png', style: 'width: 150px;' %>
|
|
7
|
+
Download an iOS Wallet Pass
|
|
8
8
|
</a>
|
|
9
9
|
</p>
|
|
10
10
|
<p>
|
|
11
11
|
<a href="<%=@passkit_url_generator.android %>">
|
|
12
|
-
<%=image_tag '
|
|
12
|
+
<%= image_tag 'passkit/add_to_walletpass_en.png', style: 'width: 150px;' %>
|
|
13
13
|
Download an Android Wallet Pass
|
|
14
14
|
</a>
|
|
15
15
|
</p>
|
|
@@ -12,7 +12,7 @@ In production, it will simply be your domain name, but in development you can us
|
|
|
12
12
|
|
|
13
13
|
This is the easy one.
|
|
14
14
|
Head to https://www.apple.com/certificateauthority/ and download the latest Apple Intermediate Certificate Worldwide Developer Relations.
|
|
15
|
-
You might want to choose the one with the longest expiration date.
|
|
15
|
+
You might want to choose the one with the longest expiration date. PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE is the path to the certificate.
|
|
16
16
|
|
|
17
17
|
### `PASSKIT_APPLE_TEAM_IDENTIFIER`
|
|
18
18
|
|
|
@@ -32,7 +32,10 @@ Head to your Apple Developers console and generate a new certificate.
|
|
|
32
32
|
|
|
33
33
|
The identifier is the `PASSKIT_PASS_TYPE_IDENTIFIER` variable.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
**Note**: When renewing your certificate, make sure that this identifier stays the same as before. Changing this identifier will
|
|
36
|
+
require existing passes to be regenerated, as they will otherwise no longer be updateable via the web service URL.
|
|
37
|
+
|
|
38
|
+
Now, create a certificate signing request: https://developer.apple.com/help/account/create-certificates/create-a-certificate-signing-request/
|
|
36
39
|
|
|
37
40
|
And create the certificate:
|
|
38
41
|
|
data/lib/passkit/base_pass.rb
CHANGED
|
@@ -5,15 +5,15 @@ module Passkit
|
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
def format_version
|
|
8
|
-
|
|
8
|
+
Passkit.configuration.format_version
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def apple_team_identifier
|
|
12
|
-
|
|
12
|
+
Passkit.configuration.apple_team_identifier
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def pass_type_identifier
|
|
16
|
-
|
|
16
|
+
Passkit.configuration.pass_type_identifier
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def language
|
|
@@ -43,34 +43,48 @@ module Passkit
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def web_service_url
|
|
46
|
-
|
|
47
|
-
"#{ENV["PASSKIT_WEB_SERVICE_HOST"]}/passkit/api"
|
|
46
|
+
"#{Passkit.configuration.web_service_host}/passkit/api"
|
|
48
47
|
end
|
|
49
48
|
|
|
49
|
+
# The foreground color, used for the values of fields shown on the front of the pass.
|
|
50
50
|
def foreground_color
|
|
51
51
|
# black
|
|
52
52
|
"rgb(0, 0, 0)"
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
# The strip color, used to print the text over the background image.
|
|
56
|
+
def strip_color
|
|
57
|
+
"rgb(0, 0, 0)"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The background color, used for the background of the front and back of the pass.
|
|
61
|
+
# If you provide a background image, any background color is ignored.
|
|
55
62
|
def background_color
|
|
56
63
|
# white
|
|
57
64
|
"rgb(255, 255, 255)"
|
|
58
65
|
end
|
|
59
66
|
|
|
67
|
+
# The label color, used for the labels of fields shown on the front of the pass.
|
|
60
68
|
def label_color
|
|
61
69
|
# black
|
|
62
70
|
"rgb(0, 0, 0)"
|
|
63
71
|
end
|
|
64
72
|
|
|
73
|
+
# The organization name is displayed on the lock screen when your pass is relevant and by apps such as Mail which
|
|
74
|
+
# act as a conduit for passes. The value for the organizationName key in the pass specifies the organization name.
|
|
75
|
+
# Choose a name that users recognize and associate with your organization or company.
|
|
65
76
|
def organization_name
|
|
66
77
|
"Passkit"
|
|
67
78
|
end
|
|
68
79
|
|
|
80
|
+
# The description lets VoiceOver make your pass accessible to blind and low-vision users. The value for the
|
|
81
|
+
# description key in the pass specifies the description.
|
|
82
|
+
# @see https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/PassKit_PG/Creating.html
|
|
69
83
|
def description
|
|
70
84
|
"A basic description for a pass"
|
|
71
85
|
end
|
|
72
86
|
|
|
73
|
-
#
|
|
87
|
+
# An array of up to 10 latitudes and longitudes. iOS uses these locations to determine when to display the pass on the lock screen
|
|
74
88
|
#
|
|
75
89
|
# @see https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/PassKit_PG/Creating.html
|
|
76
90
|
def locations
|
|
@@ -85,7 +99,8 @@ module Passkit
|
|
|
85
99
|
def add_other_files(path)
|
|
86
100
|
end
|
|
87
101
|
|
|
88
|
-
# Distance in meters from locations; if blank uses pass default
|
|
102
|
+
# Distance in meters from locations; if blank uses pass default.
|
|
103
|
+
# The system uses the smaller of either this distance or the default distance.
|
|
89
104
|
def max_distance
|
|
90
105
|
end
|
|
91
106
|
|
|
@@ -118,30 +133,19 @@ module Passkit
|
|
|
118
133
|
|
|
119
134
|
# Information specific to a boarding pass
|
|
120
135
|
# Returns a hash representing Pass.BoardingPass
|
|
136
|
+
# https://developer.apple.com/documentation/walletpasses/pass/boardingpass
|
|
137
|
+
# i.e {transitType: 'PKTransitTypeGeneric'}
|
|
121
138
|
def boarding_pass
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# Information specific to a coupon
|
|
125
|
-
# Returns a hash representing Pass.Coupon
|
|
126
|
-
def coupon
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Information specific to an event ticket
|
|
130
|
-
# Returns a hash representing Pass.EventTicket
|
|
131
|
-
def event_ticket
|
|
139
|
+
{}
|
|
132
140
|
end
|
|
133
141
|
|
|
134
142
|
# Date and time the pass expires, must include
|
|
135
143
|
# days, hours and minutes (seconds are optional)
|
|
136
|
-
# Returns a String representing the date and time in W3C format
|
|
144
|
+
# Returns a String representing the date and time in W3C format ('%Y-%m-%dT%H:%M:%S%:z')
|
|
145
|
+
# For example, 1980-05-07T10:30-05:00.
|
|
137
146
|
def expiration_date
|
|
138
147
|
end
|
|
139
148
|
|
|
140
|
-
# Information specific to a generic pass
|
|
141
|
-
# Returns a hash representing Pass.Generic
|
|
142
|
-
def generic
|
|
143
|
-
end
|
|
144
|
-
|
|
145
149
|
# A key to identify group multiple passes together
|
|
146
150
|
# (e.g. a number of boarding passes for the same trip)
|
|
147
151
|
# Returns a String
|
|
@@ -157,7 +161,7 @@ module Passkit
|
|
|
157
161
|
# Date and time when the pass becomes relevant and should be
|
|
158
162
|
# displayed, must include days, hours and minutes
|
|
159
163
|
# (seconds are optional)
|
|
160
|
-
# Returns a String representing the date and time in W3C format
|
|
164
|
+
# Returns a String representing the date and time in W3C format ('%Y-%m-%dT%H:%M:%S%:z')
|
|
161
165
|
def relevant_date
|
|
162
166
|
end
|
|
163
167
|
|
|
@@ -167,11 +171,6 @@ module Passkit
|
|
|
167
171
|
def semantics
|
|
168
172
|
end
|
|
169
173
|
|
|
170
|
-
# Information specific to a store card
|
|
171
|
-
# Returns a hash representing Pass.StoreCard
|
|
172
|
-
def store_card
|
|
173
|
-
end
|
|
174
|
-
|
|
175
174
|
# Display the strip image without a shine effect
|
|
176
175
|
# Returns a boolean
|
|
177
176
|
def suppress_strip_shine
|
|
@@ -58,17 +58,13 @@ module Passkit
|
|
|
58
58
|
"Loyalty Card"
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
def app_launch_url
|
|
62
|
-
"https://github.com/coorasse/passkit"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
61
|
def relevant_date
|
|
66
|
-
Time.current.strftime(
|
|
62
|
+
Time.current.strftime('%Y-%m-%dT%H:%M:%S%:z')
|
|
67
63
|
end
|
|
68
64
|
|
|
69
65
|
def expiration_date
|
|
70
66
|
# Expire the pass tomorrow
|
|
71
|
-
(Time.current +
|
|
67
|
+
(Time.current + 1.day).strftime('%Y-%m-%dT%H:%M:%S%:z')
|
|
72
68
|
end
|
|
73
69
|
|
|
74
70
|
def semantics
|
data/lib/passkit/factory.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module Passkit
|
|
2
2
|
class Factory
|
|
3
3
|
class << self
|
|
4
|
+
# generator is an optional ActiveRecord object, the application data for the pass
|
|
4
5
|
def create_pass(pass_class, generator = nil)
|
|
5
6
|
pass = Passkit::Pass.create!(klass: pass_class, generator: generator)
|
|
6
7
|
Passkit::Generator.new(pass).generate_and_sign
|
data/lib/passkit/generator.rb
CHANGED
|
@@ -23,6 +23,19 @@ module Passkit
|
|
|
23
23
|
compress_pass_file
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
def self.compress_passes_files(files)
|
|
27
|
+
zip_path = TMP_FOLDER.join("#{SecureRandom.uuid}.pkpasses")
|
|
28
|
+
zipped_file = File.open(zip_path, "w")
|
|
29
|
+
|
|
30
|
+
Zip::OutputStream.open(zipped_file.path) do |z|
|
|
31
|
+
files.each do |file|
|
|
32
|
+
z.put_next_entry(File.basename(file))
|
|
33
|
+
z.print File.read(file)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
zip_path
|
|
37
|
+
end
|
|
38
|
+
|
|
26
39
|
private
|
|
27
40
|
|
|
28
41
|
def check_necessary_files
|
|
@@ -59,6 +72,7 @@ module Passkit
|
|
|
59
72
|
passTypeIdentifier: @pass.pass_type_identifier,
|
|
60
73
|
serialNumber: @pass.serial_number,
|
|
61
74
|
sharingProhibited: @pass.sharing_prohibited,
|
|
75
|
+
stripColor: @pass.strip_color,
|
|
62
76
|
suppressStripShine: @pass.suppress_strip_shine,
|
|
63
77
|
voided: @pass.voided,
|
|
64
78
|
webServiceURL: @pass.web_service_url
|
|
@@ -75,20 +89,14 @@ module Passkit
|
|
|
75
89
|
else
|
|
76
90
|
pass[:barcodes] = @pass.barcodes
|
|
77
91
|
end
|
|
78
|
-
|
|
79
92
|
pass[:appLaunchURL] = @pass.app_launch_url if @pass.app_launch_url
|
|
80
93
|
pass[:associatedStoreIdentifiers] = @pass.associated_store_identifiers unless @pass.associated_store_identifiers.empty?
|
|
81
94
|
pass[:beacons] = @pass.beacons unless @pass.beacons.empty?
|
|
82
|
-
pass[:boardingPass] = @pass.boarding_pass if @pass.boarding_pass
|
|
83
|
-
pass[:coupon] = @pass.coupon if @pass.coupon
|
|
84
|
-
pass[:eventTicket] = @pass.event_ticket if @pass.event_ticket
|
|
85
95
|
pass[:expirationDate] = @pass.expiration_date if @pass.expiration_date
|
|
86
|
-
pass[:generic] = @pass.generic if @pass.generic
|
|
87
96
|
pass[:groupingIdentifier] = @pass.grouping_identifier if @pass.grouping_identifier
|
|
88
97
|
pass[:nfc] = @pass.nfc if @pass.nfc
|
|
89
98
|
pass[:relevantDate] = @pass.relevant_date if @pass.relevant_date
|
|
90
99
|
pass[:semantics] = @pass.semantics if @pass.semantics
|
|
91
|
-
pass[:store_card] = @pass.store_card if @pass.store_card
|
|
92
100
|
pass[:userInfo] = @pass.user_info if @pass.user_info
|
|
93
101
|
|
|
94
102
|
pass[@pass.pass_type] = {
|
|
@@ -99,11 +107,11 @@ module Passkit
|
|
|
99
107
|
backFields: @pass.back_fields
|
|
100
108
|
}
|
|
101
109
|
|
|
110
|
+
pass[:boardingPass].merge(@pass.boarding_pass) if @pass.pass_type == :boardingPass && @pass.boarding_pass
|
|
111
|
+
|
|
102
112
|
File.write(@temporary_path.join("pass.json"), pass.to_json)
|
|
103
113
|
end
|
|
104
114
|
|
|
105
|
-
# rubocop:enable Metrics/AbcSize
|
|
106
|
-
|
|
107
115
|
def generate_json_manifest
|
|
108
116
|
manifest = {}
|
|
109
117
|
Dir.glob(@temporary_path.join("**")).each do |file|
|
|
@@ -114,14 +122,18 @@ module Passkit
|
|
|
114
122
|
File.write(@manifest_url, manifest.to_json)
|
|
115
123
|
end
|
|
116
124
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
125
|
+
def certificate_path
|
|
126
|
+
Rails.root.join(Passkit.configuration.private_p12_certificate)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def intermediate_certificate_path
|
|
130
|
+
Rails.root.join(Passkit.configuration.apple_intermediate_certificate)
|
|
131
|
+
end
|
|
120
132
|
|
|
121
133
|
# :nocov:
|
|
122
134
|
def sign_manifest
|
|
123
|
-
p12_certificate = OpenSSL::PKCS12.new(File.read(
|
|
124
|
-
intermediate_certificate = OpenSSL::X509::Certificate.new(File.read(
|
|
135
|
+
p12_certificate = OpenSSL::PKCS12.new(File.read(certificate_path), Passkit.configuration.certificate_key)
|
|
136
|
+
intermediate_certificate = OpenSSL::X509::Certificate.new(File.read(intermediate_certificate_path))
|
|
125
137
|
|
|
126
138
|
flag = OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
|
|
127
139
|
signed = OpenSSL::PKCS7.sign(p12_certificate.certificate,
|
|
@@ -2,17 +2,18 @@ module Passkit
|
|
|
2
2
|
class PayloadGenerator
|
|
3
3
|
VALIDITY = 30.days
|
|
4
4
|
|
|
5
|
-
def self.encrypted(pass_class, generator = nil)
|
|
6
|
-
UrlEncrypt.encrypt(hash(pass_class, generator))
|
|
5
|
+
def self.encrypted(pass_class, generator = nil, collection_name = nil)
|
|
6
|
+
UrlEncrypt.encrypt(hash(pass_class, generator, collection_name))
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
def self.hash(pass_class, generator = nil)
|
|
9
|
+
def self.hash(pass_class, generator = nil, collection_name = nil)
|
|
10
10
|
valid_until = VALIDITY.from_now
|
|
11
11
|
|
|
12
12
|
{valid_until: valid_until,
|
|
13
13
|
generator_class: generator&.class&.name,
|
|
14
14
|
generator_id: generator&.id,
|
|
15
|
-
pass_class: pass_class.name
|
|
15
|
+
pass_class: pass_class.name,
|
|
16
|
+
collection_name: collection_name}
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
end
|
|
@@ -2,9 +2,9 @@ module Passkit
|
|
|
2
2
|
class UrlGenerator
|
|
3
3
|
include Passkit::Engine.routes.url_helpers
|
|
4
4
|
|
|
5
|
-
def initialize(pass_class, generator = nil)
|
|
6
|
-
@url = passes_api_url(host:
|
|
7
|
-
payload: PayloadGenerator.encrypted(pass_class, generator))
|
|
5
|
+
def initialize(pass_class, generator = nil, collection_name = nil)
|
|
6
|
+
@url = passes_api_url(host: Passkit.configuration.web_service_host,
|
|
7
|
+
payload: PayloadGenerator.encrypted(pass_class, generator, collection_name))
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def ios
|
data/lib/passkit/version.rb
CHANGED
data/lib/passkit.rb
CHANGED
|
@@ -18,6 +18,11 @@ module Passkit
|
|
|
18
18
|
def self.configure
|
|
19
19
|
self.configuration ||= Configuration.new
|
|
20
20
|
yield(configuration) if block_given?
|
|
21
|
+
configuration.verify!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.configured?
|
|
25
|
+
self.configuration&.configured?
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
class Configuration
|
|
@@ -27,11 +32,24 @@ module Passkit
|
|
|
27
32
|
:private_p12_certificate,
|
|
28
33
|
:apple_intermediate_certificate,
|
|
29
34
|
:apple_team_identifier,
|
|
30
|
-
:pass_type_identifier
|
|
35
|
+
:pass_type_identifier,
|
|
36
|
+
:dashboard_username,
|
|
37
|
+
:dashboard_password,
|
|
38
|
+
:format_version,
|
|
39
|
+
:skip_verification
|
|
40
|
+
|
|
41
|
+
REQUIRED_ATTRIBUTES = %i[
|
|
42
|
+
web_service_host
|
|
43
|
+
certificate_key
|
|
44
|
+
private_p12_certificate
|
|
45
|
+
apple_intermediate_certificate
|
|
46
|
+
apple_team_identifier
|
|
47
|
+
pass_type_identifier
|
|
48
|
+
]
|
|
31
49
|
|
|
32
50
|
DEFAULT_AUTHENTICATION = proc do
|
|
33
51
|
authenticate_or_request_with_http_basic("Passkit Dashboard. Login required") do |username, password|
|
|
34
|
-
username ==
|
|
52
|
+
username == Passkit.configuration.dashboard_username && password == Passkit.configuration.dashboard_password
|
|
35
53
|
end
|
|
36
54
|
end
|
|
37
55
|
def authenticate_dashboard_with(&block)
|
|
@@ -40,14 +58,34 @@ module Passkit
|
|
|
40
58
|
end
|
|
41
59
|
|
|
42
60
|
def initialize
|
|
43
|
-
|
|
44
|
-
@
|
|
45
|
-
|
|
46
|
-
@
|
|
47
|
-
@
|
|
48
|
-
@
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
# Required
|
|
62
|
+
@certificate_key = ENV["PASSKIT_CERTIFICATE_KEY"]
|
|
63
|
+
@private_p12_certificate = ENV["PASSKIT_PRIVATE_P12_CERTIFICATE"]
|
|
64
|
+
@apple_intermediate_certificate = ENV["PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE"]
|
|
65
|
+
@apple_team_identifier = ENV["PASSKIT_APPLE_TEAM_IDENTIFIER"]
|
|
66
|
+
@pass_type_identifier = ENV["PASSKIT_PASS_TYPE_IDENTIFIER"]
|
|
67
|
+
|
|
68
|
+
# Optional
|
|
69
|
+
@skip_verification = false
|
|
70
|
+
@web_service_host = ENV["PASSKIT_WEB_SERVICE_HOST"] || "https://localhost:3000"
|
|
71
|
+
@available_passes = { "Passkit::ExampleStoreCard" => -> {} }
|
|
72
|
+
@format_version = ENV["PASSKIT_FORMAT_VERSION"] || 1
|
|
73
|
+
@dashboard_username = ENV["PASSKIT_DASHBOARD_USERNAME"]
|
|
74
|
+
@dashboard_password = ENV["PASSKIT_DASHBOARD_PASSWORD"]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def configured?
|
|
78
|
+
REQUIRED_ATTRIBUTES.all? { |attr| send(attr).present? }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def verify!
|
|
82
|
+
return if skip_verification
|
|
83
|
+
|
|
84
|
+
REQUIRED_ATTRIBUTES.each do |attr|
|
|
85
|
+
raise Error, "Please set #{attr.upcase}" unless send(attr).present?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
raise Error, "PASSKIT_WEB_SERVICE_HOST must start with https://" unless web_service_host.start_with?("https://")
|
|
51
89
|
end
|
|
52
90
|
end
|
|
53
91
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: passkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alessandro Rodi
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -222,7 +221,6 @@ metadata:
|
|
|
222
221
|
source_code_uri: https://github.com/coorasse/passkit
|
|
223
222
|
changelog_uri: https://github.com/coorasse/passkit/blob/master/CHANGELOG.md
|
|
224
223
|
funding_uri: https://github.com/sponsors/coorasse
|
|
225
|
-
post_install_message:
|
|
226
224
|
rdoc_options: []
|
|
227
225
|
require_paths:
|
|
228
226
|
- lib
|
|
@@ -237,8 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
237
235
|
- !ruby/object:Gem::Version
|
|
238
236
|
version: '0'
|
|
239
237
|
requirements: []
|
|
240
|
-
rubygems_version: 3.
|
|
241
|
-
signing_key:
|
|
238
|
+
rubygems_version: 3.6.9
|
|
242
239
|
specification_version: 4
|
|
243
240
|
summary: Serve Wallet Passes for iOS and Android directly from your Rails app
|
|
244
241
|
test_files: []
|