signwell_sdk 0.1.0 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a977e911e6115277298236df5ce9a715dacb3cf8986d387c718a31db5138566a
4
- data.tar.gz: 1e8c8cc0051656d1ebafd9489d7564508683784d3ffb8dd6572e54c356610bc0
3
+ metadata.gz: 55bbaffc014507a2d8aac8a98b5050e8283576e119595023bcdc80de464a3d10
4
+ data.tar.gz: 2f3338dfec89e4806bffa9fc1bf5ad5004c72e5fff5b6b99dd843cdaac38d139
5
5
  SHA512:
6
- metadata.gz: 1f9f4838d88deebd9c7647ed1eaf6ff06892bfd00fbd1f16de2fb0aacc9e0ff07e26db4b21aeb8e631f3f8f771a2a6afc42152570e303bec9b1608e5ddbf88c6
7
- data.tar.gz: 3a5b9325c09517a14ea25680a5b9f4afc2d54f9362c8e9f0538258364cc896101bd6df8b9cc3a9bafe4d9d09e30a10d8abef89cd39893edfb27ab9564036fe52
6
+ metadata.gz: 67288c6373cb34e4f1ac755f5b2ceb94468aabb32112a7637407c759c6dc1088d35b8d6cce81884ab3d244328822fad089828a321f13d40001a7efbf05fc3d7d
7
+ data.tar.gz: 58db67c107fade9f556042632aea5a3b22c39ff94ce811bd1293fbfc2469c36cf293d84b377236fe5f435eccbfb5f24ccfcd5c4ded112f09dd2a8e55fa1830aa
@@ -3,9 +3,14 @@
3
3
  # Webhook Signature Validation
4
4
  #
5
5
  # When SignWell sends a webhook to your endpoint, each payload includes
6
- # a `hash` field with an HMAC-SHA256 signature. Use SignWell::Webhook.verify_event
6
+ # an `event.hash` field with an HMAC-SHA256 signature. Use SignWell::Webhook.verify_event
7
7
  # to validate the signature before processing the event.
8
8
  #
9
+ # The webhook payload has this structure:
10
+ # { "event": { "type": "...", "time": ..., "hash": "..." }, "data": { ... } }
11
+ #
12
+ # You must pass `payload['event']` (not the full payload) to verify_event.
13
+ #
9
14
  # Your webhook ID is available in the SignWell dashboard (Settings > Webhooks).
10
15
 
11
16
  require 'signwell_sdk'
@@ -13,7 +18,8 @@ require 'sinatra'
13
18
  require 'json'
14
19
 
15
20
  post '/webhooks/signwell' do
16
- event = JSON.parse(request.body.read)
21
+ payload = JSON.parse(request.body.read)
22
+ event = payload['event']
17
23
 
18
24
  unless SignWell::Webhook.verify_event(event: event, webhook_id: ENV['SIGNWELL_WEBHOOK_ID'])
19
25
  halt 401, 'Invalid signature'
@@ -21,9 +27,9 @@ post '/webhooks/signwell' do
21
27
 
22
28
  case event['type']
23
29
  when 'document_completed'
24
- puts "Document #{event.dig('data', 'id')} completed"
30
+ puts "Document #{payload.dig('data', 'id')} completed"
25
31
  when 'document_declined'
26
- puts "Document #{event.dig('data', 'id')} declined"
32
+ puts "Document #{payload.dig('data', 'id')} declined"
27
33
  end
28
34
 
29
35
  status 200
@@ -11,5 +11,5 @@ Generator version: 7.12.0
11
11
  =end
12
12
 
13
13
  module SignWell
14
- VERSION = '0.1.0'
14
+ VERSION = '0.1.1'
15
15
  end
@@ -10,17 +10,15 @@ module SignWell
10
10
  # computed from the event type and timestamp, using your webhook's secret ID
11
11
  # as the key.
12
12
  #
13
- # @example Verify a webhook in a Rails controller
13
+ # @example Verify a webhook in a Rails controller (using verify_event!)
14
14
  # class WebhooksController < ApplicationController
15
15
  # skip_before_action :verify_authenticity_token
16
16
  #
17
17
  # def create
18
- # event = JSON.parse(request.body.read)
18
+ # payload = JSON.parse(request.body.read)
19
+ # event = payload['event']
19
20
  #
20
- # unless SignWell::Webhook.verify_event(event: event, webhook_id: ENV['SIGNWELL_WEBHOOK_ID'])
21
- # head :unauthorized
22
- # return
23
- # end
21
+ # SignWell::Webhook.verify_event!(event: event, webhook_id: ENV['SIGNWELL_WEBHOOK_ID'])
24
22
  #
25
23
  # # Process the verified event
26
24
  # case event['type']
@@ -29,12 +27,15 @@ module SignWell
29
27
  # end
30
28
  #
