flow_chat 0.4.1 โ†’ 0.5.1

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.
data/README.md CHANGED
@@ -8,7 +8,7 @@ FlowChat is a Rails framework designed for building sophisticated conversational
8
8
  - โœ… **Input Validation & Transformation** - Built-in validation and data conversion
9
9
  - ๐ŸŒŠ **Middleware Architecture** - Flexible request processing pipeline
10
10
  - ๐Ÿ“ฑ **USSD Gateway Support** - Currently supports Nalo gateways
11
- - ๐Ÿ’ฌ **WhatsApp Integration** - Full WhatsApp Cloud API support with multiple processing modes
11
+ - ๐Ÿ’ฌ **WhatsApp Integration** - Full WhatsApp Cloud API support with multiple processing modes and webhook signature validation
12
12
  - ๐Ÿ”ง **Reusable WhatsApp Client** - Standalone client for out-of-band messaging
13
13
  - ๐Ÿงช **Built-in Testing Tools** - Unified simulator for both USSD and WhatsApp testing
14
14
 
@@ -118,8 +118,8 @@ whatsapp:
118
118
  verify_token: "your_verify_token"
119
119
  app_id: "your_app_id"
120
120
  app_secret: "your_app_secret"
121
- webhook_url: "your_webhook_url"
122
121
  business_account_id: "your_business_account_id"
122
+ skip_signature_validation: false # Set to true only for development/testing
123
123
  ```
124
124
 
125
125
  **Option B: Using Environment Variables**
@@ -133,8 +133,8 @@ export WHATSAPP_PHONE_NUMBER_ID="your_phone_number_id"
133
133
  export WHATSAPP_VERIFY_TOKEN="your_verify_token"
134
134
  export WHATSAPP_APP_ID="your_app_id"
135
135
  export WHATSAPP_APP_SECRET="your_app_secret"
136
- export WHATSAPP_WEBHOOK_URL="your_webhook_url"
137
136
  export WHATSAPP_BUSINESS_ACCOUNT_ID="your_business_account_id"
137
+ export WHATSAPP_SKIP_SIGNATURE_VALIDATION="false" # Set to "true" only for development/testing
138
138
  ```
139
139
 
140
140
  FlowChat will automatically use Rails credentials first, falling back to environment variables if credentials are not available.
@@ -152,6 +152,7 @@ custom_config.verify_token = "your_specific_verify_token"
152
152
  custom_config.app_id = "your_specific_app_id"
153
153
  custom_config.app_secret = "your_specific_app_secret"
154
154
  custom_config.business_account_id = "your_specific_business_account_id"
155
+ custom_config.skip_signature_validation = false # Security setting
155
156
 
156
157
  # Use in processor
157
158
  processor = FlowChat::Whatsapp::Processor.new(self) do |config|
@@ -162,7 +163,55 @@ end
162
163
 
163
164
  ๐Ÿ’ก **Tip**: See [examples/multi_tenant_whatsapp_controller.rb](examples/multi_tenant_whatsapp_controller.rb) for comprehensive multi-tenant and per-setup configuration examples.
164
165
 
165
- ### 2. Choose Message Handling Mode
166
+ ### 2. Security Configuration
167
+
168
+ FlowChat includes robust security features for WhatsApp webhook validation:
169
+
170
+ **Webhook Signature Validation** (Recommended for Production)
171
+
172
+ FlowChat automatically validates WhatsApp webhook signatures using your app secret:
173
+
174
+ ```ruby
175
+ # config/initializers/flowchat.rb
176
+
177
+ # Global security configuration
178
+ FlowChat::Config.simulator_secret = "your_secure_random_secret_for_simulator"
179
+
180
+ # WhatsApp security is configured per-configuration
181
+ # The app_secret from your WhatsApp configuration is used for webhook validation
182
+ ```
183
+
184
+ **Security Options:**
185
+
186
+ ```ruby
187
+ # Option 1: Full security (recommended for production)
188
+ custom_config.app_secret = "your_whatsapp_app_secret" # Required for signature validation
189
+ custom_config.skip_signature_validation = false # Default: enforce validation
190
+
191
+ # Option 2: Disable validation (development/testing only)
192
+ custom_config.app_secret = nil # Not required when disabled
193
+ custom_config.skip_signature_validation = true # Explicitly disable validation
194
+ ```
195
+
196
+ โš ๏ธ **Security Warning**: Only disable signature validation in development/testing environments. Production environments should always validate webhook signatures using your WhatsApp app secret.
197
+
198
+ **Simulator Authentication**
199
+
200
+ The simulator mode requires authentication:
201
+
202
+ ```ruby
203
+ # config/initializers/flowchat.rb
204
+ FlowChat::Config.simulator_secret = Rails.application.secret_key_base + "_simulator"
205
+
206
+ # Or use a dedicated secret
207
+ FlowChat::Config.simulator_secret = "your_secure_random_secret_here"
208
+ ```
209
+
210
+ The simulator uses HMAC-SHA256 signed cookies for authentication with 24-hour expiration.
211
+
212
+ ๐Ÿ“š **For comprehensive security documentation, see [SECURITY.md](SECURITY.md)**
213
+
214
+ ### 3. Choose Message Handling Mode
166
215
 
