flow_chat 0.6.1 → 0.8.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +44 -0
  3. data/.gitignore +2 -1
  4. data/README.md +85 -1229
  5. data/docs/configuration.md +360 -0
  6. data/docs/flows.md +320 -0
  7. data/docs/images/simulator.png +0 -0
  8. data/docs/instrumentation.md +216 -0
  9. data/docs/media.md +153 -0
  10. data/docs/sessions.md +433 -0
  11. data/docs/testing.md +475 -0
  12. data/docs/ussd-setup.md +322 -0
  13. data/docs/whatsapp-setup.md +162 -0
  14. data/examples/multi_tenant_whatsapp_controller.rb +9 -37
  15. data/examples/simulator_controller.rb +13 -22
  16. data/examples/ussd_controller.rb +41 -41
  17. data/examples/whatsapp_controller.rb +32 -125
  18. data/examples/whatsapp_media_examples.rb +68 -336
  19. data/examples/whatsapp_message_job.rb +5 -3
  20. data/flow_chat.gemspec +6 -2
  21. data/lib/flow_chat/base_processor.rb +79 -2
  22. data/lib/flow_chat/config.rb +31 -5
  23. data/lib/flow_chat/context.rb +13 -1
  24. data/lib/flow_chat/instrumentation/log_subscriber.rb +176 -0
  25. data/lib/flow_chat/instrumentation/metrics_collector.rb +197 -0
  26. data/lib/flow_chat/instrumentation/setup.rb +155 -0
  27. data/lib/flow_chat/instrumentation.rb +70 -0
  28. data/lib/flow_chat/prompt.rb +20 -20
  29. data/lib/flow_chat/session/cache_session_store.rb +73 -7
  30. data/lib/flow_chat/session/middleware.rb +130 -12
  31. data/lib/flow_chat/session/rails_session_store.rb +36 -1
  32. data/lib/flow_chat/simulator/controller.rb +8 -8
  33. data/lib/flow_chat/simulator/views/simulator.html.erb +5 -5
  34. data/lib/flow_chat/ussd/gateway/nalo.rb +31 -0
  35. data/lib/flow_chat/ussd/gateway/nsano.rb +36 -2
  36. data/lib/flow_chat/ussd/middleware/choice_mapper.rb +109 -0
  37. data/lib/flow_chat/ussd/middleware/executor.rb +24 -2
  38. data/lib/flow_chat/ussd/middleware/pagination.rb +87 -7
  39. data/lib/flow_chat/ussd/processor.rb +16 -4
  40. data/lib/flow_chat/ussd/renderer.rb +1 -1
  41. data/lib/flow_chat/version.rb +1 -1
  42. data/lib/flow_chat/whatsapp/client.rb +99 -12
  43. data/lib/flow_chat/whatsapp/configuration.rb +35 -4
  44. data/lib/flow_chat/whatsapp/gateway/cloud_api.rb +121 -34
  45. data/lib/flow_chat/whatsapp/middleware/executor.rb +24 -2
  46. data/lib/flow_chat/whatsapp/processor.rb +7 -1
  47. data/lib/flow_chat/whatsapp/renderer.rb +4 -9
  48. data/lib/flow_chat.rb +23 -0
  49. metadata +23 -12
  50. data/.travis.yml +0 -6
  51. data/app/controllers/demo_controller.rb +0 -101
  52. data/app/flow_chat/demo_restaurant_flow.rb +0 -889
  53. data/config/routes_demo.rb +0 -59
  54. data/examples/initializer.rb +0 -86
  55. data/examples/media_prompts_examples.rb +0 -27
  56. data/images/ussd_simulator.png +0 -0
  57. data/lib/flow_chat/ussd/middleware/resumable_session.rb +0 -39