31
29
  # head :ok
30
+ # rescue ArgumentError
31
+ # head :unauthorized
32
32
  # end
33
33
  # end
34
34
  #
35
- # @example Verify a webhook in a Sinatra app
35
+ # @example Verify a webhook in a Sinatra app (using verify_event)
36
36
  # post '/webhooks/signwell' do
37
- # event = JSON.parse(request.body.read)
37
+ # payload = JSON.parse(request.body.read)
38
+ # event = payload['event']
38
39
  #
39
40
  # halt 401 unless SignWell::Webhook.verify_event(event: event, webhook_id: ENV['SIGNWELL_WEBHOOK_ID'])
40
41
  #
@@ -45,23 +46,56 @@ module SignWell
45
46
  # @see https://developers.signwell.com/reference/webhooks SignWell Webhooks Documentation
46
47
  module Webhook
47
48
  # Verifies the authenticity of a SignWell webhook event using HMAC-SHA256.
49
+ # Returns +false+ for missing or invalid arguments instead of raising.
50
+ #
51
+ # @param event [Hash] The +event+ object from the webhook payload.
52
+ # Must contain +'type'+, +'time'+, and +'hash'+ keys.
53
+ # @param webhook_id [String] Your webhook's secret ID.
54
+ # @return [Boolean] +true+ if the signature is valid, +false+ otherwise
55
+ # @see #verify_event! Bang version that raises on invalid input
56
+ def self.verify_event(event:, webhook_id:)
57
+ verify_event!(event: event, webhook_id: webhook_id)
58
+ rescue ArgumentError
59
+ false
60
+ end
61
+
62
+ # Verifies the authenticity of a SignWell webhook event using HMAC-SHA256.
63
+ # Raises +ArgumentError+ with a descriptive message when input is invalid.
48
64
  #
49
65
  # Computes +HMAC-SHA256(webhook_id, "type@time")+ and compares it to the
50
66
  # +hash+ field in the event payload using a constant-time comparison.
51
67
  #
52
- # @param event [Hash] Parsed webhook JSON payload. Must contain:
68
+ # @param event [Hash] The +event+ object from the webhook payload.
69
+ # Must contain:
53
70
  # - +'type'+ (String) - the event type, e.g. +"document_completed"+
54
- # - +'time'+ (String) - ISO 8601 timestamp of the event
71
+ # - +'time'+ (String, Integer) - timestamp of the event
55
72
  # - +'hash'+ (String) - HMAC-SHA256 hex digest to verify against
56
73
  # @param webhook_id [String] Your webhook's secret ID (found in your
57
74
  # SignWell dashboard under API > Webhooks). Used as the HMAC key.
58
75
  # @return [Boolean] +true+ if the signature is valid, +false+ otherwise
59
- # @raise [NoMethodError] if +event+ is missing required keys
60
- def self.verify_event(event:, webhook_id:)
61
- data = "#{event['type']}@#{event['time']}"
62
- expected = event['hash']
76
+ # @raise [ArgumentError] if +webhook_id+ is missing or +event+ is missing
77
+ # required keys
78
+ def self.verify_event!(event:, webhook_id:)
79
+ raise ArgumentError, 'webhook_id must be a non-empty string' unless webhook_id.is_a?(String) && !webhook_id.empty?
80
+ raise ArgumentError, 'event must be a Hash' unless event.is_a?(Hash)
81
+
82
+ type = event['type']
83
+ time = event['time']
84
+ hash = event['hash']
85
+
86
+ missing = []
87
+ missing << 'type' unless type
88
+ missing << 'time' unless time
89
+ missing << 'hash' unless hash
90
+ unless missing.empty?
91
+ raise ArgumentError,
92
+ "event is missing required keys: #{missing.join(', ')}. " \
93
+ 'Make sure you pass payload["event"], not the full webhook payload'
94
+ end
95
+
96
+ data = "#{type}@#{time}"
63
97
  calculated = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('SHA256'), webhook_id, data)
64
- secure_compare(calculated, expected)
98
+ secure_compare(calculated, hash)
65
99
  end
66
100
 
67
101
  # @api private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: signwell_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - SignWell
@@ -413,7 +413,7 @@ homepage: https://github.com/Bidsketch/signwell-sdk-ruby
413
413
  licenses:
414
414
  - MIT
415
415
  metadata:
416
- documentation_uri: https://gemdocs.org/gems/signwell_sdk/0.1.0/
416
+ documentation_uri: https://gemdocs.org/gems/signwell_sdk/0.1.1/
417
417
  source_code_uri: https://github.com/Bidsketch/signwell-sdk-ruby
418
418
  homepage_uri: https://github.com/Bidsketch/signwell-sdk-ruby
419
419
  changelog_uri: https://github.com/Bidsketch/signwell-sdk-ruby/blob/main/CHANGELOG.md