shopify-gold 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +144 -0
  3. data/Rakefile +32 -0
  4. data/app/assets/config/gold_manifest.js +0 -0
  5. data/app/assets/images/gold/app-logo.svg +17 -0
  6. data/app/assets/images/gold/aside-bg.svg +54 -0
  7. data/app/assets/javascripts/gold/billing.js +30 -0
  8. data/app/assets/stylesheets/gold/billing.sass +64 -0
  9. data/app/assets/stylesheets/shopify/polaris.css +1 -0
  10. data/app/controllers/gold/admin/billing_controller.rb +190 -0
  11. data/app/controllers/gold/application_controller.rb +17 -0
  12. data/app/controllers/gold/authenticated_controller.rb +15 -0
  13. data/app/controllers/gold/billing_controller.rb +185 -0
  14. data/app/controllers/gold/concerns/merchant_facing.rb +85 -0
  15. data/app/jobs/app_uninstalled_job.rb +2 -0
  16. data/app/jobs/gold/after_authenticate_job.rb +36 -0
  17. data/app/jobs/gold/app_uninstalled_job.rb +10 -0
  18. data/app/jobs/gold/application_job.rb +20 -0
  19. data/app/mailers/gold/application_mailer.rb +7 -0
  20. data/app/mailers/gold/billing_mailer.rb +36 -0
  21. data/app/models/gold/billing.rb +157 -0
  22. data/app/models/gold/concerns/gilded.rb +22 -0
  23. data/app/models/gold/machine.rb +301 -0
  24. data/app/models/gold/shopify_plan.rb +70 -0
  25. data/app/models/gold/tier.rb +97 -0
  26. data/app/models/gold/transition.rb +26 -0
  27. data/app/operations/gold/accept_or_decline_charge_op.rb +119 -0
  28. data/app/operations/gold/accept_or_decline_terms_op.rb +26 -0
  29. data/app/operations/gold/apply_discount_op.rb +32 -0
  30. data/app/operations/gold/apply_tier_op.rb +51 -0
  31. data/app/operations/gold/charge_op.rb +99 -0
  32. data/app/operations/gold/check_charge_op.rb +50 -0
  33. data/app/operations/gold/cleanup_op.rb +20 -0
  34. data/app/operations/gold/convert_affiliate_to_paid_op.rb +32 -0
  35. data/app/operations/gold/freeze_op.rb +15 -0
  36. data/app/operations/gold/install_op.rb +30 -0
  37. data/app/operations/gold/issue_credit_op.rb +55 -0
  38. data/app/operations/gold/mark_as_delinquent_op.rb +21 -0
  39. data/app/operations/gold/resolve_outstanding_charge_op.rb +90 -0
  40. data/app/operations/gold/select_tier_op.rb +58 -0
  41. data/app/operations/gold/suspend_op.rb +21 -0
  42. data/app/operations/gold/uninstall_op.rb +20 -0
  43. data/app/operations/gold/unsuspend_op.rb +20 -0
  44. data/app/views/gold/admin/billing/_active_charge.erb +29 -0
  45. data/app/views/gold/admin/billing/_credit.erb +46 -0
  46. data/app/views/gold/admin/billing/_discount.erb +23 -0
  47. data/app/views/gold/admin/billing/_history.erb +65 -0
  48. data/app/views/gold/admin/billing/_overview.erb +14 -0
  49. data/app/views/gold/admin/billing/_shopify_plan.erb +21 -0
  50. data/app/views/gold/admin/billing/_state.erb +26 -0
  51. data/app/views/gold/admin/billing/_status.erb +25 -0
  52. data/app/views/gold/admin/billing/_tier.erb +155 -0
  53. data/app/views/gold/admin/billing/_trial_days.erb +42 -0
  54. data/app/views/gold/billing/_inner_head.html.erb +1 -0
  55. data/app/views/gold/billing/declined_charge.html.erb +20 -0
  56. data/app/views/gold/billing/expired_charge.html.erb +14 -0
  57. data/app/views/gold/billing/missing_charge.html.erb +22 -0
  58. data/app/views/gold/billing/outstanding_charge.html.erb +12 -0
  59. data/app/views/gold/billing/suspended.html.erb +8 -0
  60. data/app/views/gold/billing/terms.html.erb +22 -0
  61. data/app/views/gold/billing/tier.html.erb +29 -0
  62. data/app/views/gold/billing/transition_error.html.erb +9 -0
  63. data/app/views/gold/billing/unavailable.erb +8 -0
  64. data/app/views/gold/billing/uninstalled.html.erb +13 -0
  65. data/app/views/gold/billing_mailer/affiliate_to_paid.erb +23 -0
  66. data/app/views/gold/billing_mailer/delinquent.html.erb +21 -0
  67. data/app/views/gold/billing_mailer/suspension.html.erb +19 -0
  68. data/app/views/layouts/gold/billing.html.erb +22 -0
  69. data/app/views/layouts/gold/mailer.html.erb +13 -0
  70. data/app/views/layouts/gold/mailer.text.erb +1 -0
  71. data/config/routes.rb +33 -0
  72. data/db/migrate/01_create_gold_billing.rb +11 -0
  73. data/db/migrate/02_create_gold_transitions.rb +29 -0
  74. data/db/migrate/03_add_foreign_key_to_billing.rb +8 -0
  75. data/lib/gold/admin_engine.rb +8 -0
  76. data/lib/gold/billing_migrator.rb +107 -0
  77. data/lib/gold/coverage.rb +89 -0
  78. data/lib/gold/diagram.rb +40 -0
  79. data/lib/gold/engine.rb +16 -0
  80. data/lib/gold/exceptions/metadata_missing.rb +5 -0
  81. data/lib/gold/outcomes.rb +77 -0
  82. data/lib/gold/retries.rb +31 -0
  83. data/lib/gold/version.rb +3 -0
  84. data/lib/gold.rb +102 -0
  85. data/lib/tasks/gold_tasks.rake +94 -0
  86. metadata +298 -0
