passkit 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.example.env +6 -0
  3. data/README.md +124 -5
  4. data/app/assets/images/passkit/add_to_apple_wallet_de.png +0 -0
  5. data/app/assets/images/passkit/add_to_apple_wallet_en.png +0 -0
  6. data/app/assets/images/passkit/add_to_apple_wallet_fr.png +0 -0
  7. data/app/assets/images/passkit/add_to_apple_wallet_it.png +0 -0
  8. data/app/assets/images/passkit/add_to_walletpass_de.png +0 -0
  9. data/app/assets/images/passkit/add_to_walletpass_en.png +0 -0
  10. data/app/assets/images/passkit/add_to_walletpass_fr.png +0 -0
  11. data/app/assets/images/passkit/add_to_walletpass_it.png +0 -0
  12. data/app/controllers/passkit/api/v1/logs_controller.rb +14 -0
  13. data/app/controllers/passkit/api/v1/passes_controller.rb +58 -0
  14. data/app/controllers/passkit/api/v1/registrations_controller.rb +115 -0
  15. data/app/controllers/passkit/logs_controller.rb +9 -0
  16. data/app/controllers/passkit/passes_controller.rb +4 -19
  17. data/app/controllers/passkit/previews_controller.rb +13 -0
  18. data/app/mailers/passkit/example_mailer.rb +8 -0
  19. data/{lib → app/models}/passkit/device.rb +2 -2
  20. data/{lib → app/models}/passkit/log.rb +0 -0
  21. data/app/models/passkit/pass.rb +49 -0
  22. data/app/models/passkit/registration.rb +6 -0
  23. data/app/views/layouts/passkit/application.html.erb +16 -0
  24. data/app/views/passkit/example_mailer/example_email.html.erb +15 -0
  25. data/app/views/passkit/logs/index.html.erb +22 -0
  26. data/app/views/passkit/passes/index.html.erb +32 -0
  27. data/app/views/passkit/previews/index.html.erb +21 -0
  28. data/app/views/shared/passkit/_navigation.html.erb +5 -0
  29. data/config/routes.rb +17 -7
  30. data/docs/membership.png +0 -0
  31. data/docs/passkit_environment_variables.md +48 -0
  32. data/docs/step1.png +0 -0
  33. data/docs/step2.png +0 -0
  34. data/docs/step3.png +0 -0
  35. data/docs/step4.png +0 -0
  36. data/docs/step5.png +0 -0
  37. data/docs/wallet.png +0 -0
  38. data/lib/generators/passkit/install_generator.rb +26 -0
  39. data/lib/generators/templates/create_passkit_tables.rb.tt +32 -0
  40. data/lib/generators/templates/passkit.rb +3 -0
  41. data/lib/passkit/base_pass.rb +124 -0
  42. data/lib/passkit/example_store_card/icon.png +0 -0
  43. data/lib/passkit/example_store_card/icon@2x.png +0 -0
  44. data/lib/passkit/example_store_card/icon@3x.png +0 -0
  45. data/lib/passkit/example_store_card/logo.png +0 -0
  46. data/lib/passkit/example_store_card.rb +110 -0
  47. data/lib/passkit/factory.rb +2 -2
  48. data/lib/passkit/generator.rb +30 -22
  49. data/lib/passkit/payload_generator.rb +18 -0
  50. data/lib/passkit/url_encrypt.rb +3 -3
  51. data/lib/passkit/url_generator.rb +20 -0
  52. data/lib/passkit/version.rb +1 -1
  53. data/lib/passkit.rb +37 -3
  54. data/passkit-0.2.0.gem +0 -0
  55. data/sig/lib/passkit/encryptable_payload.rbs +5 -0
  56. data/sig/lib/passkit/factory.rbs +5 -0
  57. data/sig/lib/passkit/generator.rbs +8 -0
  58. data/sig/lib/passkit/generator_object.rbs +5 -0
  59. data/sig/lib/passkit/pass.rbs +5 -0
  60. data/sig/lib/passkit/url_encrypt.rbs +6 -0
  61. data/sig/lib/passkit/url_generator.rbs +9 -0
  62. metadata +140 -8
  63. data/lib/passkit/pass.rb +0 -8
  64. data/sig/passkit.rbs +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14879f088666efc426ff4d2861589f526991129dd83f91a70d7bf8867f64071c
