whop 1.0.2 → 1.0.4

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: b66a5c1ff726bf0d3142a0d2f3263f990c114f1de42c05dfe3d564d63719e702
4
- data.tar.gz: c36bc051232dafa141659f0a11df945f5ce06aa8b852ae37504b752d72ed2cfc
3
+ metadata.gz: 3c6ea2caf5959059d7e27c54eecb479e8007420a1e3989fcc8ab9b3cf3d6be4e
4
+ data.tar.gz: 9f2d86766b0cd27d265e9fd87c1be3102b9baf0c9d836539d4217427d864e998
5
5
  SHA512:
6
- metadata.gz: 13b4f042216946a03e922f4c7642c6b8a478b3ea95b9c3e727a39e756f989ad7ae6cf248e7dfa74a3101637ea3eef5c9e89acf80d7cd4a322067c0eb4e529cf2
7
- data.tar.gz: 417ab6ac4239fbe3bbbdccb3a7bf41326e847152318658449fc92fd74e28b2999ac556a8a27656322235eaf74e3ecafe384191a074a1030b15a6fa07ebef72b6
6
+ metadata.gz: 7e08a3e8c6bc48cad71cfc3bd5c7e67feb0718df8077bbdbbbd046d24de8e27e5a29e60b8c37a9c30a82952ba11c15a21708e215f62fff670a1bff4a3881054d
7
+ data.tar.gz: f9697d3d60918f218c6bc61798733cc83bc7d46b4a6e679042e7434c39c5d84d8ec3d0d16f623603ebe9ae32ddb05993f7a0e52f6210adb5f16259823341ad6e
data/README.md CHANGED
@@ -116,6 +116,16 @@ curl -i -X POST http://localhost:3000/whop/webhooks \
116
116
  ```
117
117
 
118
118
  ## Using the client
119
+ # Frontend (iframe) helper
120
+
121
+ Add the SDK tags in your layout head:
122
+
123
+ ```erb
124
+ <%= extend(Whop::IframeHelper) && whop_iframe_sdk_tags %>
125
+ ```
126
+
127
+ Ensure your CSP allows Whop domains; the installer adds `config/initializers/whop_iframe.rb` with sensible defaults (script/connect/frame to unpkg.com, esm.sh, whop.com/*).
128
+
119
129
 
120
130
  ```ruby
121
131
  # With app/company context from env