@@ -0,0 +1,322 @@
1
+ # USSD Setup Guide
2
+
3
+ This guide covers USSD configuration and implementation patterns for FlowChat.
4
+
5
+ ## Basic Setup
6
+
7
+ ### 1. Create a Flow
8
+
9
+ Create a flow in `app/flow_chat/welcome_flow.rb`:
10
+
11
+ ```ruby
12
+ class WelcomeFlow < FlowChat::Flow
13
+ def main_page
14
+ name = app.screen(:name) do |prompt|
15
+ prompt.ask "Welcome! What's your name?",
16
+ transform: ->(input) { input.strip.titleize }
17
+ end
18
+
19
+ service = app.screen(:service) do |prompt|
20
+ prompt.select "Choose a service:", {
21
+ "balance" => "Check Balance",
22
+ "transfer" => "Transfer Money",
23
+ "history" => "Transaction History"
24
+ }
25
+ end
26
+
27
+ case service
28
+ when "balance"
29
+ show_balance
30
+ when "transfer"
31
+ transfer_money
32
+ when "history"
33
+ show_history
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def show_balance
40
+ # Simulate balance check
41
+ balance = fetch_user_balance(app.phone_number)
42
+ app.say "Hello #{app.session.get(:name)}! Your balance is $#{balance}."
43
+ end
44
+ end
45
+ ```
46
+
47
+ ### 2. Create Controller
48
+
49
+ ```ruby
50
+ class UssdController < ApplicationController
51
+ skip_forgery_protection
52
+
53
+ def process_request
54
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
55
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
56
+ config.use_session_store FlowChat::Session::CacheSessionStore
57
+ end
58
+
59
+ processor.run WelcomeFlow, :main_page
60
+ end
61
+ end
62
+ ```
63
+
64
+ ### 3. Configure Routes
65
+
66
+ ```ruby
67
+ # config/routes.rb
68
+ Rails.application.routes.draw do
69
+ post 'ussd' => 'ussd#process_request'
70
+ end
71
+ ```
72
+
73
+ ## Gateway Configuration
74
+
75
+ ### Nalo Gateway
76
+
77
+ FlowChat currently supports Nalo USSD gateways:
78
+
79
+ ```ruby
80
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
81
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
82
+ config.use_session_store FlowChat::Session::CacheSessionStore
83
+ end
84
+ ```
85
+
86
+ The Nalo gateway expects these parameters in the request:
87
+ - `msisdn` - User's phone number
88
+ - `input` - User's input text
89
+ - `session_id` - Session identifier
90
+
91
+ ## Pagination
92
+
93
+ USSD messages are automatically paginated when they exceed character limits:
94
+
95
+ ```ruby
96
+ # Configure pagination behavior
97
+ FlowChat::Config.ussd.pagination_page_size = 140 # characters per page
98
+ FlowChat::Config.ussd.pagination_next_option = "#" # option for next page
99
+ FlowChat::Config.ussd.pagination_next_text = "More" # text for next option
100
+ FlowChat::Config.ussd.pagination_back_option = "0" # option for previous page
101
+ FlowChat::Config.ussd.pagination_back_text = "Back" # text for back option
102
+ ```
103
+
104
+ ### Example Long Message
105
+
106
+ ```ruby
107
+ def show_terms
108
+ terms = "Very long terms and conditions text that exceeds 140 characters..."
109
+ app.say terms # Automatically paginated
110
+ end
111
+ ```
112
+
113
+ **USSD Output:**
114
+ ```
115
+ Terms and Conditions:
116
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt...
117
+
118
+ # More
119
+ 0 Back
120
+ ```
121
+
122
+ ## Session Management
123
+
124
+ Configure session behavior for better user experience:
125
+
126
+ ```ruby
127
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
128
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
129
+ config.use_session_store FlowChat::Session::CacheSessionStore
130
+
131
+ # Enable durable sessions (shorthand)
132
+ config.use_durable_sessions
133
+ end
134
+ ```
135
+
136
+ ### Session Boundaries
137
+
138
+ Session boundaries control how session IDs are constructed:
139
+
140
+ - **`:flow`** - Separate sessions per flow class
141
+ - **`:platform`** - Separate USSD from WhatsApp sessions
142
+ - **`:gateway`** - Separate sessions per gateway
143
+ - **`[]`** - Global sessions (no boundaries)
144
+
145
+ Session identifier options:
146
+
147
+ - **`nil`** - Platform chooses default (`:request_id` for USSD, `:msisdn` for WhatsApp)
148
+ - **`:msisdn`** - Use phone number (durable sessions)
149
+ - **`:request_id`** - Use request ID (ephemeral sessions)
150
+ - **`hash_phone_numbers`** - Hash phone numbers for privacy (recommended)
151
+
152
+ ## Middleware
153
+
154
+ ### Custom Middleware
155
+
156
+ ```ruby
157
+ class AuthenticationMiddleware
158
+ def initialize(app)
159
+ @app = app
160
+ end
161
+
162
+ def call(context)
163
+ phone = context["request.msisdn"]
164
+
165
+ unless user_exists?(phone)
166
+ # Return appropriate response for unregistered user
167
+ return [:prompt, "Please register first. Visit our website to sign up.", nil, nil]
168
+ end
169
+
170
+ @app.call(context)
171
+ end
172
+
173
+ private
174
+
175
+ def user_exists?(phone)
176
+ User.exists?(phone: phone)
177
+ end
178
+ end
179
+
180
+ # Use custom middleware
181
+ processor = FlowChat::Ussd::Processor.new(self) do |config|
182
+ config.use_gateway FlowChat::Ussd::Gateway::Nalo
183
+ config.use_session_store FlowChat::Session::CacheSessionStore
184
+ config.use_middleware AuthenticationMiddleware
185
+ end
186
+ ```
187
+
188
+ ## Advanced Patterns
189
+
190
+ ### Menu-Driven Navigation
191
+
192
+ ```ruby
193
+ class MenuFlow < FlowChat::Flow
194
+ def main_page
195
+ choice = app.screen(:main_menu) do |prompt|
196
+ prompt.select "Main Menu:", {
197
+ "1" => "Account Services",
198
+ "2" => "Transfer Services",
199
+ "3" => "Customer Support"
200
+ }
201
+ end
202
+
203
+ case choice
204
+ when "1"
205
+ account_services
206
+ when "2"
207
+ transfer_services
208
+ when "3"
209
+ customer_support
210
+ end
211
+ end
212
+
213
+ def account_services
214
+ choice = app.screen(:account_menu) do |prompt|
215
+ prompt.select "Account Services:", {
216
+ "1" => "Check Balance",
217
+ "2" => "Mini Statement",
218
+ "0" => "Back to Main Menu"
219
+ }
220
+ end
221
+
222
+ case choice
223
+ when "1"
224
+ check_balance
225
+ when "2"
226
+ mini_statement
227
+ when "0"
228
+ main_page # Return to main menu
229
+ end
230
+ end
231
+ end
232
+ ```
233
+
234
+ ### Error Handling
235
+
236
+ ```ruby
237
+ def transfer_money
238
+ begin
239
+ amount = app.screen(:amount) do |prompt|
240
+ prompt.ask "Enter amount:",
241
+ validate: ->(input) {
242
+ return "Amount must be numeric" unless input.match?(/^\d+(\.\d{2})?$/)
243
+ return "Minimum transfer is $1" unless input.to_f >= 1.0
244
+ nil
245
+ },
246
+ transform: ->(input) { input.to_f }
247
+ end
248
+
249
+ recipient = app.screen(:recipient) do |prompt|
250
+ prompt.ask "Enter recipient phone number:",
251
+ validate: ->(input) {
252
+ return "Invalid phone number" unless valid_phone?(input)
253
+ nil
254
+ }
255
+ end
256
+
257
+ process_transfer(amount, recipient)
258
+ app.say "Transfer of $#{amount} to #{recipient} successful!"
259
+
260
+ rescue TransferError => e
261
+ app.say "Transfer failed: #{e.message}. Please try again."
262
+ transfer_money # Retry
263
+ end
264
+ end
265
+ ```
266
+
267
+ ### Session Data Management
268
+
269
+ ```ruby
270
+ class RegistrationFlow < FlowChat::Flow
271
+ def main_page
272
+ collect_personal_info
273
+ collect_preferences
274
+ confirm_and_save
275
+ end
276
+
277
+ private
278
+
279
+ def collect_personal_info
280
+ app.screen(:first_name) { |p| p.ask "First name:" }
281
+ app.screen(:last_name) { |p| p.ask "Last name:" }
282
+ app.screen(:email) { |p| p.ask "Email address:" }
283
+ end
284
+
285
+ def collect_preferences
286
+ app.screen(:language) { |p| p.select "Language:", ["English", "French"] }
287
+ app.screen(:notifications) { |p| p.yes? "Enable SMS notifications?" }
288
+ end
289
+
290
+ def confirm_and_save
291
+ summary = build_summary_from_session
292
+ confirmed = app.screen(:confirm) { |p| p.yes? "Save profile?\n\n#{summary}" }
293
+
294
+ if confirmed
295
+ save_user_profile
296
+ app.say "Registration complete!"
297
+ else
298
+ app.say "Registration cancelled."
299
+ end
300
+ end
301
+
302
+ def build_summary_from_session
303
+ first_name = app.session.get(:first_name)
304
+ last_name = app.session.get(:last_name)
305
+ email = app.session.get(:email)
306
+ language = app.session.get(:language)
307
+
308
+ "Name: #{first_name} #{last_name}\nEmail: #{email}\nLanguage: #{language}"
309
+ end
310
+ end
311
+ ```
312
+
313
+ ## Testing USSD Flows
314
+
315
+ Use the built-in simulator for testing USSD flows:
316
+
317
+ ```ruby
318
+ # config/initializers/flowchat.rb
319
+ FlowChat::Config.simulator_secret = "your_secure_secret"
320
+ ```
321
+
322
+ See [Testing Guide](testing.md) for comprehensive testing strategies and [examples/ussd_controller.rb](../examples/ussd_controller.rb) for complete implementation examples.
@@ -0,0 +1,162 @@
1
+ # WhatsApp Setup Guide
2
+
3
+ This guide covers comprehensive WhatsApp configuration for FlowChat, including security, multi-tenant setups, and different processing modes.
4
+
5
+ ## Credential Configuration
6
+
7
+ FlowChat supports multiple ways to configure WhatsApp credentials:
8
+
9
+ ### Option 1: Rails Credentials
10
+
11
+ ```bash
12
+ rails credentials:edit
13
+ ```
14
+
15
+ ```yaml
16
+ whatsapp:
17
+ access_token: "your_access_token"
18
+ phone_number_id: "your_phone_number_id"
19
+ verify_token: "your_verify_token"
20
+ app_id: "your_app_id"
21
+ app_secret: "your_app_secret"
22
+ business_account_id: "your_business_account_id"
23
+ skip_signature_validation: false # Set to true only for development/testing
24
+ ```
25
+
26
+ ### Option 2: Environment Variables
27
+
28
+ ```bash
29
+ export WHATSAPP_ACCESS_TOKEN="your_access_token"
30
+ export WHATSAPP_PHONE_NUMBER_ID="your_phone_number_id"
31
+ export WHATSAPP_VERIFY_TOKEN="your_verify_token"
32
+ export WHATSAPP_APP_ID="your_app_id"
33
+ export WHATSAPP_APP_SECRET="your_app_secret"
34
+ export WHATSAPP_BUSINESS_ACCOUNT_ID="your_business_account_id"
35
+ export WHATSAPP_SKIP_SIGNATURE_VALIDATION="false"
36
+ ```
37
+
38
+ ### Option 3: Per-Setup Configuration
39
+
40
+ For multi-tenant applications:
41
+
42
+ ```ruby
43
+ custom_config = FlowChat::Whatsapp::Configuration.new
44
+ custom_config.access_token = "your_specific_access_token"
45
+ custom_config.phone_number_id = "your_specific_phone_number_id"
46
+ custom_config.verify_token = "your_specific_verify_token"
47
+ custom_config.app_id = "your_specific_app_id"
48
+ custom_config.app_secret = "your_specific_app_secret"
49
+ custom_config.business_account_id = "your_specific_business_account_id"
50
+ custom_config.skip_signature_validation = false
51
+
52
+ processor = FlowChat::Whatsapp::Processor.new(self) do |config|
53
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, custom_config
54
+ config.use_session_store FlowChat::Session::CacheSessionStore
55
+ end
56
+ ```
57
+
58
+ ## Message Handling Modes
59
+
60
+ Configure message processing behavior in `config/initializers/flowchat.rb`:
61
+
62
+ ### Inline Mode (Default)
63
+
64
+ Process messages synchronously:
65
+
66
+ ```ruby
67
+ FlowChat::Config.whatsapp.message_handling_mode = :inline
68
+ ```
69
+
70
+ ### Background Mode
71
+
72
+ Process flows synchronously, send responses asynchronously:
73
+
74
+ ```ruby
75
+ FlowChat::Config.whatsapp.message_handling_mode = :background
76
+ FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
77
+ ```
78
+
79
+ ### Simulator Mode
80
+
81
+ Return response data instead of sending via WhatsApp API:
82
+
83
+ ```ruby
84
+ FlowChat::Config.whatsapp.message_handling_mode = :simulator
85
+ ```
86
+
87
+ ## Security Configuration
88
+
89
+ ### Webhook Signature Validation (Production)
90
+
91
+ ```ruby
92
+ custom_config.app_secret = "your_whatsapp_app_secret"
93
+ custom_config.skip_signature_validation = false # Default: enforce validation
94
+ ```
95
+
96
+ ### Development Mode
97
+
98
+ ```ruby
99
+ custom_config.app_secret = nil
100
+ custom_config.skip_signature_validation = true # Disable validation
101
+ ```
102
+
103
+ ⚠️ **Security Warning**: Only disable signature validation in development/testing environments.
104
+
105
+ ## Multi-Tenant Setup
106
+
107
+ See [examples/multi_tenant_whatsapp_controller.rb](../examples/multi_tenant_whatsapp_controller.rb) for a complete multi-tenant implementation.
108
+
109
+ ## Background Job Setup
110
+
111
+ When using background jobs with programmatic WhatsApp configurations, you must register configurations in an initializer so they're available to background jobs:
112
+
113
+ ```ruby
114
+ # config/initializers/whatsapp_configs.rb
115
+ tenant_a_config = FlowChat::Whatsapp::Configuration.new(:tenant_a)
116
+ tenant_a_config.access_token = "tenant_a_token"
117
+ tenant_a_config.phone_number_id = "tenant_a_phone"
118
+ tenant_a_config.verify_token = "tenant_a_verify"
119
+ tenant_a_config.app_secret = "tenant_a_secret"
120
+ # Configuration is automatically registered as :tenant_a
121
+
122
+ tenant_b_config = FlowChat::Whatsapp::Configuration.new(:tenant_b)
123
+ tenant_b_config.access_token = "tenant_b_token"
124
+ tenant_b_config.phone_number_id = "tenant_b_phone"
125
+ tenant_b_config.verify_token = "tenant_b_verify"
126
+ tenant_b_config.app_secret = "tenant_b_secret"
127
+ # Configuration is automatically registered as :tenant_b
128
+ ```
129
+
130
+ Then reference the named configuration in your controller:
131
+
132
+ ```ruby
133
+ processor = FlowChat::Whatsapp::Processor.new(self) do |config|
134
+ # Use registered configuration by name
135
+ tenant_config = FlowChat::Whatsapp::Configuration.get(:tenant_a)
136
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, tenant_config
137
+ config.use_session_store FlowChat::Session::CacheSessionStore
138
+ end
139
+ ```
140
+
141
+ **Why this is required:** Background jobs run in a separate context and cannot access configurations created in controller actions. Registered configurations are globally available.
142
+
143
+ **Alternative:** Override the job's configuration resolution logic (see examples).
144
+
145
+ See [examples/whatsapp_message_job.rb](../examples/whatsapp_message_job.rb) for job implementation and [Background Jobs Guide](background-jobs.md) for detailed setup.
146
+
147
+ ## Troubleshooting
148
+
149
+ ### Common Issues
150
+
151
+ **Configuration Error**: Check that all required credentials are set
152
+ **Signature Validation Failed**: Verify app_secret matches your WhatsApp app
153
+ **Timeout Issues**: Consider using background mode for high-volume applications
154
+
155
+ ### Debug Mode
156
+
157
+ Enable debug logging in development:
158
+
159
+ ```ruby
160
+ # config/environments/development.rb
161
+ config.log_level = :debug
162
+ ```
@@ -1,26 +1,19 @@
1
1
  # Example Multi-Tenant WhatsApp Controller