4
- data.tar.gz: d52fca69a103bb5fd958502449e61ad194ba41928b8d1337c7f715cd799add79
3
+ metadata.gz: 37dfbf2ceba4423e2a1eba28d94aa8f5fbf676c011429efb8759af2f7fd8c477
4
+ data.tar.gz: 5909695a00d94a28da5f4ae1ee318a1d7d7320c8599646457a408b7c2a780af0
5
5
  SHA512:
6
- metadata.gz: 37a9f00cc34e6f272ebb811a199fb9457eb750297b616bfd487cb091b48dad0fae0d5782674c50a70033eadd803767bdccc11b53f6e6b4149b5ecbf5c619bf54
7
- data.tar.gz: ac193511be72f80a214a3e4bf4dd149430fc2682b4381368e6352e19635010298c623fe1b6c51b9b6f1323a062f023f1c2c45e53123a4fc801e2dfe9785be80d
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
@@ -1,6 +1,31 @@
1
- # Passkit
1
+ # <img src="./docs/wallet.png" alt="Goboony" height="50"/> Passkit
2
2
 
3
- Generate Wallet Passes for iOS.
3
+ Your out-of-the-box solution to start serving Wallet Passes in your Ruby On Rails application.
4
+
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
+
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
+
9
+ **We provide:**
10
+
11
+ * A (not yet) fancy dashboard to manage your passes, registered devices and logs.
12
+ * All API endpoints to serve your passes: create, register, update, unregister, etc...
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
+
18
+ **We don't provide (yet):**
19
+
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
+
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)
4
29
 
5
30
  ## Installation
6
31
 
@@ -18,9 +43,97 @@ Or install it yourself as:
18
43
 
19
44
  $ gem install passkit
20
45
 
46
+ Run the initializer:
47
+
48
+ $ rails g passkit:install
49
+
50
+ that will generate the migrations and the initializer file.
51
+
52
+ Mount the engine in your `config/routes.rb`:
53
+
54
+ ```ruby
55
+ mount Passkit::Engine => '/passkit', as: 'passkit'
56
+ ```
57
+
58
+ and run `bin/rails db:migrate`.
59
+
60
+ ### Setup environment variables
61
+
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.
64
+
65
+ Now is your turn. Before proceeding, you need to set these ENV variables:
66
+ * `PASSKIT_WEB_SERVICE_HOST`
67
+ * `PASSKIT_CERTIFICATE_KEY`
68
+ * `PASSKIT_PRIVATE_P12_CERTIFICATE`
69
+ * `PASSKIT_APPLE_INTERMEDIATE_CERTIFICATE`
70
+ * `PASSKIT_APPLE_TEAM_IDENTIFIER`
71
+ * `PASSKIT_PASS_TYPE_IDENTIFIER`
72
+
73
+ We have a [specific guide on how to get all these](docs/passkit_environment_variables.md), please follow it.
74
+ You cannot start using this library without these variables set, and we cannot do the work for you.
75
+
21
76
  ## Usage
22
77
 
