spree_cm_commissioner 2.9.8 → 2.10.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +0 -5
  3. data/Gemfile.lock +1 -1
  4. data/app/controllers/concerns/spree_cm_commissioner/content_cachable.rb +1 -0
  5. data/app/controllers/spree/admin/users_controller_decorator.rb +36 -0
  6. data/app/controllers/spree/api/v2/base_controller_decorator.rb +41 -0
  7. data/app/controllers/spree/api/v2/storefront/payment_methods_controller.rb +40 -0
  8. data/app/controllers/spree/api/v2/storefront/telegram_oauth_tokens_controller.rb +8 -22
  9. data/app/controllers/spree/api/v2/tenant/payment_methods_controller.rb +54 -0
  10. data/app/controllers/spree/api/v2/tenant/telegram_oauth_tokens_controller.rb +48 -0
  11. data/app/finders/spree_cm_commissioner/payment_methods/find.rb +59 -0
  12. data/app/interactors/spree_cm_commissioner/base_interactor.rb +10 -0
  13. data/app/interactors/spree_cm_commissioner/user_fb_token_authenticator.rb +1 -1
  14. data/app/interactors/spree_cm_commissioner/user_id_token_authenticator.rb +1 -1
  15. data/app/interactors/spree_cm_commissioner/user_password_authenticator.rb +1 -1
  16. data/app/interactors/spree_cm_commissioner/user_telegram_oauth_authenticator.rb +4 -4
  17. data/app/interactors/spree_cm_commissioner/user_telegram_web_app_authenticator.rb +4 -0
  18. data/app/interactors/spree_cm_commissioner/user_vattanac_bank_web_app_authenticator.rb +4 -0
  19. data/app/interactors/spree_cm_commissioner/waiting_guests_caller.rb +94 -67
  20. data/app/interactors/spree_cm_commissioner/waiting_room_latest_system_metadata_puller.rb +20 -6
  21. data/app/interactors/spree_cm_commissioner/waiting_room_session_creator.rb +83 -20
  22. data/app/jobs/spree_cm_commissioner/waiting_guests_caller_job.rb +0 -4
  23. data/app/models/spree_cm_commissioner/guest.rb +5 -3
  24. data/app/models/spree_cm_commissioner/user_decorator.rb +19 -1
  25. data/app/models/spree_cm_commissioner/waiting_room_session.rb +9 -2
  26. data/app/overrides/spree/admin/stores/_form/telegram_auth.html.erb.deface +1 -1
  27. data/app/overrides/spree/admin/users/edit/account_status.html.erb.deface +42 -0
  28. data/app/overrides/spree/admin/users/index/body.html.erb.deface +25 -0
  29. data/app/overrides/spree/admin/users/index/ransack.html.erb.deface +12 -0
  30. data/app/serializers/spree/v2/storefront/user_serializer_decorator.rb +2 -1
  31. data/app/serializers/spree/v2/tenant/user_serializer.rb +1 -1
  32. data/app/serializers/spree/v2/tenant/waiting_room_session_serializer.rb +1 -1
  33. data/app/serializers/spree_cm_commissioner/v2/storefront/waiting_room_session_serializer.rb +1 -1
  34. data/app/services/spree_cm_commissioner/api_caches/invalidate.rb +29 -14
  35. data/app/services/spree_cm_commissioner/draft_order/create.rb +14 -0
  36. data/app/services/spree_cm_commissioner/telegram/check_id_token.rb +33 -0
  37. data/app/services/spree_cm_commissioner/telegram/exchange_oauth_token.rb +81 -0
  38. data/app/services/spree_cm_commissioner/telegram/verify_oauth_id_token.rb +98 -0
  39. data/app/services/spree_cm_commissioner/ticket_transfers/initiate.rb +1 -1
  40. data/app/services/spree_cm_commissioner/transit_order/create.rb +15 -1
  41. data/app/services/spree_cm_commissioner/users/block.rb +29 -0
  42. data/app/services/spree_cm_commissioner/users/unblock.rb +18 -0
  43. data/app/services/spree_cm_commissioner/waiting_room/publish_lobby_path.rb +7 -11
  44. data/app/services/spree_cm_commissioner/waiting_room/stamp_queue_positions.rb +94 -95
  45. data/app/views/spree/admin/tenants/form/_telegram_auth.html.erb +1 -1
  46. data/config/locales/en.yml +16 -1
  47. data/config/locales/km.yml +1 -1
  48. data/config/routes.rb +8 -0
  49. data/db/migrate/20260723083947_add_max_expired_at_to_cm_waiting_room_sessions.rb +9 -0
  50. data/db/migrate/20260805000000_add_blocked_at_to_spree_users.rb +6 -0
  51. data/lib/spree_cm_commissioner/version.rb +1 -1
  52. metadata +15 -6
  53. data/app/interactors/spree_cm_commissioner/telegram_oauth_id_token_provider.rb +0 -99
  54. data/app/interactors/spree_cm_commissioner/telegram_oauth_token_exchanger.rb +0 -71
  55. data/app/interactors/spree_cm_commissioner/waiting_room_session_firebase_logger.rb +0 -30
  56. data/app/jobs/spree_cm_commissioner/waiting_room_session_firebase_logger_job.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8eb692e530b34906dfca4d757e6382b9b643e18760fd38ba31507ceb24bb687b