2
2
  # This shows how to configure different WhatsApp accounts per tenant/client
3
3
 
4
+ # Controller supporting multiple WhatsApp accounts per tenant
4
5
  class MultiTenantWhatsappController < ApplicationController
5
6
  skip_forgery_protection
6
7
 
7
8
  def webhook
8
- # Determine tenant from subdomain, path, or other logic
9
9
  tenant = determine_tenant(request)
10
-
11
- # Get tenant-specific WhatsApp configuration
12
10
  whatsapp_config = get_whatsapp_config_for_tenant(tenant)
13
11
 
14
- # Enable simulator for local endpoint testing during development
15
- # This allows testing different tenant endpoints via the simulator interface
16
- enable_simulator = Rails.env.development? || Rails.env.staging?
17
-
18
- processor = FlowChat::Whatsapp::Processor.new(self, enable_simulator: enable_simulator) do |config|
12
+ processor = FlowChat::Whatsapp::Processor.new(self, enable_simulator: !Rails.env.production?) do |config|
19
13
  config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi, whatsapp_config
20
14
  config.use_session_store FlowChat::Session::CacheSessionStore
21
15
  end
22
16
 
23
- # Use tenant-specific flow
24
17
  flow_class = get_flow_for_tenant(tenant)
25
18
  processor.run flow_class, :main_page