23
- TODO: Write usage instructions here
78
+ If you followed the installation steps and you have the ENV variables set, we can start looking at what is provided for you.
79
+
80
+ ### Dashboard
81
+
82
+ Head to `http://localhost:3000/passkit/previews` and you will see a first `ExampleStoreCard` available for download.
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
87
+
88
+ If you use mailer previews, you can create the following file in `spec/mailers/previews/passkit/example_mailer_preview.rb`:
89
+
90
+ ```ruby
91
+ module Passkit
92
+ class ExampleMailerPreview < ActionMailer::Preview
93
+ def example_email
94
+ Passkit::ExampleMailer.example_email
95
+ end
96
+ end
97
+ end
98
+ ```
99
+
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.
106
+
107
+ Again, looking at these examples, is the easiest way to get started.
108
+
109
+ ### Create your own Wallet Pass
110
+
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
+
114
+ You can define colors, fields and texts. You can also define the logo and the background image.
115
+
116
+ You should place the images in a 'private/passkit/<your_downcased_passname>' folder.
117
+ There is a [dummy app in the gem](test/dummy) that you can use to check how to create your own Wallet Passes.
118
+
119
+ ### Serve your Wallet Pass
120
+
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:
123
+
124
+ ```ruby
125
+ Passkit::UrlGenerator.new(pass_class: Passkit::MyPass, generator: User.find(1))
126
+ ```
127
+
128
+ and then use `.android` or `.ios` to get the URL to serve the Wallet Pass.
129
+ Again, check the example mailer included in the gem to see how to use it.
130
+
131
+ ## Debug issues
132
+
133
+ * On Mac, open the `Console.app` to view the errors.
134
+ * Check the logs on http://localhost:3000/passkit/logs
135
+ * In case of error "The passTypeIdentifier or teamIdentifier provided may not match your certificate,
136
+ or the certificate trust chain could not be verified." the certificate (p12) might be expired.
24
137
 
25
138
  ## Development
26
139
 
@@ -30,7 +143,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
30
143
 
31
144
  ## Contributing
32
145
 
33
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/passkit. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/passkit/blob/master/CODE_OF_CONDUCT.md).
146
+ Bug reports and pull requests are welcome on GitHub at https://github.com/coorasse/passkit. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/coorasse/passkit/blob/master/CODE_OF_CONDUCT.md).
34
147
 
35
148
  ## License
36
149
 