167
216
  FlowChat offers three WhatsApp message handling modes. Configure them in an initializer:
168
217
 
@@ -237,9 +286,7 @@ class WhatsappController < ApplicationController
237
286
  end
238
287
  ```
239
288
 
240
- ๐Ÿ’ก **See [examples/README_whatsapp_modes.md](examples/README_whatsapp_modes.md) for detailed mode explanations and use cases.**
241
-
242
- ### 3. Add WhatsApp Route
289
+ ### 4. Add WhatsApp Route
243
290
 
244
291
  ```ruby
245
292
  Rails.application.routes.draw do
@@ -247,117 +294,115 @@ Rails.application.routes.draw do
247
294
  end
248
295
  ```
249
296
 
250
- ### 4. Enhanced Features for WhatsApp
251
-
252
- The same flow works for both USSD and WhatsApp, but WhatsApp provides additional data and better interactive features:
297
+ ### 5. Enhanced Simulator Setup
253
298
 
254
- ```ruby
255
- class WelcomeFlow < FlowChat::Flow
256
- def main_page
257
- # Access WhatsApp-specific data
258
- Rails.logger.info "Contact: #{app.contact_name}, Phone: #{app.phone_number}"
259
- Rails.logger.info "Message ID: #{app.message_id}, Timestamp: #{app.timestamp}"
260
-
261
- # Handle location sharing
262
- if app.location
263
- app.say "Thanks for sharing your location! We see you're at #{app.location['latitude']}, #{app.location['longitude']}"
264
- return
265
- end
266
-
267
- # Handle media messages
268
- if app.media
269
- app.say "Thanks for the #{app.media['type']} file! We received: #{app.media['id']}"
270
- return
271
- end
299
+ FlowChat provides a powerful built-in simulator for testing flows in both USSD and WhatsApp modes. The simulator allows you to test different endpoints on your local server without needing actual USSD or WhatsApp infrastructure.
272
300
 
273
- name = app.screen(:name) do |prompt|
274
- prompt.ask "Hello! Welcome to our WhatsApp service. What's your name?",
275
- transform: ->(input) { input.strip.titleize }
276
- end
301
+ **Setup Simulator Controller:**
277
302
 
278
- # WhatsApp supports interactive buttons and lists via prompt.select
279
- choice = app.screen(:main_menu) do |prompt|
280
- prompt.select "Hi #{name}! How can I help you?", {
281
- "info" => "๐Ÿ“‹ Get Information",
282
- "support" => "๐Ÿ†˜ Contact Support",
283
- "feedback" => "๐Ÿ’ฌ Give Feedback"
284
- }
285
- end
303
+ ```ruby
304
+ # app/controllers/simulator_controller.rb
305
+ class SimulatorController < ApplicationController
306
+ include FlowChat::Simulator::Controller
286
307
 
287
- case choice
288
- when "info"
289
- show_information_menu
290
- when "support"
291
- contact_support
292
- when "feedback"
293
- collect_feedback
294
- end
308
+ def index
309
+ flowchat_simulator
295
310
  end
296
311
 
297
- private
312
+ protected
298
313
 
299
- def show_information_menu
300
- info_choice = app.screen(:info_menu) do |prompt|
301
- prompt.select "What information do you need?", {
302
- "hours" => "๐Ÿ•’ Business Hours",
303
- "location" => "๐Ÿ“ Our Location",
304
- "services" => "๐Ÿ’ผ Our Services"
314
+ def configurations
315
+ {
316
+ ussd: {
317
+ name: "USSD Integration",
318
+ icon: "๐Ÿ“ฑ",
319
+ processor_type: "ussd",
320
+ provider: "nalo",
321
+ endpoint: "/ussd",
322
+ color: "#007bff"
323
+ },
324
+ whatsapp: {
325
+ name: "WhatsApp Integration",
326
+ icon: "๐Ÿ’ฌ",
327
+ processor_type: "whatsapp",
328
+ provider: "cloud_api",
329
+ endpoint: "/whatsapp/webhook",
330
+ color: "#25D366"
331
+ },
332
+ alternative_whatsapp: {
333
+ name: "Alternative WhatsApp Endpoint",
334
+ icon: "๐Ÿ”„",
335
+ processor_type: "whatsapp",
336
+ provider: "cloud_api",
337
+ endpoint: "/alternative_whatsapp/webhook",
338
+ color: "#17a2b8"
305
339
  }
306
- end
340
+ }
341
+ end
307
342
 
308
- case info_choice
309
- when "hours"
310
- app.say "We're open Monday-Friday 9AM-6PM, Saturday 10AM-4PM. Closed Sundays."
311
- when "location"
312
- app.say "๐Ÿ“ Visit us at 123 Main Street, Downtown. We're next to the coffee shop!"
313
- when "services"
314
- app.say "๐Ÿ’ผ We offer: Web Development, Mobile Apps, Cloud Services, and IT Consulting."
315
- end
343
+ def default_config_key
344
+ :local_whatsapp
316
345
  end
317
346
 
318
- def contact_support
319
- support_choice = app.screen(:support_menu) do |prompt|
320
- prompt.select "How would you like to contact support?", {
321
- "call" => "๐Ÿ“ž Call Us",
322
- "email" => "๐Ÿ“ง Email Us",
323
- "chat" => "๐Ÿ’ฌ Continue Here"
324
- }
325
- end
347
+ def default_phone_number
348
+ "+1234567890"
349
+ end
326
350
 
327
- case support_choice
328
- when "call"
329
- app.say "๐Ÿ“ž Call us at: +1-555-HELP (4357)\nAvailable Mon-Fri 9AM-5PM"
330
- when "email"
331
- app.say "๐Ÿ“ง Email us at: support@company.com\nWe typically respond within 24 hours"
332
- when "chat"
333
- app.say "๐Ÿ’ฌ Great! Please describe your issue and we'll help you right away."
334
- end
351
+ def default_contact_name
352
+ "John Doe"
335
353
  end
354
+ end
355
+ ```
336
356
 
