kapso-client-ruby 1.0.0 → 1.0.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.
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example Rails controller showing various WhatsApp integration patterns
4
+ class NotificationsController < ApplicationController
5
+ before_action :authenticate_user!
6
+ before_action :ensure_phone_number, only: [:send_notification, :send_order_update]
7
+
8
+ # GET /notifications
9
+ # Show notification preferences and send test message
10
+ def index
11
+ @user = current_user
12
+ @message_service = KapsoMessageService.new
13
+ @templates = @message_service.list_templates&.dig('data') || []
14
+ end
15
+
16
+ # POST /notifications/test
17
+ # Send a test message to the current user
18
+ def send_test
19
+ service = KapsoMessageService.new
20
+
21
+ begin
22
+ result = service.send_text(
23
+ phone_number: current_user.phone_number,
24
+ message: "Hello #{current_user.name}! This is a test message from #{Rails.application.class.name.deconstantize}."
25
+ )
26
+
27
+ if result
28
+ flash[:success] = "Test message sent successfully! Message ID: #{result.dig('messages', 0, 'id')}"
29
+ else
30
+ flash[:error] = "Failed to send test message. Please check the logs."
31
+ end
32
+ rescue KapsoClientRuby::Error => e
33
+ flash[:error] = "Error sending message: #{e.message}"
34
+ Rails.logger.error "WhatsApp test message error: #{e.message}"
35
+ end
36
+
37
+ redirect_to notifications_path
38
+ end
39
+
40
+ # POST /notifications/welcome
41
+ # Send welcome message using template
42
+ def send_welcome
43
+ service = KapsoMessageService.new
44
+
45
+ begin
46
+ result = service.send_welcome_message(current_user)
47
+
48
+ if result
49
+ flash[:success] = "Welcome message sent!"
50
+
51
+ # Track the sent message
52
+ current_user.whatsapp_messages.create!(
53
+ message_id: result.dig('messages', 0, 'id'),
54
+ message_type: 'welcome',
55
+ status: 'sent',
56
+ sent_at: Time.current
57
+ )
58
+ else
59
+ flash[:error] = "Failed to send welcome message."
60
+ end
61
+ rescue KapsoClientRuby::Error => e
62
+ flash[:error] = "Error: #{e.message}"
63
+ end
64
+
65
+ redirect_to notifications_path
66
+ end
67
+
68
+ # POST /notifications/custom
69
+ # Send custom message with template
70
+ def send_custom
71
+ template_name = params[:template_name]
72
+ custom_params = params[:template_params] || {}
73
+
74
+ unless template_name.present?
75
+ flash[:error] = "Please select a template"
76
+ redirect_to notifications_path
77
+ return
78
+ end
79
+
80
+ service = KapsoClientRuby::Rails::Service.new
81
+
82
+ begin
83
+ # Build template components from form params
84
+ components = []
85
+
86
+ if custom_params.present?
87
+ body_params = custom_params.values.map do |param|
88
+ { type: 'text', text: param.to_s }
89
+ end
90
+
91
+ components << {
92
+ type: 'body',
93
+ parameters: body_params
94
+ } if body_params.any?
95
+ end
96
+
97
+ result = service.send_template_message(
98
+ to: current_user.phone_number,
99
+ template_name: template_name,
100
+ language: current_user.preferred_language || 'en',
101
+ components: components
102
+ )
103
+
104
+ flash[:success] = "Template message sent! ID: #{result.dig('messages', 0, 'id')}"
105
+
106
+ # Log the message
107
+ Rails.logger.info "Template '#{template_name}' sent to #{current_user.phone_number}"
108
+
109
+ rescue KapsoClientRuby::Error => e
110
+ flash[:error] = "Failed to send template: #{e.message}"
111
+ Rails.logger.error "Template send error: #{e.message}"
112
+ end
113
+
114
+ redirect_to notifications_path
115
+ end
116
+
117
+ # POST /notifications/document
118
+ # Send a document to the user
119
+ def send_document
120
+ document_url = params[:document_url]
121
+ filename = params[:filename]
122
+ caption = params[:caption]
123
+
124
+ unless document_url.present?
125
+ flash[:error] = "Document URL is required"
126
+ redirect_to notifications_path
127
+ return
128
+ end
129
+
130
+ service = KapsoMessageService.new
131
+
132
+ begin
133
+ result = service.send_document(
134
+ phone_number: current_user.phone_number,
135
+ document_url: document_url,
136
+ filename: filename,
137
+ caption: caption
138
+ )
139
+
140
+ flash[:success] = "Document sent successfully!"
141
+ Rails.logger.info "Document sent to #{current_user.phone_number}: #{document_url}"
142
+
143
+ rescue KapsoClientRuby::Error => e
144
+ flash[:error] = "Failed to send document: #{e.message}"
145
+ end
146
+
147
+ redirect_to notifications_path
148
+ end
149
+
150
+ # GET /notifications/message_status/:message_id
151
+ # Check status of a sent message
152
+ def message_status
153
+ message_id = params[:id]
154
+ service = KapsoMessageService.new
155
+
156
+ begin
157
+ status = service.get_message_status(message_id)
158
+
159
+ if status
160
+ render json: {
161
+ message_id: status['id'],
162
+ status: status['status'],
163
+ timestamp: status['timestamp'],
164
+ recipient_id: status['recipient_id']
165
+ }
166
+ else
167
+ render json: { error: 'Message not found' }, status: :not_found
168
+ end
169
+ rescue KapsoClientRuby::Error => e
170
+ render json: { error: e.message }, status: :unprocessable_entity
171
+ end
172
+ end
173
+
174
+ # POST /notifications/bulk
175
+ # Send bulk messages (should be queued in production)
176
+ def send_bulk
177
+ recipients = params[:recipients]&.split(',')&.map(&:strip) || []
178
+ message = params[:message]
179
+
180
+ if recipients.empty? || message.blank?
181
+ flash[:error] = "Recipients and message are required"
182
+ redirect_to notifications_path
183
+ return
184
+ end
185
+
186
+ # In production, this should be done via background jobs
187
+ successful_sends = 0
188
+ failed_sends = 0
189
+
190
+ recipients.each do |phone_number|
191
+ begin
192
+ service = KapsoMessageService.new
193
+ result = service.send_text(phone_number: phone_number, message: message)
194
+
195
+ if result
196
+ successful_sends += 1
197
+ Rails.logger.info "Bulk message sent to #{phone_number}"
198
+ else
199
+ failed_sends += 1
200
+ end
201
+
202
+ # Rate limiting - small delay between messages
203
+ sleep(0.5)
204
+
205
+ rescue KapsoClientRuby::Error => e
206
+ failed_sends += 1
207
+ Rails.logger.error "Bulk message failed for #{phone_number}: #{e.message}"
208
+ end
209
+ end
210
+
211
+ flash[:success] = "Bulk send complete: #{successful_sends} sent, #{failed_sends} failed"
212
+ redirect_to notifications_path
213
+ end
214
+
215
+ private
216
+
217
+ def ensure_phone_number
218
+ unless current_user.phone_number.present?
219
+ flash[:error] = "Please add a phone number to your profile first"
220
+ redirect_to edit_user_registration_path
221
+ end
222
+ end
223
+
224
+ def notification_params
225
+ params.permit(:message, :template_name, :document_url, :filename, :caption, :recipients, template_params: {})
226
+ end
227
+ end
@@ -5,7 +5,7 @@ require 'whatsapp_cloud_api'
5
5
  puts "=== Template Management Examples ==="
