disco_app 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Rakefile +37 -0
- data/app/assets/images/disco_app/icon.svg +1 -0
- data/app/assets/images/disco_app/icons.svg +0 -0
- data/app/assets/javascripts/disco_app/components/custom/filterable_shop_list.js.jsx +60 -0
- data/app/assets/javascripts/disco_app/components/custom/inline-radio-options.es6.jsx +59 -0
- data/app/assets/javascripts/disco_app/components/custom/rules-editor.es6.jsx +360 -0
- data/app/assets/javascripts/disco_app/components/custom/shop_filter_tab.js.jsx +34 -0
- data/app/assets/javascripts/disco_app/components/custom/shop_filter_tabs.js.jsx +21 -0
- data/app/assets/javascripts/disco_app/components/custom/shop_list.js.jsx +142 -0
- data/app/assets/javascripts/disco_app/components/custom/shop_row.js.jsx +43 -0
- data/app/assets/javascripts/disco_app/components/custom/shopify_admin_link.js.jsx +29 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/cards/card-section.es6.jsx +30 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/cards/card.es6.jsx +9 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/cards/cart-section-title.es6.jsx +7 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/forms/base_form.es6.jsx +72 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/forms/base_input.es6.jsx +20 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/forms/button.es6.jsx +13 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-radio.es6.jsx +30 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-select.es6.jsx +39 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-text.es6.jsx +59 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-textarea.es6.jsx +48 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/icons/icon-chevron.es6.jsx +33 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/icons/next-icon.es6.jsx +18 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/input_select.es6.jsx +21 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-annotated-section.es6.jsx +29 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-empty-state.es6.jsx +35 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-footer-help.es6.jsx +13 -0
- data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-page-actions.es6.jsx +39 -0
- data/app/assets/javascripts/disco_app/components.js +2 -0
- data/app/assets/javascripts/disco_app/disco_app.js +10 -0
- data/app/assets/javascripts/disco_app/frame.js +152 -0
- data/app/assets/javascripts/disco_app/shopify-turbolinks.js +7 -0
- data/app/assets/javascripts/disco_app/ui-kit.js +1 -0
- data/app/assets/stylesheets/disco_app/admin/_header.scss +66 -0
- data/app/assets/stylesheets/disco_app/admin/_layout.scss +40 -0
- data/app/assets/stylesheets/disco_app/admin/_nav.scss +172 -0
- data/app/assets/stylesheets/disco_app/admin.scss +11 -0
- data/app/assets/stylesheets/disco_app/disco_app.scss +16 -0
- data/app/assets/stylesheets/disco_app/frame/_buttons.scss +54 -0
- data/app/assets/stylesheets/disco_app/frame/_forms.scss +26 -0
- data/app/assets/stylesheets/disco_app/frame/_layout.scss +77 -0
- data/app/assets/stylesheets/disco_app/frame/_type.scss +32 -0
- data/app/assets/stylesheets/disco_app/frame.scss +9 -0
- data/app/assets/stylesheets/disco_app/mixins/_flexbox.scss +400 -0
- data/app/assets/stylesheets/disco_app/ui-kit/_ui-empty-state.scss +94 -0
- data/app/assets/stylesheets/disco_app/ui-kit/_ui-footer-help.scss +25 -0
- data/app/assets/stylesheets/disco_app/ui-kit/_ui-icons.scss +28 -0
- data/app/assets/stylesheets/disco_app/ui-kit/_ui-kit.scss +5086 -0
- data/app/assets/stylesheets/disco_app/ui-kit/_ui-layout.scss +10 -0
- data/app/assets/stylesheets/disco_app/ui-kit/_ui-page-actions.scss +21 -0
- data/app/assets/stylesheets/disco_app/ui-kit/_ui-tabs.scss +63 -0
- data/app/assets/stylesheets/disco_app/ui-kit/_ui-type.scss +13 -0
- data/app/controllers/disco_app/admin/app_settings_controller.rb +3 -0
- data/app/controllers/disco_app/admin/application_controller.rb +3 -0
- data/app/controllers/disco_app/admin/concerns/app_settings_controller.rb +24 -0
- data/app/controllers/disco_app/admin/concerns/authenticated_controller.rb +20 -0
- data/app/controllers/disco_app/admin/concerns/plans_controller.rb +54 -0
- data/app/controllers/disco_app/admin/concerns/shops_controller.rb +7 -0
- data/app/controllers/disco_app/admin/concerns/subscriptions_controller.rb +32 -0
- data/app/controllers/disco_app/admin/plans_controller.rb +3 -0
- data/app/controllers/disco_app/admin/resources/shops_controller.rb +3 -0
- data/app/controllers/disco_app/admin/shops_controller.rb +3 -0
- data/app/controllers/disco_app/admin/subscriptions_controller.rb +3 -0
- data/app/controllers/disco_app/charges_controller.rb +47 -0
- data/app/controllers/disco_app/concerns/app_proxy_controller.rb +40 -0
- data/app/controllers/disco_app/concerns/authenticated_controller.rb +56 -0
- data/app/controllers/disco_app/concerns/carrier_request_controller.rb +21 -0
- data/app/controllers/disco_app/frame_controller.rb +9 -0
- data/app/controllers/disco_app/install_controller.rb +27 -0
- data/app/controllers/disco_app/subscriptions_controller.rb +32 -0
- data/app/controllers/disco_app/webhooks_controller.rb +46 -0
- data/app/controllers/sessions_controller.rb +28 -0
- data/app/helpers/disco_app/application_helper.rb +50 -0
- data/app/jobs/disco_app/app_installed_job.rb +3 -0
- data/app/jobs/disco_app/app_uninstalled_job.rb +3 -0
- data/app/jobs/disco_app/concerns/app_installed_job.rb +39 -0
- data/app/jobs/disco_app/concerns/app_uninstalled_job.rb +20 -0
- data/app/jobs/disco_app/concerns/shop_update_job.rb +16 -0
- data/app/jobs/disco_app/concerns/subscription_changed_job.rb +7 -0
- data/app/jobs/disco_app/concerns/synchronise_carrier_service_job.rb +52 -0
- data/app/jobs/disco_app/concerns/synchronise_webhooks_job.rb +61 -0
- data/app/jobs/disco_app/shop_job.rb +27 -0
- data/app/jobs/disco_app/shop_update_job.rb +3 -0
- data/app/jobs/disco_app/subscription_changed_job.rb +3 -0
- data/app/jobs/disco_app/synchronise_carrier_service_job.rb +3 -0
- data/app/jobs/disco_app/synchronise_webhooks_job.rb +3 -0
- data/app/models/disco_app/app_settings.rb +3 -0
- data/app/models/disco_app/application_charge.rb +18 -0
- data/app/models/disco_app/concerns/app_settings.rb +7 -0
- data/app/models/disco_app/concerns/plan.rb +26 -0
- data/app/models/disco_app/concerns/plan_code.rb +15 -0
- data/app/models/disco_app/concerns/shop.rb +76 -0
- data/app/models/disco_app/concerns/subscription.rb +60 -0
- data/app/models/disco_app/concerns/synchronises.rb +39 -0
- data/app/models/disco_app/plan.rb +3 -0
- data/app/models/disco_app/plan_code.rb +3 -0
- data/app/models/disco_app/recurring_application_charge.rb +18 -0
- data/app/models/disco_app/session_storage.rb +18 -0
- data/app/models/disco_app/shop.rb +3 -0
- data/app/models/disco_app/subscription.rb +3 -0
- data/app/resources/disco_app/admin/resources/concerns/shop_resource.rb +82 -0
- data/app/resources/disco_app/admin/resources/shop_resource.rb +4 -0
- data/app/services/disco_app/carrier_request_service.rb +15 -0
- data/app/services/disco_app/charges_service.rb +81 -0
- data/app/services/disco_app/proxy_service.rb +17 -0
- data/app/services/disco_app/subscription_service.rb +46 -0
- data/app/services/disco_app/webhook_service.rb +30 -0
- data/app/views/disco_app/admin/app_settings/edit.html.erb +5 -0
- data/app/views/disco_app/admin/plans/_form.html.erb +72 -0
- data/app/views/disco_app/admin/plans/_plan_code_fields.html.erb +15 -0
- data/app/views/disco_app/admin/plans/edit.html.erb +7 -0
- data/app/views/disco_app/admin/plans/index.html.erb +43 -0
- data/app/views/disco_app/admin/plans/new.html.erb +7 -0
- data/app/views/disco_app/admin/shops/index.html.erb +13 -0
- data/app/views/disco_app/admin/subscriptions/edit.html.erb +33 -0
- data/app/views/disco_app/charges/activate.html.erb +1 -0
- data/app/views/disco_app/charges/create.html.erb +1 -0
- data/app/views/disco_app/charges/new.html.erb +12 -0
- data/app/views/disco_app/frame/frame.html.erb +36 -0
- data/app/views/disco_app/install/installing.html.erb +7 -0
- data/app/views/disco_app/install/uninstalling.html.erb +1 -0
- data/app/views/disco_app/proxy_errors/404.html.erb +1 -0
- data/app/views/disco_app/shared/_card.html.erb +14 -0
- data/app/views/disco_app/shared/_icons.html.erb +3 -0
- data/app/views/disco_app/shared/_section.html.erb +17 -0
- data/app/views/disco_app/subscriptions/new.html.erb +25 -0
- data/app/views/layouts/admin/_nav_items.erb +20 -0
- data/app/views/layouts/admin.html.erb +67 -0
- data/app/views/layouts/application.html.erb +18 -0
- data/app/views/layouts/embedded_app.html.erb +43 -0
- data/app/views/layouts/embedded_app_modal.html.erb +28 -0
- data/app/views/sessions/new.html.erb +26 -0
- data/config/routes.rb +48 -0
- data/db/migrate/20150525000000_create_shops_if_not_existent.rb +15 -0
- data/db/migrate/20150525162112_add_status_to_shops.rb +5 -0
- data/db/migrate/20150525171422_add_meta_to_shops.rb +11 -0
- data/db/migrate/20150629210346_add_charge_status_to_shop.rb +5 -0
- data/db/migrate/20150814214025_add_more_meta_to_shops.rb +15 -0
- data/db/migrate/20151017231302_create_disco_app_plans.rb +13 -0
- data/db/migrate/20151017232027_create_disco_app_subscriptions.rb +15 -0
- data/db/migrate/20151017234409_move_shop_to_disco_app_engine.rb +5 -0
- data/db/migrate/20160112233706_create_disco_app_sessions.rb +12 -0
- data/db/migrate/20160113194418_add_shop_id_to_disco_app_sessions.rb +6 -0
- data/db/migrate/20160223111044_create_disco_app_settings.rb +8 -0
- data/db/migrate/20160301223215_update_plans.rb +22 -0
- data/db/migrate/20160301224558_update_subscriptions.rb +13 -0
- data/db/migrate/20160302104816_create_disco_app_recurring_application_charges.rb +14 -0
- data/db/migrate/20160302105259_create_disco_app_application_charges.rb +14 -0
- data/db/migrate/20160302134728_drop_charge_status_from_shops.rb +5 -0
- data/db/migrate/20160302142941_add_shopify_attributes_to_charges.rb +8 -0
- data/db/migrate/20160331093148_create_disco_app_plan_codes.rb +14 -0
- data/db/migrate/20160401044420_add_status_to_plan_codes.rb +5 -0
- data/db/migrate/20160401045551_add_amount_and_plan_code_to_disco_app_subscriptions.rb +7 -0
- data/db/migrate/20160425205211_add_source_to_disco_app_subscriptions.rb +5 -0
- data/db/migrate/20160426033520_add_trial_period_days_to_disco_app_subscriptions.rb +5 -0
- data/lib/disco_app/configuration.rb +39 -0
- data/lib/disco_app/constants.rb +4 -0
- data/lib/disco_app/engine.rb +27 -0
- data/lib/disco_app/session.rb +14 -0
- data/lib/disco_app/support/file_fixtures.rb +23 -0
- data/lib/disco_app/test_help.rb +11 -0
- data/lib/disco_app/version.rb +3 -0
- data/lib/disco_app.rb +7 -0
- data/lib/generators/disco_app/USAGE +5 -0
- data/lib/generators/disco_app/disco_app_generator.rb +197 -0
- data/lib/generators/disco_app/mailify/mailify_generator.rb +54 -0
- data/lib/generators/disco_app/monitorify/monitorify_generator.rb +28 -0
- data/lib/generators/disco_app/monitorify/templates/config/newrelic.yml +26 -0
- data/lib/generators/disco_app/monitorify/templates/initializers/rollbar.rb +12 -0
- data/lib/generators/disco_app/templates/assets/javascripts/application.js +17 -0
- data/lib/generators/disco_app/templates/assets/javascripts/components.js +3 -0
- data/lib/generators/disco_app/templates/assets/stylesheets/application.scss +5 -0
- data/lib/generators/disco_app/templates/config/database.yml.tt +20 -0
- data/lib/generators/disco_app/templates/config/puma.rb +15 -0
- data/lib/generators/disco_app/templates/controllers/home_controller.rb +7 -0
- data/lib/generators/disco_app/templates/initializers/disco_app.rb +19 -0
- data/lib/generators/disco_app/templates/initializers/session_store.rb +2 -0
- data/lib/generators/disco_app/templates/initializers/shopify_app.rb +7 -0
- data/lib/generators/disco_app/templates/initializers/shopify_session_repository.rb +7 -0
- data/lib/generators/disco_app/templates/root/Procfile +2 -0
- data/lib/generators/disco_app/templates/views/home/index.html.erb +2 -0
- data/lib/tasks/carrier_service.rake +10 -0
- data/lib/tasks/database.rake +8 -0
- data/lib/tasks/sessions.rake +9 -0
- data/lib/tasks/start.rake +3 -0
- data/lib/tasks/webhooks.rake +10 -0
- data/test/controllers/disco_app/admin/shops_controller_test.rb +54 -0
- data/test/controllers/disco_app/charges_controller_test.rb +99 -0
- data/test/controllers/disco_app/install_controller_test.rb +50 -0
- data/test/controllers/disco_app/subscriptions_controller_test.rb +68 -0
- data/test/controllers/disco_app/webhooks_controller_test.rb +58 -0
- data/test/controllers/home_controller_test.rb +92 -0
- data/test/controllers/proxy_controller_test.rb +42 -0
- data/test/disco_app_test.rb +7 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +17 -0
- data/test/dummy/app/assets/stylesheets/application.scss +5 -0
- data/test/dummy/app/controllers/application_controller.rb +6 -0
- data/test/dummy/app/controllers/disco_app/admin/shops_controller.rb +8 -0
- data/test/dummy/app/controllers/home_controller.rb +7 -0
- data/test/dummy/app/controllers/proxy_controller.rb +8 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/disco_app/app_installed_job.rb +16 -0
- data/test/dummy/app/jobs/disco_app/app_uninstalled_job.rb +11 -0
- data/test/dummy/app/jobs/products_create_job.rb +7 -0
- data/test/dummy/app/jobs/products_delete_job.rb +7 -0
- data/test/dummy/app/jobs/products_update_job.rb +7 -0
- data/test/dummy/app/models/disco_app/shop.rb +15 -0
- data/test/dummy/app/models/product.rb +6 -0
- data/test/dummy/app/views/home/index.html.erb +2 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config/application.rb +38 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.codeship.yml +23 -0
- data/test/dummy/config/database.yml +20 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +85 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/disco_app.rb +19 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/omniauth.rb +9 -0
- data/test/dummy/config/initializers/session_store.rb +2 -0
- data/test/dummy/config/initializers/shopify_app.rb +7 -0
- data/test/dummy/config/initializers/shopify_session_repository.rb +7 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +10 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/migrate/20160307182229_create_products.rb +11 -0
- data/test/dummy/db/schema.rb +140 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/fixtures/api/widget_store/charges/activate_application_charge_request.json +16 -0
- data/test/fixtures/api/widget_store/charges/activate_application_charge_response.json +1 -0
- data/test/fixtures/api/widget_store/charges/activate_recurring_application_charge_request.json +20 -0
- data/test/fixtures/api/widget_store/charges/activate_recurring_application_charge_response.json +1 -0
- data/test/fixtures/api/widget_store/charges/create_application_charge_request.json +9 -0
- data/test/fixtures/api/widget_store/charges/create_application_charge_response.json +16 -0
- data/test/fixtures/api/widget_store/charges/create_recurring_application_charge_request.json +9 -0
- data/test/fixtures/api/widget_store/charges/create_recurring_application_charge_response.json +20 -0
- data/test/fixtures/api/widget_store/charges/create_second_recurring_application_charge_request.json +9 -0
- data/test/fixtures/api/widget_store/charges/create_second_recurring_application_charge_response.json +20 -0
- data/test/fixtures/api/widget_store/charges/get_accepted_application_charge_response.json +16 -0
- data/test/fixtures/api/widget_store/charges/get_accepted_recurring_application_charge_response.json +20 -0
- data/test/fixtures/api/widget_store/charges/get_declined_application_charge_response.json +16 -0
- data/test/fixtures/api/widget_store/charges/get_declined_recurring_application_charge_response.json +20 -0
- data/test/fixtures/api/widget_store/charges/get_pending_application_charge_response.json +16 -0
- data/test/fixtures/api/widget_store/charges/get_pending_recurring_application_charge_response.json +20 -0
- data/test/fixtures/api/widget_store/shop.json +46 -0
- data/test/fixtures/api/widget_store/webhooks.json +1 -0
- data/test/fixtures/disco_app/application_charges.yml +11 -0
- data/test/fixtures/disco_app/plan_codes.yml +13 -0
- data/test/fixtures/disco_app/plans.yml +37 -0
- data/test/fixtures/disco_app/recurring_application_charges.yml +11 -0
- data/test/fixtures/disco_app/shops.yml +10 -0
- data/test/fixtures/disco_app/subscriptions.yml +22 -0
- data/test/fixtures/products.yml +4 -0
- data/test/fixtures/webhooks/app_uninstalled.json +46 -0
- data/test/fixtures/webhooks/product_created.json +167 -0
- data/test/fixtures/webhooks/product_deleted.json +3 -0
- data/test/fixtures/webhooks/product_updated.json +167 -0
- data/test/integration/synchronises_test.rb +55 -0
- data/test/jobs/disco_app/app_installed_job_test.rb +55 -0
- data/test/jobs/disco_app/app_uninstalled_job_test.rb +30 -0
- data/test/models/disco_app/plan_test.rb +5 -0
- data/test/models/disco_app/session_test.rb +31 -0
- data/test/models/disco_app/shop_test.rb +27 -0
- data/test/models/disco_app/subscription_test.rb +19 -0
- data/test/services/disco_app/charges_service_test.rb +112 -0
- data/test/services/disco_app/subscription_service_test.rb +60 -0
- data/test/support/test_file_fixtures.rb +29 -0
- data/test/support/test_shopify_api.rb +16 -0
- data/test/test_helper.rb +55 -0
- metadata +722 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module DiscoApp::Concerns::AppInstalledJob
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
before_enqueue { @shop.awaiting_install! }
|
|
6
|
+
before_perform { @shop.installing! }
|
|
7
|
+
after_perform { @shop.installed! }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Perform application installation.
|
|
11
|
+
#
|
|
12
|
+
# - Synchronise webhooks.
|
|
13
|
+
# - Synchronise carrier service, if required.
|
|
14
|
+
# - Perform initial update of shop information.
|
|
15
|
+
# - Subscribe to default plan, if any exists.
|
|
16
|
+
#
|
|
17
|
+
def perform(shopify_domain, plan_code = nil, source = nil)
|
|
18
|
+
DiscoApp::SynchroniseWebhooksJob.perform_now(shopify_domain)
|
|
19
|
+
DiscoApp::SynchroniseCarrierServiceJob.perform_now(shopify_domain)
|
|
20
|
+
DiscoApp::ShopUpdateJob.perform_now(shopify_domain)
|
|
21
|
+
|
|
22
|
+
@shop.reload
|
|
23
|
+
|
|
24
|
+
if default_plan.present?
|
|
25
|
+
DiscoApp::SubscriptionService.subscribe(@shop, default_plan, plan_code, source)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Provide an overridable hook for applications to examine the @shop object
|
|
30
|
+
# and return the default plan, if any, the shop should be subscribed to. If
|
|
31
|
+
# nil is returned, no automatic subscription will take place and the store
|
|
32
|
+
# owner will be forced to choose a plan after installation.
|
|
33
|
+
#
|
|
34
|
+
# If implementing this method, it should be memoized.
|
|
35
|
+
def default_plan
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module DiscoApp::Concerns::AppUninstalledJob
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
before_enqueue { @shop.awaiting_uninstall! }
|
|
6
|
+
before_perform { @shop.uninstalling! }
|
|
7
|
+
after_perform { @shop.uninstalled! }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Perform application uninstallation.
|
|
11
|
+
#
|
|
12
|
+
# - Mark any recurring application charges as cancelled.
|
|
13
|
+
# - Remove any stored sessions for the shop.
|
|
14
|
+
#
|
|
15
|
+
def perform(domain, shop_data)
|
|
16
|
+
DiscoApp::ChargesService.cancel_recurring_charges(@shop)
|
|
17
|
+
@shop.sessions.delete_all
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module DiscoApp::Concerns::ShopUpdateJob
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
# Perform an update of the current shop's information.
|
|
5
|
+
def perform(shopify_domain, shop_data = nil)
|
|
6
|
+
# If we weren't provided with shop data (eg from a webhook), fetch it.
|
|
7
|
+
shop_data ||= ActiveSupport::JSON::decode(ShopifyAPI::Shop.current.to_json)
|
|
8
|
+
|
|
9
|
+
# Ensure we can access shop data through symbols.
|
|
10
|
+
shop_data = HashWithIndifferentAccess.new(shop_data)
|
|
11
|
+
|
|
12
|
+
# Update model attributes present in both our model and the data hash.
|
|
13
|
+
@shop.update_attributes(shop_data.except(:id, :created_at).slice(*DiscoApp::Shop.column_names))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module DiscoApp::Concerns::SynchroniseCarrierServiceJob
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
# Ensure that any carrier service required by our app is registered.
|
|
5
|
+
def perform(shopify_domain)
|
|
6
|
+
# Don't proceed unless we have a name and callback url.
|
|
7
|
+
return unless carrier_service_name and callback_url
|
|
8
|
+
|
|
9
|
+
# Registered the carrier service if it hasn't been registered yet.
|
|
10
|
+
unless current_carrier_service_names.include?(carrier_service_name)
|
|
11
|
+
ShopifyAPI::CarrierService.create(
|
|
12
|
+
name: carrier_service_name,
|
|
13
|
+
callback_url: callback_url,
|
|
14
|
+
service_discovery: true,
|
|
15
|
+
format: :json
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Ensure any existing carrier services (with the correct name) are active
|
|
20
|
+
# and have a current callback URL.
|
|
21
|
+
current_carrier_services.each do |carrier_service|
|
|
22
|
+
if carrier_service.name == carrier_service_name
|
|
23
|
+
carrier_service.callback_url = callback_url
|
|
24
|
+
carrier_service.active = true
|
|
25
|
+
carrier_service.save
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
protected
|
|
31
|
+
|
|
32
|
+
def carrier_service_name
|
|
33
|
+
DiscoApp.configuration.app_name
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def callback_url
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# Return a list of currently registered carrier service names.
|
|
43
|
+
def current_carrier_service_names
|
|
44
|
+
current_carrier_services.map(&:name)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Return a list of currently registered carrier services.
|
|
48
|
+
def current_carrier_services
|
|
49
|
+
@current_carrier_service ||= ShopifyAPI::CarrierService.find(:all)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module DiscoApp::Concerns::SynchroniseWebhooksJob
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
# Ensure the webhooks registered with our shop are the same as those listed
|
|
5
|
+
# in our application configuration.
|
|
6
|
+
def perform(shopify_domain)
|
|
7
|
+
# Get the full list of expected webhook topics.
|
|
8
|
+
expected_topics = [:'app/uninstalled', :'shop/update'] + topics
|
|
9
|
+
|
|
10
|
+
# Registered any webhooks that haven't been registered yet.
|
|
11
|
+
(expected_topics - current_topics).each do |topic|
|
|
12
|
+
ShopifyAPI::Webhook.create(
|
|
13
|
+
topic: topic,
|
|
14
|
+
address: webhooks_url,
|
|
15
|
+
format: 'json'
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Remove any extraneous topics.
|
|
20
|
+
current_webhooks.each do |webhook|
|
|
21
|
+
unless expected_topics.include?(webhook.topic.to_sym)
|
|
22
|
+
ShopifyAPI::Webhook.delete(webhook.id)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Ensure webhook addresses are current.
|
|
27
|
+
current_webhooks.each do |webhook|
|
|
28
|
+
unless webhook.address == webhooks_url
|
|
29
|
+
webhook.address = webhooks_url
|
|
30
|
+
webhook.save
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
protected
|
|
36
|
+
|
|
37
|
+
# Return a list of additional webhook topics to listen for. This method
|
|
38
|
+
# can be overridden in the application to provide a list of app-specific
|
|
39
|
+
# webhooks that should be created during synchronisation.
|
|
40
|
+
def topics
|
|
41
|
+
[]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Return a list of currently registered topics.
|
|
47
|
+
def current_topics
|
|
48
|
+
current_webhooks.map(&:topic).map(&:to_sym)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Return a list of current registered webhooks.
|
|
52
|
+
def current_webhooks
|
|
53
|
+
@current_webhooks ||= ShopifyAPI::Webhook.find(:all)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Return the absolute URL to the webhooks endpoint.
|
|
57
|
+
def webhooks_url
|
|
58
|
+
DiscoApp::Engine.routes.url_helpers.webhooks_url
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# The base class for all jobs that should be performed in the context of a
|
|
2
|
+
# particular Shop's API session. The first argument to any job inheriting from
|
|
3
|
+
# this class must be the domain of the relevant store, so that the appropriate
|
|
4
|
+
# Shop model can be fetched and the temporary API session created.
|
|
5
|
+
class DiscoApp::ShopJob < ActiveJob::Base
|
|
6
|
+
|
|
7
|
+
queue_as :default
|
|
8
|
+
|
|
9
|
+
before_perform { |job| find_shop(job) }
|
|
10
|
+
before_enqueue { |job| find_shop(job) }
|
|
11
|
+
|
|
12
|
+
around_enqueue { |job, block| shop_context(job, block) }
|
|
13
|
+
around_perform { |job, block| shop_context(job, block) }
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def find_shop(job)
|
|
18
|
+
@shop ||= DiscoApp::Shop.find_by!(shopify_domain: job.arguments.first)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def shop_context(job, block)
|
|
22
|
+
@shop.temp {
|
|
23
|
+
block.call(job.arguments)
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class DiscoApp::ApplicationCharge < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
belongs_to :shop
|
|
4
|
+
belongs_to :subscription
|
|
5
|
+
|
|
6
|
+
enum status: [:pending, :accepted, :declined, :expired, :active]
|
|
7
|
+
|
|
8
|
+
scope :active, -> { where status: statuses[:active] }
|
|
9
|
+
|
|
10
|
+
def recurring?
|
|
11
|
+
false
|
|
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,26 @@
|
|
|
1
|
+
module DiscoApp::Concerns::Plan
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
|
|
6
|
+
has_many :subscriptions
|
|
7
|
+
has_many :shops, through: :subscriptions
|
|
8
|
+
has_many :plan_codes, dependent: :destroy
|
|
9
|
+
|
|
10
|
+
accepts_nested_attributes_for :plan_codes, allow_destroy: true
|
|
11
|
+
|
|
12
|
+
enum status: [:available, :unavailable]
|
|
13
|
+
enum plan_type: [:recurring, :one_time]
|
|
14
|
+
enum interval: [:month, :year]
|
|
15
|
+
|
|
16
|
+
scope :available, -> { where status: statuses[:available] }
|
|
17
|
+
|
|
18
|
+
validates_presence_of :name
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def has_trial?
|
|
23
|
+
trial_period_days.present? and trial_period_days > 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module DiscoApp::Concerns::Shop
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
include ShopifyApp::Shop
|
|
6
|
+
include ActionView::Helpers::DateHelper
|
|
7
|
+
|
|
8
|
+
# Define relationships to plans and subscriptions.
|
|
9
|
+
has_many :subscriptions
|
|
10
|
+
has_many :plans, through: :subscriptions
|
|
11
|
+
|
|
12
|
+
# Define relationship to sessions.
|
|
13
|
+
has_many :sessions, class_name: 'DiscoApp::Session', dependent: :destroy
|
|
14
|
+
|
|
15
|
+
# Define possible installation statuses as an enum.
|
|
16
|
+
enum status: [:never_installed, :awaiting_install, :installing, :installed, :awaiting_uninstall, :uninstalling, :uninstalled]
|
|
17
|
+
|
|
18
|
+
# Define some useful scopes.
|
|
19
|
+
scope :status, -> (status) { where status: status }
|
|
20
|
+
scope :installed, -> { where status: statuses[:installed] }
|
|
21
|
+
scope :has_active_shopify_plan, -> { where.not(plan_name: [:cancelled, :frozen, :fraudulent]) }
|
|
22
|
+
|
|
23
|
+
# Alias 'with_shopify_session' as 'temp', as per our existing conventions.
|
|
24
|
+
alias_method :temp, :with_shopify_session
|
|
25
|
+
|
|
26
|
+
# Return a hash of attributes that should be used to create a new charge for this shop.
|
|
27
|
+
# This method can be overridden by the inheriting Shop class in order to provide charges
|
|
28
|
+
# customised to a particular shop. Otherwise, the default settings configured in application.rb
|
|
29
|
+
# will be used.
|
|
30
|
+
def new_charge_attributes
|
|
31
|
+
{
|
|
32
|
+
type: Rails.configuration.x.shopify_charges_default_type,
|
|
33
|
+
name: DiscoApp.configuration.app_name,
|
|
34
|
+
price: Rails.configuration.x.shopify_charges_default_price,
|
|
35
|
+
trial_days: Rails.configuration.x.shopify_charges_default_trial_days,
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Convenience method to check if this shop has a current subscription.
|
|
40
|
+
def current_subscription?
|
|
41
|
+
current_subscription.present?
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Convenience method to get the current subscription for this shop, if any.
|
|
45
|
+
def current_subscription
|
|
46
|
+
subscriptions.current.first
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Convenience method to get the current plan for this shop, if any.
|
|
50
|
+
def current_plan
|
|
51
|
+
current_subscription&.plan
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Return the absolute URL to the shop's storefront.
|
|
55
|
+
def url
|
|
56
|
+
"#{protocol}://#{domain}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Return the protocol the shop's storefront uses. This should now always be
|
|
60
|
+
# https as all Shopify stores have SSL enabled.
|
|
61
|
+
def protocol
|
|
62
|
+
'https'
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Return the absolute URL to the shop's admin.
|
|
66
|
+
def admin_url
|
|
67
|
+
"https://#{shopify_domain}/admin"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def installed_duration
|
|
71
|
+
distance_of_time_in_words_to_now(created_at.time)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module DiscoApp::Concerns::Subscription
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
included do
|
|
5
|
+
|
|
6
|
+
belongs_to :shop
|
|
7
|
+
belongs_to :plan
|
|
8
|
+
belongs_to :plan_code
|
|
9
|
+
|
|
10
|
+
has_many :one_time_charges, class_name: 'DiscoApp::ApplicationCharge', dependent: :destroy
|
|
11
|
+
has_many :recurring_charges, class_name: 'DiscoApp::RecurringApplicationCharge', dependent: :destroy
|
|
12
|
+
|
|
13
|
+
enum status: [:trial, :active, :cancelled]
|
|
14
|
+
enum subscription_type: [:recurring, :one_time]
|
|
15
|
+
|
|
16
|
+
scope :current, -> { where status: [statuses[:trial], statuses[:active]] }
|
|
17
|
+
|
|
18
|
+
after_commit :cancel_charge
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Only require an active charge if the amount to be charged is > 0.
|
|
23
|
+
def requires_active_charge?
|
|
24
|
+
amount > 0
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Convenience method to check if this subscription has an active charge.
|
|
28
|
+
def active_charge?
|
|
29
|
+
active_charge.present?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Convenience method to get the active charge for this subscription.
|
|
33
|
+
def active_charge
|
|
34
|
+
charges.active.first
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Return the appropriate set of charges for this subscription's type.
|
|
38
|
+
def charges
|
|
39
|
+
recurring? ? recurring_charges : one_time_charges
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def charge_class
|
|
43
|
+
recurring? ? DiscoApp::RecurringApplicationCharge : DiscoApp::ApplicationCharge
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def shopify_charge_class
|
|
47
|
+
recurring? ? ShopifyAPI::RecurringApplicationCharge : ShopifyAPI::ApplicationCharge
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# If the amount or trial period for this subscription changes, clear any
|
|
53
|
+
# active charge, as the user will need to re-authorize the charge.
|
|
54
|
+
def cancel_charge
|
|
55
|
+
return if (previous_changes.keys & ['amount', 'trial_period_days']).empty?
|
|
56
|
+
return unless active_charge?
|
|
57
|
+
active_charge.cancelled!
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module DiscoApp::Concerns::Synchronises
|
|
2
|
+
extend ActiveSupport::Concern
|
|
3
|
+
|
|
4
|
+
class_methods do
|
|
5
|
+
|
|
6
|
+
def should_synchronise?(shop, data)
|
|
7
|
+
true
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def synchronise(shop, data)
|
|
11
|
+
data = data.with_indifferent_access
|
|
12
|
+
|
|
13
|
+
return unless should_synchronise?(shop, data)
|
|
14
|
+
|
|
15
|
+
instance = self.find_or_create_by!(id: data[:id]) do |instance|
|
|
16
|
+
instance.shop = shop
|
|
17
|
+
instance.data = data
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
instance.update(data: data)
|
|
21
|
+
|
|
22
|
+
instance
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def should_synchronise_deletion?(shop, data)
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def synchronise_deletion(shop, data)
|
|
30
|
+
data = data.with_indifferent_access
|
|
31
|
+
|
|
32
|
+
return unless should_synchronise_deletion?(shop, data)
|
|
33
|
+
|
|
34
|
+
self.destroy_all(shop: shop, id: data[:id])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
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,82 @@
|
|
|
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, :email, :country_name
|
|
9
|
+
attributes :currency, :plan_display_name, :created_at
|
|
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 :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 :status
|
|
30
|
+
return records.where(status: value.map { |v| DiscoApp::Shop.statuses[v.to_sym] } )
|
|
31
|
+
else
|
|
32
|
+
return super(records, filter, value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Don't allow the update of any fields via the API.
|
|
37
|
+
def self.updatable_fields(context)
|
|
38
|
+
[]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Don't allow the creation of any fields via the API.
|
|
42
|
+
def self.creatable_fields(context)
|
|
43
|
+
[]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def current_subscription_id
|
|
47
|
+
if @model.current_subscription?
|
|
48
|
+
@model.current_subscription.id
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def current_subscription_display_amount
|
|
53
|
+
if @model.current_subscription?
|
|
54
|
+
@model.current_subscription.amount
|
|
55
|
+
else
|
|
56
|
+
'-'
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def current_subscription_display_plan
|
|
61
|
+
if @model.current_subscription?
|
|
62
|
+
@model.current_plan.name
|
|
63
|
+
else
|
|
64
|
+
'None'
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def current_subscription_display_plan_code
|
|
69
|
+
@model.current_subscription&.plan_code&.code
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def current_subscription_source
|
|
73
|
+
if @model.current_subscription?
|
|
74
|
+
@model.current_subscription.source || '-'
|
|
75
|
+
else
|
|
76
|
+
'-'
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
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
|