26
19
  end
@@ -31,14 +24,13 @@ class MultiTenantWhatsappController < ApplicationController
31
24
  # Option 1: From subdomain
32
25
  return request.subdomain if request.subdomain.present?
33
26
 
34
- # Option 2: From path
27
+ # Option 2: From path (e.g., /whatsapp/acme/webhook)
35
28
  tenant_from_path = request.path.match(%r{^/whatsapp/(\w+)/})&.captures&.first
36
29
  return tenant_from_path if tenant_from_path
37
30
 
38
- # Option 3: From custom header
31
+ # Option 3: From header
39
32
  return request.headers["X-Tenant-ID"] if request.headers["X-Tenant-ID"]
40
33
 
41
- # Fallback to default
42
34
  "default"
43
35
  end
44
36
 
@@ -49,9 +41,7 @@ class MultiTenantWhatsappController < ApplicationController
49
41
  config.access_token = ENV["ACME_WHATSAPP_ACCESS_TOKEN"]
50
42
  config.phone_number_id = ENV["ACME_WHATSAPP_PHONE_NUMBER_ID"]
51
43
  config.verify_token = ENV["ACME_WHATSAPP_VERIFY_TOKEN"]
52
- config.app_id = ENV["ACME_WHATSAPP_APP_ID"]
53
44
  config.app_secret = ENV["ACME_WHATSAPP_APP_SECRET"]
