passkit 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: abe44c75069e0fc397f9a1f7155dd811a0341de723223ca3fdfbe7543b9a8a6c
4
- data.tar.gz: c8a36754d91a1dd241064cdf0e5b3d3defbf5df54d13c3135bf56b5dc8564b19
3
+ metadata.gz: 37dfbf2ceba4423e2a1eba28d94aa8f5fbf676c011429efb8759af2f7fd8c477
4
+ data.tar.gz: 5909695a00d94a28da5f4ae1ee318a1d7d7320c8599646457a408b7c2a780af0
5
5
  SHA512:
6
- metadata.gz: 1137d58cf9f2b560a0363ab2c14868eca913960bd1eea70e3737dcdc5cff3411de06a72988ffce0cb910d14ab30de68502da3542bbe48d4f17e262b1c3c48f8e
7
- data.tar.gz: 4d6e5d66d65705b8b01c84820559b4244be5ad1f9ea71ec58b9c86fd133933bea766783c192250571cc80ad28c4b90c0e57e22affc8bbdfeff8c9ded6cbc3827
6
+ metadata.gz: 637340fef65ed2655a1e8bedaf9d86a5ece533782bbd6c3c92a26d2e49d5153de1be57e27977994b13b63e1f47a2b39b30e481c57539771b8c06b3f2db4b6691
7
+ data.tar.gz: 5350a3793c359fbbf00d197541e7b51577c93c46c68068f8e0f7cb852ff46f6c9d1f60e4a3a8c00fc66a52c82c6e95b94b768ff602b1c188bb887f3128214821
data/.example.env ADDED
@@ -0,0 +1,6 @@
1
+ PASSKIT_WEB_SERVICE_HOST=https://localhost:3000
2
+ PASSKIT_CERTIFICATE_KEY=path/to/certificate.key
3
+ PASSKIT_PRIVATE_P12_CERTIFICATE=path/to/certificate.p12
4
+ PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE=path/to/AppleWWDRCA.cer
5
+ PASSKIT_APPLE_TEAM_IDENTIFIER=XXXXXXXXXX
6
+ PASSKIT_PASS_TYPE_IDENTIFIER=pass.com.example.pass
data/README.md CHANGED
@@ -4,24 +4,28 @@ Your out-of-the-box solution to start serving Wallet Passes in your Ruby On Rail
4
4
 
5
5
  Do you have a QRCode or a Barcode anywhere in your app that you want to distribute as Wallet Pass, compatible for iOS and Android? Look no further!
6
6
 
7
- This gem provides everything that is necessary, and gives you all the steps to follow for what we cannot provide.
7
+ This gem provides everything necessary to distribute Wallet Passes in pkpass format, and gives you all the steps to follow for what we cannot provide.
8
8
 
9
- We provide:
9
+ **We provide:**
10
10
 
11
- * A (not yet) fancy dashboard to manage your passes, registered devices and logs
11
+ * A (not yet) fancy dashboard to manage your passes, registered devices and logs.
12
12
  * All API endpoints to serve your passes: create, register, update, unregister, etc...
13
- * All ActiveRecord models necessary
14
- * A BasePass model that you can extend to create your own passes
15
- * Some helpers to generate the necessary URLs, so that you can include them in the emails
16
- * Examples for everything
13
+ * All necessary ActiveRecord models.
14
+ * A BasePass model that you can extend to create your own passes.
15
+ * Some helpers to generate the necessary URLs, so that you can include them in the emails.
16
+ * Examples for everything.
17
17
 
18
- We don't provide yet (WIP):
18
+ **We don't provide (yet):**
19
19
 
20
- * Full tests coverage. We are working on it!
21
- * A fancy dashboard. Pull requests are welcome!
22
- * Push notifications. Pull requests are welcome!
23
- * Google Wallet passes. We use https://walletpasses.io/ on Android to read .pkpass format.
20
+ * Full tests coverage: we are working on it!
21
+ * A fancy dashboard: our dashboard is really really simple right now. Pull requests are welcome!
22
+ * Push notifications: this is the most wanted feature I believe. Pull requests are welcome!
23
+ * Google Wallet integration: we use https://walletpasses.io/ on Android to read .pkpass format.
24
24
 
