Skip to content

Commit 1ad4d44

Browse files
committed
docs(webhook): clarify Rails ActionController::Parameters needs to_unsafe_h
1 parent 4cec06c commit 1ad4d44

2 files changed

Lines changed: 22 additions & 7 deletions

File tree

README.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,16 +286,23 @@ In your webhook handler, verify the signature, parse the payload, and
286286
acknowledge it by replying with the string `"100"`:
287287

288288
```ruby
289+
# In Rails, params[:data] is ActionController::Parameters, not a Hash — convert
290+
# it with .to_unsafe_h first, or the numeric-key ordering the signature depends
291+
# on is skipped and the check below rejects the payload. The payload is
292+
# signature-verified, so to_unsafe_h is safe here (.to_h would drop keys).
293+
# In bare Rack params["data"] is already a Hash; pass it as-is.
294+
data = params[:data].to_unsafe_h
295+
289296
# Reject forged callbacks: SMS.ru signs every payload with your api_id.
290297
# The check is constant-time (timing-attack safe).
291-
unless SmsRu::Webhook.valid?(params["data"], params["hash"], "YOUR_API_ID")
298+
unless SmsRu::Webhook.valid?(data, params[:hash], "YOUR_API_ID")
292299
return head(:forbidden)
293300
end
294301

295302
# SMS.ru sends up to 100 records as POST fields data[0]..data[N]
296-
# (a Hash in Rack/Rails, an Array in PHP). #parse handles either shape and
303+
# (a Hash in Rack, an Array in PHP). #parse handles either shape and
297304
# returns a typed event per record.
298-
SmsRu::Webhook.parse(params["data"]).each do |event|
305+
SmsRu::Webhook.parse(data).each do |event|
299306
case event
300307
when SmsRu::Events::SmsStatus # delivery report
301308
# event.id, event.status_code, event.created_at; event.delivered? => 103

lib/sms_ru/webhook.rb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,23 @@
33
class SmsRu
44
# Parses the inbound webhook payload SMS.ru POSTs to your callback URL and
55
# verifies its signature. SMS.ru sends the records as POST fields
6-
# `data[0]..data[N]` (so `params["data"]` is a Hash in Rack/Rails, an Array
7-
# in PHP) plus a `hash` field; acknowledge the webhook by replying with "100".
6+
# `data[0]..data[N]` plus a `hash` field; acknowledge the webhook by replying
7+
# with "100".
8+
#
9+
# The `data` param arrives as a Hash in bare Rack and an Array in PHP-style
10+
# clients. In Rails it is an `ActionController::Parameters`, which is **not** a
11+
# Hash — convert it with `.to_unsafe_h` first, or the numeric-key ordering the
12+
# signature depends on is skipped and {valid?} rejects the payload. The
13+
# payload is signature-verified, so `to_unsafe_h` is safe here (`.to_h` would
14+
# drop unpermitted keys).
815
#
916
# {parse} returns one typed event per record — a {SmsRu::Events::SmsStatus},
1017
# {SmsRu::Events::CallcheckStatus}, {SmsRu::Events::Test}, or
1118
# {SmsRu::Events::Unknown} — best handled with a case match:
1219
#
13-
# return head(:forbidden) unless SmsRu::Webhook.valid?(params["data"], params["hash"], api_id)
14-
# SmsRu::Webhook.parse(params["data"]).each do |event|
20+
# data = params[:data].to_unsafe_h # Rails; pass params["data"] as-is in bare Rack
21+
# return head(:forbidden) unless SmsRu::Webhook.valid?(data, params[:hash], api_id)
22+
# SmsRu::Webhook.parse(data).each do |event|
1523
# case event
1624
# when SmsRu::Events::SmsStatus then update_delivery(event.id, event.status_code)
1725
# when SmsRu::Events::CallcheckStatus then confirm(event.id) if event.confirmed?

0 commit comments

Comments
 (0)