54
- config.business_account_id = ENV["ACME_WHATSAPP_BUSINESS_ACCOUNT_ID"]
55
45
  end
56
46
 
57
47
  when "tech_startup"
@@ -59,9 +49,7 @@ class MultiTenantWhatsappController < ApplicationController
59
49
  config.access_token = ENV["TECHSTARTUP_WHATSAPP_ACCESS_TOKEN"]
60
50
  config.phone_number_id = ENV["TECHSTARTUP_WHATSAPP_PHONE_NUMBER_ID"]
61
51
  config.verify_token = ENV["TECHSTARTUP_WHATSAPP_VERIFY_TOKEN"]
62
- config.app_id = ENV["TECHSTARTUP_WHATSAPP_APP_ID"]
63
52
  config.app_secret = ENV["TECHSTARTUP_WHATSAPP_APP_SECRET"]
64
- config.business_account_id = ENV["TECHSTARTUP_WHATSAPP_BUSINESS_ACCOUNT_ID"]
65
53
  end
66
54
 
67
55
  when "retail_store"
@@ -71,13 +59,10 @@ class MultiTenantWhatsappController < ApplicationController
71
59
  config.access_token = tenant_config.access_token
72
60
  config.phone_number_id = tenant_config.phone_number_id
73
61
  config.verify_token = tenant_config.verify_token
