whop_sdk 1.0.12 → 1.0.13

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: 6b9d224f549b7e16ee1bb5de5d10d1a50706dbd1b746be4c450feeb700a160d5
4
- data.tar.gz: 3429efb80021dc870ba0bc4c9599c4ca97efa2e4ad9ddce312e54dc2124f340e
3
+ metadata.gz: 9fc15ef6d359b6cf4784e9b9b4a43a677cd03a56bebe3b559dbf7ee65a611f21
4
+ data.tar.gz: 771adb6fcaf1b7564ee7383eead94c7a909a24522ddb0709f0c7e0f8cdfd35e4
5
5
  SHA512:
6
- metadata.gz: 687fe8d7db55950f6a70e20fcbd4778ceeb6187290c6fc37dda4611b0f367b0626ca3a4a58832a13383fceb8f42eb63b602e0f6387308d7be0f4ae3bc2a397ea
7
- data.tar.gz: 8f378d3b28200bf60ab53e36581482a8b7f79d5eb9529c5b652a1a57213c52d33be85ec2f0dfadbdb5d93b83ba5ba8382434c364e9c94e74e4ed244163796966
6
+ metadata.gz: 62de0ee7e2ea242700d78b8e87bbc6f60257328bb7834cc9c8e66eee3338b1ac7f2510c2ea1ca6847e516fe154490ee97f6f23b0e9b2137ab71a872d3e8c324c
7
+ data.tar.gz: a587e338a0c270925b9de6b890cf4112a924c4906c5003b90d53f9b29ed172a11f18519db16261e186157e4d6f4493350f46acb3670a0a9777bcebab944ae13a
data/.fernignore CHANGED
@@ -20,25 +20,37 @@
20
20
  # one, so without this entry that history would disappear from the tree on the first
21
21
  # generated PR.
22
22
  #
23
- # lib/whop_sdk/helpers holds verify_user_token, the one hand-written helper in this SDK
24
- # the x-whop-user-token verifier. It depends only on the standard library and the jwt gem,
25
- # never on generated client code, so it survives the client being replaced. This entry only
26
- # keeps the file: two things it needs are in Fern-owned files and are restored from the
27
- # generator config in whop-monorepo's sdks/fern/generators.yml, not from here.
28
- #
29
- # - lib/whop_sdk.rb is regenerated, so the require_relative that loads the helper is lost.
30
- # `requirePaths: [helpers/verify_user_token]` re-emits it as a File.exist?-guarded
31
- # require_relative at the bottom of the generated lib/whop_sdk.rb.
32
- # - whop_sdk.gemspec is regenerated, so `add_dependency "jwt"` is lost, the same way
33
- # whopsdk-python's [user-tokens] extra does not survive. `extraDependencies: {jwt: ...}`
34
- # re-declares it.
35
- #
36
- # Those two travel together. The helper does `require "jwt"` at load, so re-emitting the
37
- # require without the dependency turns a silently missing verifier into a LoadError on
38
- # `require "whop_sdk"` — the gem stops loading for everyone, not just user-token callers.
39
- # ci.yml asserts all three every run so a regeneration that drops any of them fails there
23
+ # lib/whop_sdk/helpers holds the hand-written helpers in this SDK. Each depends only on the
24
+ # standard library and one third-party gem, never on generated client code, so each survives
25
+ # the client being replaced:
26
+ #
27
+ # - verify_user_token the x-whop-user-token verifier, on the jwt gem.
28
+ # - verify_webhook — the Standard Webhooks signature verifier, on the standardwebhooks gem.
29
+ # It restores the verification half of `client.webhooks.unwrap`, which the Stainless gem
30
+ # shipped through 0.0.41 and Fern cannot generate: Fern generates from OpenAPI paths and
31
+ # `unwrap` was never a path.
32
+ #
33
+ # This entry only keeps the files. Two things each helper needs are in Fern-owned files and
34
+ # are restored from the generator config in whop-monorepo's sdks/fern/generators.yml, not
35
+ # from here.
36
+ #
37
+ # - lib/whop_sdk.rb is regenerated, so the require_relative that loads a helper is lost.
38
+ # `requirePaths: [helpers/verify_user_token, helpers/verify_webhook]` re-emits them as
39
+ # File.exist?-guarded require_relatives at the bottom of the generated lib/whop_sdk.rb.
40
+ # - whop_sdk.gemspec is regenerated, so `add_dependency "jwt"` and
41
+ # `add_dependency "standardwebhooks"` are lost, the same way whopsdk-python's
42
+ # [user-tokens] extra does not survive. `extraDependencies: {jwt: ..., standardwebhooks: ...}`
43
+ # re-declares them.
44
+ #
45
+ # The two travel together, per helper. Each helper requires its gem at load, so re-emitting
46
+ # the require without the dependency turns a silently missing helper into a LoadError on
47
+ # `require "whop_sdk"` — the gem stops loading for everyone, not just that helper's callers.
48
+ # ci.yml asserts all of it every run so a regeneration that drops any of it fails there
40
49
  # instead of on a caller's machine.
