disco_app 0.9.9

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.
Files changed (335) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +37 -0
  3. data/app/assets/images/disco_app/icon.svg +1 -0
  4. data/app/assets/images/disco_app/icons.svg +0 -0
  5. data/app/assets/javascripts/disco_app/components/custom/filterable_shop_list.js.jsx +61 -0
  6. data/app/assets/javascripts/disco_app/components/custom/inline-radio-options.es6.jsx +59 -0
  7. data/app/assets/javascripts/disco_app/components/custom/rules-editor.es6.jsx +417 -0
  8. data/app/assets/javascripts/disco_app/components/custom/shop_filter_query.js.jsx +13 -0
  9. data/app/assets/javascripts/disco_app/components/custom/shop_filter_tab.js.jsx +34 -0
  10. data/app/assets/javascripts/disco_app/components/custom/shop_filter_tabs.js.jsx +21 -0
  11. data/app/assets/javascripts/disco_app/components/custom/shop_list.js.jsx +142 -0
  12. data/app/assets/javascripts/disco_app/components/custom/shop_row.js.jsx +43 -0
  13. data/app/assets/javascripts/disco_app/components/custom/shopify_admin_link.js.jsx +29 -0
  14. data/app/assets/javascripts/disco_app/components/ui-kit/cards/card-footer.es6.jsx +11 -0
  15. data/app/assets/javascripts/disco_app/components/ui-kit/cards/card-header.es6.jsx +11 -0
  16. data/app/assets/javascripts/disco_app/components/ui-kit/cards/card-section.es6.jsx +30 -0
  17. data/app/assets/javascripts/disco_app/components/ui-kit/cards/card.es6.jsx +16 -0
  18. data/app/assets/javascripts/disco_app/components/ui-kit/cards/cart-section-title.es6.jsx +9 -0
  19. data/app/assets/javascripts/disco_app/components/ui-kit/forms/base_form.es6.jsx +72 -0
  20. data/app/assets/javascripts/disco_app/components/ui-kit/forms/base_input.es6.jsx +20 -0
  21. data/app/assets/javascripts/disco_app/components/ui-kit/forms/button.es6.jsx +13 -0
  22. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-radio.es6.jsx +30 -0
  23. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-select.es6.jsx +39 -0
  24. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-text.es6.jsx +59 -0
  25. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-textarea.es6.jsx +48 -0
  26. data/app/assets/javascripts/disco_app/components/ui-kit/icons/icon-chevron.es6.jsx +33 -0
  27. data/app/assets/javascripts/disco_app/components/ui-kit/icons/next-icon.es6.jsx +18 -0
  28. data/app/assets/javascripts/disco_app/components/ui-kit/input_select.es6.jsx +21 -0
  29. data/app/assets/javascripts/disco_app/components/ui-kit/tables/table.es6.jsx +22 -0
  30. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-annotated-section.es6.jsx +29 -0
  31. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-empty-state.es6.jsx +35 -0
  32. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-footer-help.es6.jsx +13 -0
  33. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-layout-item.es6.jsx +11 -0
  34. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-layout-section.es6.jsx +19 -0
  35. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-layout-sections.es6.jsx +11 -0
  36. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-layout.es6.jsx +11 -0
  37. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-page-actions.es6.jsx +48 -0
  38. data/app/assets/javascripts/disco_app/components.js +2 -0
  39. data/app/assets/javascripts/disco_app/disco_app.js +9 -0
  40. data/app/assets/javascripts/disco_app/frame.js +152 -0
  41. data/app/assets/javascripts/disco_app/shopify-turbolinks.js +7 -0
  42. data/app/assets/javascripts/disco_app/ui-kit.js +1 -0
  43. data/app/assets/stylesheets/disco_app/admin/_header.scss +75 -0
  44. data/app/assets/stylesheets/disco_app/admin/_layout.scss +32 -0
  45. data/app/assets/stylesheets/disco_app/admin/_nav.scss +184 -0
  46. data/app/assets/stylesheets/disco_app/admin.scss +11 -0
  47. data/app/assets/stylesheets/disco_app/disco_app.scss +19 -0
  48. data/app/assets/stylesheets/disco_app/frame/_buttons.scss +54 -0
  49. data/app/assets/stylesheets/disco_app/frame/_forms.scss +26 -0
  50. data/app/assets/stylesheets/disco_app/frame/_layout.scss +77 -0
  51. data/app/assets/stylesheets/disco_app/frame/_type.scss +25 -0
  52. data/app/assets/stylesheets/disco_app/frame.scss +10 -0
  53. data/app/assets/stylesheets/disco_app/mixins/_flexbox.scss +400 -0
  54. data/app/assets/stylesheets/disco_app/ui-kit/_ui-empty-state.scss +121 -0
  55. data/app/assets/stylesheets/disco_app/ui-kit/_ui-footer-help.scss +28 -0
  56. data/app/assets/stylesheets/disco_app/ui-kit/_ui-icons.scss +28 -0
  57. data/app/assets/stylesheets/disco_app/ui-kit/_ui-kit.scss +5113 -0
  58. data/app/assets/stylesheets/disco_app/ui-kit/_ui-layout.scss +15 -0
  59. data/app/assets/stylesheets/disco_app/ui-kit/_ui-page-actions.scss +21 -0
  60. data/app/assets/stylesheets/disco_app/ui-kit/_ui-tabs.scss +75 -0
  61. data/app/assets/stylesheets/disco_app/ui-kit/_ui-type.scss +13 -0
  62. data/app/controllers/disco_app/admin/app_settings_controller.rb +3 -0
  63. data/app/controllers/disco_app/admin/application_controller.rb +3 -0
  64. data/app/controllers/disco_app/admin/concerns/app_settings_controller.rb +24 -0
  65. data/app/controllers/disco_app/admin/concerns/authenticated_controller.rb +20 -0
  66. data/app/controllers/disco_app/admin/concerns/plans_controller.rb +54 -0
  67. data/app/controllers/disco_app/admin/concerns/shops_controller.rb +7 -0
  68. data/app/controllers/disco_app/admin/concerns/subscriptions_controller.rb +29 -0
  69. data/app/controllers/disco_app/admin/plans_controller.rb +3 -0
  70. data/app/controllers/disco_app/admin/resources/shops_controller.rb +3 -0
  71. data/app/controllers/disco_app/admin/shops_controller.rb +3 -0
  72. data/app/controllers/disco_app/admin/subscriptions_controller.rb +3 -0
  73. data/app/controllers/disco_app/charges_controller.rb +47 -0
  74. data/app/controllers/disco_app/concerns/app_proxy_controller.rb +40 -0
  75. data/app/controllers/disco_app/concerns/authenticated_controller.rb +56 -0
  76. data/app/controllers/disco_app/concerns/carrier_request_controller.rb +21 -0
  77. data/app/controllers/disco_app/frame_controller.rb +9 -0
  78. data/app/controllers/disco_app/install_controller.rb +27 -0
  79. data/app/controllers/disco_app/subscriptions_controller.rb +32 -0
  80. data/app/controllers/disco_app/webhooks_controller.rb +46 -0
  81. data/app/controllers/sessions_controller.rb +28 -0
  82. data/app/helpers/disco_app/application_helper.rb +50 -0
  83. data/app/jobs/disco_app/app_installed_job.rb +3 -0
  84. data/app/jobs/disco_app/app_uninstalled_job.rb +3 -0
  85. data/app/jobs/disco_app/concerns/app_installed_job.rb +39 -0
  86. data/app/jobs/disco_app/concerns/app_uninstalled_job.rb +20 -0
  87. data/app/jobs/disco_app/concerns/render_asset_group_job.rb +8 -0
  88. data/app/jobs/disco_app/concerns/shop_update_job.rb +13 -0
  89. data/app/jobs/disco_app/concerns/subscription_changed_job.rb +7 -0
  90. data/app/jobs/disco_app/concerns/synchronise_carrier_service_job.rb +52 -0
  91. data/app/jobs/disco_app/concerns/synchronise_resources_job.rb +12 -0
  92. data/app/jobs/disco_app/concerns/synchronise_webhooks_job.rb +61 -0
  93. data/app/jobs/disco_app/render_asset_group_job.rb +3 -0
  94. data/app/jobs/disco_app/shop_job.rb +27 -0
  95. data/app/jobs/disco_app/shop_update_job.rb +3 -0
  96. data/app/jobs/disco_app/subscription_changed_job.rb +3 -0
  97. data/app/jobs/disco_app/synchronise_carrier_service_job.rb +3 -0
  98. data/app/jobs/disco_app/synchronise_resources_job.rb +3 -0
  99. data/app/jobs/disco_app/synchronise_webhooks_job.rb +3 -0
  100. data/app/models/disco_app/app_settings.rb +3 -0
  101. data/app/models/disco_app/application_charge.rb +18 -0
  102. data/app/models/disco_app/concerns/app_settings.rb +7 -0
  103. data/app/models/disco_app/concerns/plan.rb +26 -0
  104. data/app/models/disco_app/concerns/plan_code.rb +15 -0
  105. data/app/models/disco_app/concerns/renders_assets.rb +166 -0
  106. data/app/models/disco_app/concerns/shop.rb +68 -0
  107. data/app/models/disco_app/concerns/subscription.rb +60 -0
  108. data/app/models/disco_app/concerns/synchronises.rb +50 -0
  109. data/app/models/disco_app/concerns/taggable.rb +16 -0
  110. data/app/models/disco_app/plan.rb +3 -0
  111. data/app/models/disco_app/plan_code.rb +3 -0
  112. data/app/models/disco_app/recurring_application_charge.rb +18 -0
  113. data/app/models/disco_app/session_storage.rb +18 -0
  114. data/app/models/disco_app/shop.rb +3 -0
  115. data/app/models/disco_app/subscription.rb +3 -0
  116. data/app/resources/disco_app/admin/resources/concerns/shop_resource.rb +100 -0
  117. data/app/resources/disco_app/admin/resources/shop_resource.rb +4 -0
  118. data/app/services/disco_app/carrier_request_service.rb +15 -0
  119. data/app/services/disco_app/charges_service.rb +81 -0
  120. data/app/services/disco_app/proxy_service.rb +17 -0
  121. data/app/services/disco_app/subscription_service.rb +46 -0
  122. data/app/services/disco_app/webhook_service.rb +30 -0
  123. data/app/views/disco_app/admin/app_settings/edit.html.erb +5 -0
  124. data/app/views/disco_app/admin/plans/_form.html.erb +72 -0
  125. data/app/views/disco_app/admin/plans/_plan_code_fields.html.erb +15 -0
  126. data/app/views/disco_app/admin/plans/edit.html.erb +7 -0
  127. data/app/views/disco_app/admin/plans/index.html.erb +43 -0
  128. data/app/views/disco_app/admin/plans/new.html.erb +7 -0
  129. data/app/views/disco_app/admin/shops/index.html.erb +13 -0
  130. data/app/views/disco_app/admin/subscriptions/edit.html.erb +33 -0
  131. data/app/views/disco_app/charges/activate.html.erb +1 -0
  132. data/app/views/disco_app/charges/create.html.erb +1 -0
  133. data/app/views/disco_app/charges/new.html.erb +23 -0
  134. data/app/views/disco_app/frame/frame.html.erb +36 -0
  135. data/app/views/disco_app/install/installing.html.erb +7 -0
  136. data/app/views/disco_app/install/uninstalling.html.erb +1 -0
  137. data/app/views/disco_app/proxy_errors/404.html.erb +1 -0
  138. data/app/views/disco_app/shared/_card.html.erb +14 -0
  139. data/app/views/disco_app/shared/_icons.html.erb +3 -0
  140. data/app/views/disco_app/shared/_section.html.erb +17 -0
  141. data/app/views/disco_app/subscriptions/new.html.erb +25 -0
  142. data/app/views/layouts/admin/_nav_items.erb +20 -0
  143. data/app/views/layouts/admin.html.erb +67 -0
  144. data/app/views/layouts/application.html.erb +18 -0
  145. data/app/views/layouts/embedded_app.html.erb +44 -0
  146. data/app/views/layouts/embedded_app_modal.html.erb +28 -0
  147. data/app/views/sessions/new.html.erb +26 -0
  148. data/config/routes.rb +48 -0
  149. data/db/migrate/20150525000000_create_shops_if_not_existent.rb +15 -0
  150. data/db/migrate/20150525162112_add_status_to_shops.rb +5 -0
  151. data/db/migrate/20150525171422_add_meta_to_shops.rb +11 -0
  152. data/db/migrate/20150629210346_add_charge_status_to_shop.rb +5 -0
  153. data/db/migrate/20150814214025_add_more_meta_to_shops.rb +15 -0
  154. data/db/migrate/20151017231302_create_disco_app_plans.rb +13 -0
  155. data/db/migrate/20151017232027_create_disco_app_subscriptions.rb +15 -0
  156. data/db/migrate/20151017234409_move_shop_to_disco_app_engine.rb +5 -0
  157. data/db/migrate/20160112233706_create_disco_app_sessions.rb +12 -0
  158. data/db/migrate/20160113194418_add_shop_id_to_disco_app_sessions.rb +6 -0
  159. data/db/migrate/20160223111044_create_disco_app_settings.rb +8 -0
  160. data/db/migrate/20160301223215_update_plans.rb +22 -0
  161. data/db/migrate/20160301224558_update_subscriptions.rb +13 -0
  162. data/db/migrate/20160302104816_create_disco_app_recurring_application_charges.rb +14 -0
  163. data/db/migrate/20160302105259_create_disco_app_application_charges.rb +14 -0
  164. data/db/migrate/20160302134728_drop_charge_status_from_shops.rb +5 -0
  165. data/db/migrate/20160302142941_add_shopify_attributes_to_charges.rb +8 -0
  166. data/db/migrate/20160331093148_create_disco_app_plan_codes.rb +14 -0
  167. data/db/migrate/20160401044420_add_status_to_plan_codes.rb +5 -0
  168. data/db/migrate/20160401045551_add_amount_and_plan_code_to_disco_app_subscriptions.rb +7 -0
  169. data/db/migrate/20160425205211_add_source_to_disco_app_subscriptions.rb +5 -0
  170. data/db/migrate/20160426033520_add_trial_period_days_to_disco_app_subscriptions.rb +5 -0
  171. data/db/migrate/20160513140727_add_name_to_disco_app_shops.rb +5 -0
  172. data/db/migrate/20160521135510_move_shop_to_synchronises.rb +61 -0
  173. data/lib/disco_app/configuration.rb +39 -0
  174. data/lib/disco_app/constants.rb +4 -0
  175. data/lib/disco_app/engine.rb +26 -0
  176. data/lib/disco_app/session.rb +14 -0
  177. data/lib/disco_app/support/file_fixtures.rb +23 -0
  178. data/lib/disco_app/test_help.rb +11 -0
  179. data/lib/disco_app/version.rb +3 -0
  180. data/lib/disco_app.rb +7 -0
  181. data/lib/generators/disco_app/USAGE +5 -0
  182. data/lib/generators/disco_app/disco_app_generator.rb +235 -0
  183. data/lib/generators/disco_app/templates/assets/javascripts/application.js +17 -0
  184. data/lib/generators/disco_app/templates/assets/javascripts/components.js +3 -0
  185. data/lib/generators/disco_app/templates/assets/stylesheets/application.scss +5 -0
  186. data/lib/generators/disco_app/templates/config/database.yml.tt +20 -0
  187. data/lib/generators/disco_app/templates/config/newrelic.yml +26 -0
  188. data/lib/generators/disco_app/templates/config/puma.rb +15 -0
  189. data/lib/generators/disco_app/templates/controllers/home_controller.rb +7 -0
  190. data/lib/generators/disco_app/templates/initializers/disco_app.rb +19 -0
  191. data/lib/generators/disco_app/templates/initializers/rollbar.rb +12 -0
  192. data/lib/generators/disco_app/templates/initializers/session_store.rb +2 -0
  193. data/lib/generators/disco_app/templates/initializers/shopify_app.rb +7 -0
  194. data/lib/generators/disco_app/templates/initializers/shopify_session_repository.rb +7 -0
  195. data/lib/generators/disco_app/templates/root/CHECKS +4 -0
  196. data/lib/generators/disco_app/templates/root/Procfile +2 -0
  197. data/lib/generators/disco_app/templates/views/home/index.html.erb +2 -0
  198. data/lib/tasks/carrier_service.rake +10 -0
  199. data/lib/tasks/database.rake +8 -0
  200. data/lib/tasks/sessions.rake +9 -0
  201. data/lib/tasks/shops.rake +10 -0
  202. data/lib/tasks/start.rake +3 -0
  203. data/lib/tasks/webhooks.rake +10 -0
  204. data/test/controllers/disco_app/admin/shops_controller_test.rb +54 -0
  205. data/test/controllers/disco_app/charges_controller_test.rb +99 -0
  206. data/test/controllers/disco_app/install_controller_test.rb +50 -0
  207. data/test/controllers/disco_app/subscriptions_controller_test.rb +68 -0
  208. data/test/controllers/disco_app/webhooks_controller_test.rb +58 -0
  209. data/test/controllers/home_controller_test.rb +92 -0
  210. data/test/controllers/proxy_controller_test.rb +42 -0
  211. data/test/disco_app_test.rb +7 -0
  212. data/test/dummy/Rakefile +6 -0
  213. data/test/dummy/app/assets/javascripts/application.js +17 -0
  214. data/test/dummy/app/assets/stylesheets/application.scss +5 -0
  215. data/test/dummy/app/controllers/application_controller.rb +6 -0
  216. data/test/dummy/app/controllers/disco_app/admin/shops_controller.rb +8 -0
  217. data/test/dummy/app/controllers/home_controller.rb +7 -0
  218. data/test/dummy/app/controllers/proxy_controller.rb +8 -0
  219. data/test/dummy/app/helpers/application_helper.rb +2 -0
  220. data/test/dummy/app/jobs/disco_app/app_installed_job.rb +16 -0
  221. data/test/dummy/app/jobs/disco_app/app_uninstalled_job.rb +11 -0
  222. data/test/dummy/app/jobs/products_create_job.rb +7 -0
  223. data/test/dummy/app/jobs/products_delete_job.rb +7 -0
  224. data/test/dummy/app/jobs/products_update_job.rb +7 -0
  225. data/test/dummy/app/models/disco_app/shop.rb +15 -0
  226. data/test/dummy/app/models/js_configuration.rb +8 -0
  227. data/test/dummy/app/models/product.rb +6 -0
  228. data/test/dummy/app/models/widget_configuration.rb +10 -0
  229. data/test/dummy/app/views/assets/script_tag.js.erb +1 -0
  230. data/test/dummy/app/views/assets/test.js.erb +1 -0
  231. data/test/dummy/app/views/assets/widget.js.erb +2 -0
  232. data/test/dummy/app/views/assets/widget.scss.erb +3 -0
  233. data/test/dummy/app/views/home/index.html.erb +2 -0
  234. data/test/dummy/app/views/snippets/widget.liquid.erb +1 -0
  235. data/test/dummy/bin/bundle +3 -0
  236. data/test/dummy/bin/rails +4 -0
  237. data/test/dummy/bin/rake +4 -0
  238. data/test/dummy/bin/setup +29 -0
  239. data/test/dummy/config/application.rb +38 -0
  240. data/test/dummy/config/boot.rb +5 -0
  241. data/test/dummy/config/database.codeship.yml +23 -0
  242. data/test/dummy/config/database.gitlab-ci.yml +24 -0
  243. data/test/dummy/config/database.yml +20 -0
  244. data/test/dummy/config/environment.rb +5 -0
  245. data/test/dummy/config/environments/development.rb +41 -0
  246. data/test/dummy/config/environments/production.rb +85 -0
  247. data/test/dummy/config/environments/test.rb +42 -0
  248. data/test/dummy/config/initializers/assets.rb +11 -0
  249. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  250. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  251. data/test/dummy/config/initializers/disco_app.rb +19 -0
  252. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  253. data/test/dummy/config/initializers/inflections.rb +16 -0
  254. data/test/dummy/config/initializers/mime_types.rb +4 -0
  255. data/test/dummy/config/initializers/omniauth.rb +9 -0
  256. data/test/dummy/config/initializers/session_store.rb +2 -0
  257. data/test/dummy/config/initializers/shopify_app.rb +7 -0
  258. data/test/dummy/config/initializers/shopify_session_repository.rb +7 -0
  259. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  260. data/test/dummy/config/locales/en.yml +23 -0
  261. data/test/dummy/config/routes.rb +10 -0
  262. data/test/dummy/config/secrets.yml +22 -0
  263. data/test/dummy/config.ru +4 -0
  264. data/test/dummy/db/migrate/20160307182229_create_products.rb +11 -0
  265. data/test/dummy/db/migrate/20160530160739_create_asset_models.rb +19 -0
  266. data/test/dummy/db/schema.rb +141 -0
  267. data/test/dummy/public/404.html +67 -0
  268. data/test/dummy/public/422.html +67 -0
  269. data/test/dummy/public/500.html +66 -0
  270. data/test/dummy/public/favicon.ico +0 -0
  271. data/test/fixtures/api/widget_store/assets/create_script_tag_js_request.json +6 -0
  272. data/test/fixtures/api/widget_store/assets/create_script_tag_js_response.json +12 -0
  273. data/test/fixtures/api/widget_store/assets/create_script_tag_request.json +6 -0
  274. data/test/fixtures/api/widget_store/assets/create_script_tag_response.json +10 -0
  275. data/test/fixtures/api/widget_store/assets/create_test_js_request.json +6 -0
  276. data/test/fixtures/api/widget_store/assets/create_test_js_response.json +12 -0
  277. data/test/fixtures/api/widget_store/assets/create_widget_js_request.json +6 -0
  278. data/test/fixtures/api/widget_store/assets/create_widget_js_response.json +12 -0
  279. data/test/fixtures/api/widget_store/assets/create_widget_liquid_request.json +6 -0
  280. data/test/fixtures/api/widget_store/assets/create_widget_liquid_response.json +12 -0
  281. data/test/fixtures/api/widget_store/assets/create_widget_scss_request.json +6 -0
  282. data/test/fixtures/api/widget_store/assets/create_widget_scss_response.json +12 -0
  283. data/test/fixtures/api/widget_store/assets/get_script_tags_empty_request.json +1 -0
  284. data/test/fixtures/api/widget_store/assets/get_script_tags_empty_response.json +1 -0
  285. data/test/fixtures/api/widget_store/assets/get_script_tags_preexisting_request.json +1 -0
  286. data/test/fixtures/api/widget_store/assets/get_script_tags_preexisting_response.json +12 -0
  287. data/test/fixtures/api/widget_store/assets/update_script_tag_request.json +10 -0
  288. data/test/fixtures/api/widget_store/assets/update_script_tag_response.json +10 -0
  289. data/test/fixtures/api/widget_store/charges/activate_application_charge_request.json +16 -0
  290. data/test/fixtures/api/widget_store/charges/activate_application_charge_response.json +1 -0
  291. data/test/fixtures/api/widget_store/charges/activate_recurring_application_charge_request.json +20 -0
  292. data/test/fixtures/api/widget_store/charges/activate_recurring_application_charge_response.json +1 -0
  293. data/test/fixtures/api/widget_store/charges/create_application_charge_request.json +9 -0
  294. data/test/fixtures/api/widget_store/charges/create_application_charge_response.json +16 -0
  295. data/test/fixtures/api/widget_store/charges/create_recurring_application_charge_request.json +9 -0
  296. data/test/fixtures/api/widget_store/charges/create_recurring_application_charge_response.json +20 -0
  297. data/test/fixtures/api/widget_store/charges/create_second_recurring_application_charge_request.json +9 -0
  298. data/test/fixtures/api/widget_store/charges/create_second_recurring_application_charge_response.json +20 -0
  299. data/test/fixtures/api/widget_store/charges/get_accepted_application_charge_response.json +16 -0
  300. data/test/fixtures/api/widget_store/charges/get_accepted_recurring_application_charge_response.json +20 -0
  301. data/test/fixtures/api/widget_store/charges/get_declined_application_charge_response.json +16 -0
  302. data/test/fixtures/api/widget_store/charges/get_declined_recurring_application_charge_response.json +20 -0
  303. data/test/fixtures/api/widget_store/charges/get_pending_application_charge_response.json +16 -0
  304. data/test/fixtures/api/widget_store/charges/get_pending_recurring_application_charge_response.json +20 -0
  305. data/test/fixtures/api/widget_store/shop.json +46 -0
  306. data/test/fixtures/api/widget_store/webhooks.json +1 -0
  307. data/test/fixtures/assets/test.js +1 -0
  308. data/test/fixtures/assets/test.min.js +1 -0
  309. data/test/fixtures/disco_app/application_charges.yml +11 -0
  310. data/test/fixtures/disco_app/plan_codes.yml +13 -0
  311. data/test/fixtures/disco_app/plans.yml +37 -0
  312. data/test/fixtures/disco_app/recurring_application_charges.yml +11 -0
  313. data/test/fixtures/disco_app/shops.yml +10 -0
  314. data/test/fixtures/disco_app/subscriptions.yml +22 -0
  315. data/test/fixtures/js_configurations.yml +3 -0
  316. data/test/fixtures/products.yml +4 -0
  317. data/test/fixtures/webhooks/app_uninstalled.json +46 -0
  318. data/test/fixtures/webhooks/product_created.json +167 -0
  319. data/test/fixtures/webhooks/product_deleted.json +3 -0
  320. data/test/fixtures/webhooks/product_updated.json +167 -0
  321. data/test/fixtures/widget_configurations.yml +4 -0
  322. data/test/integration/synchronises_test.rb +55 -0
  323. data/test/jobs/disco_app/app_installed_job_test.rb +55 -0
  324. data/test/jobs/disco_app/app_uninstalled_job_test.rb +30 -0
  325. data/test/models/disco_app/plan_test.rb +5 -0
  326. data/test/models/disco_app/renders_assets_test.rb +109 -0
  327. data/test/models/disco_app/session_test.rb +31 -0
  328. data/test/models/disco_app/shop_test.rb +27 -0
  329. data/test/models/disco_app/subscription_test.rb +19 -0
  330. data/test/services/disco_app/charges_service_test.rb +112 -0
  331. data/test/services/disco_app/subscription_service_test.rb +60 -0
  332. data/test/support/test_file_fixtures.rb +29 -0
  333. data/test/support/test_shopify_api.rb +16 -0
  334. data/test/test_helper.rb +55 -0
  335. metadata +830 -0