337
- def collect_feedback
338
- rating = app.screen(:rating) do |prompt|
339
- prompt.select "How would you rate our service?", {
340
- "5" => "โญโญโญโญโญ Excellent",
341
- "4" => "โญโญโญโญ Good",
342
- "3" => "โญโญโญ Average",
343
- "2" => "โญโญ Poor",
344
- "1" => "โญ Very Poor"
345
- }
346
- end
357
+ **Add Simulator Route:**
347
358
 
348
- feedback = app.screen(:feedback_text) do |prompt|
349
- prompt.ask "Thank you for the #{rating}-star rating! Please share any additional feedback:"
350
- end
359
+ ```ruby
360
+ # config/routes.rb
361
+ Rails.application.routes.draw do
362
+ get '/simulator' => 'simulator#index'
363
+ # ... other routes
364
+ end
365
+ ```
366
+
367
+ **Configure Simulator Security:**
368
+
369
+ ```ruby
370
+ # config/initializers/flowchat.rb
371
+ FlowChat::Config.simulator_secret = Rails.application.secret_key_base + "_simulator"
372
+ ```
373
+
374
+ **Enable Simulator Mode in Controllers:**
375
+
376
+ For controllers that should support simulator mode, enable it in the processor:
377
+
378
+ ```ruby
379
+ # app/controllers/whatsapp_controller.rb
380
+ class WhatsappController < ApplicationController
381
+ skip_forgery_protection
351
382
 
352
- # Use WhatsApp-specific data for logging
353
- Rails.logger.info "Feedback from #{app.contact_name} (#{app.phone_number}): #{rating} stars - #{feedback}"
383
+ def webhook
384
+ processor = FlowChat::Whatsapp::Processor.new(self, enable_simulator: Rails.env.local?) do |config|
385
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
386
+ config.use_session_store FlowChat::Session::CacheSessionStore
387
+ end
354
388
 
355
- app.say "Thank you for your feedback! We really appreciate it. ๐Ÿ™"
389
+ processor.run WelcomeFlow, :main_page
356
390
  end
357
391
  end
358
392
  ```
359
393
 
360
- For detailed WhatsApp setup instructions, see [WhatsApp Integration Guide](docs/whatsapp_setup.md).
394
+ This is enabled by default in `Rails.env.local?` (development and testing) environments.
395
+
396
+ **Simulator Features:**
397
+ - ๐Ÿ”„ **Endpoint Toggle** - Switch between different integration endpoints
398
+ - ๐Ÿ“ฑ **USSD Mode** - Classic terminal simulation with pagination
399
+ - ๐Ÿ’ฌ **WhatsApp Mode** - Full WhatsApp interface with interactive elements
400
+ - ๐ŸŽจ **Modern UI** - Beautiful, responsive interface
401
+ - ๐Ÿ“Š **Request Logging** - View HTTP requests and responses in real-time
402
+ - ๐Ÿ”ง **Developer Tools** - Character counts, connection status, error handling
403
+ - ๐Ÿ”’ **Secure Authentication** - HMAC-signed cookies with expiration
404
+
405
+ Visit `http://localhost:3000/simulator` to access the simulator interface and test your local endpoints.
361
406
 