41
50
  #
51
+ # test/unit/helpers holds those helpers' tests. Fern owns test/, so without this entry a
52
+ # regeneration would prune them and the helpers would go untested rather than fail loudly.
53
+ #
42
54
  # lib/whop_sdk/internal/iterators/cursor_page_iterator.rb is a patched copy of a generated
43
55
  # file. The generated clients all pass cursor_field: :end_cursor, but the response models
44
56
  # declare end_cursor on page_info rather than at the root and Internal::Types::Model has no
@@ -60,3 +72,4 @@ CHANGELOG.md
60
72
  lib/whop_sdk/helpers
61
73
  lib/whop_sdk/internal/iterators/cursor_page_iterator.rb
62
74
  lib/whop_sdk/internal/types/utils.rb
75
+ test/unit/helpers
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "json"
5
+ require "standardwebhooks"
6
+
7
+ module WhopSDK
8
+ module Helpers
9
+ # Verifies the Standard Webhooks signature Whop sends on every webhook
10
+ # delivery, then parses the body.
11
+ #
12
+ # This is the verification half of the `client.webhooks.unwrap` the
13
+ # Stainless-generated gem shipped through 0.0.41. Fern generates from
14
+ # OpenAPI paths and `unwrap` was never a path, so the generated client has
15
+ # no equivalent. It is a standalone module rather than a method on the
16
+ # client so that nothing generated has to be patched: it depends only on
17
+ # the standardwebhooks gem and the standard library, never on generated
18
+ # client code, so it survives the client being replaced.
19
+ #
20
+ # What it does NOT do, and the Stainless version did: coerce the parsed
21
+ # body into one of 42 typed event models. Fern generates no webhook event
22
+ # models — `Whop_sdk::Types::WebhookEvent` is the enum of event *names* a
23
+ # webhook subscribes to, not a payload type — so there is nothing to coerce
24
+ # into. The parsed Hash is returned as-is.
25
+ module VerifyWebhook
26
+ MISSING_KEY_MESSAGE =
27
+ "Cannot verify a webhook without a key. Pass the endpoint's signing secret as `key:`."
28
+
29
+ # Verifies `payload` against the signature headers and returns the parsed body.
30
+ #
31
+ # @param payload [String] The raw, unmodified request body. Verifying a
32
+ # re-serialized body fails: the signature covers the exact bytes sent.
33
+ # @param headers [Hash] The request headers. Only `webhook-id`,
34
+ # `webhook-timestamp` and `webhook-signature` are read, and the lookup
35
+ # is case-insensitive.
36
+ # @param key [String] The endpoint's signing secret, exactly as Whop
37
+ # shows it — a `ws_`-prefixed string. Pass it verbatim; do not strip the
38
+ # prefix and do not pre-encode it.
39
+ # @return [Hash] The parsed body, with symbol keys.
40
+ # @raise [ArgumentError] when `key` is missing or empty.
41
+ # @raise [StandardWebhooks::WebhookVerificationError] when a signature
42
+ # header is missing, the timestamp is outside the tolerance window, or
43
+ # no signature matches.
44
+ def self.unwrap(payload, headers:, key:)
45
+ raise ArgumentError, MISSING_KEY_MESSAGE if key.nil? || key.to_s.empty?
46
+
47
+ ::StandardWebhooks::Webhook.new(hmac_key(key)).verify(payload, downcased_headers(headers))
48
+ JSON.parse(payload, symbolize_names: true)
49
+ end
50
+
51
+ # Whop's backend HMACs with the *literal bytes* of the secret it issued
52
+ # (`WebhooksManager::SignWebhook` passes `webhook.webhook_secret` straight
53
+ # to `OpenSSL::HMAC`). `StandardWebhooks::Webhook` instead base64-decodes
54
+ # whatever it is handed to derive its key, so handing it the secret raw
55
+ # derives the wrong key and every genuine delivery fails to verify.
56
+ # Encoding here cancels that decode out, leaving exactly the bytes the
57
+ # backend signed with.
58
+ #
59
+ # The whole secret is encoded, prefix included, because the backend never
60
+ # strips a prefix either. That also disarms the library's own `whsec_`
61
+ # stripping: base64 output cannot begin with `whsec_`, since `_` is not in
62
+ # the base64 alphabet.
63
+ #
64
+ # @api private
65
+ def self.hmac_key(key)
66
+ Base64.strict_encode64(key.to_s)
67
+ end
68
+
69
+ # StandardWebhooks::Webhook#verify looks its three headers up by exact
70
+ # lowercase key, but HTTP header names are case-insensitive and a plain
71
+ # Rack/Sinatra headers Hash arrives capitalized.
72
+ #
73
+ # @api private
74
+ def self.downcased_headers(headers)
75
+ raise ArgumentError, "headers must be a Hash-like object of header names to values" unless headers.respond_to?(:each_pair)
76
+
77
+ headers.each_pair.with_object({}) do |(name, value), downcased|
78
+ downcased[name.to_s.downcase] = value
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -35,7 +35,7 @@ module Whop_sdk
35
35
  @default_headers = {
36
36
  "X-Fern-Language": "Ruby",
37
37
  "X-Fern-SDK-Name": "whop_sdk",
38
- "X-Fern-SDK-Version": "1.0.12"
38
+ "X-Fern-SDK-Version": "1.0.13"
39
39
  }.merge(headers)