@@ -38,4 +151,10 @@ The gem is available as open source under the terms of the [MIT License](https:/
38
151
 
39
152
  ## Code of Conduct
40
153
 
41
- Everyone interacting in the Passkit project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/passkit/blob/master/CODE_OF_CONDUCT.md).
154
+ Everyone interacting in the Passkit project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/coorasse/passkit/blob/master/CODE_OF_CONDUCT.md).
155
+
156
+ ## Credits
157
+
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/
@@ -0,0 +1,14 @@
1
+ module Passkit
2
+ module Api
3
+ module V1
4
+ class LogsController < ActionController::API
5
+ def create
6
+ params[:logs].each do |message|
7
+ Log.create!(content: message)
8
+ end
9
+ render json: {}, status: :ok
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,58 @@
1
+ module Passkit
2
+ module Api
3
+ module V1
4
+ class PassesController < ActionController::API
5
+ before_action :decrypt_payload, only: :create
6
+
7
+ def create
8
+ send_file(fetch_pass(@payload))
9
+ end
10
+
11
+ # @return If request is authorized, returns HTTP status 200 with a payload of the pass data.
12
+ # @return If the request is not authorized, returns HTTP status 401.
13
+ # @return Otherwise, returns the appropriate standard HTTP status.
14
+ def show
15
+ authentication_token = request.headers["Authorization"]&.split(" ")&.last
16
+ unless authentication_token.present?
17
+ render json: {}, status: :unauthorized
18
+ return
19
+ end
20
+
21
+ pass = Pass.find_by(serial_number: params[:serial_number], authentication_token: authentication_token)
22
+ unless pass
23
+ render json: {}, status: :unauthorized
24
+ return
25
+ end
26
+
27
+ pass_output_path = Passkit::Generator.new(pass).generate_and_sign
28
+
29
+ response.headers["last-modified"] = pass.last_update.strftime("%Y-%m-%d %H:%M:%S")
30
+ if request.headers["If-Modified-Since"].nil? ||
31
+ (pass.last_update.to_i > Time.zone.parse(request.headers["If-Modified-Since"]).to_i)
32
+ send_file(pass_output_path)
33
+ else
34
+ head :not_modified
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def decrypt_payload
41
+ @payload = Passkit::UrlEncrypt.decrypt(params[:payload])
42
+ if DateTime.parse(@payload[:valid_until]).past?
43
+ head :not_found
44
+ end
45
+ end
46
+
47
+ def fetch_pass(payload)
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)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,115 @@
1
+ module Passkit
2
+ module Api
3
+ module V1
4
+ # TODO: check with authentication_token
5
+ # This Class Implements the Apple PassKit API
6
+ # @see Apple: https://developer.apple.com/library/archive/documentation/PassKit/Reference/PassKit_WebService/WebService.html
7
+ # @see Android: https://walletpasses.io/developer/
8
+ class RegistrationsController < ActionController::API
9
+ before_action :load_pass, only: %i[create destroy]
10
+ before_action :load_device, only: %i[show]
11
+
12
+ # @return If the serial number is already registered for this device, returns HTTP status 200.
13
+ # @return If registration succeeds, returns HTTP status 201.
14
+ # @return If the request is not authorized, returns HTTP status 401.
15
+ # @return Otherwise, returns the appropriate standard HTTP status.
16
+ def create
17
+ if @pass.devices.find_by(identifier: params[:device_id])
18
+ render json: {}, status: :ok
19
+ return
20
+ end
21
+
22
+ register_device
23
+ render json: {}, status: :created
24
+ end
25
+
26
+ # @return If there are matching passes, returns HTTP status 200
27
+ # with a JSON dictionary with the following keys and values:
28
+ # lastUpdated (string): The current modification tag.
29
+ # serialNumbers (array of strings): The serial numbers of the matching passes.
30
+ # @return If there are no matching passes, returns HTTP status 204.
31
+ # @return Otherwise, returns the appropriate standard HTTP status
32
+ def show
33
+ if @device.nil?
34
+ render json: {}, status: :not_found
35
+ return
36
+ end
37
+
38
+ passes = fetch_registered_passes
39
+ if passes.none?
40
+ render json: {}, status: :no_content
41
+ return
42
+ end
43
+
44
+ render json: updatable_passes(passes).to_json
45
+ end
46
+
47
+ # @return If disassociation succeeds, returns HTTP status 200.
48
+ # @return If the request is not authorized, returns HTTP status 401.
49
+ # @return Otherwise, returns the appropriate standard HTTP status.
50
+ def destroy
51
+ devices = @pass.devices.where(device_id: params[:device_id],
52
+ pass_type_id: params[:pass_type_id])
53
+ if devices.any?
54
+ devices.delete_all
55
+ render json: {}, status: :ok
56
+ else
57
+ render json: {}, status: :unauthorized
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def load_pass
64
+ authentication_token = request.headers["Authorization"]&.split(" ")&.last
65
+ unless authentication_token.present?
66
+ render json: {}, status: :unauthorized
67
+ return
68
+ end
69
+
70
+ @pass = Pass.find_by(serial_number: params[:serial_number], authentication_token: authentication_token)
71
+ unless @pass
72
+ render json: {}, status: :unauthorized
73
+ end
74
+ end
75
+
76
+ def load_device
77
+ @device = Passkit::Device.find_by(identifier: params[:device_id])
78
+ end
79
+
80
+ def register_device
81
+ @pass.devices.create!(identifier: params[:device_id], push_token: push_token)
82
+ end
83
+
84
+ def fetch_registered_passes
85
+ passes = @device.passes
86
+
87
+ if params[:passesUpdatedSince]&.present?
88
+ passes.all.filter { |a| a.generator.updated_at >= Date.parse(params[:passesUpdatedSince]) }
89
+ else
90
+ passes
91
+ end
92
+ end
93
+
94
+ def updatable_passes(passes)
95
+ {lastUpdated: Time.zone.now, serialNumbers: passes.pluck(:serial_number)}
96
+ end
97
+
98
+ # TODO: add authentication_token
99
+ # The value is the word ApplePass, followed by a space
100
+ # The value is the word AndroidPass (instead of ApplePass), followed by a space
101
+ def authentication_token
102
+ ""
103
+ end
104
+
105
+ def push_token
106
+ return unless request&.body
107
+
108
+ request.body.rewind
109
+ json_body = JSON.parse(request.body.read)
110
+ json_body["pushToken"]
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,9 @@
1
+ module Passkit
2
+ class LogsController < ActionController::Base
3
+ layout "passkit/application"
4
+
5
+ def index
6
+ @logs = Passkit::Log.order(created_at: :desc).first(100)
7
+ end
8
+ end
9
+ end
@@ -1,24 +1,9 @@
1
1
  module Passkit