362
407
  ## ๐Ÿ”ง Reusable WhatsApp Client
363
408
 
@@ -437,7 +482,6 @@ class NotificationService
437
482
  end
438
483
  ```
439
484
 
440
- ๐Ÿ’ก **See [examples/whatsapp_controller_modes.rb](examples/whatsapp_controller_modes.rb) for comprehensive usage examples.**
441
485
 
442
486
  ## Cross-Platform Compatibility
443
487
 
@@ -905,6 +949,54 @@ config.use_gateway FlowChat::Ussd::Gateway::Nalo
905
949
 
906
950
  ## Testing
907
951
 
952
+ FlowChat provides comprehensive testing capabilities for both USSD and WhatsApp flows. This section covers all testing approaches from simple unit tests to complex integration scenarios.
953
+
954
+ ### Testing Approaches Overview
955
+
956
+ FlowChat supports two main testing strategies:
957
+
958
+ **๐ŸŽฏ Option 1: Simulator Mode (Recommended)**
959
+ - Bypasses WhatsApp API entirely
960
+ - No webhook signature validation required
961
+ - Responses returned as JSON instead of sent to WhatsApp
962
+ - Perfect for unit and integration testing
963
+ - Works with built-in web simulator interface
964
+
965
+ **๐Ÿ”’ Option 2: Skip Signature Validation**
966
+ - Tests real webhook endpoints without security complexity
967
+ - Useful for staging environments
968
+ - Set `skip_signature_validation = true`
969
+
970
+ ### Environment-Specific Testing Configuration
971
+
972
+ Configure testing behavior per environment:
973
+
974
+ ```ruby
975
+ # config/initializers/flowchat.rb
976
+ case Rails.env
977
+ when 'development'
978
+ # Enable simulator mode for testing
979
+ FlowChat::Config.whatsapp.message_handling_mode = :simulator
980
+ FlowChat::Config.simulator_secret = Rails.application.secret_key_base + "_dev"
981
+
982
+ when 'test'
983
+ # Enable simulator mode for automated testing
984
+ FlowChat::Config.whatsapp.message_handling_mode = :simulator
985
+ FlowChat::Config.simulator_secret = "test_secret"
986
+
987
+ when 'staging'
988
+ # Use inline mode but allow simulator for testing
989
+ FlowChat::Config.whatsapp.message_handling_mode = :inline
990
+ FlowChat::Config.simulator_secret = ENV['FLOWCHAT_SIMULATOR_SECRET']
991
+
992
+ when 'production'
993
+ # Background processing in production, no simulator
994
+ FlowChat::Config.whatsapp.message_handling_mode = :background
995
+ FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
996
+ FlowChat::Config.simulator_secret = nil
997
+ end
998
+ ```
999
+
908
1000
  ### Unit Testing Flows
909
1001
 
910
1002
  Test your flows in isolation using the provided test helpers:
@@ -929,257 +1021,288 @@ class WelcomeFlowTest < Minitest::Test
929
1021
 
930
1022
  assert_equal "Hello, John Doe! Welcome to FlowChat.", error.prompt
931
1023
  end
932
-
933
- def test_welcome_flow_without_input
934
- @context.input = nil
935
- app = FlowChat::Ussd::App.new(@context)
936
-
937
- error = assert_raises(FlowChat::Interrupt::Prompt) do
938
- flow = WelcomeFlow.new(app)
939
- flow.main_page
940
- end
941
-
942
- assert_equal "Welcome! What's your name?", error.prompt
943
- end
944
1024
  end
945
1025
  ```
946
1026
 
947
1027
  ### Integration Testing
948
1028
 
949
- Test complete user journeys:
1029
+ #### Simulator Mode Testing
950
1030
 
951
1031
  ```ruby
952
- class RegistrationFlowIntegrationTest < Minitest::Test
953
- def test_complete_registration_flow
954
- controller = mock_controller
955
- processor = FlowChat::Ussd::Processor.new(controller) do |config|
956
- config.use_gateway MockGateway
957
- config.use_session_store FlowChat::Session::RailsSessionStore
958
- end
1032
+ test "complete flow via simulator" do
1033
+ # Generate valid simulator cookie for authentication
1034
+ valid_cookie = generate_simulator_cookie
1035
+
1036
+ webhook_payload = {
1037
+ entry: [{ changes: [{ value: { messages: [{ from: "1234567890", text: { body: "Hello" }, type: "text" }] } }] }],
1038
+ simulator_mode: true
1039
+ }
959
1040
 
960
- # Simulate the complete flow
961
- # First request - ask for phone
962
- # Second request - provide phone, ask for age
963
- # Third request - provide age, complete registration
964
- end
1041
+ post "/whatsapp/webhook", params: webhook_payload, cookies: { flowchat_simulator: valid_cookie }
1042
+ assert_response :success
965
1043
  end
966
1044
  ```