6
6
 
7
7
  # Initialize client
8
- client = WhatsAppCloudApi::Client.new(
8
+ client = KapsoClientRuby::Client.new(
9
9
  access_token: ENV['WHATSAPP_ACCESS_TOKEN']
10
10
  )
11
11
 
@@ -27,7 +27,7 @@ begin
27
27
  puts "\nMore templates available. Next cursor: #{templates.paging.after}"
28
28
  end
29
29
 
30
- rescue WhatsAppCloudApi::Errors::GraphApiError => e
30
+ rescue KapsoClientRuby::Errors::GraphApiError => e
31
31
  puts "Error listing templates: #{e.message}"
32
32
  end
33
33
 
@@ -72,7 +72,7 @@ begin
72
72
  puts "Template ID: #{response.id}"
73
73
  puts "Status: #{response.status}"
74
74
 
75
- rescue WhatsAppCloudApi::Errors::GraphApiError => e
75
+ rescue KapsoClientRuby::Errors::GraphApiError => e
76
76
  puts "Error creating marketing template: #{e.message}"
77
77
 
78
78
  if e.template_error?
@@ -102,7 +102,7 @@ begin
102
102
  puts "Template ID: #{response.id}"
103
103
  puts "Status: #{response.status}"
104
104
 
105
- rescue WhatsAppCloudApi::Errors::GraphApiError => e
105
+ rescue KapsoClientRuby::Errors::GraphApiError => e
106
106
  puts "Error creating auth template: #{e.message}"
107
107
  end
108
108
 
@@ -140,7 +140,7 @@ begin
140
140
  puts "Utility template created!"
141
141
  puts "Template ID: #{response.id}"
142
142
 
143
- rescue WhatsAppCloudApi::Errors::GraphApiError => e
143
+ rescue KapsoClientRuby::Errors::GraphApiError => e
144
144
  puts "Error creating utility template: #{e.message}"
145
145
  end
146
146
 
@@ -204,7 +204,7 @@ begin
204
204
  puts "Complex template created!"
205
205
  puts "Template ID: #{response.id}"
206
206
 
207
- rescue WhatsAppCloudApi::Errors::GraphApiError => e
207
+ rescue KapsoClientRuby::Errors::GraphApiError => e
208
208
  puts "Error creating complex template: #{e.message}"
209
209
  end
210
210
 
@@ -257,7 +257,7 @@ begin
257
257
 
258
258
  puts "Parameterized template sent: #{response2.messages.first.id}"
259
259
 
260
- rescue WhatsAppCloudApi::Errors::GraphApiError => e
260
+ rescue KapsoClientRuby::Errors::GraphApiError => e
261
261
  puts "Error sending template: #{e.message}"
262
262
 
263
263
  case e.category
@@ -299,7 +299,7 @@ begin
299
299
  puts "Template updated successfully"
300
300
  end
301
301
 
302
- rescue WhatsAppCloudApi::Errors::GraphApiError => e
302
+ rescue KapsoClientRuby::Errors::GraphApiError => e
303
303
  puts "Template management error: #{e.message}"
304
304
  end
305
305
 
@@ -324,7 +324,7 @@ begin
324
324
 
325
325
  puts "Template deleted by name"
326
326
 
327
- rescue WhatsAppCloudApi::Errors::GraphApiError => e
327
+ rescue KapsoClientRuby::Errors::GraphApiError => e
328
328
  puts "Delete error: #{e.message}"
329
329
 
330
330
  if e.http_status == 404
@@ -374,7 +374,7 @@ def create_bad_template_example(client, business_account_id)
374
374
  }
