spree_cm_commissioner 2.9.1.pre.pre2 → 2.9.1.pre.pre4

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 (22) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/controllers/spree/api/v2/tenant/user_account_linkages_controller.rb +1 -2
  4. data/app/controllers/spree_cm_commissioner/api/v2/storefront/checkout_controller_decorator.rb +14 -1
  5. data/app/interactors/spree_cm_commissioner/account_linkage.rb +1 -13
  6. data/app/interactors/spree_cm_commissioner/user_fb_token_authenticator.rb +0 -1
  7. data/app/models/spree_cm_commissioner/current.rb +8 -0
  8. data/app/models/spree_cm_commissioner/pricing_model_route.rb +8 -0
  9. data/app/models/spree_cm_commissioner/trip.rb +3 -0
  10. data/app/request_schemas/spree_cm_commissioner/user_account_linkage_request_schema.rb +2 -4
  11. data/app/services/spree_cm_commissioner/inventory_holds/validate_limits.rb +1 -1
  12. data/app/services/spree_cm_commissioner/pricing_models/create_with_rule_groups.rb +9 -3
  13. data/app/services/spree_cm_commissioner/pricing_models/preview.rb +49 -6
  14. data/app/services/spree_cm_commissioner/pricing_models/update_with_rule_groups.rb +10 -7
  15. data/app/services/spree_cm_commissioner/user_authenticator.rb +0 -5
  16. data/db/migrate/20260630000001_add_trip_to_cm_pricing_model_routes.rb +7 -0
  17. data/lib/spree_cm_commissioner/test_helper/factories/pricing_model_trip_factory.rb +11 -0
  18. data/lib/spree_cm_commissioner/version.rb +1 -1
  19. metadata +5 -5
  20. data/app/interactors/spree_cm_commissioner/fetch_facebook_user_data_from_jwt.rb +0 -90
  21. data/app/interactors/spree_cm_commissioner/user_fb_jwt_authenticator.rb +0 -37
  22. data/app/interactors/spree_cm_commissioner/user_fb_jwt_checker.rb +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44f0714e8ec1fab5074038112f26db55308feffc75b103ccda9e9d5d5d38409a
4
- data.tar.gz: 7f963001ae3a8de9f3f829d03eb83f6a2d310cf5a28ddafc477473e1b2fa9678
3
+ metadata.gz: ebefceb78297eb71b4dcb417b47de1c9440786f17942f341aa5cff394558b2aa
4
+ data.tar.gz: 4fae27019658db8352dd7001445b331355d7b20f321b079c211e4e87889789eb
5
5
  SHA512:
6
- metadata.gz: 2afbdbb418fbdca1c4ebfb9c6327a5fb0394663b87881e3ce56950c1aca7e4a1467708c6249ad2bab098564349d432187866966b6704e662ca87b34966466760
7
- data.tar.gz: 569d3213e604558bf4d17134aa51fd8cb2e854780f141187a6bff0af167070fe4c53b5f9eda404f4c38c47659aa79fed702ad1f801dc61d9d62d9ceb775e4480
6
+ metadata.gz: ed1b54c40e5288bc4e04e467c978b531f80b49985e387e4ed5a65908dd9b8152fe024fd4dcfdadba2f279b385fc8250588b3cd674125abf4a4ddd4d0306560b1
7
+ data.tar.gz: 283fa4cceb6e50ef28eba6e0eb4ecbdba5ca2984a377009934d057606840ce101db088d4f766c8d37b623bff61fe5b030867c99540ea00e33ed3067c20a4c243
data/Gemfile.lock CHANGED
@@ -34,7 +34,7 @@ GIT
34
34
  PATH
35
35
  remote: .
36
36
  specs:
37
- spree_cm_commissioner (2.9.1.pre.pre2)
37
+ spree_cm_commissioner (2.9.1.pre.pre4)
38
38
  activerecord-multi-tenant
39
39
  activerecord_json_validator (~> 2.1, >= 2.1.3)
40
40
  aws-sdk-cloudfront