967
1045
 
968
- ### Testing Middleware
969
-
970
- Test your custom middleware in isolation:
1046
+ #### Testing with Skipped Validation
971
1047
 
972
1048
  ```ruby
973
- class LoggingMiddlewareTest < Minitest::Test
974
- def test_logs_request_and_response
975
- # Mock the next app in the chain
976
- app = lambda { |context| [:prompt, "Test response", []] }
977
- middleware = LoggingMiddleware.new(app)
978
-
979
- context = FlowChat::Context.new
980
- context.input = "test input"
981
-
982
- # Capture log output
983
- log_output = StringIO.new
984
- Rails.stub(:logger, Logger.new(log_output)) do
985
- type, prompt, choices = middleware.call(context)
986
-
987
- assert_equal :prompt, type
988
- assert_equal "Test response", prompt
989
- assert_includes log_output.string, "Processing USSD request: test input"
990
- assert_includes log_output.string, "Response: Test response"
991
- end
992
- end
1049
+ test "webhook processing with skipped validation" do
1050
+ config = FlowChat::Whatsapp::Configuration.new
1051
+ config.skip_signature_validation = true # Skip for testing
1052
+
1053
+ # No signature required when validation is skipped
1054
+ post "/whatsapp/webhook", params: webhook_payload.to_json
1055
+ assert_response :success
993
1056
  end
994
1057
  ```
995
1058
 
996
- ### Testing Middleware Stack Modification
997
-
998
- Test runtime middleware modifications:
1059
+ ### Test Helper Methods
999
1060
 
1000
1061
  ```ruby
1001
- class ProcessorMiddlewareTest < Minitest::Test
1002
- def test_custom_middleware_insertion
1003
- controller = mock_controller
1004
- processor = FlowChat::Ussd::Processor.new(controller) do |config|
1005
- config.use_gateway MockGateway
1006
- config.use_session_store FlowChat::Session::RailsSessionStore
1007
- end
1008
-
1009
- custom_middleware_called = false
1010
- custom_middleware = Class.new do
1011
- define_method(:initialize) { |app| @app = app }
1012
- define_method(:call) do |context|
1013
- custom_middleware_called = true
1014
- @app.call(context)
1015
- end
1016
- end
1017
-
1018
- processor.run(TestFlow, :main_page) do |stack|
1019
- stack.use custom_middleware
1020
- stack.insert_before FlowChat::Ussd::Middleware::Executor, custom_middleware
1021
- end
1022
-
1023
- assert custom_middleware_called, "Custom middleware should have been executed"
1024
- end
1062
+ private
1063
+
1064
+ def generate_webhook_signature(payload_json, secret = @app_secret)
1065
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), secret, payload_json)
1066
+ end
1067
+
1068
+ def generate_simulator_cookie(secret = FlowChat::Config.simulator_secret)
1069
+ timestamp = Time.now.to_i
1070
+ message = "simulator:#{timestamp}"
1071
+ signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), secret, message)
1072
+ "#{timestamp}:#{signature}"
1025
1073
  end
1026
1074
  ```
1027
1075
 
1028
- ### FlowChat Unified Simulator
1076
+ ## Configuration Reference
1029
1077
 
1030
- Use the built-in unified simulator for interactive testing of both USSD and WhatsApp flows:
1078
+ ### Framework Configuration
1031
1079
 