40
40
  @overridable_headers = overridable_headers.to_set { |name| name.to_s.downcase }
41
41
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Whop_sdk
4
- VERSION = "1.0.12"
4
+ VERSION = "1.0.13"
5
5
  end
data/lib/whop_sdk.rb CHANGED
@@ -3002,7 +3002,7 @@ require_relative "whop_sdk/environment"
3002
3002
 
3003
3003
  # Load user-defined files if present (e.g., for Sentry integration)
3004
3004
  # Files are loaded from lib/whop_sdk/ if they exist
3005
- ["whop_sdk/helpers/verify_user_token"].each do |relative_path|
3005
+ ["whop_sdk/helpers/verify_user_token", "whop_sdk/helpers/verify_webhook"].each do |relative_path|
3006
3006
  absolute_path = File.join(__dir__, "#{relative_path}.rb")
3007
3007
  require_relative relative_path if File.exist?(absolute_path)
3008
3008
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whop_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.12
4
+ version: 1.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Whop_sdk
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-22 00:00:00.000000000 Z
11
+ date: 2026-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: standardwebhooks
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
27
41
  description: The Whop_sdk Ruby library provides convenient access to the Whop_sdk
28
42
  API from Ruby.
29
43
  email:
@@ -886,6 +900,7 @@ files:
886
900
  - lib/whop_sdk/forums/types/update_forums_request.rb
887
901
  - lib/whop_sdk/forums/types/update_forums_request_banner_image.rb
888
902
  - lib/whop_sdk/helpers/verify_user_token.rb
903
+ - lib/whop_sdk/helpers/verify_webhook.rb
889
904
  - lib/whop_sdk/identity_profiles/client.rb
890
905
  - lib/whop_sdk/identity_profiles/types/list_identity_profile_request.rb
891
906
  - lib/whop_sdk/identity_profiles/types/list_identity_profile_response.rb