25
+ ## Apple documentation
26
+
27
+ * [Apple Wallet Passes](https://developer.apple.com/documentation/walletpasses)
28
+ * [Send Push Notifications](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns)
25
29
 
26
30
  ## Installation
27
31
 
@@ -45,18 +49,18 @@ Run the initializer:
45
49
 
46
50
  that will generate the migrations and the initializer file.
47
51
 
48
- Mount the engine in your routes.rb:
52
+ Mount the engine in your `config/routes.rb`:
49
53
 
50
54
  ```ruby
51
55
  mount Passkit::Engine => '/passkit', as: 'passkit'
52
56
  ```
53
57
 
54
- and run `rails db:migrate`.
58
+ and run `bin/rails db:migrate`.
55
59
 
56
- ## Usage
60
+ ### Setup environment variables
57
61
 
58
- If you followed the installation steps, you already saw that Passkit provides
59
- you the tables and ActiveRecord models, and also an engine the necessary APIs already implemented.
62
+ If you followed the installation steps, you already saw that Passkit provides
63
+ you the tables and ActiveRecord models, and also an engine with the necessary APIs already implemented.
60
64
 
61
65
  Now is your turn. Before proceeding, you need to set these ENV variables:
62
66
  * `PASSKIT_WEB_SERVICE_HOST`
@@ -67,14 +71,19 @@ Now is your turn. Before proceeding, you need to set these ENV variables:
67
71
  * `PASSKIT_PASS_TYPE_IDENTIFIER`
68
72
 
69
73
  We have a [specific guide on how to get all these](docs/passkit_environment_variables.md), please follow it.
70
- You cannot do any of the following steps without these variables set, and we cannot do the work for you there.
74
+ You cannot start using this library without these variables set, and we cannot do the work for you.
71
75
 
72
- ### Check what is available
76
+ ## Usage
73
77
 
74
78
  If you followed the installation steps and you have the ENV variables set, we can start looking at what is provided for you.
75
79
 
76
- Head to `http://localhost:3000/passkit/previews` and you will see a first ExampleStoreCard available for download.
80
+ ### Dashboard
81
+
82
+ Head to `http://localhost:3000/passkit/previews` and you will see a first `ExampleStoreCard` available for download.
77
83
  You can click on the button and you will obtain a `.pkpass` file that you can simply open or install on your phone.
84
+ The dashboard has also a view for logs, and a view for emitted passes.
85
+
86
+ ### Mailer Helpers
78
87
 
79
88
  If you use mailer previews, you can create the following file in `spec/mailers/previews/passkit/example_mailer_preview.rb`:
80
89
 
@@ -89,21 +98,28 @@ end
89
98
  ```
90
99
 
91
100
  and head to `http://localhost:3000/rails/mailers/` to see an example of email with links to download the Wallet Pass.
101
+ Please check the source code of [ExampleMailer](app/mailers/passkit/example_mailer.rb) to see how to distribute your own Wallet Passes.
102
+
103
+ ### Example Passes
104
+
105
+ Please check the source code of the [ExampleStoreCard](lib/passkit/example_store_card.rb) to see how to create your own Wallet Passes.
92
106
 
93
- Please check the source code of the [ExampleStoreCard](lib/passkit/example_store_card.rb) and [ExampleMailer](app/mailers/passkit/example_mailer.rb) to see how to create and distribute your own Wallet Passes.
94
- Looking at the examples, is the easiest way to get started.
107
+ Again, looking at these examples, is the easiest way to get started.
95
108
 
96
109
  ### Create your own Wallet Pass
97
110
 
98
- You can create your own Wallet Passes by creating a new class that inherits from `Passkit::BasePass` and defining the methods that you want to override.
111
+ You can create your own Wallet Passes by creating a new class that inherits from `Passkit::BasePass` and
112
+ defining the methods that you want to override.
113
+
99
114
  You can define colors, fields and texts. You can also define the logo and the background image.
100
115
 
101
- You can place the images in a 'private/passkit/<your_downcased_passname>' folder.
116
+ You should place the images in a 'private/passkit/<your_downcased_passname>' folder.
102
117
  There is a [dummy app in the gem](test/dummy) that you can use to check how to create your own Wallet Passes.
103
118
 
104
119
  ### Serve your Wallet Pass
105
120
 
106
- Use the [Passkit::UrlGenerator](lib/passkit/url_generator.rb) to generate the URL to serve your Wallet Pass. You can initialize it with:
121
+ Use the [Passkit::UrlGenerator](lib/passkit/url_generator.rb) to generate the URL to serve your Wallet Pass.
122
+ You can initialize it with:
107
123
 
108
124
  ```ruby
109
125
  Passkit::UrlGenerator.new(pass_class: Passkit::MyPass, generator: User.find(1))
@@ -119,21 +135,6 @@ Again, check the example mailer included in the gem to see how to use it.
119
135
  * In case of error "The passTypeIdentifier or teamIdentifier provided may not match your certificate,
120
136
  or the certificate trust chain could not be verified." the certificate (p12) might be expired.
121
137
 
122
-
123
- * Setup ngrok and start it
124
- * Configure your `HOST: 'https://xxx.ngrok.io'` on `config/application.yml` (make sure is https!)
125
- * Set `config.hosts = nil` in `config/environments/development.rb`
126
- * Head to the `/rails/mailers/` and preview an email containing a pass
127
- * Download the pkpass and open it
128
-
129
-
130
- Here are the rough steps to generate a new one:
131
- * Access https://developer.apple.com/account/resources/identifiers/passTypeId/edit/F578W7LDT9/pass.ch.renuo.loyalty.
132
- * Click "Create Certificate".
133
- * Follow the guide to generate a new Certificate Signing Request and use operations@renuo.ch as email address and Renuo AG as organization name.
134
- * Convert the certificate to p12 using Keychain Access and re-use the same password.
135
- * Re-upload the new certificate to the servers.
136
-
137
138
  ## Development
138
139
 
139
140
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -154,4 +155,6 @@ Everyone interacting in the Passkit project's codebases, issue trackers, chat ro
154
155
 
155
156
  ## Credits
156
157
 
157
- <a href="https://www.flaticon.com/free-icons/credit-card" title="credit card icons">Credit card icons created by Iconfromus - Flaticon</a>
158
+ * <a href="https://www.flaticon.com/free-icons/credit-card" title="credit card icons">Credit card icons created by Iconfromus - Flaticon</a>
159
+
160
+ * https://www.sitepoint.com/whats-in-your-wallet-handling-ios-passbook-with-ruby/
@@ -12,7 +12,7 @@ module Passkit
12
12
  # @return If the request is not authorized, returns HTTP status 401.
13
13
  # @return Otherwise, returns the appropriate standard HTTP status.
14
14
  def show
15
- authentication_token = request.headers["Authorization"]&.split(" ") & [1]
15
+ authentication_token = request.headers["Authorization"]&.split(" ")&.last
16
16
  unless authentication_token.present?
17
17
  render json: {}, status: :unauthorized
18
18
  return
@@ -26,9 +26,9 @@ module Passkit
26
26
 
27
27
  pass_output_path = Passkit::Generator.new(pass).generate_and_sign
28
28
 
29
- response.headers["last-modified"] = pass.generator.updated_at.strftime("%Y-%m-%d %H:%M:%S")
29
+ response.headers["last-modified"] = pass.last_update.strftime("%Y-%m-%d %H:%M:%S")
30
30
  if request.headers["If-Modified-Since"].nil? ||
31
- (pass.generator.updated_at.to_i > Time.zone.parse(request.headers["If-Modified-Since"]).to_i)
31
+ (pass.last_update.to_i > Time.zone.parse(request.headers["If-Modified-Since"]).to_i)
32
32
  send_file(pass_output_path)
33
33
  else
34
34
  head :not_modified
@@ -45,9 +45,12 @@ module Passkit
45
45
  end
46
46
 
47
47
  def fetch_pass(payload)
48
- generator_class = payload[:generator_class].constantize
49
- generator = generator_class.find(payload[:generator_id])
50
- Passkit::Factory.create_pass(generator, payload[:pass_class])
48
+ generator = nil
49
+ if payload[:generator_class].present? && payload[:generator_id].present?
50
+ generator_class = payload[:generator_class].constantize
51
+ generator = generator_class.find(payload[:generator_id])
52
+ end
53
+ Passkit::Factory.create_pass(payload[:pass_class], generator)
51
54
  end
52
55
  end
53
56
  end
@@ -61,7 +61,7 @@ module Passkit
61
61
  private
62
62
 
63
63
  def load_pass
64
- authentication_token = request.headers["Authorization"]&.split(" ") & [1]
64
+ authentication_token = request.headers["Authorization"]&.split(" ")&.last
65
65
  unless authentication_token.present?
66
66
  render json: {}, status: :unauthorized
67
67
  return
@@ -41,5 +41,9 @@ module Passkit
41
41
  def instance
42
42
  @instance ||= klass.constantize.new(generator)
43
43
  end
44
+
45
+ def last_update
46
+ instance.last_update || updated_at
47
+ end
44
48
  end
45
49
  end
Binary file
@@ -0,0 +1,48 @@
1
+ # Setup Passkit Environment variables
2
+
3
+ ## `PASSKIT_WEB_SERVICE_HOST`
4
+
5
+ This is the host where your Rails app is running. It is used to generate the URLs for the passes.
6
+ When the device wants to update the Pass, it will invoke services on this host.
7
+ In production, it will simply be your domain name, but in development you can use [ngrok](https://ngrok.com/) to expose your local server to the internet.
8
+
9
+ **Remember that it must always start with `https://`.**
10
+
11
+ ## `PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE`
12
+
13
+ This is the easy one.
14
+ Head to https://www.apple.com/certificateauthority/ and download the latest Apple Intermediate Certificate Worldwide Developer Relations.
15
+
16
+
17
+ ## `PASSKIT_APPLE_TEAM_IDENTIFIER`
18
+
19
+ You find this in your Apple Developer dashboard, under Membership.
20
+
21
+ ![Membership](membership.png)
22
+
23
+ ## `PASSKIT_PASS_TYPE_IDENTIFIER`, `PASSKIT_PRIVATE_P12_CERTIFICATE` and `PASSKIT_CERTIFICATE_KEY`
24
+
25
+ Head to your Apple Developers console and generate a new certificate.
26
+
27
+ ![Step 1](step1.png)
28
+
29
+ ![Step 2](step2.png)
30
+
31
+ ![Step 3](step3.png)
32
+
33
+ The identifier is the `PASSKIT_PASS_TYPE_IDENTIFIER` variable.
34
+
35
+ Now, create a certificate signing request: https://help.apple.com/developer-account/#/devbfa00fef7
36
+
37
+ And create the certificate:
38
+
39
+ ![Step 4](step4.png)
40
+
41
+ At the end, you'll have a `pass.cer` file.
42
+
43
+ Open it in the Keychain Access tool and export it:
44
+
45
+ ![Step 5](step5.png)
46
+
47
+ Set a password and get your p12 file. The path to this file is the `PASSKIT_PRIVATE_P12_CERTIFICATE` variable.
48
+ Save the password. This is the `PASSKIT_CERTIFICATE_KEY` variable.
data/docs/step1.png ADDED
Binary file
data/docs/step2.png ADDED
Binary file
data/docs/step3.png ADDED
Binary file
data/docs/step4.png ADDED
Binary file
data/docs/step5.png ADDED
Binary file
@@ -20,6 +20,10 @@ module Passkit
20
20
  nil
21
21
  end
22
22
 
23
+ def last_update
24
+ @generator&.updated_at
25
+ end
26
+
23
27
  def pass_path
24
28
  rails_folder = Rails.root.join("private/passkit/#{folder_name}")
25
29
  # if folder exists, otherwise is in the gem itself under lib/passkit/base_pass
@@ -1,8 +1,8 @@
1
1
  module Passkit
2
2
  class Factory
3
3
  class << self
4
- def create_pass(generator, pass_class)
5
- pass = Pass.create!(generator: generator, klass: pass_class)
4
+ def create_pass(pass_class, generator = nil)
5
+ pass = Pass.create!(klass: pass_class, generator: generator)
6
6
  Passkit::Generator.new(pass).generate_and_sign
7
7
  end
8
8
  end
@@ -0,0 +1,18 @@
1
+ module Passkit
2
+ class PayloadGenerator
3
+ VALIDITY = 30.days
4
+
5
+ def self.encrypted(pass_class, generator = nil)
6
+ UrlEncrypt.encrypt(hash(pass_class, generator))
7
+ end
8
+
9
+ def self.hash(pass_class, generator = nil)
10
+ valid_until = VALIDITY.from_now
11
+
12
+ {valid_until: valid_until,
13
+ generator_class: generator&.class&.name,
14
+ generator_id: generator&.id,
15
+ pass_class: pass_class.name}
16
+ end
17
+ end
18
+ end
@@ -2,16 +2,9 @@ module Passkit
2
2
  class UrlGenerator
3
3
  include Passkit::Engine.routes.url_helpers
4
4
 
5
- VALIDITY = 30.days
6
-
7
5
  def initialize(pass_class, generator = nil)
8
- valid_until = VALIDITY.from_now
9
-
10
- payload = {valid_until: valid_until,
11
- generator_class: generator&.class&.name,
12
- generator_id: generator&.id,
13
- pass_class: pass_class.name}
14
- @url = passes_api_url(host: ENV["PASSKIT_WEB_SERVICE_HOST"], payload: UrlEncrypt.encrypt(payload))
6
+ @url = passes_api_url(host: ENV["PASSKIT_WEB_SERVICE_HOST"],
7
+ payload: PayloadGenerator.encrypted(pass_class, generator))
15
8
  end
16
9
 
17
10
  def ios
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Passkit
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/passkit.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rails"
3
4
  require "passkit/engine"
4
5
 
5
6
  require "zeitwerk"
@@ -31,7 +32,7 @@ module Passkit
31
32
  def initialize
32
33
  @available_passes = {"Passkit::ExampleStoreCard" => -> {}}
33
34
  @web_service_host = ENV["PASSKIT_WEB_SERVICE_HOST"] || (raise "Please set PASSKIT_WEB_SERVICE_HOST")
34
- raise("PASSKIT_WEB_SERVICE_HOST must start with https://") unless @web_service_host.starts_with?("https://")
35
+ raise("PASSKIT_WEB_SERVICE_HOST must start with https://") unless @web_service_host.start_with?("https://")
35
36
  @certificate_key = ENV["PASSKIT_CERTIFICATE_KEY"] || (raise "Please set PASSKIT_CERTIFICATE_KEY")
36
37
  @private_p12_certificate = ENV["PASSKIT_PRIVATE_P12_CERTIFICATE"] || (raise "Please set PASSKIT_PRIVATE_P12_CERTIFICATE")
37
38
  @apple_intermediate_certificate = ENV["PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE"] || (raise "Please set PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE")
data/passkit-0.2.0.gem ADDED
Binary file
@@ -1,5 +1,5 @@
1
1
  module Passkit
2
2
  class Factory
3
- def self.create_pass: (Passkit::GeneratorObject generator, String pass_class) -> String
3
+ def self.create_pass: (String pass_class, Passkit::GeneratorObject ?generator) -> String
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Rodi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-30 00:00:00.000000000 Z
11
+ date: 2022-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: dotenv
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: standard
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -88,6 +102,7 @@ executables: []
88
102
  extensions: []
89
103
  extra_rdoc_files: []
90
104
  files:
105
+ - ".example.env"
91
106
  - ".standard.yml"
92
107
  - CHANGELOG.md
93
108
  - CODE_OF_CONDUCT.md
@@ -123,6 +138,13 @@ files:
123
138
  - bin/console
124
139
  - bin/setup
125
140
  - config/routes.rb
141
+ - docs/membership.png
142
+ - docs/passkit_environment_variables.md
143
+ - docs/step1.png
144
+ - docs/step2.png
145
+ - docs/step3.png
146
+ - docs/step4.png
147
+ - docs/step5.png
126
148
  - docs/wallet.png
127
149
  - lib/generators/passkit/install_generator.rb
128
150
  - lib/generators/templates/create_passkit_tables.rb.tt
@@ -137,9 +159,11 @@ files:
137
159
  - lib/passkit/example_store_card/logo.png
138
160
  - lib/passkit/factory.rb
139
161
  - lib/passkit/generator.rb
162
+ - lib/passkit/payload_generator.rb
140
163
  - lib/passkit/url_encrypt.rb
141
164
  - lib/passkit/url_generator.rb
142
165
  - lib/passkit/version.rb
166
+ - passkit-0.2.0.gem
143
167
  - sig/lib/passkit/encryptable_payload.rbs
144
168
  - sig/lib/passkit/factory.rbs
145
169
  - sig/lib/passkit/generator.rbs