1032
1080
  ```ruby
1033
- class SimulatorController < ApplicationController
1034
- include FlowChat::Simulator::Controller
1035
-
1036
- def index
1037
- flowchat_simulator
1038
- end
1039
-
1040
- protected
1041
-
1042
- def configurations
1043
- {
1044
- production_ussd: {
1045
- name: "Production USSD",
1046
- icon: "๐Ÿญ",
1047
- processor_type: "ussd",
1048
- provider: "nalo",
1049
- endpoint: "/ussd",
1050
- color: "#28a745"
1051
- },
1052
- staging_whatsapp: {
1053
- name: "Staging WhatsApp",
1054
- icon: "๐Ÿงช",
1055
- processor_type: "whatsapp",
1056
- provider: "cloud_api",
1057
- endpoint: "/whatsapp/webhook",
1058
- color: "#17a2b8"
1059
- },
1060
- local_ussd: {
1061
- name: "Local USSD",
1062
- icon: "๐Ÿ’ป",
1063
- processor_type: "ussd",
1064
- provider: "nalo",
1065
- endpoint: "http://localhost:3000/ussd",
1066
- color: "#6f42c1"
1067
- }
1068
- }
1069
- end
1081
+ # config/initializers/flowchat.rb
1070
1082
 
1071
- def default_config_key
1072
- :local_ussd
1073
- end
1083
+ # Core configuration
1084
+ FlowChat::Config.logger = Rails.logger
1085
+ FlowChat::Config.cache = Rails.cache
1086
+ FlowChat::Config.simulator_secret = "your_secure_secret_here"
1087
+
1088
+ # USSD configuration
1089
+ FlowChat::Config.ussd.pagination_page_size = 140
1090
+ FlowChat::Config.ussd.pagination_next_option = "#"
1091
+ FlowChat::Config.ussd.pagination_next_text = "More"
1092
+ FlowChat::Config.ussd.pagination_back_option = "0"
1093
+ FlowChat::Config.ussd.pagination_back_text = "Back"
1094
+ FlowChat::Config.ussd.resumable_sessions_enabled = true
1095
+ FlowChat::Config.ussd.resumable_sessions_timeout_seconds = 300
1096
+
1097
+ # WhatsApp configuration
1098
+ FlowChat::Config.whatsapp.message_handling_mode = :inline # :inline, :background, :simulator
1099
+ FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
1100
+ ```
1074
1101
 
1075
- def default_phone_number
1076
- "+254712345678"
1077
- end
1102
+ ### WhatsApp Security Configuration
1078
1103
 
1079
- def default_contact_name
1080
- "John Doe"
1081
- end
1082
- end
1104
+ ```ruby
1105
+ # Per-configuration security settings
1106
+ config = FlowChat::Whatsapp::Configuration.new
1107
+ config.app_secret = "your_whatsapp_app_secret" # Required for signature validation
1108
+ config.skip_signature_validation = false # Default: false (enforce validation)
1109
+
1110
+ # Security modes:
1111
+ # 1. Full security (production)
1112
+ config.app_secret = "secret"
1113
+ config.skip_signature_validation = false
1114
+
1115
+ # 2. Development mode (disable validation)
1116
+ config.app_secret = nil
1117
+ config.skip_signature_validation = true
1083
1118
  ```
1084
1119
 
1085
- Add to routes and visit `http://localhost:3000/simulator`.
1086
-
1087
- **Key Features:**
1088
- - ๐Ÿ”„ **Platform Toggle** - Switch between USSD and WhatsApp modes with configuration selection
1089
- - ๐Ÿ“ฑ **USSD Mode** - Classic green-screen terminal simulation with provider support (Nalo, Nsano)
1090
- - ๐Ÿ’ฌ **WhatsApp Mode** - Full WhatsApp interface with interactive buttons, lists, and rich messaging
1091
- - โš™๏ธ **Multi-Environment** - Support for different configurations (local, staging, production)
1092
- - ๐ŸŽจ **Modern UI** - Beautiful, responsive interface with real-time status indicators
1093
- - ๐Ÿ“Š **Request Logging** - View all HTTP requests and responses in real-time
1094
- - ๐Ÿ”ง **Developer Tools** - Character counts, connection status, and comprehensive error handling
1120
+ ## Best Practices
1095
1121
 
1096
- The simulator automatically adapts its interface based on the selected configuration:
1097
- - **USSD**: Shows traditional terminal-style interface with character limits and pagination
1098
- - **WhatsApp**: Displays realistic WhatsApp chat interface with support for interactive elements
1122
+ ### 1. Security Best Practices
1099
1123
 
1100
- ๐Ÿ’ก **Tip**: See [examples/simulator_controller.rb](examples/simulator_controller.rb) for advanced configurations including multi-tenant support and environment-specific endpoints.
1124
+ **Production Security Checklist:**
1101
1125
 
1102
- ## Best Practices
1126
+ โœ… **Always configure app_secret** for webhook validation
1127
+ ```ruby
1128
+ config.app_secret = "your_whatsapp_app_secret" # Never leave empty in production
1129
+ config.skip_signature_validation = false # Never disable in production
1130
+ ```
1103
1131
 
1104
- ### 1. Keep Flows Focused
1132
+ โœ… **Use secure simulator secrets**
1133
+ ```ruby
1134
+ # Use Rails secret_key_base + suffix for uniqueness
1135
+ FlowChat::Config.simulator_secret = Rails.application.secret_key_base + "_simulator"
1105
1136
 
1106
- Create separate flows for different user journeys:
1137
+ # Or use environment variables
1138
+ FlowChat::Config.simulator_secret = ENV['FLOWCHAT_SIMULATOR_SECRET']
1139
+ ```
1107
1140
 