@@ -15,8 +15,7 @@ module Spree
15
15
  context = SpreeCmCommissioner::AccountLinkage.call(
16
16
  user: spree_current_user,
17
17
  id_token: params[:id_token],
18
- fb_access_token: params[:fb_access_token],
19
- fb_jwt: params[:fb_jwt]
18
+ fb_access_token: params[:fb_access_token]
20
19
  )
21
20
 
22
21
  if context.success?
@@ -7,9 +7,22 @@ module SpreeCmCommissioner
7
7
  base.include SpreeCmCommissioner::OrderConcern
8
8
  base.include SpreeCmCommissioner::TurnstileProtectable
9
9
 
10
- base.before_action -> { verify_turnstile! }, only: %i[advance create_payment]
10
+ base.before_action :set_load_test_bypass_current
11
+ base.before_action -> { verify_turnstile! }, only: %i[create_payment]
11
12
  end
12
13
 
14
+ private
15
+
16
+ def set_load_test_bypass_current
17
+ key = ENV.fetch('LOAD_TEST_BYPASS_KEY', nil)
18
+ return if key.blank?
19
+
20
+ provided = request.headers.fetch('X-Request-Verify', nil).to_s
21
+ SpreeCmCommissioner::Current.load_test_bypass = ActiveSupport::SecurityUtils.secure_compare(provided, key)
22
+ end
23
+
24
+ public
25
+
13
26
  # Override Spree's default behavior:
14
27
  # By default, Spree raises an error if `complete` is called on a completed or confirmed order.
15
28
  # We want to allow this call to succeed, so the API can be used both to:
@@ -1,7 +1,7 @@
1
1
  module SpreeCmCommissioner
2
2
  class AccountLinkage
3
3
  include Interactor
4
- delegate :id_token, :fb_access_token, :fb_jwt, :user, to: :context
4
+ delegate :id_token, :fb_access_token, :user, to: :context
5
5
 
6
6
  # id_token:, user:
7
7
  def call
@@ -10,8 +10,6 @@ module SpreeCmCommissioner
10
10
  handle_id_token
11
11
  elsif fb_access_token.present?
12
12
  handle_fb_access_token
13
- elsif fb_jwt.present?
14
- handle_fb_jwt
15
13
  else
16
14
  context.fail!(message: 'missing_social_token')
17
15
  end
@@ -39,16 +37,6 @@ module SpreeCmCommissioner
39
37
  end
40
38
  end
41
39
 
42
- def handle_fb_jwt
43
- facebook_context = FetchFacebookUserDataFromJwt.call(fb_jwt: fb_jwt)
44
-
45
- if facebook_context.success?
46
- build_user_identify_provider(facebook_context.provider)
47
- else
48
- context.fail!(message: facebook_context.message)
49
- end
50
- end
51
-
52
40
  # {
53
41
  # provider_name: provider_name,
54
42
  # email: email,
@@ -5,7 +5,6 @@ module SpreeCmCommissioner
5
5
  # :fb_access_token, :tenant_id (optional)
6
6
  def call
7
7
  context.fail!(message: 'fb_access_token_missing') unless fb_access_token
8
- context.fail!(message: checker.message) if checker.failure?
9
8
 
10
9
  context.user = if checker.user.nil?
11
10
  register_user
@@ -0,0 +1,8 @@
1
+ module SpreeCmCommissioner
2
+ class Current < ActiveSupport::CurrentAttributes
3
+ # Set to true for the duration of a load-test request so that rate-limiting
4
+ # services deeper in the call chain (e.g. InventoryHolds::ValidateLimits) can
5
+ # skip their checks without needing the request object threaded through them.
6
+ attribute :load_test_bypass
7
+ end
8
+ end
@@ -1,10 +1,18 @@
1
1
  module SpreeCmCommissioner
2
+ # A pricing-model assignment. A row is either:
3
+ # - route-scoped: trip_id NULL, keyed by route + O-D (unchanged legacy behaviour), or
4
+ # - trip-scoped: trip_id set, keyed by that specific trip + its own O-D (overrides route pricing
5
+ # for that trip only).
2
6
  class PricingModelRoute < Base
