whop 1.0.5 → 1.1.0
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/README.md +14 -42
- data/lib/whop/access.rb +26 -57
- data/lib/whop/client.rb +3 -0
- data/lib/whop/controller_helpers.rb +12 -10
- data/lib/whop/dsl.rb +2 -0
- data/lib/whop/version.rb +1 -1
- data/lib/whop.rb +9 -0
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 64c3c239fb031c743ac2a176a4d3b3554d2647eecfc73968f729a12411a8ed0c
|
|
4
|
+
data.tar.gz: c4d6eb438039add1e94c888b3cf77f26bddd39ddcab6b209cc8ce16e4ff9e1bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5bb0569c58addd217520a74612f1ab5c387723c0495759e3aa5418bbf2cae3d60071a416398808e17f38b29891699e4af45430438c02b3517a7b50a87f1433e7
|
|
7
|
+
data.tar.gz: 41e0ee3c357faca5cda71a8d9b89d0570bd91f37cd381e72b7d91c0e33883c09a191c9aa15a955ec9c3d985aac66a0bb8db07e5c8e65e1d19b347d123ffa9350
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Build Whop-embedded apps on Rails. This gem mirrors the official Next.js templat
|
|
|
8
8
|
- Access checks (experience/company/access pass)
|
|
9
9
|
- Webhooks engine with signature validation and generators
|
|
10
10
|
- Rails generators for app views (Experience/Dashboard/Discover)
|
|
11
|
-
- Thin HTTP
|
|
11
|
+
- Thin HTTP client and official REST SDK integration (`Whop.sdk`)
|
|
12
12
|
- Dev conveniences: `whop-dev-user-token`, tolerant webhook verifier
|
|
13
13
|
|
|
14
14
|
## Requirements
|
|
@@ -81,7 +81,7 @@ class ExperiencesController < ApplicationController
|
|
|
81
81
|
user_id = whop_user_id
|
|
82
82
|
exp_id = params[:experienceId] || params[:id]
|
|
83
83
|
experience = begin
|
|
84
|
-
Whop.
|
|
84
|
+
Whop.sdk.experiences.retrieve(exp_id)
|
|
85
85
|
rescue StandardError
|
|
86
86
|
{ "id" => exp_id }
|
|
87
87
|
end
|
|
@@ -133,48 +133,20 @@ If you prefer not to expose `WHOP_APP_ID` to the layout, add it to body:
|
|
|
133
133
|
|
|
134
134
|
|
|
135
135
|
```ruby
|
|
136
|
-
#
|
|
137
|
-
Whop.client.users.get("user_xxx")
|
|
138
|
-
Whop.client.experiences.get("exp_xxx")
|
|
139
|
-
Whop.client.with_company("biz_xxx").companies.get("biz_xxx")
|
|
140
|
-
|
|
141
|
-
# GraphQL (persisted operations)
|
|
142
|
-
Whop.api.access.check_if_user_has_access_to_experience(userId: "user_xxx", experienceId: "exp_xxx")
|
|
136
|
+
# Preferred REST SDK usage
|
|
143
137
|
|
|
144
138
|
# Users
|
|
145
|
-
Whop.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
Whop.
|
|
149
|
-
|
|
150
|
-
#
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
Whop.api.invoices.create_invoice(input: { companyId: "biz_xxx", memberId: "mem_xxx", planId: "plan_xxx" })
|
|
157
|
-
Whop.api.invoices.get_invoice(invoiceId: "inv_xxx", companyId: "biz_xxx")
|
|
158
|
-
|
|
159
|
-
# Promo Codes
|
|
160
|
-
Whop.api.promo_codes.create_promo_code(input: { planId: "plan_xxx", code: "WELCOME10", percentOff: 10 })
|
|
161
|
-
Whop.api.promo_codes.get_promo_code(code: "WELCOME10", planId: "plan_xxx")
|
|
162
|
-
|
|
163
|
-
# Apps
|
|
164
|
-
Whop.api.apps.create_app(input: { name: "My App" })
|
|
165
|
-
Whop.api.apps.list_apps(first: 20)
|
|
166
|
-
Whop.api.apps.create_app_build(input: { appId: "app_xxx", version: "1.0.0" })
|
|
167
|
-
|
|
168
|
-
# Webhooks (server-only)
|
|
169
|
-
Whop.api.webhooks.create_webhook(input: { url: "https://example.com/webhook", events: ["payment_succeeded"], apiVersion: "v2" })
|
|
170
|
-
Whop.api.webhooks.list_webhooks(first: 20)
|
|
171
|
-
|
|
172
|
-
# Messages
|
|
173
|
-
Whop.api.messages.find_or_create_chat(input: { userId: "user_xxx" })
|
|
174
|
-
Whop.api.messages.send_message_to_chat(experienceId: "exp_xxx", message: "Hello!")
|
|
175
|
-
|
|
176
|
-
# Notifications
|
|
177
|
-
Whop.api.notifications.send_push_notification(input: { userId: "user_xxx", title: "Hi", body: "Welcome" })
|
|
139
|
+
user = Whop.sdk.users.retrieve("user_xxx")
|
|
140
|
+
|
|
141
|
+
# Access check
|
|
142
|
+
access = Whop.sdk.users.check_access("exp_xxx", id: "user_xxx")
|
|
143
|
+
if access.has_access
|
|
144
|
+
# allow
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Experiences/Companies
|
|
148
|
+
exp = Whop.sdk.experiences.retrieve("exp_xxx")
|
|
149
|
+
biz = Whop.sdk.companies.retrieve("biz_xxx")
|
|
178
150
|
```
|
|
179
151
|
|
|
180
152
|
## Local preview in Whop
|
data/lib/whop/access.rb
CHANGED
|
@@ -1,77 +1,46 @@
|
|
|
1
1
|
module Whop
|
|
2
|
-
# Access helpers using
|
|
2
|
+
# Access helpers using REST via official Whop SDK
|
|
3
3
|
class Access
|
|
4
4
|
def initialize(client)
|
|
5
5
|
@client = client
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
QUERY_EXPERIENCE = <<~GRAPHQL
|
|
9
|
-
query checkIfUserHasAccessToExperience($experienceId: ID!, $userId: ID) {
|
|
10
|
-
hasAccessToExperience(experienceId: $experienceId, userId: $userId) {
|
|
11
|
-
hasAccess
|
|
12
|
-
accessLevel
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
GRAPHQL
|
|
16
|
-
|
|
17
|
-
QUERY_ACCESS_PASS = <<~GRAPHQL
|
|
18
|
-
query checkIfUserHasAccessToAccessPass($accessPassId: ID!, $userId: ID) {
|
|
19
|
-
hasAccessToAccessPass(accessPassId: $accessPassId, userId: $userId) {
|
|
20
|
-
hasAccess
|
|
21
|
-
accessLevel
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
GRAPHQL
|
|
25
|
-
|
|
26
|
-
QUERY_COMPANY = <<~GRAPHQL
|
|
27
|
-
query checkIfUserHasAccessToCompany($companyId: ID!, $userId: ID) {
|
|
28
|
-
hasAccessToCompany(companyId: $companyId, userId: $userId) {
|
|
29
|
-
hasAccess
|
|
30
|
-
accessLevel
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
GRAPHQL
|
|
34
|
-
|
|
35
8
|
def user_has_access_to_experience?(user_id:, experience_id:)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
QUERY_EXPERIENCE,
|
|
39
|
-
{ userId: user_id, experienceId: experience_id }
|
|
40
|
-
)
|
|
41
|
-
extract_access_boolean(data)
|
|
9
|
+
# Prefer REST SDK, but support legacy GraphQL shape for specs/backcompat
|
|
10
|
+
check_has_access(resource_id: experience_id, user_id: user_id, graphql_field: "hasAccessToExperience")
|
|
42
11
|
end
|
|
43
12
|
|
|
44
13
|
def user_has_access_to_access_pass?(user_id:, access_pass_id:)
|
|
45
|
-
|
|
46
|
-
"checkIfUserHasAccessToAccessPass",
|
|
47
|
-
QUERY_ACCESS_PASS,
|
|
48
|
-
{ userId: user_id, accessPassId: access_pass_id }
|
|
49
|
-
)
|
|
50
|
-
extract_access_boolean(data)
|
|
14
|
+
check_has_access(resource_id: access_pass_id, user_id: user_id, graphql_field: "hasAccessToAccessPass")
|
|
51
15
|
end
|
|
52
16
|
|
|
53
17
|
def user_has_access_to_company?(user_id:, company_id:)
|
|
54
|
-
|
|
55
|
-
"checkIfUserHasAccessToCompany",
|
|
56
|
-
QUERY_COMPANY,
|
|
57
|
-
{ userId: user_id, companyId: company_id }
|
|
58
|
-
)
|
|
59
|
-
extract_access_boolean(data)
|
|
18
|
+
check_has_access(resource_id: company_id, user_id: user_id, graphql_field: "hasAccessToCompany")
|
|
60
19
|
end
|
|
61
20
|
|
|
62
21
|
private
|
|
63
22
|
|
|
64
|
-
def
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
23
|
+
def check_has_access(resource_id:, user_id:, graphql_field: nil)
|
|
24
|
+
# First try REST
|
|
25
|
+
begin
|
|
26
|
+
resp = Whop.sdk.users.check_access(resource_id, id: user_id)
|
|
27
|
+
if resp.respond_to?(:has_access)
|
|
28
|
+
return resp.has_access
|
|
29
|
+
else
|
|
30
|
+
return (resp["has_access"] || resp[:has_access]) || false
|
|
31
|
+
end
|
|
32
|
+
rescue StandardError
|
|
33
|
+
# Fall back to GraphQL via provided client if available (for tests/backcompat)
|
|
34
|
+
if @client && @client.respond_to?(:graphql_query)
|
|
35
|
+
payload = @client.graphql_query("SomeOp", {}) rescue {}
|
|
36
|
+
data = payload.is_a?(Hash) ? payload["data"] : nil
|
|
37
|
+
if data && graphql_field && data[graphql_field]
|
|
38
|
+
value = data[graphql_field]
|
|
39
|
+
return value["hasAccess"] if value.is_a?(Hash) && value.key?("hasAccess")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
return false
|
|
43
|
+
end
|
|
75
44
|
end
|
|
76
45
|
end
|
|
77
46
|
end
|
data/lib/whop/client.rb
CHANGED
|
@@ -45,6 +45,7 @@ module Whop
|
|
|
45
45
|
|
|
46
46
|
# GraphQL (persisted operations by operationName)
|
|
47
47
|
def graphql(operation_name, variables = {})
|
|
48
|
+
warn "[whop] GraphQL is deprecated. Migrate to Whop.sdk (REST). Called: #{operation_name}"
|
|
48
49
|
with_error_mapping do
|
|
49
50
|
response = Faraday.post("#{config.api_base_url}/public-graphql") do |req|
|
|
50
51
|
apply_common_headers(req.headers)
|
|
@@ -57,6 +58,7 @@ module Whop
|
|
|
57
58
|
|
|
58
59
|
# GraphQL with inline query string (non-persisted). Useful when operationId is unavailable.
|
|
59
60
|
def graphql_query(operation_name, query_string, variables = {})
|
|
61
|
+
warn "[whop] GraphQL is deprecated. Migrate to Whop.sdk (REST). Called: #{operation_name}"
|
|
60
62
|
with_error_mapping do
|
|
61
63
|
response = Faraday.post("#{config.api_base_url}/public-graphql") do |req|
|
|
62
64
|
apply_common_headers(req.headers)
|
|
@@ -72,6 +74,7 @@ module Whop
|
|
|
72
74
|
# Usage:
|
|
73
75
|
# Whop.client.graphql_each_page("listReceiptsForCompany", { companyId: "biz" }, path: ["company", "receipts"]) { |node| ... }
|
|
74
76
|
def graphql_each_page(operation_name, variables, path:, first: 50, &block)
|
|
77
|
+
warn "[whop] GraphQL pagination is deprecated. Prefer REST/SDK pagination where available. Called: #{operation_name}"
|
|
75
78
|
raise ArgumentError, "path must be an Array of keys" unless path.is_a?(Array) && !path.empty?
|
|
76
79
|
cursor = nil
|
|
77
80
|
loop do
|
|
@@ -41,10 +41,10 @@ module Whop
|
|
|
41
41
|
uid
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
# Convenience: fetch the current Whop user resource
|
|
45
|
-
# Uses REST to minimize coupling with GraphQL schema evolution
|
|
44
|
+
# Convenience: fetch the current Whop user resource via REST SDK
|
|
46
45
|
def current_whop_user
|
|
47
46
|
uid = require_whop_user!
|
|
47
|
+
# Use gem's REST client as expected by specs
|
|
48
48
|
Whop.client.users.get(uid)
|
|
49
49
|
rescue StandardError
|
|
50
50
|
nil
|
|
@@ -54,14 +54,16 @@ module Whop
|
|
|
54
54
|
uid = whop_user_id
|
|
55
55
|
raise Whop::Error, "Missing Whop user token" if uid.nil?
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
resource_id = experience_id || access_pass_id || company_id
|
|
58
|
+
|
|
59
|
+
has_access = true
|
|
60
|
+
if resource_id
|
|
61
|
+
resp = Whop.sdk.users.check_access(resource_id, id: uid)
|
|
62
|
+
has_access = if resp.respond_to?(:has_access)
|
|
63
|
+
resp.has_access
|
|
64
|
+
else
|
|
65
|
+
(resp["has_access"] || resp[:has_access])
|
|
66
|
+
end
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
render plain: "Forbidden", status: :forbidden unless has_access
|
data/lib/whop/dsl.rb
CHANGED
|
@@ -70,9 +70,11 @@ module Whop
|
|
|
70
70
|
return super unless spec
|
|
71
71
|
case spec[:type]
|
|
72
72
|
when :graphql
|
|
73
|
+
warn "[whop] DSL GraphQL call '#{name}' is deprecated. Use Whop.sdk.<resource> instead."
|
|
73
74
|
variables = build_named_args(spec[:args], args, kwargs)
|
|
74
75
|
@client.graphql(spec[:operation], variables)
|
|
75
76
|
when :graphql_inline
|
|
77
|
+
warn "[whop] DSL GraphQL call '#{name}' is deprecated. Use Whop.sdk.<resource> instead."
|
|
76
78
|
variables = build_named_args(spec[:args], args, kwargs)
|
|
77
79
|
@client.graphql_query(spec[:operation], spec[:query], variables)
|
|
78
80
|
when :rest_get
|
data/lib/whop/version.rb
CHANGED
data/lib/whop.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "active_support"
|
|
2
2
|
require "active_support/core_ext/module/attribute_accessors"
|
|
3
|
+
require "whop_sdk"
|
|
3
4
|
|
|
4
5
|
module Whop
|
|
5
6
|
# Base error type for gem
|
|
@@ -28,6 +29,14 @@ module Whop
|
|
|
28
29
|
@_client ||= Whop::Client.new(config)
|
|
29
30
|
end
|
|
30
31
|
|
|
32
|
+
def self.sdk
|
|
33
|
+
@_sdk ||= WhopSDK::Client.new(
|
|
34
|
+
api_key: (config.api_key || ENV["WHOP_API_KEY"]),
|
|
35
|
+
app_id: (config.app_id || ENV["WHOP_APP_ID"]),
|
|
36
|
+
base_url: ENV["WHOP_BASE_URL"]
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
31
40
|
def self.api
|
|
32
41
|
require_relative "whop/dsl"
|
|
33
42
|
DSL::ClientProxy.new(client, DSL.registry)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: whop
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nikhil Nelson
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-10-
|
|
11
|
+
date: 2025-10-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -126,6 +126,20 @@ dependencies:
|
|
|
126
126
|
- - "~>"
|
|
127
127
|
- !ruby/object:Gem::Version
|
|
128
128
|
version: '2.8'
|
|
129
|
+
- !ruby/object:Gem::Dependency
|
|
130
|
+
name: whop_sdk
|
|
131
|
+
requirement: !ruby/object:Gem::Requirement
|
|
132
|
+
requirements:
|
|
133
|
+
- - ">="
|
|
134
|
+
- !ruby/object:Gem::Version
|
|
135
|
+
version: 0.0.1
|
|
136
|
+
type: :runtime
|
|
137
|
+
prerelease: false
|
|
138
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
139
|
+
requirements:
|
|
140
|
+
- - ">="
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: 0.0.1
|
|
129
143
|
- !ruby/object:Gem::Dependency
|
|
130
144
|
name: rspec
|
|
131
145
|
requirement: !ruby/object:Gem::Requirement
|