1141
+ โœ… **Environment-specific configuration**
1108
1142
  ```ruby
1109
- # Good: Focused flows
1110
- class LoginFlow < FlowChat::Flow
1111
- # Handle user authentication
1112
- end
1113
-
1114
- class RegistrationFlow < FlowChat::Flow
1115
- # Handle user registration
1143
+ # Different security levels per environment
1144
+ if Rails.env.production?
1145
+ config.skip_signature_validation = false # Enforce validation
1146
+ else
1147
+ config.skip_signature_validation = true # Allow for development
1116
1148
  end
1149
+ ```
1117
1150
 
1118
- class AccountFlow < FlowChat::Flow
1119
- # Handle account management
1120
- end
1151
+ โœ… **Enable simulator only when needed**
1152
+ ```ruby
1153
+ # Only enable simulator in development/staging (default)
1154
+ enable_simulator = Rails.env.development? || Rails.env.staging?
1155
+ processor = FlowChat::Whatsapp::Processor.new(self, enable_simulator: enable_simulator)
1121
1156
  ```
1122
1157
 
1123
- ### 2. Use Descriptive Screen Names
1158
+ ### 5. Choose the Right WhatsApp Mode
1124
1159
 
1125
- Screen names should clearly indicate their purpose:
1160
+ Configure the appropriate mode based on your environment and requirements:
1126
1161
 
1127
1162
  ```ruby
1128
- # Good
1129
- app.screen(:customer_phone_number) { |p| p.ask "Phone:" }
1130
- app.screen(:payment_confirmation) { |p| p.yes? "Confirm payment?" }
1131
-
1132
- # Avoid
1133
- app.screen(:input1) { |p| p.ask "Phone:" }
1134
- app.screen(:confirm) { |p| p.yes? "Confirm payment?" }
1135
- ```
1163
+ # config/initializers/flowchat.rb
1136
1164
 
1137
- ### 3. Validate Early and Often
1165
+ # Development/Testing - use simulator mode with security
1166
+ if Rails.env.development? || Rails.env.test?
1167
+ FlowChat::Config.whatsapp.message_handling_mode = :simulator
1168
+ FlowChat::Config.simulator_secret = Rails.application.secret_key_base + "_dev"
1169
+ end
1138
1170
 
1139
- Always validate user input to provide clear feedback:
1171
+ # Staging - use inline mode with full security
1172
+ if Rails.env.staging?
1173
+ FlowChat::Config.whatsapp.message_handling_mode = :inline
1174
+ FlowChat::Config.simulator_secret = ENV['FLOWCHAT_SIMULATOR_SECRET']
1175
+ end
1140
1176
 
1141
- ```ruby
1142
- app.screen(:amount) do |prompt|
1143
- prompt.ask "Enter amount:",
1144
- convert: ->(input) { input.to_f },
1145
- validate: ->(amount) {
1146
- return "Amount must be positive" if amount <= 0
1147
- return "Maximum amount is $1000" if amount > 1000
1148
- nil
1149
- }
1177
+ # Production - use background mode with full security
1178
+ if Rails.env.production?
1179
+ FlowChat::Config.whatsapp.message_handling_mode = :background
1180
+ FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
1181
+ FlowChat::Config.simulator_secret = ENV['FLOWCHAT_SIMULATOR_SECRET']
1150
1182
  end
1151
1183
  ```
1152
1184
 
1153
- ### 4. Handle Edge Cases
1185
+ ### 6. Error Handling Best Practices
1154
1186
 
1155
- Consider error scenarios and provide helpful messages:
1187
+ Handle security and configuration errors gracefully:
1156
1188
 
1157
1189
  ```ruby
1158
- def main_page
1190
+ def webhook
1159
1191
  begin
1160
- process_user_request
1161
- rescue PaymentError => e
1162
- app.say "Payment failed: #{e.message}. Please try again."
1163
- rescue SystemError
1164
- app.say "System temporarily unavailable. Please try again later."
1192
+ processor = FlowChat::Whatsapp::Processor.new(self, enable_simulator: Rails.env.development?) do |config|
1193
+ config.use_gateway FlowChat::Whatsapp::Gateway::CloudApi
1194
+ config.use_session_store FlowChat::Session::CacheSessionStore
1195
+ end
1196
+
1197
+ processor.run WelcomeFlow, :main_page
1198
+
1199
+ rescue FlowChat::Whatsapp::ConfigurationError => e
1200
+ Rails.logger.error "WhatsApp configuration error: #{e.message}"
1201
+ head :internal_server_error
1202
+
1203
+ rescue => e
1204
+ Rails.logger.error "Unexpected error processing WhatsApp webhook: #{e.message}"
1205
+ head :internal_server_error
1165
1206
  end
1166
1207
  end
1167
1208
  ```
1168
1209
 