@@ -0,0 +1,50 @@
1
+ module DiscoApp::Concerns::Synchronises
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+
6
+ # Define the number of resources per page to fetch.
7
+ SYNCHRONISES_PAGE_LIMIT = 250
8
+
9
+ def should_synchronise?(shop, data)
10
+ true
11
+ end
12
+
13
+ def synchronise(shop, data)
14
+ data = data.with_indifferent_access
15
+
16
+ return unless should_synchronise?(shop, data)
17
+
18
+ instance = self.find_or_create_by!(id: data[:id]) do |instance|
19
+ instance.shop = shop
20
+ instance.data = data
21
+ end
22
+
23
+ instance.update(data: data)
24
+
25
+ instance
26
+ end
27
+
28
+ def should_synchronise_deletion?(shop, data)
29
+ true
30
+ end
31
+
32
+ def synchronise_deletion(shop, data)
33
+ data = data.with_indifferent_access
34
+
35
+ return unless should_synchronise_deletion?(shop, data)
36
+
37
+ self.destroy_all(shop: shop, id: data[:id])
38
+ end
39
+
40
+ def synchronise_all(shop, params = {})
41
+ resource_count = shop.temp { self::SHOPIFY_API_CLASS.count(params) }
42
+
43
+ (1..(resource_count / SYNCHRONISES_PAGE_LIMIT.to_f).ceil).each do |page|
44
+ DiscoApp::SynchroniseResourcesJob.perform_later(shop, self.name, params.merge(page: page, limit: SYNCHRONISES_PAGE_LIMIT))
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,16 @@
1
+ module DiscoApp::Concerns::Taggable
2
+ extend ActiveSupport::Concern
3
+
4
+ def tags
5
+ data['tags'].split(',').map(&:strip)
6
+ end
7
+
8
+ def add_tag(tag)
9
+ data['tags'] = (tags + [tag]).uniq.join(',')
10
+ end
11
+
12
+ def remove_tag(tag)
13
+ data['tags'] = (tags - [tag]).uniq.join(',')
14
+ end
15
+
16
+ end
@@ -0,0 +1,3 @@
1
+ class DiscoApp::Plan < ActiveRecord::Base
2
+ include DiscoApp::Concerns::Plan
3
+ end
@@ -0,0 +1,3 @@
1
+ class DiscoApp::PlanCode < ActiveRecord::Base
2
+ include DiscoApp::Concerns::PlanCode
3
+ end
@@ -0,0 +1,18 @@
1
+ class DiscoApp::RecurringApplicationCharge < ActiveRecord::Base
2
+
3
+ belongs_to :shop
4
+ belongs_to :subscription
5
+
6
+ enum status: [:pending, :accepted, :declined, :active, :cancelled, :expired]
7
+
8
+ scope :active, -> { where status: statuses[:active] }
9
+
10
+ def recurring?
11
+ true
12
+ end
13
+
14
+ def activate_url
15
+ DiscoApp::Engine.routes.url_helpers.activate_subscription_charge_url(subscription, self)
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ module DiscoApp
2
+ class SessionStorage
3
+ def self.store(session)
4
+ shop = Shop.find_or_initialize_by(shopify_domain: session.url)
5
+ shop.shopify_token = session.token
6
+ shop.save!
7
+ shop.id
8
+ end
9
+
10
+ def self.retrieve(id)
11
+ return unless id
12
+ shop = Shop.find(id)
13
+ ShopifyAPI::Session.new(shop.shopify_domain, shop.shopify_token)
14
+ rescue ActiveRecord::RecordNotFound
15
+ nil
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ class DiscoApp::Shop < ActiveRecord::Base
2
+ include DiscoApp::Concerns::Shop
3
+ end
@@ -0,0 +1,3 @@
1
+ class DiscoApp::Subscription < ActiveRecord::Base
2
+ include DiscoApp::Concerns::Subscription
3
+ end
@@ -0,0 +1,100 @@
1
+ require 'jsonapi/resource'
2
+
3
+ module DiscoApp::Admin::Resources::Concerns::ShopResource
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+
8
+ attributes :domain, :status, :created_at
9
+ attributes :email, :country_name, :currency, :plan_display_name
10
+ attributes :current_subscription_id, :current_subscription_display_amount, :current_subscription_display_plan, :current_subscription_display_plan_code, :current_subscription_source
11
+ attributes :installed_duration
12
+
13
+ model_name 'DiscoApp::Shop'
14
+
15
+ filters :query, :status
16
+
17
+ # Adjust the base records method to ensure only models for the authenticated domain are retrieved.
18
+ def self.records(options = {})
19
+ records = DiscoApp::Shop.order(created_at: :desc)
20
+ records
21
+ end
22
+
23
+ # Apply filters.
24
+ def self.apply_filter(records, filter, value, options)
25
+ return records if value.blank?
26
+
27
+ # Perform appropriate filtering.
28
+ case filter
29
+ when :query
30
+ return records.where('name LIKE ? OR shopify_domain LIKE ? OR domain LIKE ?', "%#{value.first}%", "%#{value.first}%", "%#{value.first}%")
31
+ when :status
32
+ return records.where(status: value.map { |v| DiscoApp::Shop.statuses[v.to_sym] } )
33
+ else
34
+ return super(records, filter, value)
35
+ end
36
+ end
37
+
38
+ # Don't allow the update of any fields via the API.
39
+ def self.updatable_fields(context)
40
+ []
41
+ end
42
+
43
+ # Don't allow the creation of any fields via the API.
44
+ def self.creatable_fields(context)
45
+ []
46
+ end
47
+
48
+ def email
49
+ @model.data['email']
50
+ end
51
+
52
+ def country_name
53
+ @model.data['country_name']
54
+ end
55
+
56
+ def currency
57
+ @model.data['currency']
58
+ end
59
+
60
+ def plan_display_name
61
+ @model.data['plan_display_name']
62
+ end
63
+
64
+ def current_subscription_id
65
+ if @model.current_subscription?
66
+ @model.current_subscription.id
67
+ end
68
+ end
69
+
70
+ def current_subscription_display_amount
71
+ if @model.current_subscription?
72
+ @model.current_subscription.amount
73
+ else
74
+ '-'
75
+ end
76
+ end
77
+
78
+ def current_subscription_display_plan
79
+ if @model.current_subscription?
80
+ @model.current_plan.name
81
+ else
82
+ 'None'
83
+ end
84
+ end
85
+
86
+ def current_subscription_display_plan_code
87
+ @model.current_subscription&.plan_code&.code
88
+ end
89
+
90
+ def current_subscription_source
91
+ if @model.current_subscription?
92
+ @model.current_subscription.source || '-'
93
+ else
94
+ '-'
95
+ end
96
+ end
97
+
98
+ end
99
+
100
+ end
@@ -0,0 +1,4 @@
1
+ class DiscoApp::Admin::Resources::ShopResource < JSONAPI::Resource
2
+ include DiscoApp::Admin::Resources::Concerns::ShopResource
3
+
4
+ end
@@ -0,0 +1,15 @@
1
+ class DiscoApp::CarrierRequestService
2
+
3
+ # Return true iff the provided hmac_to_verify matches that calculated from the
4
+ # given data and secret.
5
+ def self.is_valid_hmac?(body, secret, hmac_to_verify)
6
+ ActiveSupport::SecurityUtils.secure_compare(self.calculated_hmac(body, secret), hmac_to_verify.to_s)
7
+ end
8
+
9
+ # Calculate the HMAC for the given data and secret.
10
+ def self.calculated_hmac(body, secret)
11
+ digest = OpenSSL::Digest.new('sha256')
12
+ Base64.encode64(OpenSSL::HMAC.digest(digest, secret, body)).strip
13
+ end
14
+
15
+ end
@@ -0,0 +1,81 @@
1
+ class DiscoApp::ChargesService
2
+
3
+ # Create the appropriate type of Shopify charge for the given subscription
4
+ # (either one-time or recurring) and return.
5
+ def self.create(shop, subscription)
6
+ # Create the charge object locally first.
7
+ charge = subscription.charge_class.create!(
8
+ shop: shop,
9
+ subscription: subscription,
10
+ )
11
+
12
+ # Create the charge object on Shopify.
13
+ shopify_charge = shop.temp {
14
+ subscription.shopify_charge_class.create(
15
+ name: subscription.plan.name,
16
+ price: '%.2f' % (subscription.amount.to_f / 100.0),
17
+ trial_days: subscription.plan.has_trial? ? subscription.trial_period_days : nil,
18
+ return_url: charge.activate_url,
19
+ test: !DiscoApp.configuration.real_charges?
20
+ )
21
+ }
22
+
23
+ # If we couldn't create the charge on Shopify, return nil.
24
+ if shopify_charge.nil?
25
+ return nil
26
+ end
27
+
28
+ # Update the local record of the charge from Shopify's created charge, then
29
+ # return it.
30
+ charge.update(
31
+ shopify_id: shopify_charge.id,
32
+ confirmation_url: shopify_charge.confirmation_url
33
+ )
34
+ charge
35
+ end
36
+
37
+ # Attempt to activate the given Shopify charge for the given Shop using the
38
+ # Shopify API. Returns true on successful activation, false otherwise.
39
+ def self.activate(shop, charge)
40
+ begin
41
+ # Start by fetching the Shopify charge to check that it was accepted.
42
+ shopify_charge = shop.temp {
43
+ charge.subscription.shopify_charge_class.find(charge.shopify_id)
44
+ }
45
+
46
+ # Update the status of the local charge based on the Shopify charge.
47
+ charge.send("#{shopify_charge.status}!") if charge.respond_to? "#{shopify_charge.status}!"
48
+
49
+ # If the charge wasn't accepted, fail and return.
50
+ return false unless charge.accepted?
51
+
52
+ # If the charge was indeed accepted, activate it via Shopify.
53
+ charge.shop.temp {
54
+ shopify_charge.activate
55
+ }
56
+
57
+ # If the charge was recurring, make sure that all other local recurring
58
+ # charges are marked inactive.
59
+ if charge.recurring?
60
+ self.cancel_recurring_charges(shop, charge)
61
+ end
62
+
63
+ charge.active!
64
+
65
+ true
66
+ rescue
67
+ false
68
+ end
69
+ end
70
+
71
+ # Cancel all recurring charges for the given shop. If the optional charge
72
+ # parameter is given, it will be excluded from the cancellation.
73
+ def self.cancel_recurring_charges(shop, charge = nil)
74
+ charges = DiscoApp::RecurringApplicationCharge.where(shop: shop)
75
+ if charge.present?
76
+ charges = charges.where.not(id: charge)
77
+ end
78
+ charges.update_all(status: DiscoApp::RecurringApplicationCharge.statuses[:cancelled])
79
+ end
80
+
81
+ end
@@ -0,0 +1,17 @@
1
+ class DiscoApp::ProxyService
2
+
3
+ # Return true iff the signature provided in the given query string matches
4
+ # that calculated from the remaining query parameters and the given secret.
5
+ def self.proxy_signature_is_valid?(query_string, secret)
6
+ query_hash = Rack::Utils.parse_query(query_string)
7
+ signature = query_hash.delete('signature').to_s
8
+ ActiveSupport::SecurityUtils.variable_size_secure_compare(self.calculated_signature(query_hash, secret), signature)
9
+ end
10
+
11
+ # Return the calculated signature for the given query hash and secret.
12
+ def self.calculated_signature(query_hash, secret)
13
+ sorted_params = query_hash.collect{ |k, v| "#{k}=#{Array(v).join(',')}" }.sort.join
14
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, sorted_params)
15
+ end
16
+
17
+ end
@@ -0,0 +1,46 @@
1
+ class DiscoApp::SubscriptionService
2
+
3
+ # Subscribe the given shop to the given plan, optionally using the given plan
4
+ # code and optionally tracking the subscription source.
5
+ def self.subscribe(shop, plan, plan_code = nil, source = nil)
6
+
7
+ # If a plan code was provided, fetch it for the given plan.
8
+ plan_code_instance = nil
9
+ if plan_code.present?
10
+ plan_code_instance = DiscoApp::PlanCode.available.find_by(plan: plan, code: plan_code)
11
+ end
12
+
13
+ # Cancel any existing current subscriptions.
14
+ shop.subscriptions.current.update_all(
15
+ status: DiscoApp::Subscription.statuses[:cancelled],
16
+ cancelled_at: Time.now
17
+ )
18
+
19
+ # Get the amount that should be charged for the subscription.
20
+ subscription_amount = plan_code_instance.present? ? plan_code_instance.amount : plan.amount
21
+
22
+ # Get the date the subscription trial should end.
23
+ subscription_trial_period_days = plan_code_instance.present? ? plan_code_instance.trial_period_days : plan.trial_period_days
24
+
25
+ # Create the new subscription.
26
+ new_subscription = DiscoApp::Subscription.create!(
27
+ shop: shop,
28
+ plan: plan,
29
+ plan_code: plan_code_instance,
30
+ status: DiscoApp::Subscription.statuses[plan.has_trial? ? :trial : :active],
31
+ subscription_type: plan.plan_type,
32
+ amount: subscription_amount,
33
+ trial_period_days: plan.has_trial? ? subscription_trial_period_days : nil,
34
+ trial_start_at: plan.has_trial? ? Time.now : nil,
35
+ trial_end_at: plan.has_trial? ? subscription_trial_period_days.days.from_now : nil,
36
+ source: source
37
+ )
38
+
39
+ # Enqueue the subscription changed background job.
40
+ DiscoApp::SubscriptionChangedJob.perform_later(shop, new_subscription)
41
+
42
+ # Return the new subscription.
43
+ new_subscription
44
+ end
45
+
46
+ end
@@ -0,0 +1,30 @@
1
+ class DiscoApp::WebhookService
2
+
3
+ # Return true iff the provided hmac_to_verify matches that calculated from the
4
+ # given data and secret.
5
+ def self.is_valid_hmac?(body, secret, hmac_to_verify)
6
+ ActiveSupport::SecurityUtils.secure_compare(self.calculated_hmac(body, secret), hmac_to_verify.to_s)
7
+ end
8
+
9
+ # Calculate the HMAC for the given data and secret.
10
+ def self.calculated_hmac(body, secret)
11
+ digest = OpenSSL::Digest.new('sha256')
12
+ Base64.encode64(OpenSSL::HMAC.digest(digest, secret, body)).strip
13
+ end
14
+
15
+ # Try to find a job class for the given webhook topic.
16
+ def self.find_job_class(topic)
17
+ begin
18
+ # First try to find a top-level matching job class.
19
+ "#{topic}_job".gsub('/', '_').classify.constantize
20
+ rescue NameError
21
+ # If that fails, try to find a DiscoApp:: prefixed job class.
22
+ begin
23
+ %Q{DiscoApp::#{"#{topic}_job".gsub('/', '_').classify}}.constantize
24
+ rescue NameError
25
+ nil
26
+ end
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,5 @@
1
+ <% provide(:title, 'App Settings') %>
2
+
3
+ <p>Update 'app/views/disco_app/admin/app_settings/edit.html.erb' on your own rails app.</p>
4
+
5
+
@@ -0,0 +1,72 @@
1
+ <section class="section">
2
+ <div class="layout-content">
3
+ <section class="layout-content__main">
4
+ <div class="next-grid">
5
+ <div class="next-grid__cell">
6
+ <div class="next-card">
7
+
8
+ <header class="next-card__header">
9
+ <h1>Plan</h1>
10
+ </header>
11
+
12
+ <section class="next-card__section">
13
+ <div class="form-group">
14
+ <%= f.label(:name, 'Name') %>
15
+ <%= f.text_field(:name) %>
16
+ </div>
17
+
18
+ <div class="form-group">
19
+ <%= f.label(:status, 'Status') %>
20
+ <%= f.select(:status, DiscoApp::Plan.statuses.map { |s| [s.first.humanize, s.first] }) %>
21
+ </div>
22
+
23
+ <div class="form-group">
24
+ <%= f.label(:plan_type, 'Plan Type') %>
25
+ <%= f.select(:plan_type, DiscoApp::Plan.plan_types.map { |s| [s.first.humanize, s.first] }) %>
26
+ </div>
27
+
28
+ <div class="form-group">
29
+ <%= f.label(:trial_period_days, 'Trial Period Days') %>
30
+ <%= f.number_field(:trial_period_days) %>
31
+ </div>
32
+
33
+ <div class="form-group">
34
+ <%= f.label(:amount, 'Amount') %>
35
+ <%= f.number_field(:amount) %>
36
+ </div>
37
+ </section>
38
+ </div>
39
+ </div>
40
+ <div class="next-grid__cell">
41
+ <div class="next-card next-card--aside">
42
+ <header class="next-card__header">
43
+ <h1>Plan Codes</h1>
44
+ </header>
45
+
46
+ <%= f.fields_for :plan_codes do |plan_code| %>
47
+ <%= render 'plan_code_fields', f: plan_code %>
48
+ <% end %>
49
+
50
+ <br>
51
+
52
+ <%= link_to_add_fields('Add New Plan Code', f, :plan_codes) %>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ <hr />
57
+ <div class="row">
58
+ <div class="col-md-12">
59
+ <%= f.submit 'Save', { class: 'btn btn-primary' } %>
60
+ </div>
61
+ </div>
62
+ </section>
63
+ </div>
64
+ </section>
65
+ <script type="text/javascript">
66
+ $('form').on('click', '.add_fields', function(event) {
67
+ var time = new Date().getTime();
68
+ var regexp = new RegExp($(this).data('id'), 'g');
69
+ $(this).before($(this).data('fields').replace(regexp, time));
70
+ event.preventDefault();
71
+ });
72
+ </script>
@@ -0,0 +1,15 @@
1
+ <section class="next-card__section">
2
+ <div class="app-container">
3
+ <%= f.label(:code, 'Code') %>
4
+ <%= f.text_field(:code) %>
5
+
6
+ <%= f.label(:trial_period_days, 'Trial Period Days') %>
7
+ <%= f.number_field(:trial_period_days) %>
8
+
9
+ <%= f.label(:amount, 'Amount') %>
10
+ <%= f.number_field(:amount) %>
11
+
12
+ <%= f.label :_destroy, 'Remove Plan' %>
13
+ <%= f.check_box :_destroy %>
14
+ </div>
15
+ </section>
@@ -0,0 +1,7 @@
1
+ <% provide(:title, 'Edit Plan') %>
2
+
3
+ <%= form_for(@plan, url: admin_plan_path(@plan)) do |f| %>
4
+ <%= render 'form', f: f %>
5
+ <% end %>
6
+
7
+ <%= link_to 'Back', admin_plans_path, { class: 'btn btn-default' } %>
@@ -0,0 +1,43 @@
1
+ <% provide(:title, 'Plans') %>
2
+
3
+ <div class="next-grid">
4
+ <div class="next-grid__cell">
5
+ <div class="next-card">
6
+ <% unless @plans.empty? %>
7
+ <table class="table">
8
+ <thead>
9
+ <tr>
10
+ <th>Name</th>
11
+ <th>Status</th>
12
+ <th>Plan Type</th>
13
+ <th>Trial Period</th>
14
+ <th>Amount</th>
15
+ <th>Currency</th>
16
+ <th>Interval</th>
17
+ <th>Interval Count</th>
18
+ <th></th>
19
+ </tr>
20
+ </thead>
21
+ <tbody>
22
+ <% @plans.each do |plan| %>
23
+ <tr>
24
+ <td><%= link_to(plan.name, edit_admin_plan_path(plan)) %></td>
25
+ <td><%= plan.status %></td>
26
+ <td><%= plan.plan_type %></td>
27
+ <td><%= plan.trial_period_days %></td>
28
+ <td><%= plan.amount %></td>
29
+ <td><%= plan.currency %></td>
30
+ <td><%= plan.interval %></td>
31
+ <td><%= plan.interval_count %></td>
32
+ <td><%= link_to 'Delete', admin_plan_path(plan), action: 'destroy', method: :delete, data: {confirm: "About to delete plan: #{plan.name}. Are you sure?"} %></td>
33
+ </tr>
34
+ <% end %>
35
+ </tbody>
36
+ </table>
37
+ <% end %>
38
+ </div>
39
+ <hr/>
40
+ <%= link_to 'Create new plan', new_admin_plan_path, { class: 'btn btn-primary' } %>
41
+ </div>
42
+ </div>
43
+
@@ -0,0 +1,7 @@
1
+ <% provide(:title, 'New Plan') %>
2
+
3
+ <%= form_for(@plan, url: admin_plans_path) do |f| %>
4
+ <%= render 'form', f: f %>
5
+ <% end %>
6
+
7
+ <%= link_to 'Back', admin_plans_path, { class: 'btn btn-default' } %>
@@ -0,0 +1,13 @@
1
+ <% provide(:title, 'Shops') %>
2
+
3
+ <div class="next-grid">
4
+ <div class="next-grid__cell">
5
+
6
+ <%= react_component('FilterableShopList', {
7
+ shopsUrl: admin_resources_shops_path(format: :json),
8
+ editShopUrl: edit_admin_shop_path(':id'),
9
+ editSubscriptionUrl: edit_admin_shop_subscription_path(':shop_id', ':id')
10
+ }) %>
11
+
12
+ </div>
13
+ </div>
@@ -0,0 +1,33 @@
1
+ <% provide(:breadcrumb, 'Shops') %>
2
+ <% provide(:breadcrumb_url, admin_shops_path) %>
3
+ <% provide(:title, 'Edit subscription') %>
4
+
5
+ <div class="next-grid">
6
+ <div class="next-grid__cell">
7
+
8
+ <%= form_for(@subscription, url: admin_shop_subscription_path(@subscription.shop, @subscription)) do |f| %>
9
+
10
+ <%= f.label(:amount) %>
11
+ <%= f.number_field(:amount) %>
12
+
13
+ <%= f.label(:trial_period_days) %>
14
+ <%= f.number_field(:trial_period_days) %>
15
+
16
+ <p>
17
+ Note that adjusting the amount or trial days of the subscription will
18
+ clear any charge previously authorized by the user, and they'll be asked
19
+ to re-authorize the next time they open the application.
20
+ </p>
21
+
22
+ <p>
23
+ If you're changing the amount of a subscription for an existing user and
24
+ don't want them to get another trial period, make sure to set "Trial
25
+ period days" to zero.
26
+ </p>
27
+
28
+ <%= f.submit 'Save', { class: 'btn btn-primary' } %>
29
+
30
+ <% end %>
31
+
32
+ </div>
33
+ </div>
@@ -0,0 +1 @@
1
+ activate_charge
@@ -0,0 +1 @@
1
+ create_charge
@@ -0,0 +1,23 @@
1
+ <% provide(:title, 'Thankyou') %>
2
+
3
+ <p>
4
+ Thanks for installing <%= DiscoApp.configuration.app_name %>!
5
+ </p>
6
+ <p id="account-warning"></p>
7
+
8
+ <%= form_tag disco_app.subscription_charges_path(@subscription), id: 'install-ok', method: 'POST', target: '_parent' do %>
9
+ <%= submit_tag 'Okay', class: 'form-input' %>
10
+ <% end %>
11
+
12
+ <script>
13
+ ShopifyApp.ready(function(){
14
+ var container = document.getElementById('account-warning');
15
+ if (ShopifyApp.User.current.accountAccess === "Account owner") {
16
+ container.innerHTML = "Before we start setting things up, we need you to authorize a charge for the application."
17
+ } else {
18
+ container.innerHTML = "Since you're not the account owner, you won't be able to setup a charge. Ask the account owner to install the app."
19
+ var okButton = document.getElementById('install-ok');
20
+ okButton.parentElement.removeChild(okButton);
21
+ }
22
+ });
23
+ </script>