Skip to content

Commit 0253a9d

Browse files
authored
Merge pull request #52 from Vonage-Community/add_whatsapp_outbound
Adding new Model, Controller logic, and Views associated with WhatsAp…
2 parents bab368b + 46fbc2d commit 0253a9d

File tree

9 files changed

+300
-2
lines changed

9 files changed

+300
-2
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ VONAGE_SERVER_HOSTNAME=''
1111

1212
# RCS Brand Name, check formatting requirements. For instance, no spaces allowed
1313
RCS_SENDER_ID=''
14+
15+
# WhatsApp Business Account Number (linked to your Vonage application)
16+
VONAGE_WHATSAPP_NUMBER=''

.ruby-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ruby-3.3.5
1+
ruby-3.4.6

WHATSAPP_SETUP.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# WhatsApp Integration Setup Guide
2+
3+
This guide will help you set up and use the WhatsApp messaging functionality that has been added to your Rails application.
4+
5+
## What Was Added
6+
7+
The following components have been integrated into your existing Rails app:
8+
9+
### 1. Database
10+
- **Migration**: `db/migrate/20251015112429_create_whatsapp_messages.rb`
11+
- **Model**: `app/models/whatsapp_message.rb`
12+
- Stores WhatsApp message records with fields: `to`, `from`, `text`, `status`, `message_uuid`, `is_inbound`
13+
14+
### 2. Controller
15+
- **File**: `app/controllers/outbound_whatsapp_controller.rb`
16+
- **Actions**:
17+
- `new` - Display the form for sending WhatsApp messages
18+
- `create` - Send a text WhatsApp message
19+
- `interactive` - Send an interactive WhatsApp message with reply buttons
20+
21+
### 3. View
22+
- **File**: `app/views/outbound_whatsapp/new.html.erb`
23+
- Contains forms for both text and interactive WhatsApp messages
24+
25+
### 4. Routes
26+
Added to `config/routes.rb`:
27+
```ruby
28+
get '/outbound_whatsapp/new', to: 'outbound_whatsapp#new', as: :new_outbound_whatsapp
29+
post '/outbound_whatsapp', to: 'outbound_whatsapp#create', as: :outbound_whatsapp
30+
post '/outbound_whatsapp/interactive', to: 'outbound_whatsapp#interactive', as: :interactive_whatsapp
31+
```
32+
33+
### 5. Environment Configuration
34+
Added `VONAGE_WHATSAPP_NUMBER` to `.env.example`
35+
36+
## Setup Instructions
37+
38+
### Step 1: Run the Migration
39+
```bash
40+
rails db:migrate
41+
```
42+
43+
### Step 2: Configure Your Vonage WhatsApp Application
44+
45+
1. Log in to your [Vonage Dashboard](https://dashboard.nexmo.com/)
46+
2. Create a new application (or use an existing one) and enable the **Messages** capability
47+
3. Add webhook URLs for:
48+
- **Inbound**: `https://your-domain.com/inbound` (or use ngrok for testing)
49+
- **Status**: `https://your-domain.com/status`
50+
4. Click **Generate public and private key**
51+
5. Move the downloaded `private.key` file to the root of your Rails app
52+
6. Note your **Application ID**
53+
7. Click **Save**
54+
8. Under the **Link external accounts** tab, link your WhatsApp number
55+
56+
### Step 3: Update Your .env File
57+
58+
Add the following to your `.env` file (create one if it doesn't exist):
59+
60+
```bash
61+
VONAGE_APPLICATION_ID=your_application_id_here
62+
VONAGE_PRIVATE_KEY=./private.key
63+
VONAGE_WHATSAPP_NUMBER=14157386102 # Your WhatsApp Business number
64+
```
65+
66+
### Step 4: Test the Integration
67+
68+
1. Start your Rails server:
69+
```bash
70+
rails s
71+
```
72+
73+
2. Navigate to: `http://localhost:3000/outbound_whatsapp/new`
74+
75+
3. You should see two forms:
76+
- **Send a WhatsApp Message** - For sending text messages
77+
- **Try an Interactive Message** - For sending messages with reply buttons
78+
79+
## Features
80+
81+
### Text Messages
82+
Send simple text messages to any WhatsApp number. The form includes:
83+
- **From**: Your WhatsApp Business number (pre-filled from ENV)
84+
- **To**: Recipient's WhatsApp number (format: +14155551234)
85+
- **Message**: The text content to send
86+
87+
### Interactive Messages
88+
Send messages with reply buttons that users can tap to respond. The example includes:
89+
- A header ("Delivery time")
90+
- Body text with a question
91+
- Footer text with additional info
92+
- Three reply buttons with different time slots
93+
94+
You can customize the interactive message structure in the `interactive` action of `OutboundWhatsappController`.
95+
96+
## How It Works
97+
98+
1. **User fills out the form** and submits
99+
2. **Controller receives the data** and creates a `WhatsappMessage` record
100+
3. **Vonage client is initialized** with your credentials
101+
4. **Message is sent** via the Vonage Messages API
102+
5. **Response is checked** - if successful (HTTP 202), the `message_uuid` is saved
103+
6. **User is redirected** back to the form with a success notice
104+
105+
## Next Steps
106+
107+
As mentioned in the tutorial, this implementation covers sending messages. To build a complete WhatsApp integration, you'll want to add:
108+
109+
1. **Inbound message handling** - Receive messages from users
110+
2. **Status webhooks** - Track message delivery and read status
111+
3. **Interactive response handling** - Process which button the user clicked
112+
113+
These features would require additional controllers and webhook endpoints, similar to the existing `inbound_sms` and `sms_message_status` controllers in your app.
114+
115+
## Troubleshooting
116+
117+
- **Make sure your WhatsApp number is linked** to your Vonage application
118+
- **Verify your .env file** has the correct credentials
119+
- **Check that private.key** is in the root directory
120+
- **Ensure the recipient number** is in E.164 format (e.g., +14155551234)
121+
- **For testing**, you can use the [Vonage Sandbox for WhatsApp](https://developer.vonage.com/en/messages/concepts/messages-api-sandbox)
122+
123+
## Resources
124+
125+
- [Vonage Messages API Documentation](https://developer.vonage.com/en/messages/overview)
126+
- [WhatsApp Business API Guide](https://developer.vonage.com/en/messages/concepts/whatsapp)
127+
- [Vonage Ruby SDK](https://github.com/Vonage/vonage-ruby-sdk)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
class OutboundWhatsappController < ApplicationController
2+
def new
3+
@whatsapp_message = WhatsappMessage.new
4+
end
5+
6+
def create
7+
@whatsapp_message = WhatsappMessage.new(safe_params)
8+
9+
if @whatsapp_message.save
10+
deliver(@whatsapp_message)
11+
redirect_to :new_outbound_whatsapp, notice: 'WhatsApp message sent!'
12+
else
13+
flash[:alert] = 'Something went wrong'
14+
render :new
15+
end
16+
end
17+
18+
def interactive
19+
unless params[:to].present?
20+
redirect_to :new_outbound_whatsapp, alert: "Recipient number is required!"
21+
return
22+
end
23+
24+
interactive_message = {
25+
from: ENV["VONAGE_WHATSAPP_NUMBER"],
26+
to: params[:to],
27+
channel: "whatsapp",
28+
message_type: "custom",
29+
custom: {
30+
type: "interactive",
31+
interactive: {
32+
type: "button",
33+
header: {
34+
type: "text",
35+
text: "Delivery time"
36+
},
37+
body: {
38+
text: "Which time would you like us to deliver your order at?"
39+
},
40+
footer: {
41+
text: "Please allow 15 minutes either side of your chosen time."
42+
},
43+
action: {
44+
buttons: [
45+
{
46+
type: "reply",
47+
reply: { id: "slot-1", title: "15:00" }
48+
},
49+
{
50+
type: "reply",
51+
reply: { id: "slot-2", title: "16:30" }
52+
},
53+
{
54+
type: "reply",
55+
reply: { id: "slot-3", title: "17:15" }
56+
}
57+
]
58+
}
59+
}
60+
}
61+
}
62+
63+
response = vonage.messaging.send(**interactive_message)
64+
Rails.logger.info("📤 Sent interactive message to #{params[:to]}: #{response.inspect}")
65+
66+
redirect_to :new_outbound_whatsapp, notice: "Interactive message sent to #{params[:to]}!"
67+
end
68+
69+
private
70+
71+
def safe_params
72+
params.require(:whatsapp_message).permit(:to, :from, :text)
73+
end
74+
75+
def vonage
76+
Vonage::Client.new(
77+
application_id: ENV["VONAGE_APPLICATION_ID"],
78+
private_key: ENV["VONAGE_PRIVATE_KEY"]
79+
)
80+
end
81+
82+
def deliver(whatsapp_message)
83+
response = vonage.messaging.send(
84+
message_type: "text",
85+
text: whatsapp_message.text,
86+
to: whatsapp_message.to,
87+
from: whatsapp_message.from,
88+
channel: "whatsapp"
89+
)
90+
91+
if response.http_response.code == "202"
92+
whatsapp_message.update(
93+
message_uuid: response.entity.attributes[:message_uuid]
94+
)
95+
end
96+
end
97+
end

app/models/whatsapp_message.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class WhatsappMessage < ApplicationRecord
2+
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<div class="whatsapp-form">
2+
<h1>Send a WhatsApp Message</h1>
3+
4+
<%= form_for @whatsapp_message, url: outbound_whatsapp_path do |f| %>
5+
<div class="field">
6+
<%= f.label :from, "From (Your WhatsApp Number)" %>
7+
<%= f.text_field :from, value: ENV['VONAGE_WHATSAPP_NUMBER'], placeholder: '+14157386102' %>
8+
</div>
9+
10+
<div class="field">
11+
<%= f.label :to, "To (Recipient WhatsApp Number)" %>
12+
<%= f.text_field :to, placeholder: '+14155551234' %>
13+
</div>
14+
15+
<div class="field">
16+
<%= f.label :text, "Message" %>
17+
<%= f.text_area :text, rows: 4, placeholder: "Type your message..." %>
18+
</div>
19+
20+
<div class="actions">
21+
<%= f.submit "Send Message" %>
22+
</div>
23+
<% end %>
24+
25+
<hr>
26+
<h3>Try an Interactive Message</h3>
27+
<p>Send a sample interactive WhatsApp message with reply buttons.</p>
28+
29+
<%= form_with url: interactive_whatsapp_path, method: :post, local: true do |f| %>
30+
<div class="field">
31+
<%= f.label :to, "To (Recipient WhatsApp Number)" %>
32+
<%= f.text_field :to, placeholder: '+14155551234', required: true %>
33+
</div>
34+
35+
<div class="actions">
36+
<%= f.submit "Send Interactive Message" %>
37+
</div>
38+
<% end %>
39+
</div>

config/routes.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,9 @@
2929

3030
# For InboundCalls controller, create
3131
resources :inbound_calls, only: [:create]
32+
33+
# For OutboundWhatsapp controller, new & create & interactive
34+
get '/outbound_whatsapp/new', to: 'outbound_whatsapp#new', as: :new_outbound_whatsapp
35+
post '/outbound_whatsapp', to: 'outbound_whatsapp#create', as: :outbound_whatsapp
36+
post '/outbound_whatsapp/interactive', to: 'outbound_whatsapp#interactive', as: :interactive_whatsapp
3237
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class CreateWhatsappMessages < ActiveRecord::Migration[8.0]
2+
def change
3+
create_table :whatsapp_messages do |t|
4+
t.string :to
5+
t.string :from
6+
t.text :text
7+
t.string :status
8+
t.string :message_uuid
9+
t.boolean :is_inbound
10+
11+
t.timestamps
12+
end
13+
end
14+
end

db/schema.rb

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)