4
- data.tar.gz: 782a651003a7817fb539def0a19697d434978c51f7f48c0f2184b48fe87c1260
3
+ metadata.gz: 6d59a44e1e25ec19ab8c634622f91006ea28bec265ea877dad8565cffa54bad3
4
+ data.tar.gz: 43415e8984f459f0387e110707963f4685c92251f589a2301342f82783c4e714
5
5
  SHA512:
6
- metadata.gz: '090263c66bf5f79ea0edfcf6602185b0d80120f33308b2d0193cacf3bb2ebc5036543f1e1000b4e823a243a31cd3dc2eaf3e4b8772c6717d16891554b3da96a0'
7
- data.tar.gz: 3540311c234819a354e154a532b7745e91ebf1faf7b15bcc40a5a6def80ea069d3820759b328decb2328f6ad60190a12686d5c23c0951c864ee89215160fd4a1
6
+ metadata.gz: 1fbfc67d4bc18eb071d854b643ef3bfbaf1312b75f5c241020d58b09855ee234a58064ce08a104ee856b48dec25ce8547ad9c442cbd2648c83ad350c04fec3e1
7
+ data.tar.gz: 53317e748ac053ed6b60ec7e9accdd915266aa6b3e547692865191868d6051693dcad05dee73bed5e196b027fc27436e72fba2c62e05b3b33f806a7558618cf9
data/.env.example CHANGED
@@ -22,11 +22,6 @@ WAITING_ROOM_MIN_SESSIONS_COUNT=5
22
22
  WAITING_ROOM_DISABLED=no
23
23
  WAITING_ROOM_POSITION_STAMP_LIMIT=1000
24
24
  WAITING_ROOM_FIRESTORE_BATCH_SIZE=500
25
- WAITING_ROOM_MIN_WAIT_TO_ENTER_SECONDS=600 # Waiting Room step floor (10 min)
26
- WAITING_ROOM_MIN_QUEUE_TO_ENTER_SECONDS=300 # Queue step floor (5 min)
27
- WAITING_ROOM_MAX_WAIT_TO_DISPLAY_SECONDS=3600 # above this the ETA is hidden (1 h)
28
- WAITING_ROOM_CALLER_INTERVAL_SECONDS=60 # how often the caller runs; match the waiting_guests_caller cron
29
- WAITING_ROOM_MAX_ETA_BATCH_SIZE=50 # cap on batch size assumed when estimating the ETA
30
25
 
31
26
  # Vattanac Bank
32
27
  VATTANAC_AES_SECRET_KEY= ""
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.9.8)
37
+ spree_cm_commissioner (2.10.0)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -33,6 +33,7 @@ module SpreeCmCommissioner
33
33
  ShowContestantsController
34
34
  VotePackagesController
35
35
  ShowEliminationSessionsController
36
+ PaymentMethodsController
36
37
  ].freeze
37
38
 
38
39
  # Priority 3: Moderate Freshness (30 minutes)
@@ -26,8 +26,44 @@ module Spree
26
26
  super
27
27
  end
28
28
 