74
- config.app_id = tenant_config.app_id
75
62
  config.app_secret = tenant_config.app_secret
76
- config.business_account_id = tenant_config.business_account_id
77
63
  end
78
64
 
79
65
  else
80
- # Use default/global configuration
81
66
  FlowChat::Whatsapp::Configuration.from_credentials
82
67
  end
83
68
  end
@@ -91,7 +76,7 @@ class MultiTenantWhatsappController < ApplicationController
91
76
  when "retail_store"
92
77
  RetailStoreFlow
93
78
  else
94
- WelcomeFlow # Default flow
79
+ WelcomeFlow
95
80
  end
96
81
  end
97
82
  end
@@ -101,21 +86,14 @@ class DatabaseWhatsappController < ApplicationController
101
86
  skip_forgery_protection
102
87
 
103
88
  def webhook
104
- # Get account from business phone number or other identifier
105
89
  business_account = find_business_account(params)
90
+ return head :not_found if business_account.nil?
106
91
 
107
- if business_account.nil?
108
- return head :not_found
109
- end
110
-
111
- # Create configuration from database record
112
92
  whatsapp_config = FlowChat::Whatsapp::Configuration.new.tap do |config|
113
93
  config.access_token = business_account.whatsapp_access_token
114
94
  config.phone_number_id = business_account.whatsapp_phone_number_id
115
95
  config.verify_token = business_account.whatsapp_verify_token
116
- config.app_id = business_account.whatsapp_app_id
117
96
  config.app_secret = business_account.whatsapp_app_secret
118
- config.business_account_id = business_account.whatsapp_business_account_id
119
97
  end