2
- class PassesController < ActionController::Base
3
- skip_before_action :verify_authenticity_token
4
- before_action :decrypt_payload, only: :create
2
+ class PassesController < ActionController::Base
3
+ layout "passkit/application"
5
4
 
6
- def create
7
- send_file(fetch_pass(@payload))
8
- end
9
-
10
- private
11
-
12
- def decrypt_payload
13
- @payload = Passkit::UrlEncrypt.decrypt(params[:payload])
14
- if DateTime.parse(@payload[:valid_until]).past?
15
- head :not_found
16
- end
17
- end
18
-
19
- def fetch_pass(payload)
20
- generator = payload[:generator_type].constantize.find(payload[:generator_id])
21
- Passkit::Factory.create_pass(generator)
5
+ def index
6
+ @passes = Passkit::Pass.order(created_at: :desc).includes(:devices).first(100)
22
7
  end
23
8
  end
24
9
  end
@@ -0,0 +1,13 @@
1
+ module Passkit
2
+ class PreviewsController < ActionController::Base
3
+ layout "passkit/application"
4
+
5
+ def index
6
+ end
7
+
8
+ def show
9
+ builder = Passkit.configuration.available_passes[params[:class_name]]
10
+ send_file Factory.create_pass(builder.call, params[:class_name].constantize)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module Passkit
2
+ class ExampleMailer < ActionMailer::Base
3
+ def example_email
4
+ @passkit_url_generator = Passkit::UrlGenerator.new(Passkit::ExampleStoreCard, nil)
5
+ mail(to: "passkit@example.com", subject: "Here is an example of a passkit email")
6
+ end
7
+ end
8
+ end
@@ -1,9 +1,9 @@
1
1
  module Passkit
2
2
  class Device < ActiveRecord::Base
3
- validates_uniqueness_of :device_identifier
3
+ validates_uniqueness_of :identifier
4
4
  validates_uniqueness_of :push_token
5
5
 
6
- has_many :registrations
6
+ has_many :registrations, foreign_key: :passkit_device_id
7
7
  has_many :passes, through: :registrations
8
8
  end
9
9
  end