29
+ def block
30
+ return redirect_back_to_user(I18n.t('spree_user.cannot_block_yourself'), :error) if own_account?
31
+ return redirect_back_to_user(I18n.t('spree_user.cannot_block_super_admin'), :error) if @user.super_admin?
32
+
33
+ result = SpreeCmCommissioner::Users::Block.call(user: @user, reason: params[:blocked_reason])
34
+
35
+ if result.success?
36
+ redirect_back_to_user(I18n.t('spree_user.successfully_blocked'), :success)
37
+ else
38
+ redirect_back_to_user(result.error.to_s, :error)
39
+ end
40
+ end
41
+
42
+ def unblock
43
+ result = SpreeCmCommissioner::Users::Unblock.call(user: @user)
44
+
45
+ if result.success?
46
+ redirect_back_to_user(I18n.t('spree_user.successfully_unblocked'), :success)
47
+ else
48
+ redirect_back_to_user(result.error.to_s, :error)
49
+ end
50
+ end
51
+
29
52
  private
30
53
 
54
+ # An admin who blocks themselves would be locked out with no way back in through the UI.
55
+ def own_account?
56
+ @user.id == try_spree_current_user&.id
57
+ end
58
+
59
+ # Blocking is triggered from two places — the row action on the users list and the account
60
+ # status card on the edit page — so return to whichever one the admin came from instead of
61
+ # always landing them on the edit page.
62
+ def redirect_back_to_user(message, type)
63
+ flash[type] = message
64
+ redirect_back fallback_location: spree.edit_admin_user_path(@user)
65
+ end
66
+
31
67
  # Role types the user form doesn't render (assigned in organizer dashboards), e.g. check-in groups and preview.
32
68
  NON_FORM_ROLE_TYPES = %i[check_in preview].freeze
33
69
 
@@ -0,0 +1,41 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ # Single enforcement point for blocked users on the API. Every authenticated v2 endpoint -
5
+ # storefront, tenant, organizer, platform - resolves its user through
6
+ # Spree::Api::V2::BaseController#spree_current_user, so guarding here covers them all,
7
+ # including the controllers that inherit BaseController directly rather than
8
+ # ResourceController.
9
+ #
10
+ # 403 rather than 401: the token is valid and the caller is authenticated, they are simply not
11
+ # allowed anymore. A 401 would tell clients to refresh the token, which would loop.
12
+ module BaseControllerDecorator
13
+ # Machine-readable, not translated, so clients can branch on it the same way they already
14
+ # branch on the authenticators' `account_temporarily_deleted`.
15
+ BLOCKED_ERROR = 'account_blocked'.freeze
16
+
17
+ def self.prepended(base)
18
+ base.before_action :reject_blocked_user
19
+ end
20
+
21
+ private
22
+
23
+ def reject_blocked_user
24
+ return unless blocked_token_owner?
25
+
26
+ render_error_payload(BLOCKED_ERROR, 403)
27
+ end
28
+
29
+ # Deliberately reads the owner off the token instead of calling spree_current_user: that
30
+ # helper runs doorkeeper_authorize!, which would start rejecting invalid tokens on public
31
+ # endpoints that today ignore them. No token (anonymous request) costs nothing.
32
+ def blocked_token_owner?
33
+ owner = doorkeeper_token&.resource_owner
34
+ owner.respond_to?(:blocked?) && owner.blocked?
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ Spree::Api::V2::BaseController.prepend(Spree::Api::V2::BaseControllerDecorator)
@@ -0,0 +1,40 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ # Order & user independent catalog of the payment methods a vendor accepts.
6
+ # Takes vendor_id as a required query param. See
7
+ # Spree::Api::V2::Tenant::PaymentMethodsController for the caching caveat
8
+ # that comes with that, and for what this deliberately leaves out.
9
+ class PaymentMethodsController < Spree::Api::V2::ResourceController
10
+ # override
11
+ def index
12
+ render_serialized_payload do
13
+ collection_serializer.new(collection).serializable_hash
14
+ end
15
+ end
16
+
17
+ # override
18
+ def collection
19
+ @collection ||= SpreeCmCommissioner::PaymentMethods::Find.new(
20
+ vendor: vendor,
21
+ store: current_store,
22
+ type: params[:type]
23
+ ).execute
24
+ end
25
+
26
+ # override
27
+ def collection_serializer
28
+ ::Spree::V2::Storefront::PaymentMethodSerializer
29
+ end
30
+
31
+ private
32
+
33
+ def vendor
34
+ @vendor ||= ::Spree::Vendor.active.find(params[:vendor_id])
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -11,13 +11,13 @@ module Spree
11
11
  def create