@@ -4,6 +4,12 @@ Rails.application.config.action_dispatch.default_headers.delete('X-Frame-Options
4
4
 
5
5
  Rails.application.config.content_security_policy do |policy|
6
6
  policy.frame_ancestors :self, "https://whop.com", "https://*.whop.com"
7
+ # Allow Whop iframe SDK (UMD) and ESM fallback
8
+ policy.script_src :self, :https, "https://unpkg.com", "https://esm.sh"
9
+ # Allow network calls to Whop API from the browser as needed
10
+ policy.connect_src :self, :https, "https://whop.com", "https://*.whop.com"
11
+ # Allow embedding Whop frames
12
+ policy.frame_src :self, "https://whop.com", "https://*.whop.com"
7
13
  end
8
14
 
9
15
 
data/lib/whop/dsl.rb CHANGED
@@ -24,6 +24,15 @@ module Whop
24
24
  @methods[method_name.to_sym] = { type: :graphql, operation: operation, args: Array(args).map(&:to_sym) }
25
25
  end
26
26
 
27
+ def graphql_inline(method_name, operation:, query:, args: [])
28
+ @methods[method_name.to_sym] = {
29
+ type: :graphql_inline,
30
+ operation: operation,
31
+ query: query,
32
+ args: Array(args).map(&:to_sym)
33
+ }
34
+ end
35
+
27
36
  def rest_get(method_name, path:, args: [], params: [])
28
37
  @methods[method_name.to_sym] = { type: :rest_get, path: path, args: Array(args).map(&:to_sym), params: Array(params).map(&:to_sym) }
29
38
  end
@@ -63,6 +72,9 @@ module Whop
63
72
  when :graphql
64
73
  variables = build_named_args(spec[:args], args, kwargs)
65
74
  @client.graphql(spec[:operation], variables)
75
+ when :graphql_inline
76
+ variables = build_named_args(spec[:args], args, kwargs)
77
+ @client.graphql_query(spec[:operation], spec[:query], variables)
66
78
  when :rest_get
67
79
  path = interpolate_path(spec[:path], build_named_args(spec[:args], args, kwargs))
68
80
  query = kwargs.select { |k, _| spec[:params].include?(k.to_sym) }
@@ -34,16 +34,100 @@ Whop::DSL.define do
34
34
  end
35
35
 
36
36
  resource :payments do
37
- graphql :create_checkout_session, operation: "createCheckoutSession", args: %i[input]
38
- graphql :charge_user, operation: "chargeUser", args: %i[input]
39
- graphql :pay_user, operation: "payUser", args: %i[input]
40
- graphql :list_receipts_for_company, operation: "listReceiptsForCompany", args: %i[companyId first after filter]
37
+ graphql_inline :create_checkout_session,
38
+ operation: "createCheckoutSession",
39
+ args: %i[input],
40
+ query: <<~GQL
41
+ mutation createCheckoutSession($input: CreateCheckoutSessionInput!) {
42
+ createCheckoutSession(input: $input) {
43
+ id
44
+ planId
45
+ }
46
+ }
47
+ GQL
48
+
49
+ graphql_inline :charge_user,
50
+ operation: "chargeUser",
51
+ args: %i[input],
52
+ query: <<~GQL
53
+ mutation chargeUser($input: ChargeUserInput!) {
54
+ chargeUser(input: $input) {
55
+ status
56
+ inAppPurchase: checkoutSession {
57
+ id
58
+ planId
59
+ }
60
+ }
61
+ }
62
+ GQL
63
+
64
+ graphql_inline :pay_user,
65
+ operation: "payUser",
66
+ args: %i[input],
67
+ query: <<~GQL
68
+ mutation payUser($input: TransferFundsInput!) {
69
+ transferFunds(input: $input)
70
+ }
71
+ GQL
72
+
73
+ graphql_inline :list_receipts_for_company,
74
+ operation: "listReceiptsForCompany",
75
+ args: %i[companyId first after filter],
76
+ query: <<~GQL
77
+ query listReceiptsForCompany($companyId: ID!, $first: Int, $after: String, $filter: ReceiptV2Filters) {
78
+ company(id: $companyId) {
79
+ receipts: receiptsV2(first: $first, after: $after, filter: $filter) {
80
+ nodes {
81
+ id
82
+ createdAt
83
+ finalAmount
84
+ currency
85
+ }
86
+ pageInfo { hasNextPage endCursor }
87
+ }
88
+ }
89
+ }
90
+ GQL
41
91
  end
42
92
 
43
93
  resource :invoices do
44
- graphql :create_invoice, operation: "createInvoice", args: %i[input]
45
- graphql :get_invoice, operation: "getInvoice", args: %i[invoiceId companyId]
46
- graphql :list_invoices, operation: "listInvoices", args: %i[companyId after before first last]
94
+ graphql_inline :create_invoice,
95
+ operation: "createInvoice",
96
+ args: %i[input],
97
+ query: <<~GQL
98
+ mutation createInvoice($input: CreateInvoiceInput!) {
99
+ createInvoice(input: $input) {
100
+ invoice { id number status createdAt }
101
+ checkoutJobId
102
+ }
103
+ }
104
+ GQL
105
+
106
+ graphql_inline :get_invoice,
107
+ operation: "getInvoice",
108
+ args: %i[invoiceId companyId],
109
+ query: <<~GQL
110
+ query getInvoice($invoiceId: ID!, $companyId: ID!) {
111
+ company(id: $companyId) {
112
+ invoice(id: $invoiceId) { id number status createdAt }
113
+ }
114
+ }
115
+ GQL
116
+
117
+ graphql_inline :list_invoices,
118
+ operation: "listInvoices",
119
+ args: %i[companyId after before first last],
120
+ query: <<~GQL
121
+ query listInvoices($companyId: ID!, $after: String, $before: String, $first: Int, $last: Int) {
122
+ company(id: $companyId) {
123
+ invoices(after: $after, before: $before, first: $first, last: $last) {
124
+ totalCount
125
+ pageInfo { endCursor hasNextPage hasPreviousPage startCursor }
126
+ nodes { id number status createdAt }
127
+ }
128
+ }
129
+ }
130
+ GQL
47
131
  end
48
132
 
49
133
  resource :promo_codes do
@@ -0,0 +1,53 @@
1
+ require "erb"
2
+
3
+ module Whop
4
+ module IframeHelper
5
+ # Renders the Whop iframe SDK UMD script and a small inline initializer that creates
6
+ # a global window.iframeSdk instance. The initializer uses the provided app_id, or
7
+ # falls back to ENV["WHOP_APP_ID"] / Whop.config.app_id, then finally to
8
+ # document.body.dataset.whopAppId if present.
9
+ #
10
+ # Usage in layout head:
11
+ # <%= whop_iframe_sdk_tags %>
12
+ # Optionally add <body data-whop-app-id="..."> if you prefer not to expose ENV.
13
+ def whop_iframe_sdk_tags(app_id: nil, nonce: nil)
14
+ resolved_app_id = app_id || ENV["WHOP_APP_ID"] || (Whop.config.app_id rescue nil) || ""
15
+ begin
16
+ # Prefer CSP nonce from Rails if available
17
+ nonce ||= respond_to?(:content_security_policy_nonce) ? content_security_policy_nonce : nil
18
+ rescue StandardError
19
+ # ignore
20
+ end
21
+
22
+ nonce_attr = nonce ? " nonce=\"#{ERB::Util.html_escape(nonce)}\"" : ""
23
+ init = <<~JS
24
+ (function () {
25
+ try {
26
+ var g = window;
27
+ var create = (g.WhopIframe && g.WhopIframe.createSdk) ||
28
+ (g.WhopIframeSdk && g.WhopIframeSdk.createSdk) ||
29
+ g.createWhopIframeSdk ||
30
+ g.createSdk;
31
+ if (create && !g.iframeSdk) {
32
+ var appId = #{resolved_app_id.to_s.strip.empty? ? '""' : ERB::Util.html_escape(resolved_app_id).inspect};
33
+ if (!appId) {
34
+ var body = document.body;
35
+ appId = (body && body.dataset && body.dataset.whopAppId) || "";
36
+ }
37
+ if (appId) {
38
+ g.iframeSdk = create({ appId: appId });
39
+ }
40
+ }
41
+ } catch (e) {
42
+ // swallow init errors
43
+ }
44
+ })();
45
+ JS
46
+
47
+ html = %Q(<script src="https://unpkg.com/@whop/iframe@latest"#{nonce_attr}></script>\n<script#{nonce_attr}>#{init.strip}</script>)
48
+ html.respond_to?(:html_safe) ? html.html_safe : html
49
+ end
50
+ end
51
+ end
52
+
53
+
data/lib/whop/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Whop
2
- VERSION = "1.0.2"
2
+ VERSION = "1.0.4"
3
3
  end
4
4
 
5
5
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nikhil Nelson
@@ -175,6 +175,7 @@ files:
175
175
  - lib/whop/dsl.rb
176
176
  - lib/whop/dsl_prelude.rb
177
177
  - lib/whop/error.rb
178
+ - lib/whop/iframe_helper.rb
178
179
  - lib/whop/token.rb
179
180
  - lib/whop/version.rb
180
181
  - lib/whop/webhooks/engine.rb