data/lib/gold.rb ADDED
@@ -0,0 +1,102 @@
1
+ require "gold/engine"
2
+ require "gold/outcomes"
3
+ require "statesman"
4
+
5
+ # Configure Statesman to store transitions in the database.
6
+ Statesman.configure do
7
+ storage_adapter Statesman::Adapters::ActiveRecord
8
+ end
9
+
10
+ # This is the main module where configuration can be set.
11
+ module Gold
12
+ class << self
13
+ attr_accessor :configuration
14
+
15
+ delegate :shop_class,
16
+ :shop_domain_attribute,
17
+ :logger,
18
+ to: :configuration
19
+ end
20
+
21
+ def self.configure
22
+ self.configuration ||= Configuration.new
23
+ yield(configuration)
24
+ end
25
+
26
+ # Config for Gold
27
+ class Configuration
28
+ attr_accessor :test_charge,
29
+ :app_name,
30
+ :contact_email,
31
+ :plan_comparison_url,
32
+ :logger,
33
+ :admin_credentials,
34
+ :shopify_api_version,
35
+ :allow_automated_charge_cancellation,
36
+ :days_until_delinquent,
37
+ :days_until_cleanup,
38
+ :shop_domain_attribute
39
+
40
+ # Callbacks
41
+ attr_accessor :on_terms,
42
+ :on_install,
43
+ :on_uninstall,
44
+ :on_cleanup,
45
+ :on_apply_tier
46
+
47
+ attr_writer :shop_class
48
+
49
+ def initialize
50
+ # The class name (string) of the the app's Active Record Shop model
51
+ @shop_class = nil
52
+
53
+ # The attribute of the *.myshopify.com domain on the Shop model
54
+ @shop_domain_attribute = nil
55
+
56
+ # Should Gold create test charges? Helpful in a development environment
57
+ @test_charge = false
58
+
59
+ # The logger Gold will use
60
+ @logger = Logger.new(STDOUT)
61
+
62
+ # How many days we'll wait before cleaning up an uninstalled shop
63
+ @days_until_cleanup = 30
64
+
65
+ # How many days we'll wait before we uninstall a shop without payment
66
+ @days_until_delinquent = 7
67
+
68
+ # The name of the app using gold (used in the charge name)
69
+ @app_name = "My App"
70
+
71
+ # The email Gold uses to send emails from
72
+ @contact_email = nil
73
+
74
+ # The URL to a plan comparison page for the app
75
+ @plan_comparison_url = nil
76
+
77
+ # Login credential's to Gold's backend
78
+ @admin_credentials = {
79
+ user: "admin",
80
+ password: "password123"
81
+ }
82
+
83
+ # The API version used by Shopify (https://help.shopify.com/en/api/versioning)
84
+ @shopify_api_version = "2019-04"
85
+
86
+ # If Gold is allowed to cancel charges (paid -> free) automatically
87
+ @allow_automated_charge_cancellation = true
88
+ end
89
+
90
+ def shop_class
91
+ @shop_constant ||= @shop_class.to_s.constantize
92
+ rescue NameError
93
+ raise "'#{@shop_class}' is not a valid class name for shop class. " \
94
+ "You must specify one in your Gold config file with " \
95
+ "'config.shop'"
96
+ end
97
+
98
+ def test_charge?
99
+ @test_charge
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,94 @@
1
+ namespace :gold do
2
+ desc "Generate a diagram of the Gold::Machine state machine"
3
+ task diagram: :environment do
4
+ # So that graphs export colors consistently
5
+ random = Random.new(1)
6
+
7
+ acceptance_states = lambda do |node|
8
+ if Gold::Machine::ACCEPTANCE_STATES.include?(node.id.to_sym)
9
+ node[:shape] = :doublecircle
10
+ end
11
+ end
12
+
13
+ # Describes a transition between two states. Used as a Hash key.
14
+ Transition = Struct.new(:from, :to)
15
+
16
+ # Extract labels for guards.
17
+ labels = {}
18
+ Gold::Machine.callbacks[:guards].each do |guard|
19
+ guard.to.each do |to|
20
+ labels[Transition.new(guard.from, to)] = guard.callback.to_s
21
+ end
22
+ end
23
+
24
+ labels_and_colors = lambda do |edge|
25
+ color = %w[red darkgreen blue orange violet].sample(random: random)
26
+
27
+ edge[:color] = color
28
+ edge[:fontcolor] = color
29
+
30
+ label = [
31
+ labels[Transition.new(nil, edge.head_node)],
32
+ labels[Transition.new(edge.tail_node, edge.head_node)]
33
+ ].compact.join(" and ")
34
+ if label
35
+ edge[:label] = label
36
+ edge[:fontsize] = 9
37
+ end
38
+ end
39
+
40
+ diagram = Gold::Diagram.new(Gold::Machine,
41
+ node_callback: acceptance_states,
42
+ edge_callback: labels_and_colors)
43
+
44
+ path = Rails.root.join("tmp", "gold_machine.svg")
45
+ diagram.render(path)
46
+ puts "Diagram generated and saved to #{path}"
47
+ end
48
+
49
+ desc "Checks charges for all billing instances"
50
+ task check_charges: :environment do
51
+ Gold::Billing.all.each do |billing|
52
+ next unless billing.state_machine.can_transition_to?(:check_charge)
53
+
54
+ billing.shop.with_shopify_session do
55
+ outcome = Gold::CheckChargeOp.new(billing).call
56
+
57
+ unless outcome.ok?
58
+ puts "Check charge for shop '#{billing.shop.shopify_domain}': #{outcome}"
59
+ Gold::ChargeOp.new(billing,
60
+ Gold::Engine.routes.url_helpers.process_charge_url).call
61
+ end
62
+ rescue ActiveResource::UnauthorizedAccess
63
+ puts "It looks like '#{billing.shop.shopify_domain}' uninstalled, running op..."
64
+ Gold::UninstallOp.new(billing).call
65
+ end
66
+ end
67
+ end
68
+
69
+ desc "Move eligible billings into cleanup"
70
+ task cleanup: :environment do
71
+ Gold::Billing.all.each do |billing|
72
+ next unless billing.state_machine.can_transition_to?(:cleanup)
73
+
74
+ billing.shop.with_shopify_session do
75
+ outcome = Gold::CleanupOp.new(billing).call
76
+ puts "Cleanup for shop '#{billing.shop.shopify_domain}': #{outcome}"
77
+ end
78
+ end
79
+ end
80
+
81
+ desc "Move eligible billings into delinquence"
82
+ task delinquents: :environment do
83
+ Gold::Billing.all.each do |billing|
84
+ unless billing.current_state == :marked_as_delinquent
85
+ next unless billing.state_machine.can_transition_to?(:marked_as_delinquent)
86
+ end
87
+
88
+ billing.shop.with_shopify_session do
89
+ Gold::MarkAsDelinquentOp.new(billing).call
90
+ puts "Shop '#{billing.shop.shopify_domain}' is delinquent"
91
+ end
92
+ end
93
+ end
94
+ end
metadata ADDED
@@ -0,0 +1,298 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shopify-gold
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Smith
8
+ - Nick Mealey
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-08-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 5.2.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 5.2.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: sass-rails
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '5.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '5.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: shopify_app
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '9'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '9'
56
+ - !ruby/object:Gem::Dependency
57
+ name: statesman
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '3'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3'
70
+ - !ruby/object:Gem::Dependency
71
+ name: dotenv-rails
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '2.5'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '2.5'
84
+ - !ruby/object:Gem::Dependency
85
+ name: faker
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '1.9'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.9'
98
+ - !ruby/object:Gem::Dependency
99
+ name: minitest-skip
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '0.0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '0.0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rubocop
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: 0.74.0
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: 0.74.0
126
+ - !ruby/object:Gem::Dependency
127
+ name: rubocop-rails
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '2.2'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '2.2'
140
+ - !ruby/object:Gem::Dependency
141
+ name: ruby-graphviz
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - "~>"
145
+ - !ruby/object:Gem::Version
146
+ version: '1.2'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: '1.2'
154
+ - !ruby/object:Gem::Dependency
155
+ name: sqlite3
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - "~>"
159
+ - !ruby/object:Gem::Version
160
+ version: '1.3'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - "~>"
166
+ - !ruby/object:Gem::Version
167
+ version: '1.3'
168
+ - !ruby/object:Gem::Dependency
169
+ name: webmock
170
+ requirement: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - "~>"
173
+ - !ruby/object:Gem::Version
174
+ version: 3.4.2
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - "~>"
180
+ - !ruby/object:Gem::Version
181
+ version: 3.4.2
182
+ description: |
183
+ Gold is Helium's approach to billing for Shopify apps. It provides a
184
+ framework to build Rails apps upon.
185
+ email:
186
+ - andrew@heliumdev.com
187
+ - nick@heliumdev.com
188
+ executables: []
189
+ extensions: []
190
+ extra_rdoc_files: []
191
+ files:
192
+ - README.md
193
+ - Rakefile
194
+ - app/assets/config/gold_manifest.js
195
+ - app/assets/images/gold/app-logo.svg
196
+ - app/assets/images/gold/aside-bg.svg
197
+ - app/assets/javascripts/gold/billing.js
198
+ - app/assets/stylesheets/gold/billing.sass
199
+ - app/assets/stylesheets/shopify/polaris.css
200
+ - app/controllers/gold/admin/billing_controller.rb
201
+ - app/controllers/gold/application_controller.rb
202
+ - app/controllers/gold/authenticated_controller.rb
203
+ - app/controllers/gold/billing_controller.rb
204
+ - app/controllers/gold/concerns/merchant_facing.rb
205
+ - app/jobs/app_uninstalled_job.rb
206
+ - app/jobs/gold/after_authenticate_job.rb
207
+ - app/jobs/gold/app_uninstalled_job.rb
208
+ - app/jobs/gold/application_job.rb
209
+ - app/mailers/gold/application_mailer.rb
210
+ - app/mailers/gold/billing_mailer.rb
211
+ - app/models/gold/billing.rb
212
+ - app/models/gold/concerns/gilded.rb
213
+ - app/models/gold/machine.rb
214
+ - app/models/gold/shopify_plan.rb
215
+ - app/models/gold/tier.rb
216
+ - app/models/gold/transition.rb
217
+ - app/operations/gold/accept_or_decline_charge_op.rb
218
+ - app/operations/gold/accept_or_decline_terms_op.rb
219
+ - app/operations/gold/apply_discount_op.rb
220
+ - app/operations/gold/apply_tier_op.rb
221
+ - app/operations/gold/charge_op.rb
222
+ - app/operations/gold/check_charge_op.rb
223
+ - app/operations/gold/cleanup_op.rb
224
+ - app/operations/gold/convert_affiliate_to_paid_op.rb
225
+ - app/operations/gold/freeze_op.rb
226
+ - app/operations/gold/install_op.rb
227
+ - app/operations/gold/issue_credit_op.rb
228
+ - app/operations/gold/mark_as_delinquent_op.rb
229
+ - app/operations/gold/resolve_outstanding_charge_op.rb
230
+ - app/operations/gold/select_tier_op.rb
231
+ - app/operations/gold/suspend_op.rb
232
+ - app/operations/gold/uninstall_op.rb
233
+ - app/operations/gold/unsuspend_op.rb
234
+ - app/views/gold/admin/billing/_active_charge.erb
235
+ - app/views/gold/admin/billing/_credit.erb
236
+ - app/views/gold/admin/billing/_discount.erb
237
+ - app/views/gold/admin/billing/_history.erb
238
+ - app/views/gold/admin/billing/_overview.erb
239
+ - app/views/gold/admin/billing/_shopify_plan.erb
240
+ - app/views/gold/admin/billing/_state.erb
241
+ - app/views/gold/admin/billing/_status.erb
242
+ - app/views/gold/admin/billing/_tier.erb
243
+ - app/views/gold/admin/billing/_trial_days.erb
244
+ - app/views/gold/billing/_inner_head.html.erb
245
+ - app/views/gold/billing/declined_charge.html.erb
246
+ - app/views/gold/billing/expired_charge.html.erb
247
+ - app/views/gold/billing/missing_charge.html.erb
248
+ - app/views/gold/billing/outstanding_charge.html.erb
249
+ - app/views/gold/billing/suspended.html.erb
250
+ - app/views/gold/billing/terms.html.erb
251
+ - app/views/gold/billing/tier.html.erb
252
+ - app/views/gold/billing/transition_error.html.erb
253
+ - app/views/gold/billing/unavailable.erb
254
+ - app/views/gold/billing/uninstalled.html.erb
255
+ - app/views/gold/billing_mailer/affiliate_to_paid.erb
256
+ - app/views/gold/billing_mailer/delinquent.html.erb
257
+ - app/views/gold/billing_mailer/suspension.html.erb
258
+ - app/views/layouts/gold/billing.html.erb
259
+ - app/views/layouts/gold/mailer.html.erb
260
+ - app/views/layouts/gold/mailer.text.erb
261
+ - config/routes.rb
262
+ - db/migrate/01_create_gold_billing.rb
263
+ - db/migrate/02_create_gold_transitions.rb
264
+ - db/migrate/03_add_foreign_key_to_billing.rb
265
+ - lib/gold.rb
266
+ - lib/gold/admin_engine.rb
267
+ - lib/gold/billing_migrator.rb
268
+ - lib/gold/coverage.rb
269
+ - lib/gold/diagram.rb
270
+ - lib/gold/engine.rb
271
+ - lib/gold/exceptions/metadata_missing.rb
272
+ - lib/gold/outcomes.rb
273
+ - lib/gold/retries.rb
274
+ - lib/gold/version.rb
275
+ - lib/tasks/gold_tasks.rake
276
+ homepage: https://gitlab.com/helium-development/infrastructure/gold
277
+ licenses: []
278
+ metadata: {}
279
+ post_install_message:
280
+ rdoc_options: []
281
+ require_paths:
282
+ - lib
283
+ required_ruby_version: !ruby/object:Gem::Requirement
284
+ requirements:
285
+ - - "~>"
286
+ - !ruby/object:Gem::Version
287
+ version: '2.5'
288
+ required_rubygems_version: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - ">"
291
+ - !ruby/object:Gem::Version
292
+ version: 1.3.1
293
+ requirements: []
294
+ rubygems_version: 3.0.1
295
+ signing_key:
296
+ specification_version: 4
297
+ summary: Helium's approach to billing for Shopify apps
298
+ test_files: []