120
98
 
121
99
  processor = FlowChat::Whatsapp::Processor.new(self) do |config|
@@ -129,20 +107,14 @@ class DatabaseWhatsappController < ApplicationController
129
107
  private
130
108
 
131
109
  def find_business_account(params)
132
- # You could identify the account by:
133
- # 1. Phone number ID from webhook
134
- # 2. Business account ID from webhook
135
- # 3. Custom routing parameter
136
-
137
- # Example: Find by phone number ID in webhook
110
+ # Find by phone number ID from webhook
138
111
  phone_number_id = extract_phone_number_id_from_webhook(params)
139
112
  BusinessAccount.find_by(whatsapp_phone_number_id: phone_number_id)
140
113
  end
141
114
 
142
115
  def extract_phone_number_id_from_webhook(params)
143
- # Extract from webhook payload structure
144
- # This would need to be implemented based on your webhook structure
145
- nil
116
+ # Extract from webhook payload - implement based on your structure
117
+ params.dig(:entry, 0, :changes, 0, :value, :metadata, :phone_number_id)
146
118
  end
147
119
  end
148
120
 
@@ -10,50 +10,41 @@ class SimulatorController < ApplicationController
10
10
 
11
11
  protected
12
12
 
13
- # Define different local endpoints to test on the same server
13
+ # Define different endpoints to test
14
14
  def configurations
15
15
  {
16
16
  ussd_main: {
17
17
  name: "Main USSD Endpoint",
18
- description: "Primary USSD integration endpoint",
18
+ description: "Primary USSD integration",
19
19
  processor_type: "ussd",
20
- provider: "nalo",
20
+ gateway: "nalo",
21
21
  endpoint: "/ussd",
22
22
  icon: "📱",
23
23
  color: "#28a745"
24
24
  },
25
25
  whatsapp_main: {
26
- name: "Main WhatsApp Endpoint",
27
- description: "Primary WhatsApp webhook endpoint",
26
+ name: "Main WhatsApp Endpoint",
27
+ description: "Primary WhatsApp webhook",
28
28
  processor_type: "whatsapp",
29
- provider: "cloud_api",
29
+ gateway: "cloud_api",
30
30
  endpoint: "/whatsapp/webhook",
31
31
  icon: "💬",
32
32
  color: "#25D366"
33
33
  },
34
- whatsapp_v2: {
35
- name: "WhatsApp API v2",
36
- description: "Alternative WhatsApp endpoint (v2 API)",
37
- processor_type: "whatsapp",
38
- provider: "cloud_api",
39
- endpoint: "/api/v2/whatsapp/webhook",
40
- icon: "🔄",
41
- color: "#17a2b8"
42
- },
43
34
  whatsapp_tenant_a: {
44
35
  name: "Tenant A WhatsApp",
45
- description: "Multi-tenant WhatsApp endpoint for Tenant A",
36
+ description: "Multi-tenant endpoint for Tenant A",
46
37
  processor_type: "whatsapp",
47
- provider: "cloud_api",
38
+ gateway: "cloud_api",
48
39
  endpoint: "/tenants/a/whatsapp/webhook",
49
40
  icon: "🏢",
50
41
  color: "#fd7e14"
51
42
  },
52
43
  whatsapp_legacy: {
53
44
  name: "Legacy WhatsApp",
54
- description: "Legacy WhatsApp endpoint for backward compatibility",
45
+ description: "Legacy endpoint for compatibility",
55
46
  processor_type: "whatsapp",
56
- provider: "cloud_api",
47
+ gateway: "cloud_api",
57
48
  endpoint: "/legacy/whatsapp",
58
49
  icon: "📦",
59
50
  color: "#6c757d"
@@ -71,7 +62,7 @@ class SimulatorController < ApplicationController
71
62
  "+1234567890"
72
63
  end
73
64
 
74
- # Default test contact name
65
+ # Default test contact name
75
66
  def default_contact_name
76
67
  "Test User"
77
68
  end
@@ -90,6 +81,6 @@ end
90
81
  # This allows you to test:
91
82
  # - Different controller implementations on the same server
92
83
  # - Different API versions (v1, v2, etc.)
93
- # - Multi-tenant endpoints with different configurations
84
+ # - Multi-tenant endpoints with different configurations
94
85
  # - Legacy endpoints alongside new ones
95
- # - Different flow implementations for different endpoints
86
+ # - Different flow implementations for different endpoints