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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2 -2
- data/RAILS_INTEGRATION.md +478 -0
- data/README.md +33 -15
- data/examples/rails/jobs.rb +388 -0
- data/examples/rails/models.rb +240 -0
- data/examples/rails/notifications_controller.rb +227 -0
- data/examples/template_management.rb +10 -10
- data/lib/kapso_client_ruby/client.rb +2 -2
- data/lib/kapso_client_ruby/rails/generators/install_generator.rb +76 -0
- data/lib/kapso_client_ruby/rails/generators/templates/env.erb +21 -0
- data/lib/kapso_client_ruby/rails/generators/templates/initializer.rb.erb +33 -0
- data/lib/kapso_client_ruby/rails/generators/templates/message_service.rb.erb +138 -0
- data/lib/kapso_client_ruby/rails/generators/templates/webhook_controller.rb.erb +62 -0
- data/lib/kapso_client_ruby/rails/railtie.rb +55 -0
- data/lib/kapso_client_ruby/rails/service.rb +189 -0
- data/lib/kapso_client_ruby/rails/tasks.rake +167 -0
- data/lib/kapso_client_ruby/version.rb +1 -1
- data/lib/kapso_client_ruby.rb +6 -0
- data/scripts/kapso_template_finder.rb +2 -2
- data/scripts/sdk_setup.rb +15 -15
- metadata +13 -1
@@ -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 =
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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 =
|
34
|
-
@logger = 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
|