375
375
  ]
376
376
  )
377
- rescue WhatsAppCloudApi::Errors::GraphApiError => e
377
+ rescue KapsoClientRuby::Errors::GraphApiError => e
378
378
  puts "Bad template rejected (expected): #{e.message}"
379
379
  end
380
380
  end
@@ -30,8 +30,8 @@ module KapsoClientRuby
30
30
  @kapso_proxy = detect_kapso_proxy(@base_url)
31
31
 
32
32
  # Configuration with defaults
33
- config = WhatsAppCloudApi.configuration
34
- @logger = logger || WhatsAppCloudApi.logger
33
+ config = KapsoClientRuby.configuration
34
+ @logger = logger || KapsoClientRuby.logger
35
35
  @debug = debug.nil? ? config.debug : debug
36
36
  @timeout = timeout || config.timeout
37
37
  @open_timeout = open_timeout || config.open_timeout
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+
5
+ module KapsoClientRuby
6
+ module Rails
7
+ module Generators
8
+ class InstallGenerator < ::Rails::Generators::Base
9
+ source_root File.expand_path('templates', __dir__)
10
+
11
+ desc 'Install KapsoClientRuby in a Rails application'
12
+
13
+ def create_initializer
14
+ template 'initializer.rb.erb', 'config/initializers/kapso_client_ruby.rb'
15
+ end
16
+
17
+ def create_env_example
18
+ template 'env.erb', '.env.example'
19
+ end
20
+
21
+ def create_webhook_controller
22
+ template 'webhook_controller.rb.erb', 'app/controllers/kapso_webhooks_controller.rb'
23
+ end
24
+
25
+ def create_service_example
26
+ template 'message_service.rb.erb', 'app/services/kapso_message_service.rb'
27
+ end
28
+
29
+ def add_routes
30
+ route <<~RUBY
31
+ # Kapso webhook endpoint
32
+ post '/webhooks/kapso', to: 'kapso_webhooks#create'
33
+ get '/webhooks/kapso', to: 'kapso_webhooks#verify' # For webhook verification
34
+ RUBY
35
+ end
36
+
37
+ def show_readme
38
+ say <<~MESSAGE
39
+
40
+ KapsoClientRuby has been successfully installed!
41
+
42
+ Next steps:
43
+ 1. Add your Kapso credentials to your environment variables:
44
+ - KAPSO_API_KEY
45
+ - KAPSO_PHONE_NUMBER_ID
46
+ - KAPSO_BUSINESS_ACCOUNT_ID
47
+
48
+ 2. Review and customize the generated files:
49
+ - config/initializers/kapso_client_ruby.rb
50
+ - app/controllers/kapso_webhooks_controller.rb
51
+ - app/services/kapso_message_service.rb
52
+
53
+ 3. Set up your webhook URL in the Kapso dashboard:
54
+ https://yourapp.com/webhooks/kapso
55
+
56
+ 4. Test the integration:
57
+ rails runner "KapsoMessageService.new.send_test_message"
58
+
59
+ For more information, see: https://github.com/PabloB07/kapso-client-ruby
60
+
61
+ MESSAGE
62
+ end
63
+
64
+ private
65
+
66
+ def application_name
67
+ if defined?(Rails) && Rails.application
68
+ Rails.application.class.name.split('::').first
69
+ else
70
+ 'YourApp'
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,21 @@
1
+ # Kapso API Configuration
2
+ # Add these to your actual .env file (don't commit API keys to version control)
3
+
4
+ # Required: Your Kapso API access token
5
+ KAPSO_API_KEY=your_api_key_here
6
+
7
+ # Required: Your WhatsApp Business phone number ID
8
+ KAPSO_PHONE_NUMBER_ID=your_phone_number_id_here
9
+
10
+ # Required: Your WhatsApp Business account ID
11
+ KAPSO_BUSINESS_ACCOUNT_ID=your_business_account_id_here
12
+
13
+ # Optional: API configuration
14
+ KAPSO_API_HOST=https://graph.facebook.com
15
+ KAPSO_API_VERSION=v18.0
16
+ KAPSO_TIMEOUT=30
17
+
18
+ # Optional: Debug and retry settings
19
+ KAPSO_DEBUG=false
20
+ KAPSO_RETRY_ON_FAILURE=true
21
+ KAPSO_MAX_RETRIES=3
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # KapsoClientRuby configuration for Rails
4
+ KapsoClientRuby.configure do |config|
5
+ # API credentials - set these in your environment variables
6
+ config.api_key = ENV['KAPSO_API_KEY']
7
+ config.phone_number_id = ENV['KAPSO_PHONE_NUMBER_ID']
8
+ config.business_account_id = ENV['KAPSO_BUSINESS_ACCOUNT_ID']
9
+
10
+ # API configuration
11
+ config.api_host = ENV.fetch('KAPSO_API_HOST', 'https://graph.facebook.com')
12
+ config.api_version = ENV.fetch('KAPSO_API_VERSION', 'v18.0')
13
+ config.timeout = ENV.fetch('KAPSO_TIMEOUT', 30).to_i
14
+
15
+ # Development/Debug settings
16
+ config.debug = Rails.env.development? || ENV.fetch('KAPSO_DEBUG', 'false') == 'true'
17
+ config.logger = Rails.logger
18
+
19
+ # Retry configuration
20
+ config.retry_on_failure = ENV.fetch('KAPSO_RETRY_ON_FAILURE', 'true') == 'true'
21
+ config.max_retries = ENV.fetch('KAPSO_MAX_RETRIES', 3).to_i
22
+ end
23
+
24
+ # Subscribe to Kapso events (optional)
25
+ # ActiveSupport::Notifications.subscribe('kapso.message_received') do |name, start, finish, id, payload|
26
+ # Rails.logger.info "Received WhatsApp message: #{payload[:message]['id']}"
27
+ # # Handle incoming message
28
+ # end
29
+
30
+ # ActiveSupport::Notifications.subscribe('kapso.message_status_updated') do |name, start, finish, id, payload|
31
+ # Rails.logger.info "Message status updated: #{payload[:status]['id']} -> #{payload[:status]['status']}"
32
+ # # Handle message status update
33
+ # end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example service class for sending WhatsApp messages in a Rails application
4
+ # This wraps the KapsoClientRuby functionality with Rails-specific features
5
+ class KapsoMessageService
6
+ include ActiveModel::Model
7
+ include ActiveModel::Attributes
8
+
9
+ # Initialize with optional custom client
10
+ def initialize(client: nil)
11
+ @kapso_service = KapsoClientRuby::Rails::Service.new(client)
12
+ end
13
+
14
+ # Send a welcome message to a new user
15
+ # @param user [User] The user to send the message to
16
+ # @return [Hash] API response
17
+ def send_welcome_message(user)
18
+ return unless user.phone_number.present?
19
+
20
+ @kapso_service.send_template_message(
21
+ to: user.phone_number,
22
+ template_name: 'welcome_message',
23
+ language: user.preferred_language || 'en',
24
+ components: [
25
+ {
26
+ type: 'body',
27
+ parameters: [
28
+ { type: 'text', text: user.first_name || 'there' }
29
+ ]
30
+ }
31
+ ]
32
+ )
33
+ rescue KapsoClientRuby::Error => e
34
+ Rails.logger.error "Failed to send welcome message to user #{user.id}: #{e.message}"
35
+ # You might want to queue for retry or notify admins
36
+ nil
37
+ end
38
+
39
+ # Send order confirmation
40
+ # @param order [Order] The order to confirm
41
+ # @return [Hash] API response
42
+ def send_order_confirmation(order)
43
+ user = order.user
44
+ return unless user.phone_number.present?
45
+
46
+ @kapso_service.send_template_message(
47
+ to: user.phone_number,
48
+ template_name: 'order_confirmation',
49
+ language: user.preferred_language || 'en',
50
+ components: [
51
+ {
52
+ type: 'body',
53
+ parameters: [
54
+ { type: 'text', text: order.id.to_s },
55
+ { type: 'currency', currency: order.currency, amount_1000: order.total_cents }
56
+ ]
57
+ }
58
+ ]
59
+ )
60
+ rescue KapsoClientRuby::Error => e
61
+ Rails.logger.error "Failed to send order confirmation for order #{order.id}: #{e.message}"
62
+ nil
63
+ end
64
+
65
+ # Send a custom text message
66
+ # @param phone_number [String] Recipient's phone number
67
+ # @param message [String] The message text
68
+ # @return [Hash] API response
69
+ def send_text(phone_number:, message:)
70
+ @kapso_service.send_text_message(to: phone_number, text: message)
71
+ rescue KapsoClientRuby::Error => e
72
+ Rails.logger.error "Failed to send text message to #{phone_number}: #{e.message}"
73
+ nil
74
+ end
75
+
76
+ # Send a document
77
+ # @param phone_number [String] Recipient's phone number
78
+ # @param document_url [String] URL of the document
79
+ # @param filename [String] Optional filename
80
+ # @param caption [String] Optional caption
81
+ # @return [Hash] API response
82
+ def send_document(phone_number:, document_url:, filename: nil, caption: nil)
83
+ @kapso_service.send_media_message(
84
+ to: phone_number,
85
+ media_type: 'document',
86
+ media_url: document_url,
87
+ filename: filename,
88
+ caption: caption
89
+ )
90
+ rescue KapsoClientRuby::Error => e
91
+ Rails.logger.error "Failed to send document to #{phone_number}: #{e.message}"
92
+ nil
93
+ end
94
+
95
+ # Send test message (for development/testing)
96
+ def send_test_message
97
+ test_number = ENV['KAPSO_TEST_PHONE_NUMBER']
98
+
99
+ unless test_number
100
+ puts "Set KAPSO_TEST_PHONE_NUMBER environment variable to test messaging"
101
+ return
102
+ end
103
+
104
+ result = send_text(
105
+ phone_number: test_number,
106
+ message: "Hello from <%= application_name %>! This is a test message sent at #{Time.current}."
107
+ )
108
+
109
+ if result
110
+ puts "✅ Test message sent successfully! Message ID: #{result.dig('messages', 0, 'id')}"
111
+ else
112
+ puts "❌ Failed to send test message"
113
+ end
114
+ end
115
+
116
+ # Get message delivery status
117
+ # @param message_id [String] The message ID
118
+ # @return [Hash] Status information
119
+ def get_message_status(message_id)
120
+ @kapso_service.get_message_status(message_id)
121
+ rescue KapsoClientRuby::Error => e
122
+ Rails.logger.error "Failed to get message status for #{message_id}: #{e.message}"
123
+ nil
124
+ end
125
+
126
+ # List available templates
127
+ # @return [Array] List of templates
128
+ def list_templates
129
+ @kapso_service.get_templates
130
+ rescue KapsoClientRuby::Error => e
131
+ Rails.logger.error "Failed to fetch templates: #{e.message}"
132
+ nil
133
+ end
134
+
135
+ private
136
+
137
+ attr_reader :kapso_service
138
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ class KapsoWebhooksController < ApplicationController
4
+ # Skip CSRF token verification for webhook endpoints
5
+ skip_before_action :verify_authenticity_token
6
+
7
+ # Webhook verification endpoint (GET)
8
+ # WhatsApp sends a GET request to verify your webhook URL
9
+ def verify
10
+ challenge = params['hub.challenge']
11
+ verify_token = params['hub.verify_token']
12
+
13
+ # Set your verification token in environment variables
14
+ expected_token = ENV['KAPSO_WEBHOOK_VERIFY_TOKEN']
15
+
16
+ if verify_token == expected_token
17
+ render plain: challenge, status: :ok
18
+ else
19
+ render plain: 'Forbidden', status: :forbidden
20
+ end
21
+ end
22
+
23
+ # Webhook payload endpoint (POST)
24
+ # WhatsApp sends webhook events here
25
+ def create
26
+ begin
27
+ # Verify webhook signature (optional but recommended)
28
+ verify_webhook_signature if ENV['KAPSO_WEBHOOK_SECRET'].present?
29
+
30
+ # Process the webhook using the Kapso Rails service
31
+ service = KapsoClientRuby::Rails::Service.new
32
+ result = service.process_webhook(webhook_params.to_h)
33
+
34
+ render json: { status: 'ok' }, status: :ok
35
+ rescue => e
36
+ Rails.logger.error "Webhook processing error: #{e.message}"
37
+ render json: { error: 'Internal server error' }, status: :internal_server_error
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def webhook_params
44
+ params.permit!
45
+ end
46
+
47
+ def verify_webhook_signature
48
+ signature = request.headers['X-Hub-Signature-256']
49
+ return unless signature
50
+
51
+ expected_signature = calculate_signature(request.raw_post)
52
+
53
+ unless Rack::Utils.secure_compare(signature, expected_signature)
54
+ raise 'Invalid webhook signature'
55
+ end
56
+ end
57
+
58
+ def calculate_signature(payload)
59
+ secret = ENV['KAPSO_WEBHOOK_SECRET']
60
+ 'sha256=' + OpenSSL::HMAC.hexdigest('sha256', secret, payload)
61
+ end
62
+ end