1169
- ### 5. Choose the Right WhatsApp Mode
1170
-
1171
- Configure the appropriate mode in your initializer:
1172
-
1173
- ```ruby
1174
- # config/initializers/flowchat.rb
1175
-
1176
- # Development/Testing - use simulator mode
1177
- FlowChat::Config.whatsapp.message_handling_mode = :simulator
1178
-
1179
- # Low-volume Applications - use inline mode
1180
- FlowChat::Config.whatsapp.message_handling_mode = :inline
1181
-
1182
- # High-volume Production - use background mode (sync processing + async sending)
1183
- FlowChat::Config.whatsapp.message_handling_mode = :background
1184
- FlowChat::Config.whatsapp.background_job_class = 'WhatsappMessageJob'
1185
- ```
1210
+ ## Appendix
1211
+
1212
+ ### Context Variables Reference
1213
+
1214
+ FlowChat provides a rich set of context variables that are available throughout your flows. These variables are automatically populated based on the gateway and message type.
1215
+
1216
+ #### Core Context Variables
1217
+
1218
+ **Request Variables**
1219
+
1220
+ | Variable | Description |
1221
+ |----------|-------------|
1222
+ | `context.input` | Current user input |
1223
+ | `context["request.id"]` | Unique request ID |
1224
+ | `context["request.timestamp"]` | Request timestamp |
1225
+ | `context["request.msisdn"]` | User's phone number |
1226
+ | `context["request.gateway"]` | Current gateway (`:whatsapp_cloud_api`, `:ussd_nalo`) |
1227
+ | `context["enable_simulator"]` | Whether simulator mode is enabled for this request |
1228
+ | `context["simulator_mode"]` | Whether simulator mode is active |
1229
+
1230
+ **Flow Variables**
1231
+
1232
+ | Variable | Description |
1233
+ |----------|-------------|
1234
+ | `context["flow.name"]` | Current flow name |
1235
+ | `context["flow.class"]` | Current flow class |
1236
+ | `context["flow.action"]` | Current flow action/method |
1237
+
1238
+ **Session Variables**
1239
+
1240
+ | Variable | Description |
1241
+ |----------|-------------|
1242
+ | `context["session.id"]` | Current session ID |
1243
+ | `context["session.store"]` | Session data store |
1244
+
1245
+ **Controller Variables**
1246
+
1247
+ | Variable | Description |
1248
+ |----------|-------------|
1249
+ | `context.controller` | Current controller instance |
1250
+
1251
+ #### WhatsApp-Specific Variables
1252
+
1253
+ | Variable | Description |
1254
+ |----------|-------------|
1255
+ | `context["whatsapp.message_result"]` | Result of last message send |
1256
+ | `context["whatsapp.message_id"]` | WhatsApp message ID |
1257
+ | `context["whatsapp.contact_name"]` | WhatsApp contact name |
1258
+ | `context["whatsapp.location"]` | Location data if shared |
1259
+ | `context["whatsapp.media"]` | Media data if attached |
1260
+
1261
+ #### USSD-Specific Variables
1262
+
1263
+ | Variable | Description |
1264
+ |----------|-------------|
1265
+ | `context["ussd.request"]` | Original USSD request |
1266
+ | `context["ussd.response"]` | USSD response object |
1267
+ | `context["ussd.pagination"]` | Pagination data for long messages |
1268
+
1269
+ #### Session Data Variables
1270
+
1271
+ | Variable | Description |
1272
+ |----------|-------------|
1273
+ | `context["$started_at$"]` | When the conversation started |
1274
+
1275
+ #### Usage Notes
1276
+
1277
+ 1. **Access Methods:**
1278
+ ```ruby
1279
+ # Direct access
1280
+ context.input
1281
+ context["request.msisdn"]
1282
+
1283
+ # Through app object in flows
1284
+ app.phone_number
1285
+ app.contact_name
1286
+ app.message_id
1287
+ ```
1288
+
1289
+ 2. **Gateway-Specific Variables:**
1290
+ - WhatsApp variables are only available when using WhatsApp gateway
1291
+ - USSD variables are only available when using USSD gateway
1292
+ - Core variables are available across all gateways
1293
+
1294
+ 3. **Session Persistence:**
1295
+ - Session data persists across requests
1296
+ - WhatsApp sessions expire after 7 days
1297
+ - USSD sessions expire after 1 hour
1298
+ - Default session TTL is 1 day
1299
+
1300
+ 4. **Security:**
1301
+ - Webhook signatures are validated for WhatsApp requests
1302
+ - Simulator mode requires valid simulator cookie
1303
+ - Session data is encrypted at rest
1304
+
1305
+ 5. **Flow Control:**
1306
+ - Context variables can be used to control flow logic
1307
+ - Session data can be used to maintain state
1308
+ - Request data can be used for validation