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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba25e2187ff6be4ded65c7b7a17cc1d3ab7d450a962929fbc4f487042f51481e
4
- data.tar.gz: 88877e474fc48d922a48f0876b2c754f2dcc7cae7ee1b81bc44ad3a233ba16ec
3
+ metadata.gz: 64c3c239fb031c743ac2a176a4d3b3554d2647eecfc73968f729a12411a8ed0c
4
+ data.tar.gz: c4d6eb438039add1e94c888b3cf77f26bddd39ddcab6b209cc8ce16e4ff9e1bc
5
5
  SHA512:
6
- metadata.gz: 7ac76a99d56ef1a6555d9d07b44470a0f0811ae0729b57b56762c99ef435c06489bc0504787dfe4f18b1af83bf6d9942337f9e26cf24f5538e31e3d731e9e328
7
- data.tar.gz: b67aad3ba1659706a4ba9a09c94c998bd78569ff577f8b208d5f76265e4c0617142a88de68a3a7ebedb313f06164e7e2329f703996caaa300f450a9f64e48a22
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 + GraphQL client, with `with_company`/`with_user` scoping
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.client.experiences.get(exp_id)
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
- # With app/company context from env
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.api.users.get_current_user
146
- Whop.api.users.get_user(userId: "user_xxx")
147
- Whop.api.users.list_user_socials(userId: "user_xxx", first: 10)
148
- Whop.api.users.ban_user(input: { userId: "user_xxx", reason: "abuse" })
149
-
150
- # Payments
151
- Whop.api.payments.create_checkout_session(input: { planId: "plan_xxx", successUrl: "https://...", cancelUrl: "https://..." })
152
- Whop.api.payments.charge_user(input: { userId: "user_xxx", amount: 1000, currency: "USD" })
153
- Whop.api.payments.list_receipts_for_company(companyId: "biz_xxx", first: 20)
154
-
155
- # Invoices
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 persisted GraphQL operations per Whop docs
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
- data = @client.graphql_query(
37
- "checkIfUserHasAccessToExperience",
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
- data = @client.graphql_query(
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
- data = @client.graphql_query(
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 extract_access_boolean(graphql_result)
65
- return false unless graphql_result.is_a?(Hash)
66
- data = graphql_result["data"] || graphql_result
67
- return false unless data.is_a?(Hash)
68
-
69
- key = %w[hasAccessToExperience hasAccessToAccessPass hasAccessToCompany].find { |k| data.key?(k) rescue false }
70
- payload = key ? data[key] : data
71
-
72
- return payload["hasAccess"] if payload.is_a?(Hash) && payload.key?("hasAccess")
73
- return payload if payload == true || payload == false
74
- false
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
- has_access = if experience_id
58
- Whop.client.access.user_has_access_to_experience?(user_id: uid, experience_id: experience_id)
59
- elsif access_pass_id
60
- Whop.client.access.user_has_access_to_access_pass?(user_id: uid, access_pass_id: access_pass_id)
61
- elsif company_id
62
- Whop.client.access.user_has_access_to_company?(user_id: uid, company_id: company_id)
63
- else
64
- true
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
@@ -1,5 +1,5 @@
1
1
  module Whop
2
- VERSION = "1.0.5"
2
+ VERSION = "1.1.0"
3
3
  end
4
4
 
5
5
 
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.5
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-18 00:00:00.000000000 Z
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