3
7
  belongs_to :route, class_name: 'SpreeCmCommissioner::Route'
8
+ belongs_to :trip, class_name: 'SpreeCmCommissioner::Trip', optional: true
4
9
  belongs_to :pricing_model, class_name: 'SpreeCmCommissioner::PricingModel'
5
10
  belongs_to :origin_place, class_name: 'SpreeCmCommissioner::Place'
6
11
  belongs_to :destination_place, class_name: 'SpreeCmCommissioner::Place'
7
12
 
13
+ scope :route_scoped, -> { where(trip_id: nil) }
14
+ scope :trip_scoped, -> { where.not(trip_id: nil) }
15
+
8
16
  scope :for_origin_destination, lambda { |origin_place_id, destination_place_id|
9
17
  where(origin_place_id: origin_place_id, destination_place_id: destination_place_id)
10
18
  }
@@ -52,6 +52,9 @@ module SpreeCmCommissioner
52
52
  has_many :trip_blazer_queries, as: :queryable, class_name: 'SpreeCmCommissioner::BlazerQueryable'
53
53
  has_many :blazer_queries, through: :trip_blazer_queries, source: :blazer_query, class_name: 'Blazer::Query'
54
54
 
55
+ has_many :pricing_model_routes, class_name: 'SpreeCmCommissioner::PricingModelRoute', dependent: :destroy
56
+ has_many :pricing_models, through: :pricing_model_routes, class_name: 'SpreeCmCommissioner::PricingModel'
57
+
55
58
  has_many :trip_stops, class_name: 'SpreeCmCommissioner::TripStop', dependent: :destroy
56
59
  has_many :boarding_trip_stops, -> { boarding.order('departure_time ASC NULLS LAST, id ASC') }, class_name: 'SpreeCmCommissioner::TripStop'
57
60
  has_many :drop_off_trip_stops, -> { drop_off.order('arrival_time ASC NULLS LAST') }, class_name: 'SpreeCmCommissioner::TripStop'
@@ -3,15 +3,13 @@ module SpreeCmCommissioner
3
3
  params do
4
4
  optional(:id_token).maybe(:string)
5
5
  optional(:fb_access_token).maybe(:string)
6
- optional(:fb_jwt).maybe(:string)
7
6
  end
8
7
 
9
- rule(:id_token, :fb_access_token, :fb_jwt) do
8
+ rule(:id_token, :fb_access_token) do
10
9
  idt = values[:id_token]
11
10
  fbt = values[:fb_access_token]
12
- fbj = values[:fb_jwt]
13
11
 
14
- key(:base).failure('id_token_or_fb_access_token_required') if blank_or_empty?(idt) && blank_or_empty?(fbt) && blank_or_empty?(fbj)
12
+ key(:base).failure('id_token_or_fb_access_token_required') if blank_or_empty?(idt) && blank_or_empty?(fbt)
15
13
  end
16
14
 
17
15
  private
@@ -15,7 +15,7 @@ module SpreeCmCommissioner
15
15
  # Validates all inventory hold limits for the given order
16
16
  def call(order:)
17
17
  validate_user_hold_limits!(order) if order.user.present?
18
- validate_ip_rate_limit!(order)
18
+ validate_ip_rate_limit!(order) unless SpreeCmCommissioner::Current.load_test_bypass
19
19
  validate_cooldown!(order)
20
20
 
21
21
  success(nil)
@@ -3,13 +3,13 @@ module SpreeCmCommissioner
3
3
  class CreateWithRuleGroups
4
4
  prepend ::Spree::ServiceModule::Base
5
5
 
6
- def call(vendor:, params:, route: nil, origin_place_id: nil, destination_place_id: nil)
6
+ def call(vendor:, params:, route: nil, trip: nil, origin_place_id: nil, destination_place_id: nil) # rubocop:disable Metrics/ParameterLists
7
7
  pricing_model = vendor.pricing_models.new(model_params(params))
8
8
 
9
9
  ApplicationRecord.transaction do