12
12
  return render_error_payload(credential_mismatch_message) if credential_mismatch_message
13
13
 
14
- result = SpreeCmCommissioner::TelegramOauthTokenExchanger.call(
14
+ result = SpreeCmCommissioner::Telegram::ExchangeOauthToken.call(
15
15
  exchange_params.merge(client_secret: resolved_client_secret)
16
16
  )
17
17
 
18
- return render_error_payload(result.message) unless result.success?
18
+ return render_error_payload(result.error) unless result.success?
19
19
 
20
- render json: result.body, status: result.status, content_type: 'application/json'
20
+ render json: result.value[:body], status: result.value[:status], content_type: 'application/json'
21
21
  end
22
22
 
23
23
  private
@@ -26,33 +26,19 @@ module Spree
26
26
  params.permit(:client_id, :code, :redirect_uri, :code_verifier).to_h.symbolize_keys
27
27
  end
28
28
 
29
- # `tenant_id` scopes which tenant's bot this login belongs to; with none
30
- # given, the default store's bot is used. Distinguished from
31
- # telegram_oauth_client_secret_not_configured (raised by the exchanger
32
- # when the owner has no secret set) so a caller can tell "wrong
33
- # tenant/client_id" apart from "correct tenant, nothing configured yet".
34
29
  def credential_mismatch_message
35
- return 'telegram_oauth_tenant_not_found' if params[:tenant_id].present? && credential_owner.nil?
36
- return nil if credential_owner.nil? || credential_owner.preferred_telegram_oauth_client_id.blank?
37
- return 'telegram_oauth_client_id_mismatch' unless client_id_matches_owner?
30
+ return nil if current_store.preferred_telegram_oauth_client_id.blank?
31
+ return 'telegram_oauth_client_id_mismatch' unless client_id_matches_store?
38
32
 
39
33
  nil
40
34
  end
41
35
 
42
- def client_id_matches_owner?
43
- credential_owner.preferred_telegram_oauth_client_id == exchange_params[:client_id]
36
+ def client_id_matches_store?
37
+ current_store.preferred_telegram_oauth_client_id == exchange_params[:client_id]
44
38
  end
45
39
 
46
40
  def resolved_client_secret
47
- credential_owner&.preferred_telegram_oauth_client_secret.presence
48
- end
49
-
50
- def credential_owner
51
- @credential_owner ||= if params[:tenant_id].present?
52
- SpreeCmCommissioner::Tenant.find_by(id: params[:tenant_id])
53
- else
54
- Spree::Store.default
55
- end
41
+ current_store.preferred_telegram_oauth_client_secret.presence
56
42
  end
57
43
  end
58
44
  end
@@ -0,0 +1,54 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ # Order & user independent catalog of the payment methods a vendor accepts.
6
+ #
7
+ # vendor_id is a required query param. A vendor id is globally unique and
8
+ # belongs to exactly one tenant, so it is what identifies tenant, vendor and
9
+ # store here — the tenant itself comes from the Doorkeeper token, not the URL.
10
+ #
11
+ # IMPORTANT for caching (see SpreeCmCommissioner::ContentCachable): the CDN
12
+ # must include vendor_id in its cache key. If query strings are stripped,
13
+ # every vendor collapses into one cache entry and the wrong payment methods
14
+ # are served. Moving to /vendors/:vendor_id/payment_methods would remove that
15
+ # dependency — tracked as a follow-up.
16
+ #
17
+ # For the per-order truth (store credit, early adopter, payout filtering)
18
+ # use CartPaymentMethodGroupsController instead.
19
+ class PaymentMethodsController < BaseController
20
+ # override
21
+ def index
22
+ render_serialized_payload do
23
+ collection_serializer.new(collection).serializable_hash
24
+ end
25
+ end
26
+
27
+ # override
28
+ def collection
29
+ @collection ||= SpreeCmCommissioner::PaymentMethods::Find.new(
30
+ vendor: vendor,
31
+ store: current_store,
32
+ type: params[:type]
33
+ ).execute
34
+ end
35
+
36
+ # override
37
+ def collection_serializer
38
+ Spree::V2::Tenant::PaymentMethodSerializer
39
+ end
40
+
41
+ private
42
+
43
+ # Scoped to the token's tenant, so a token from another tenant gets a 404
44
+ # rather than a cache hit on someone else's vendor.
45
+ def vendor
46
+ @vendor ||= MultiTenant.with(@tenant) do
47
+ ::Spree::Vendor.active.find(params[:vendor_id])
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,48 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Tenant
5
+ # Identical PKCE code->id_token relay as Storefront::TelegramOauthTokensController,
6
+ # but scoped to the tenant behind the Doorkeeper app access token (see
7
+ # Tenant::BaseController#require_tenant), exposed under /api/v2/tenant so
8
+ # tenant-scoped clients can call it alongside /api/v2/tenant/token
9
+ class TelegramOauthTokensController < BaseController
10
+ def create
11
+ return render_error_payload(credential_mismatch_message) if credential_mismatch_message
12
+
13
+ result = SpreeCmCommissioner::Telegram::ExchangeOauthToken.call(
14
+ exchange_params.merge(client_secret: resolved_client_secret)
15
+ )
16
+
17
+ return render_error_payload(result.error) unless result.success?
18
+
19
+ render json: result.value[:body], status: result.value[:status], content_type: 'application/json'
20
+ end
21
+
22
+ private
23
+
24
+ def exchange_params
25
+ params.permit(:client_id, :code, :redirect_uri, :code_verifier).to_h.symbolize_keys
26
+ end
27
+
28
+ # @tenant is already resolved from the Doorkeeper app (see BaseController#require_tenant),
29
+ # so unlike the storefront endpoint there's no separate tenant_id param to validate.
30
+ def credential_mismatch_message
31
+ return nil if @tenant.preferred_telegram_oauth_client_id.blank?
32
+ return 'telegram_oauth_client_id_mismatch' unless client_id_matches_tenant?
33
+
34
+ nil
35
+ end
36
+
37
+ def client_id_matches_tenant?
38
+ @tenant.preferred_telegram_oauth_client_id == exchange_params[:client_id]
39
+ end
40
+
41
+ def resolved_client_secret
42
+ @tenant.preferred_telegram_oauth_client_secret.presence
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,59 @@
1
+ module SpreeCmCommissioner
2
+ module PaymentMethods
3
+ # Order & user independent lookup of the payment methods a vendor accepts.
4
+ #
5
+ # Mirrors the scope selection of Vpago::OrderDecorator#available_payment_methods,
6
+ # minus everything that depends on the order or the current user, so the result
7
+ # is safe to cache on the vendor id alone:
8
+ #
9
+ # - available_for_order? (only Spree::PaymentMethod::StoreCredit varies, on user balance)
10
+ # - available_on_frontend_for_early_adopter (user attribute)
11
+ # - support_payout? (order line items)
12
+ # - preferred_payment_method_id (user, affects sort only)
13
+ #
14
+ # Callers needing the per-order truth must keep using
15
+ # Spree::Api::V2::*::CartPaymentMethodGroupsController.
16
+ class Find
17
+ attr_reader :vendor, :store, :type
18
+
19
+ def initialize(vendor:, store: nil, type: nil)
20
+ @vendor = vendor
21
+ @store = store
22
+ @type = type
23
+ end
24
+
25
+ def execute
26
+ return mini_app_scope if type.present?
27
+
28
+ scope.reject(&:mini_app?)
29
+ end
30
+
31
+ private
32
+
33
+ def scope
34
+ @scope ||= source.available_on_front_end.order(:position).to_a
35
+ end
36
+
37
+ # Vendors without their own payment methods fall back to the store's,
38
+ # same as Vpago::OrderDecorator#available_payment_methods.
39
+ def source
40
+ vendor_payment_methods.exists? ? vendor_payment_methods : store_payment_methods
41
+ end
42
+
43
+ def vendor_payment_methods
44
+ @vendor_payment_methods ||= vendor&.payment_methods || Spree::PaymentMethod.none
45
+ end
46
+
47
+ def store_payment_methods
48
+ (store || Spree::Store.default).payment_methods
49
+ end
50
+
51
+ def mini_app_scope
52
+ mapped_type = SpreeCmCommissioner::PaymentMethodTypeMapper.call(type)
53
+ return [] if mapped_type.blank?
54
+
55
+ scope.select { |payment_method| payment_method.mini_app? && payment_method.type == mapped_type }
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,5 +1,15 @@
1
1
  module SpreeCmCommissioner
2
2
  class BaseInteractor
3
3
  include Interactor
4
+
5
+ private
6
+
7
+ # Shared tail guard for the authenticators: an account can be resolved and hold a valid
8
+ # credential and still not be allowed to authenticate. Both messages are error_description values
9
+ # the mobile/web clients branch on, so they stay raw strings rather than translations.
10
+ def reject_unauthenticatable_user!(user)
11
+ context.fail!(message: 'account_temporarily_deleted') if user.soft_deleted?
12
+ context.fail!(message: 'account_blocked') if user.blocked?
13
+ end
4
14
  end
5
15
  end
@@ -12,7 +12,7 @@ module SpreeCmCommissioner
12
12
  checker.user
13
13
  end
14
14
 
15
- context.fail!(message: 'account_temporarily_deleted') if context.user.soft_deleted?
15
+ reject_unauthenticatable_user!(context.user)
16
16
  end
17
17
 
18
18
  private
@@ -7,7 +7,7 @@ module SpreeCmCommissioner
7
7
 
8
8
  update_user_email if context.user.email.blank?
9
9
 
10
- context.fail!(message: 'account_temporarily_deleted') if context.user.soft_deleted?
10
+ reject_unauthenticatable_user!(context.user)
11
11
  end
12
12
 
13
13
  def register_user
@@ -16,7 +16,7 @@ module SpreeCmCommissioner
16
16
  end
17
17
 
18
18
  context.fail!(message: I18n.t('authenticator.incorrect_password')) unless validate_password(context.user)
19
- context.fail!(message: 'account_temporarily_deleted') if context.user.soft_deleted?
19
+ reject_unauthenticatable_user!(context.user)
20
20
  end
21
21
 
22
22
  private
@@ -13,16 +13,16 @@ module SpreeCmCommissioner
13
13
  provider = verified_provider
14
14
  context.user = find_or_register_user(provider)
15
15
 
16
- context.fail!(message: 'account_temporarily_deleted') if context.user.soft_deleted?
16
+ reject_unauthenticatable_user!(context.user)
17
17
  end
18
18
 
19
19
  private
20
20
 
21
21
  def verified_provider
22
- result = SpreeCmCommissioner::TelegramOauthIdTokenProvider.call(id_token: id_token, client_id: telegram_client_id)
23
- context.fail!(message: result.message) unless result.success?
22
+ result = SpreeCmCommissioner::Telegram::VerifyOauthIdToken.call(id_token: id_token, client_id: telegram_client_id)
23
+ context.fail!(message: result.error.to_s) unless result.success?
24
24
 
25
- result.provider
25
+ result.value[:provider]
26
26
  end
27
27
 
28
28
  # Each tenant runs its own Telegram bot, so the audience an id_token must
@@ -8,6 +8,10 @@ module SpreeCmCommissioner
8
8
 
9
9
  context.telegram_user = ActiveSupport::JSON.decode(checker_context.decoded_telegram_init_data['user'])
10
10
  context.user = find_or_create_telegram_user
11
+
12
+ # Block check only — this flow has never rejected soft-deleted accounts and this change is not
13
+ # the place to start.
14
+ context.fail!(message: 'account_blocked') if context.user.blocked?
11
15
  end
12
16
 
13
17
  def find_or_create_telegram_user
@@ -8,6 +8,10 @@ module SpreeCmCommissioner
8
8
 
9
9
  verify_token!
10
10
 
11
+ # Block check only — this flow has never rejected soft-deleted accounts and this change is not
12
+ # the place to start.
13
+ return context.fail!(message: 'account_blocked') if user.blocked?
14
+
11
15
  context.user = user
12
16
  rescue JWT::DecodeError => e
13
17
  context.fail!(message: "Decode error: #{e.message}")