File without changes
@@ -0,0 +1,49 @@
1
+ module Passkit
2
+ class Pass < ActiveRecord::Base
3
+ validates_uniqueness_of :serial_number
4
+ validates_presence_of :klass
5
+
6
+ belongs_to :generator, polymorphic: true, optional: true
7
+ has_many :registrations, foreign_key: :passkit_pass_id
8
+ has_many :devices, through: :registrations
9
+
10
+ delegate :file_name,
11
+ :pass_path,
12
+ :language,
13
+ :format_version,
14
+ :apple_team_identifier,
15
+ :foreground_color,
16
+ :background_color,
17
+ :web_service_url,
18
+ :barcode,
19
+ :voided,
20
+ :organization_name,
21
+ :description,
22
+ :logo_text,
23
+ :locations,
24
+ :pass_type_identifier,
25
+ :pass_type,
26
+ :header_fields,
27
+ :primary_fields,
28
+ :secondary_fields,
29
+ :auxiliary_fields,
30
+ :back_fields,
31
+ to: :instance
32
+
33
+ before_validation on: :create do
34
+ self.authentication_token ||= SecureRandom.hex
35
+ loop do
36
+ self.serial_number = SecureRandom.uuid
37
+ break unless self.class.exists?(serial_number: serial_number)
38
+ end
39
+ end
40
+
41
+ def instance
42
+ @instance ||= klass.constantize.new(generator)
43
+ end
44
+
45
+ def last_update
46
+ instance.last_update || updated_at
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,6 @@
1
+ module Passkit
2
+ class Registration < ActiveRecord::Base
3
+ belongs_to :device, foreign_key: :passkit_device_id
4
+ belongs_to :pass, foreign_key: :passkit_pass_id
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ <html>
2
+ <head>
3
+ <title>Passkit Previews</title>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
7
+ </head>
8
+ <body>
9
+
10
+ <%= yield %>
11
+
12
+ <footer>
13
+ Made with ❤️ by <a href="https://github.com/sponsors/coorasse/">coorasse</a>.
14
+ </footer>
15
+ </body>
16
+ </html>
@@ -0,0 +1,15 @@
1
+ <p>
2
+ Here is an example of an email message that contains a link to Download a Wallet Pass.
3
+ </p>
4
+ <p>
5
+ <a href="<%=@passkit_url_generator.ios %>">
6
+ <%=image_tag 'passkit/add_to_apple_wallet_en.png', style: 'width: 150px;' %>
7
+ Download a iOS Wallet Pass
8
+ </a>
9
+ </p>
10
+ <p>
11
+ <a href="<%=@passkit_url_generator.android %>">
12
+ <%=image_tag 'https://walletpasses.io/badges/badge_web_black_en@3x.png', style: 'width: 150px;' %>
13
+ Download an Android Wallet Pass
14
+ </a>
15
+ </p>
@@ -0,0 +1,22 @@
1
+ <header>
2
+ <h1>Passkit Logs</h1>
3
+ <%= render 'shared/passkit/navigation' %>
4
+ </header>
5
+
6
+ <main>
7
+ <h2>Last 100 log entries</h2>
8
+ <table>
9
+ <thead>
10
+ <th>Timestamp</th>
11
+ <th>Content</th>
12
+ </thead>
13
+ <tbody>
14
+ <% @logs.each do |log| %>
15
+ <tr>
16
+ <td><%= I18n.l(log.created_at) %></td>
17
+ <td><%= log.content.sub(/^\[.*\]/, '') %></td>
18
+ </tr>
19
+ <% end %>
20
+ </tbody>
21
+ </table>
22
+ </main>
@@ -0,0 +1,32 @@
1
+ <header>
2
+ <h1>Passkit Passes</h1>
3
+ <%= render 'shared/passkit/navigation' %>
4
+ </header>
5
+
6
+ <main>
7
+ <h2>Last 100 passes and registered devices</h2>
8
+ <table>
9
+ <thead>
10
+ <th>Created at</th>
11
+ <th>Generator</th>
12
+ <th>Pass Class</th>
13
+ <th>Devices</th>
14
+ </thead>
15
+ <tbody>
16
+ <% @passes.each do |pass| %>
17
+ <tr>
18
+ <td><%= I18n.l(pass.created_at) %></td>
19
+ <td><%= pass.generator.to_s %></td>
20
+ <td><%= pass.klass %></td>
21
+ <td>
22
+ <% pass.devices.each do |device| %>
23
+ <div>
24
+ <%= device.push_token %>
25
+ </div>
26
+ <% end %>
27
+ </td>
28
+ </tr>
29
+ <% end %>
30
+ </tbody>
31
+ </table>
32
+ </main>
@@ -0,0 +1,21 @@
1
+ <header>
2
+ <h1>Passkit Previews</h1>
3
+ <%= render 'shared/passkit/navigation' %>
4
+ </header>
5
+ <main>
6
+ <p>Here you can generate a Pass from the ones available. Configure your available passes with:</p>
7
+ <pre>
8
+ Passkit.configure do |config|
9
+ config.available_passes['Passkit::YourPass'] = -> { FactoryBot.create(:user) }
10
+ end
11
+ </pre>
12
+ <p>Where you specify the class name as key and a function to generate a builder,
13
+ that will be passed used to generte the Pass</p>
14
+
15
+ <% Passkit.configuration.available_passes.each do |pass_class_name, _builder_function| %>
16
+ <article>
17
+ <h2><%= pass_class_name %></h2>
18
+ <%= link_to 'Generate and download', preview_path(pass_class_name) %>
19
+ </article>
20
+ <% end %>
21
+ </main>
@@ -0,0 +1,5 @@
1
+ <nav>
2
+ <%=link_to 'Logs', logs_path %>
3
+ <%=link_to 'Passes', passes_path %>
4
+ <%=link_to 'Passes Previews', previews_path %>
5
+ </nav>