10
10
  pricing_model.save!
11
11
  build_rule_groups(pricing_model, params[:rule_groups])
12
- create_route_assignment(pricing_model, route, origin_place_id, destination_place_id) if route.present?
12
+ create_route_assignment(pricing_model, route, trip, origin_place_id, destination_place_id) if route.present?
13
13
  end
14
14
 
15
15
  success(pricing_model)
@@ -41,11 +41,17 @@ module SpreeCmCommissioner
41
41
  end
42
42
  end
43
43
 
44
- def create_route_assignment(pricing_model, route, origin_place_id, destination_place_id)
44
+ # A row is trip-scoped when a trip is given (overrides route pricing for that trip);
45
+ # otherwise route-scoped (unchanged legacy behaviour). Trip-scoped rows take their O-D from
46
+ # the trip so they match the trip's own journey at resolution time.
47
+ def create_route_assignment(pricing_model, route, trip, origin_place_id, destination_place_id)
48
+ origin_place_id = trip.origin_place_id if trip.present?
49
+ destination_place_id = trip.destination_place_id if trip.present?
45
50
  return unless origin_place_id.present? && destination_place_id.present?
46
51
 
47
52
  PricingModelRoute.create!(
48
53
  route: route,
54
+ trip: trip,
49
55
  pricing_model: pricing_model,
50
56
  origin_place_id: origin_place_id,
51
57
  destination_place_id: destination_place_id
@@ -39,29 +39,72 @@ module SpreeCmCommissioner
39
39
  end
40
40
 
41
41
  def find_pricing_models(group_contexts)
42
- route_pricing_models(group_contexts) + variant_pricing_models(group_contexts)
42
+ transit_pricing_models(group_contexts) + variant_pricing_models(group_contexts)
43
43
  end
44
44
 
45
- def route_pricing_models(group_contexts)
45
+ # Trip-level pricing overrides route-level pricing for the same journey; route pricing
46
+ # is the fallback when the trip (or connected journey's parent trip) has none assigned.
47
+ def transit_pricing_models(group_contexts)
46
48
  transit_contexts = group_contexts.select { |ctx| ctx.trip_id.present? }
47
49
  return [] if transit_contexts.blank?
48
50
 
49
51
  origin_place_id, destination_place_id = determine_origin_destination(transit_contexts)
50
52
  return [] if origin_place_id.blank? || destination_place_id.blank?
51
53
 
52
- vendor_ids = transit_contexts.map { |ctx| variant_vendor_id(ctx.variant_id) }.compact.uniq
54
+ vendor_ids = transit_vendor_ids(transit_contexts)
53
55
  return [] if vendor_ids.blank?
54
56
 
55
- route_ids = trip_route_ids(transit_contexts)
57
+ trip_level_pricing_models(
58
+ trip_ids: trip_candidate_ids(transit_contexts),
59
+ origin_place_id: origin_place_id,
60
+ destination_place_id: destination_place_id,
61
+ vendor_ids: vendor_ids
62
+ ).presence || route_level_pricing_models(
63
+ route_ids: trip_route_ids(transit_contexts),
64
+ origin_place_id: origin_place_id,
65
+ destination_place_id: destination_place_id,
66
+ vendor_ids: vendor_ids
67
+ )
68
+ end
69
+
70
+ def trip_level_pricing_models(trip_ids:, origin_place_id:, destination_place_id:, vendor_ids:)
71
+ return [] if trip_ids.empty?
56
72
 
57
73
  scope = SpreeCmCommissioner::PricingModelRoute
58
74
  .active
75
+ .trip_scoped
76
+ .where(trip_id: trip_ids)
59
77
  .for_origin_destination(origin_place_id, destination_place_id)
60
- .includes(:pricing_model)
61
78
 
79
+ pricing_models_for(scope, vendor_ids)
80
+ end
81
+
82
+ def route_level_pricing_models(route_ids:, origin_place_id:, destination_place_id:, vendor_ids:)
83
+ scope = SpreeCmCommissioner::PricingModelRoute
84
+ .active
85
+ .route_scoped
86
+ .for_origin_destination(origin_place_id, destination_place_id)
62
87
  scope = scope.where(route_id: route_ids) if route_ids.any?
63
88
 
64
- scope.map(&:pricing_model).select { |pm| vendor_ids.include?(pm.vendor_id) }
89
+ pricing_models_for(scope, vendor_ids)
90
+ end
91
+
92
+ # Resolves a PricingModelRoute scope into its pricing models, restricted to vendors
93
+ # actually present among the line items being priced.
94
+ def pricing_models_for(scope, vendor_ids)
95
+ scope.includes(:pricing_model).map(&:pricing_model).select { |pm| vendor_ids.include?(pm.vendor_id) }
96
+ end
97
+
98
+ # A booking's legs can each have their own trip-level price, and a connected journey
99
+ # (multiple legs sharing connected_trip_id) can also have its own price on the shared
100
+ # parent trip. This returns every trip id worth checking for either case.
101
+ def trip_candidate_ids(transit_contexts)
102
+ connected_trip_id = Integer(transit_contexts.first&.connected_trip_id.to_s, exception: false)
103
+ (transit_contexts.map(&:trip_id) + [connected_trip_id]).compact.uniq
104
+ end
105
+
106
+ def transit_vendor_ids(transit_contexts)
107
+ transit_contexts.map { |ctx| variant_vendor_id(ctx.variant_id) }.compact.uniq
65
108
  end
66
109
 
67
110
  def variant_pricing_models(group_contexts)
@@ -3,11 +3,11 @@ module SpreeCmCommissioner
3
3
  class UpdateWithRuleGroups
4
4
  prepend ::Spree::ServiceModule::Base
5
5
 
6
- def call(pricing_model:, params:, route: nil, origin_place_id: nil, destination_place_id: nil)
6
+ def call(pricing_model:, params:, route: nil, trip: nil, origin_place_id: nil, destination_place_id: nil) # rubocop:disable Metrics/ParameterLists
7
7
  ApplicationRecord.transaction do
8
8
  pricing_model.update!(model_params(params))
9
9
  sync_rule_groups(pricing_model, params[:rule_groups])
10
- update_route_assignment(pricing_model, route, origin_place_id, destination_place_id) if route.present?
10
+ update_route_assignment(pricing_model, route, trip, origin_place_id, destination_place_id) if route.present?
11
11
  end
12
12
 
13
13
  success(pricing_model)
@@ -47,14 +47,17 @@ module SpreeCmCommissioner
47
47
  end
48
48
  end
49
49
 
50
- def update_route_assignment(pricing_model, route, origin_place_id, destination_place_id)
50
+ # Each pricing model created via this flow has a single assignment row; update it in place so
51
+ # the assigned trip (and O-D) can be changed on edit. Trip-scoped rows take O-D from the trip.
52
+ def update_route_assignment(pricing_model, route, trip, origin_place_id, destination_place_id)
53
+ origin_place_id = trip.origin_place_id if trip.present?
54
+ destination_place_id = trip.destination_place_id if trip.present?
51
55
  return unless origin_place_id.present? && destination_place_id.present?
52
56
 
53
- route_assignment = PricingModelRoute.find_or_initialize_by(
54
- route: route,
55
- pricing_model: pricing_model
56
- )
57
+ route_assignment = PricingModelRoute.find_or_initialize_by(pricing_model: pricing_model)
57
58
  route_assignment.update!(
59
+ route: route,
60
+ trip: trip,
58
61
  origin_place_id: origin_place_id,
59
62
  destination_place_id: destination_place_id
60
63
  )
@@ -4,7 +4,6 @@ module SpreeCmCommissioner
4
4
  # :username, :password
5
5
  # :id_token
6
6
  # :fb_access_token
7
- # :fb_jwt
8
7
  # :telegram_init_data, :tg_bot
9
8
  # :session_id
10
9
 
@@ -43,9 +42,6 @@ module SpreeCmCommissioner
43
42
  when 'facebook_auth'
44
43
  options = { fb_access_token: params[:fb_access_token], tenant_id: tenant_id }
45
44
  SpreeCmCommissioner::UserFbTokenAuthenticator.call(options)
46
- when 'facebook_jwt_auth'
47
- options = { fb_jwt: params[:fb_jwt], tenant_id: tenant_id }
48
- SpreeCmCommissioner::UserFbJwtAuthenticator.call(options)
49
45
  when 'telegram_web_app_auth'
50
46
  options = { telegram_init_data: params[:telegram_init_data], telegram_bot_username: params[:tg_bot] }
51
47
  SpreeCmCommissioner::UserTelegramWebAppAuthenticator.call(options)
@@ -59,7 +55,6 @@ module SpreeCmCommissioner
59
55
  return 'login_auth' if params.key?(:username) && params.key?(:password)
60
56
  return 'social_auth' if params.key?(:id_token)
61
57
  return 'facebook_auth' if params.key?(:fb_access_token)
62
- return 'facebook_jwt_auth' if params.key?(:fb_jwt)
63
58
  return 'telegram_web_app_auth' if params.key?(:telegram_init_data) && params.key?(:tg_bot)
64
59
  return 'vattanac_bank_web_app_auth' if params.key?(:session_id)
65
60
 
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+ class AddTripToCmPricingModelRoutes < ActiveRecord::Migration[7.0]
3
+ def change
4
+ add_reference :cm_pricing_model_routes, :trip, null: true,
5
+ foreign_key: { to_table: :cm_trips }, index: true
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ FactoryBot.define do
2
+ # A trip-scoped PricingModelRoute row — overrides route pricing for one specific trip.
3
+ # origin/destination default to the trip's own O-D (mirrors how trip pricing is resolved).
4
+ factory :cm_pricing_model_trip, class: 'SpreeCmCommissioner::PricingModelRoute' do
5
+ association :pricing_model, factory: :cm_pricing_model
6
+ association :trip, factory: :cm_trip
7
+ route { trip.route || create(:cm_route, origin_place: trip.origin_place, destination_place: trip.destination_place) }
8
+ origin_place { trip.origin_place }
9
+ destination_place { trip.destination_place }
10
+ end
11
+ end
@@ -1,5 +1,5 @@
1
1
  module SpreeCmCommissioner
2
- VERSION = '2.9.1.pre.pre2'.freeze
2
+ VERSION = '2.9.1.pre.pre4'.freeze
3
3
 
4
4
  module_function
5
5
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_cm_commissioner
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.1.pre.pre2
4
+ version: 2.9.1.pre.pre4
5
5
  platform: ruby
6
6
  authors:
7
7
  - You
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-06 00:00:00.000000000 Z
11
+ date: 2026-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: spree
@@ -1243,7 +1243,6 @@ files:
1243
1243
  - app/interactors/spree_cm_commissioner/event_qr_generator.rb
1244
1244
  - app/interactors/spree_cm_commissioner/existing_account_checker.rb
1245
1245
  - app/interactors/spree_cm_commissioner/fetch_facebook_user_data.rb
1246
- - app/interactors/spree_cm_commissioner/fetch_facebook_user_data_from_jwt.rb
1247
1246
  - app/interactors/spree_cm_commissioner/firebase_email_fetcher.rb
1248
1247
  - app/interactors/spree_cm_commissioner/firebase_email_fetcher_cron_executor.rb
1249
1248
  - app/interactors/spree_cm_commissioner/firebase_id_token_provider.rb
@@ -1320,8 +1319,6 @@ files:
1320
1319
  - app/interactors/spree_cm_commissioner/user_contact_updater.rb
1321
1320
  - app/interactors/spree_cm_commissioner/user_device_token_deregister.rb
1322
1321
  - app/interactors/spree_cm_commissioner/user_device_token_register.rb
1323
- - app/interactors/spree_cm_commissioner/user_fb_jwt_authenticator.rb
1324
- - app/interactors/spree_cm_commissioner/user_fb_jwt_checker.rb
1325
1322
  - app/interactors/spree_cm_commissioner/user_fb_token_authenticator.rb
1326
1323
  - app/interactors/spree_cm_commissioner/user_fb_token_checker.rb
1327
1324
  - app/interactors/spree_cm_commissioner/user_forgotten_password_updater.rb
@@ -1515,6 +1512,7 @@ files:
1515
1512
  - app/models/spree_cm_commissioner/configuration.rb
1516
1513
  - app/models/spree_cm_commissioner/crew_invite.rb
1517
1514
  - app/models/spree_cm_commissioner/crew_invite_link.rb
1515
+ - app/models/spree_cm_commissioner/current.rb
1518
1516
  - app/models/spree_cm_commissioner/customer.rb
1519
1517
  - app/models/spree_cm_commissioner/customer_notification.rb
1520
1518
  - app/models/spree_cm_commissioner/customer_promotion_rule.rb
@@ -3412,6 +3410,7 @@ files:
3412
3410
  - db/migrate/20260623082233_create_cm_claims.rb
3413
3411
  - db/migrate/20260624175658_add_unique_index_to_cm_claims.rb
3414
3412
  - db/migrate/20260629000001_add_term_of_use_to_spree_vendors.rb
3413
+ - db/migrate/20260630000001_add_trip_to_cm_pricing_model_routes.rb
3415
3414
  - db/migrate/20260701000001_add_device_info_to_cm_waiting_room_sessions.rb
3416
3415
  - db/migrate/20260703065239_add_position_to_cm_voting_contestants.rb
3417
3416
  - docker-compose.yml
@@ -3512,6 +3511,7 @@ files:
3512
3511
  - lib/spree_cm_commissioner/test_helper/factories/pricing_action_factory.rb
3513
3512
  - lib/spree_cm_commissioner/test_helper/factories/pricing_model_factory.rb
3514
3513
  - lib/spree_cm_commissioner/test_helper/factories/pricing_model_route_factory.rb
3514
+ - lib/spree_cm_commissioner/test_helper/factories/pricing_model_trip_factory.rb
3515
3515
  - lib/spree_cm_commissioner/test_helper/factories/pricing_model_variant_factory.rb
3516
3516
  - lib/spree_cm_commissioner/test_helper/factories/pricing_rule_factory.rb
3517
3517
  - lib/spree_cm_commissioner/test_helper/factories/pricing_rule_group_factory.rb
@@ -1,90 +0,0 @@
1
- module SpreeCmCommissioner
2
- class FetchFacebookUserDataFromJwt < BaseInteractor
3
- JWKS_URL = 'https://limited.facebook.com/.well-known/oauth/openid/jwks/'.freeze
4
- EXPECTED_ISSUER = 'https://www.facebook.com'.freeze
5
-
6
- delegate :fb_jwt, to: :context
7
-
8
- def call
9
- context.fail!(message: 'fb_jwt_missing') if fb_jwt.blank?
10
-
11
- claim = decode_jwt
12
-
13
- context.provider = {
14
- identity_type: SpreeCmCommissioner::UserIdentityProvider.identity_types[:facebook],
15
- sub: claim['sub'],
16
- name: claim['name'],
17
- email: nil, # Facebook Limited Login JWT does not include email
18
- first_name: claim['given_name'],
19
- last_name: claim['family_name'],
20
- picture: claim['picture']
21
- }
22
- end
23
-
24
- private
25
-
26
- def decode_jwt
27
- result = JWT.decode(fb_jwt, nil, true, algorithms: ['RS256']) do |header|
28
- kid = header['kid']
29
- jwk_data = find_jwk(fetch_jwks, kid) || find_jwk(refresh_jwks, kid)
30
- context.fail!(message: 'invalid_facebook_jwt_kid') unless jwk_data
31
- public_key_from_jwk(jwk_data)
32
- end
33
-
34
- claim = result[0]
35
- context.fail!(message: 'invalid_facebook_issuer') if claim['iss'] != EXPECTED_ISSUER
36
- claim
37
- rescue JWT::ExpiredSignature
38
- context.fail!(message: 'fb_jwt_expired')
39
- rescue JWT::DecodeError => e
40
- context.fail!(message: e.message)
41
- end
42
-
43
- def find_jwk(jwks, kid)
44
- jwks['keys'].find { |k| k['kid'] == kid }
45
- end
46
-
47
- def public_key_from_jwk(jwk_data)
48
- n = OpenSSL::BN.new(Base64.urlsafe_decode64(jwk_data['n']), 2)
49
- e = OpenSSL::BN.new(Base64.urlsafe_decode64(jwk_data['e']), 2)
50
-
51
- pkcs1 = OpenSSL::ASN1::Sequence([
52
- OpenSSL::ASN1::Integer(n),
53
- OpenSSL::ASN1::Integer(e)
54
- ]
55
- )
56
-
57
- spki = OpenSSL::ASN1::Sequence([
58
- OpenSSL::ASN1::Sequence([
59
- OpenSSL::ASN1::ObjectId('rsaEncryption'),
60
- OpenSSL::ASN1::Null(nil)
61
- ]
62
- ),
63
- OpenSSL::ASN1::BitString(pkcs1.to_der)
64
- ]
65
- )
66
-
67
- OpenSSL::PKey::RSA.new(spki.to_der)
68
- end
69
-
70
- def refresh_jwks
71
- Rails.cache.delete('facebook-limited-jwks')
72
- fetch_jwks
73
- end
74
-
75
- def fetch_jwks
76
- Rails.cache.fetch('facebook-limited-jwks', expires_in: 1.hour) do
77
- url = URI(JWKS_URL)
78
- http = Net::HTTP.new(url.host, url.port)
79
- http.use_ssl = true
80
- http.open_timeout = 5
81
- http.read_timeout = 10
82
- response = http.get(url.request_uri)
83
- JSON.parse(response.body)
84
- end
85
- rescue StandardError => e
86
- CmAppLogger.error("Facebook JWKS fetch error: #{e.message}")
87
- context.fail!(message: 'facebook_jwks_fetch_error')
88
- end
89
- end
90
- end
@@ -1,37 +0,0 @@
1
- module SpreeCmCommissioner
2
- class UserFbJwtAuthenticator < BaseInteractor
3
- delegate :fb_jwt, :tenant_id, to: :context
4
-
5
- # :fb_jwt, :tenant_id (optional)
6
- def call
7
- context.fail!(message: 'fb_jwt_missing') unless fb_jwt
8
- context.fail!(message: checker.message) if checker.failure?
9
-
10
- context.user = if checker.user.nil?
11
- register_user
12
- else
13
- checker.user
14
- end
15
-
16
- context.fail!(message: 'account_temporarily_deleted') if context.user.soft_deleted?
17
- end
18
-
19
- private
20
-
21
- def checker
22
- @checker ||= SpreeCmCommissioner::UserFbJwtChecker.call(
23
- fb_jwt: fb_jwt,
24
- tenant_id: tenant_id
25
- )
26
- end
27
-
28
- def register_user
29
- register_context = SpreeCmCommissioner::UserRegistrationWithFbToken.call(
30
- provider: checker.provider,
31
- tenant_id: tenant_id
32
- )
33
-
34
- register_context.user
35
- end
36
- end
37
- end
@@ -1,20 +0,0 @@
1
- module SpreeCmCommissioner
2
- class UserFbJwtChecker < BaseInteractor
3
- delegate :tenant_id, :fb_jwt, to: :context
4
-
5
- def call
6
- fb_data = SpreeCmCommissioner::FetchFacebookUserDataFromJwt.call(fb_jwt: fb_jwt)
7
- context.fail!(message: fb_data.message) if fb_data.failure?
8
-
9
- context.provider = fb_data.provider
10
-
11
- identity_checker = SpreeCmCommissioner::UserIdentityChecker.call(
12
- identity_type: context.provider[:identity_type],
13
- sub: context.provider[:sub],
14
- tenant_id: tenant_id
15
- )
16
-
17
- context.user = identity_checker.success? ? identity_checker.user : nil
18
- end
19
- end
20
- end