@whop/elements 1.2.0 → 1.4.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.
package/checkout.d.cts CHANGED
@@ -152,10 +152,10 @@ export interface CheckoutElementCreateOptions {
152
152
  */
153
153
  export interface ExpressCheckoutElementProps {
154
154
  /**
155
- * Which wallets may render — a filter over what the buyer’s device actually offers. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first whatever order this lists. An empty list renders nothing (warned in dev builds). Both wallets need the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
156
- * @default ["apple_pay","google_pay"]
155
+ * Which wallets may render — a filter over what the buyer’s device and the seller actually offer. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first, with PayPal last, whatever order this lists. An empty list renders nothing (warned in dev builds). Every wallet needs the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
156
+ * @default ["apple_pay","google_pay","paypal_express"]
157
157
  */
158
- wallets?: ("apple_pay" | "google_pay")[];
158
+ wallets?: ("apple_pay" | "google_pay" | "paypal_express")[];
159
159
  /**
160
160
  * How the buttons stack. `auto` follows the element’s own container — stacked in narrow containers, side by side in wide ones; `horizontal` and `vertical` force one arrangement.
161
161
  * @default "auto"
@@ -193,10 +193,10 @@ export interface ExpressCheckoutElementHandle {
193
193
  */
194
194
  export interface ExpressCheckoutElementCreateOptions {
195
195
  /**
196
- * Which wallets may render — a filter over what the buyer’s device actually offers. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first whatever order this lists. An empty list renders nothing (warned in dev builds). Both wallets need the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
197
- * @default ["apple_pay","google_pay"]
196
+ * Which wallets may render — a filter over what the buyer’s device and the seller actually offer. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first, with PayPal last, whatever order this lists. An empty list renders nothing (warned in dev builds). Every wallet needs the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
197
+ * @default ["apple_pay","google_pay","paypal_express"]
198
198
  */
199
- wallets?: ("apple_pay" | "google_pay")[];
199
+ wallets?: ("apple_pay" | "google_pay" | "paypal_express")[];
200
200
  /**
201
201
  * How the buttons stack. `auto` follows the element’s own container — stacked in narrow containers, side by side in wide ones; `horizontal` and `vertical` force one arrangement.
202
202
  * @default "auto"
@@ -227,7 +227,7 @@ export interface CheckoutHandle {
227
227
  */
228
228
  create(name: "checkout", options?: CheckoutElementCreateOptions): CheckoutElementHandle;
229
229
  /**
230
- * One-press Apple Pay and Google Pay buttons for a checkout — the OS payment sheet collects whatever the session still needs (the buyer's email, a phone number where the seller collects one, a shipping address for physical goods, and in Apple Pay a promo code where the seller offers entry) and one press finishes the purchase. It shares the checkout's entry slot with the full checkout element, so a handle mounts exactly one of the two: this button alone where the purchase is simple enough for a sheet to finish, or the full checkout surface for everything else. Renders only the wallets the buyer's device can actually pay with — and the checkout's own payment method configuration allows — using the served button art, and renders nothing where no wallet is available. Both wallets require the page's domain to be a verified payment method domain: first-party whop.com pages are pre-approved, and any other site must register its domain through the Payment Method Domains API before either button renders there. Offering Google Pay on your own site is subject to the Google Pay API Terms of Service. A checkout the sheet cannot honestly finish — a waitlist join, a transfer, a form question only a page can ask — refuses loudly instead of rendering a doomed button.
230
+ * One-press Apple Pay, Google Pay and PayPal buttons for a checkout — the wallet collects whatever the session still needs (the buyer's email, a shipping address for physical goods, a phone number where the seller collects one in Apple Pay and Google Pay, and in Apple Pay a promo code where the seller offers entry) and one press finishes the purchase. It shares the checkout's entry slot with the full checkout element, so a handle mounts exactly one of the two: this button alone where the purchase is simple enough for a sheet to finish, or the full checkout surface for everything else. Renders only the wallets the buyer's device can actually pay with — and the checkout's own payment method configuration allows — using the served button art, and renders nothing where no wallet is available. PayPal appears on one-time purchases where the seller accepts it and opens in its own window. All three wallets require the page's domain to be a verified payment method domain: first-party whop.com pages are pre-approved, and any other site must register its domain through the Payment Method Domains API before any of these buttons renders there. Offering Google Pay on your own site is subject to the Google Pay API Terms of Service. A checkout the sheet cannot honestly finish — a waitlist join, a transfer, a form question only a page can ask — refuses loudly instead of rendering a doomed button.
231
231
  * Exclusive slot `entry`. Mount exactly one of: checkout | expressCheckout.
232
232
  */
233
233
  create(name: "expressCheckout", options?: ExpressCheckoutElementCreateOptions): ExpressCheckoutElementHandle;
package/checkout.d.ts CHANGED
@@ -152,10 +152,10 @@ export interface CheckoutElementCreateOptions {
152
152
  */
153
153
  export interface ExpressCheckoutElementProps {
154
154
  /**
155
- * Which wallets may render — a filter over what the buyer’s device actually offers. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first whatever order this lists. An empty list renders nothing (warned in dev builds). Both wallets need the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
156
- * @default ["apple_pay","google_pay"]
155
+ * Which wallets may render — a filter over what the buyer’s device and the seller actually offer. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first, with PayPal last, whatever order this lists. An empty list renders nothing (warned in dev builds). Every wallet needs the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
156
+ * @default ["apple_pay","google_pay","paypal_express"]
157
157
  */
158
- wallets?: ("apple_pay" | "google_pay")[];
158
+ wallets?: ("apple_pay" | "google_pay" | "paypal_express")[];
159
159
  /**
160
160
  * How the buttons stack. `auto` follows the element’s own container — stacked in narrow containers, side by side in wide ones; `horizontal` and `vertical` force one arrangement.
161
161
  * @default "auto"
@@ -193,10 +193,10 @@ export interface ExpressCheckoutElementHandle {
193
193
  */
194
194
  export interface ExpressCheckoutElementCreateOptions {
195
195
  /**
196
- * Which wallets may render — a filter over what the buyer’s device actually offers. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first whatever order this lists. An empty list renders nothing (warned in dev builds). Both wallets need the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
197
- * @default ["apple_pay","google_pay"]
196
+ * Which wallets may render — a filter over what the buyer’s device and the seller actually offer. The element never shows a wallet the device, the served matrix, or the checkout’s payment method configuration cannot back, and the render order stays best-native-first, with PayPal last, whatever order this lists. An empty list renders nothing (warned in dev builds). Every wallet needs the page’s domain verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain) — first-party whop.com pages are pre-approved; on any other site, register and verify the domain or the buttons stay hidden. Offering Google Pay on your own site is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos).
197
+ * @default ["apple_pay","google_pay","paypal_express"]
198
198
  */
199
- wallets?: ("apple_pay" | "google_pay")[];
199
+ wallets?: ("apple_pay" | "google_pay" | "paypal_express")[];
200
200
  /**
201
201
  * How the buttons stack. `auto` follows the element’s own container — stacked in narrow containers, side by side in wide ones; `horizontal` and `vertical` force one arrangement.
202
202
  * @default "auto"
@@ -227,7 +227,7 @@ export interface CheckoutHandle {
227
227
  */
228
228
  create(name: "checkout", options?: CheckoutElementCreateOptions): CheckoutElementHandle;
229
229
  /**
230
- * One-press Apple Pay and Google Pay buttons for a checkout — the OS payment sheet collects whatever the session still needs (the buyer's email, a phone number where the seller collects one, a shipping address for physical goods, and in Apple Pay a promo code where the seller offers entry) and one press finishes the purchase. It shares the checkout's entry slot with the full checkout element, so a handle mounts exactly one of the two: this button alone where the purchase is simple enough for a sheet to finish, or the full checkout surface for everything else. Renders only the wallets the buyer's device can actually pay with — and the checkout's own payment method configuration allows — using the served button art, and renders nothing where no wallet is available. Both wallets require the page's domain to be a verified payment method domain: first-party whop.com pages are pre-approved, and any other site must register its domain through the Payment Method Domains API before either button renders there. Offering Google Pay on your own site is subject to the Google Pay API Terms of Service. A checkout the sheet cannot honestly finish — a waitlist join, a transfer, a form question only a page can ask — refuses loudly instead of rendering a doomed button.
230
+ * One-press Apple Pay, Google Pay and PayPal buttons for a checkout — the wallet collects whatever the session still needs (the buyer's email, a shipping address for physical goods, a phone number where the seller collects one in Apple Pay and Google Pay, and in Apple Pay a promo code where the seller offers entry) and one press finishes the purchase. It shares the checkout's entry slot with the full checkout element, so a handle mounts exactly one of the two: this button alone where the purchase is simple enough for a sheet to finish, or the full checkout surface for everything else. Renders only the wallets the buyer's device can actually pay with — and the checkout's own payment method configuration allows — using the served button art, and renders nothing where no wallet is available. PayPal appears on one-time purchases where the seller accepts it and opens in its own window. All three wallets require the page's domain to be a verified payment method domain: first-party whop.com pages are pre-approved, and any other site must register its domain through the Payment Method Domains API before any of these buttons renders there. Offering Google Pay on your own site is subject to the Google Pay API Terms of Service. A checkout the sheet cannot honestly finish — a waitlist join, a transfer, a form question only a page can ask — refuses loudly instead of rendering a doomed button.
231
231
  * Exclusive slot `entry`. Mount exactly one of: checkout | expressCheckout.
232
232
  */
233
233
  create(name: "expressCheckout", options?: ExpressCheckoutElementCreateOptions): ExpressCheckoutElementHandle;
package/index.cjs CHANGED
@@ -22,18 +22,27 @@ var npm_loader_exports = {};
22
22
  __export(npm_loader_exports, {
23
23
  SDK_URL: () => SDK_URL,
24
24
  WHOP_ELEMENTS_LOCALES: () => WHOP_ELEMENTS_LOCALES,
25
+ contentSecurityPolicy: () => contentSecurityPolicy,
25
26
  loadWhop: () => loadWhop,
26
27
  preloadHints: () => preloadHints
27
28
  });
28
29
  module.exports = __toCommonJS(npm_loader_exports);
29
30
 
31
+ // <define:__WHOP_ELEMENTS_API_ORIGINS__>
32
+ var define_WHOP_ELEMENTS_API_ORIGINS_default = { production: "https://api.whop.com", sandbox: "https://sandbox-api.whop.com" };
33
+
30
34
  // <define:__WHOP_ELEMENTS_LOCALES__>
31
35
  var define_WHOP_ELEMENTS_LOCALES_default = ["en", "es", "zh", "nl", "pt", "de", "hu", "it", "fr", "ja", "pl", "tr"];
32
36
 
37
+ // <define:__WHOP_ELEMENTS_PAGE_REQUIREMENTS__>
38
+ var define_WHOP_ELEMENTS_PAGE_REQUIREMENTS_default = { wallets: { production: { sources: { "frame-src": ["https://checkout.paypal.com", "https://assets.braintreegateway.com", "https://www.paypal.com"], "script-src": ["https://pay.google.com", "https://applepay.cdn-apple.com", "https://js.braintreegateway.com", "https://www.paypal.com", "https://c.paypal.com"], "connect-src": ["https://pay.google.com", "https://google.com", "https://account.google.com", "https://www.google.com", "https://api.basistheory.com", "https://payments.braintree-api.com", "https://api.braintreegateway.com", "https://api-m.paypal.com", "https://www.paypal.com"], "img-src": ["https://www.paypalobjects.com"] }, hints: [{ rel: "preload", href: "https://pay.google.com/gp/p/js/pay.js", as: "script" }, { rel: "preload", href: "https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js", as: "script", crossOrigin: "anonymous" }, { rel: "preload", href: "https://js.braintreegateway.com/web/3.146.0/js/paypal-checkout-v6.min.js", as: "script" }, { rel: "preload", href: "https://www.paypal.com/web-sdk/v6/core", as: "script" }, { rel: "preconnect", href: "https://api.basistheory.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://payments.braintree-api.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://api.braintreegateway.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://checkout.paypal.com" }, { rel: "preconnect", href: "https://assets.braintreegateway.com" }, { rel: "preconnect", href: "https://c.paypal.com" }, { rel: "preconnect", href: "https://api-m.paypal.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://www.paypal.com", crossOrigin: "anonymous" }] }, sandbox: { sources: { "frame-src": ["https://checkout.paypal.com", "https://assets.braintreegateway.com", "https://www.sandbox.paypal.com"], "script-src": ["https://pay.google.com", "https://applepay.cdn-apple.com", "https://js.braintreegateway.com", "https://www.sandbox.paypal.com", "https://c.paypal.com"], "connect-src": ["https://pay.google.com", "https://google.com", "https://account.google.com", "https://www.google.com", "https://api.basistheory.com", "https://payments.sandbox.braintree-api.com", "https://api.sandbox.braintreegateway.com", "https://api-m.sandbox.paypal.com", "https://www.sandbox.paypal.com"], "img-src": ["https://www.paypalobjects.com"] }, hints: [{ rel: "preload", href: "https://pay.google.com/gp/p/js/pay.js", as: "script" }, { rel: "preload", href: "https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js", as: "script", crossOrigin: "anonymous" }, { rel: "preload", href: "https://js.braintreegateway.com/web/3.146.0/js/paypal-checkout-v6.min.js", as: "script" }, { rel: "preload", href: "https://www.sandbox.paypal.com/web-sdk/v6/core", as: "script" }, { rel: "preconnect", href: "https://api.basistheory.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://payments.sandbox.braintree-api.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://api.sandbox.braintreegateway.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://checkout.paypal.com" }, { rel: "preconnect", href: "https://assets.braintreegateway.com" }, { rel: "preconnect", href: "https://c.paypal.com" }, { rel: "preconnect", href: "https://api-m.sandbox.paypal.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://www.sandbox.paypal.com", crossOrigin: "anonymous" }] } } };
39
+
33
40
  // ../../packages/elements-codegen/src/cli/build/npm-loader.ts
34
41
  var ORIGIN = "https://cdn.whop.com";
35
42
  var SDK_URL = "https://cdn.whop.com/elements/amber/elements.js";
36
43
  var WHOP_ELEMENTS_LOCALES = define_WHOP_ELEMENTS_LOCALES_default;
44
+ var API_ORIGINS = define_WHOP_ELEMENTS_API_ORIGINS_default;
45
+ var PAGE_REQUIREMENTS = define_WHOP_ELEMENTS_PAGE_REQUIREMENTS_default;
37
46
  var TAG = "script[data-whop-elements]";
38
47
  var resolved = null;
39
48
  function withRetry(promise) {
@@ -131,9 +140,187 @@ function watch(w, script, created) {
131
140
  }
132
141
  return script.__whopReady;
133
142
  }
134
- function preloadHints() {
135
- return [
136
- { rel: "preconnect", href: ORIGIN },
137
- { rel: "preload", href: SDK_URL, as: "script" }
138
- ];
143
+ function environmentOf(environment) {
144
+ if (environment === void 0 || environment === "production") return "production";
145
+ if (environment === "sandbox") return "sandbox";
146
+ throw new Error(`unknown environment ${JSON.stringify(environment)}: expected "production" or "sandbox"`);
147
+ }
148
+ function apiOrigin(environment) {
149
+ return API_ORIGINS[environmentOf(environment)];
150
+ }
151
+ function escapeAttribute(value) {
152
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
153
+ }
154
+ var PreloadHints = class extends Array {
155
+ // map/flatMap produce plain arrays: rendered <link> tags are not a hint list. The methods below
156
+ // keep hints as hints, so a narrowed list still has toLinkHeader() and friends.
157
+ static get [Symbol.species]() {
158
+ return Array;
159
+ }
160
+ filter(predicate, thisArg) {
161
+ return hintsFrom(super.filter(predicate, thisArg));
162
+ }
163
+ slice(start, end) {
164
+ return hintsFrom(super.slice(start, end));
165
+ }
166
+ concat(...items) {
167
+ return hintsFrom(super.concat(...items));
168
+ }
169
+ // copies sorted or reversed without Array.prototype.toSorted/toReversed, which older browsers lack.
170
+ toSorted(compareFn) {
171
+ return hintsFrom(this).sort(compareFn);
172
+ }
173
+ toReversed() {
174
+ const hints = hintsFrom(this);
175
+ hints.reverse();
176
+ return hints;
177
+ }
178
+ toLinkHeader() {
179
+ return this.map((h) => `<${h.href}>; rel=${h.rel}${h.as ? `; as=${h.as}` : ""}${h.crossOrigin ? `; crossorigin=${h.crossOrigin}` : ""}`).join(", ");
180
+ }
181
+ toHtml() {
182
+ return this.map((h) => `<link rel="${escapeAttribute(h.rel)}" href="${escapeAttribute(h.href)}"${h.as ? ` as="${escapeAttribute(h.as)}"` : ""}${h.crossOrigin ? ` crossorigin="${h.crossOrigin}"` : ""}>`).join("\n");
183
+ }
184
+ applyTo(headers) {
185
+ const value = this.toLinkHeader();
186
+ if (value) headers.append("Link", value);
187
+ }
188
+ };
189
+ function hintsFrom(items) {
190
+ const hints = new PreloadHints();
191
+ for (const item of items) hints.push(item);
192
+ return hints;
193
+ }
194
+ function preloadHints(options = {}) {
195
+ const hints = new PreloadHints();
196
+ const add = (hint) => {
197
+ if (!hints.some((h) => h.rel === hint.rel && h.href === hint.href && h.crossOrigin === hint.crossOrigin)) hints.push(hint);
198
+ };
199
+ add({ rel: "preconnect", href: ORIGIN });
200
+ add({ rel: "preload", href: SDK_URL, as: "script" });
201
+ add({ rel: "preconnect", href: apiOrigin(options.environment), crossOrigin: "anonymous" });
202
+ const environment = environmentOf(options.environment);
203
+ for (const [group, byEnvironment] of Object.entries(PAGE_REQUIREMENTS)) {
204
+ if (options[group] === false) continue;
205
+ for (const hint of byEnvironment[environment].hints) add(hint);
206
+ }
207
+ return hints;
208
+ }
209
+ var FALLBACKS = { "frame-src": ["child-src", "default-src"], "script-src": ["default-src"], "connect-src": ["default-src"], "img-src": ["default-src"] };
210
+ var POLICY_HEADERS = ["Content-Security-Policy", "Content-Security-Policy-Report-Only"];
211
+ var camelToKebab = (key) => key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
212
+ var kebabToCamel = (key) => key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
213
+ function addSources(values, sources) {
214
+ const out = values.filter((v) => typeof v !== "string" || v.toLowerCase() !== "'none'");
215
+ for (const source of sources) if (!out.includes(source)) out.push(source);
216
+ return out;
217
+ }
218
+ function mergeDirectives(directives, sources) {
219
+ for (const [name, list] of Object.entries(sources)) {
220
+ if (list.length === 0) continue;
221
+ const own = directives.find((d) => d.name === name);
222
+ const elem = name === "script-src" ? directives.find((d) => d.name === "script-src-elem") : void 0;
223
+ if (elem) elem.values = addSources(elem.values, list);
224
+ if (own) {
225
+ own.values = addSources(own.values, list);
226
+ continue;
227
+ }
228
+ const fallback = FALLBACKS[name].map((f) => directives.find((d) => d.name === f)).find((d) => d !== void 0);
229
+ if (fallback) directives.push({ name, values: addSources([...fallback.values], list) });
230
+ }
231
+ }
232
+ function parsePolicy(policy) {
233
+ const directives = [];
234
+ for (const part of policy.split(";")) {
235
+ const [name, ...values] = part.trim().split(/\s+/).filter(Boolean);
236
+ if (name && !directives.some((d) => d.name === name.toLowerCase())) directives.push({ name: name.toLowerCase(), values });
237
+ }
238
+ return directives;
239
+ }
240
+ var serializePolicy = (directives) => directives.map((d) => [d.name, ...d.values].join(" ")).join("; ");
241
+ var HELMET_DEFAULTS = { "script-src": ["'self'"], "img-src": ["'self'", "data:"] };
242
+ function mergeHelmetDirectives(policy, sources) {
243
+ const keys = Object.keys(policy);
244
+ const camel = keys.length > 0 && keys.every((k) => !k.includes("-"));
245
+ const keyOf = (name) => keys.find((k) => camelToKebab(k) === name);
246
+ const read = /* @__PURE__ */ new Map();
247
+ const valuesOf = (key) => {
248
+ if (key === void 0) return void 0;
249
+ const seen = read.get(key);
250
+ if (seen) return [...seen];
251
+ const value = policy[key];
252
+ const values = typeof value === "string" ? value.split(/\s+/).filter(Boolean) : value != null && typeof value[Symbol.iterator] === "function" ? [...value] : void 0;
253
+ if (values) read.set(key, values);
254
+ return values && [...values];
255
+ };
256
+ const isIterator = (value) => value != null && typeof value.next === "function" && value[Symbol.iterator]?.() === value;
257
+ const shaped = (key, values) => {
258
+ const value = policy[key];
259
+ if (typeof value === "string") return values.join(" ");
260
+ if (value instanceof Set) return new Set(values);
261
+ return isIterator(value) ? values.values() : values;
262
+ };
263
+ const out = { ...policy };
264
+ for (const [name, list] of Object.entries(sources)) {
265
+ if (list.length === 0) continue;
266
+ const ownKey = keyOf(name);
267
+ const own = valuesOf(ownKey);
268
+ const helmetDefault = ownKey !== void 0 && policy[ownKey] === null ? void 0 : HELMET_DEFAULTS[name];
269
+ const seed = own ?? (helmetDefault ? [...helmetDefault] : FALLBACKS[name].map((f) => valuesOf(keyOf(f))).find((v) => v !== void 0)) ?? ["'self'"];
270
+ const merged = addSources(seed, list);
271
+ const key = ownKey ?? (camel ? kebabToCamel(name) : name);
272
+ out[key] = shaped(key, merged);
273
+ if (name === "script-src") {
274
+ const elemKey = keyOf("script-src-elem");
275
+ const elem = valuesOf(elemKey);
276
+ if (elemKey !== void 0 && elem) out[elemKey] = shaped(elemKey, addSources(elem, list));
277
+ }
278
+ }
279
+ for (const [key, values] of read) if (out[key] === policy[key] && isIterator(policy[key])) out[key] = values.values();
280
+ return out;
281
+ }
282
+ var ContentSecurityPolicy = class {
283
+ directives;
284
+ constructor(directives) {
285
+ this.directives = Object.freeze({
286
+ "frame-src": Object.freeze([...directives["frame-src"]]),
287
+ "script-src": Object.freeze([...directives["script-src"]]),
288
+ "connect-src": Object.freeze([...directives["connect-src"]]),
289
+ "img-src": Object.freeze([...directives["img-src"]])
290
+ });
291
+ }
292
+ // A directive with no sources is left out: written bare, it would allow nothing.
293
+ toString() {
294
+ return serializePolicy(
295
+ Object.entries(this.directives).filter(([, values]) => values.length > 0).map(([name, values]) => ({ name, values: [...values] }))
296
+ );
297
+ }
298
+ merge(policy) {
299
+ if (typeof policy !== "string") return mergeHelmetDirectives(policy, this.directives);
300
+ return policy.split(",").map((one) => {
301
+ const directives = parsePolicy(one);
302
+ mergeDirectives(directives, this.directives);
303
+ return serializePolicy(directives);
304
+ }).join(", ");
305
+ }
306
+ applyTo(headers) {
307
+ for (const name of POLICY_HEADERS) {
308
+ const value = headers.get(name);
309
+ if (value !== null) headers.set(name, this.merge(value));
310
+ }
311
+ }
312
+ toHtml(policy) {
313
+ return `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(this.merge(policy))}">`;
314
+ }
315
+ };
316
+ function contentSecurityPolicy(options = {}) {
317
+ const directives = { "frame-src": [ORIGIN], "script-src": [ORIGIN], "connect-src": [apiOrigin(options.environment)], "img-src": [] };
318
+ const environment = environmentOf(options.environment);
319
+ for (const [group, byEnvironment] of Object.entries(PAGE_REQUIREMENTS)) {
320
+ if (options[group] === false) continue;
321
+ for (const [name, list] of Object.entries(byEnvironment[environment].sources)) {
322
+ for (const source of list) if (!directives[name].includes(source)) directives[name].push(source);
323
+ }
324
+ }
325
+ return new ContentSecurityPolicy(directives);
139
326
  }
package/index.d.cts CHANGED
@@ -61,8 +61,71 @@ export type WhopLoad = Promise<WhopConstructor> & { retry(): WhopLoad };
61
61
  /** Load the hosted SDK: injects the `<script data-whop-elements>` tag once and resolves the global constructor. Safe to call any number of times: one script, one shared promise. A failed load is held, so every later call returns the same rejected promise and nothing retries on its own; call `retry()` on it to load again. */
62
62
  export declare function loadWhop(): WhopLoad;
63
63
 
64
- /** Resource hints (preconnect/preload) for the hosted SDK. Render them into `<head>` to start the fetch before `loadWhop()` runs. */
65
- export declare function preloadHints(): { rel: string; href: string; as?: string }[];
64
+ /** One `<link>` hint: render it into `<head>`, or send the list as a `Link` header. */
65
+ export interface PreloadHint {
66
+ rel: string;
67
+ href: string;
68
+ as?: string;
69
+ crossOrigin?: "anonymous";
70
+ }
71
+
72
+ /** Which connections `preloadHints()` warms beyond the hosted SDK. Unknown keys are ignored. */
73
+ export interface PreloadHintsOptions {
74
+ /** Which environment to warm for: the Whop API, and each provider's sandbox hosts in sandbox. Defaults to production. */
75
+ environment?: NonNullable<WhopGlobalConfig["environment"]>;
76
+ /**
77
+ * Apple Pay, Google Pay, and PayPal: preloads its scripts and warms the connections it opens from your page. Pass false on pages that don't show it.
78
+ */
79
+ wallets?: boolean;
80
+ }
81
+
82
+ /** The hints `preloadHints()` returns: an array of {@link PreloadHint}, plus ways to apply it. `filter`, `slice`, `concat`, `toSorted` and `toReversed` keep these methods; `map` returns a plain array. */
83
+ export interface WhopPreloadHints<T extends PreloadHint = PreloadHint> extends Array<T> {
84
+ filter<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: unknown): WhopPreloadHints<S>;
85
+ filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: unknown): WhopPreloadHints<T>;
86
+ slice(start?: number, end?: number): WhopPreloadHints<T>;
87
+ concat(...items: (T | ConcatArray<T>)[]): WhopPreloadHints<T>;
88
+ toSorted(compareFn?: (a: T, b: T) => number): WhopPreloadHints<T>;
89
+ toReversed(): WhopPreloadHints<T>;
90
+ /** The hints as one `Link` header value (servers and CDNs can turn it into 103 Early Hints). */
91
+ toLinkHeader(): string;
92
+ /** The hints as `<link>` tags, for server-rendered templates. */
93
+ toHtml(): string;
94
+ /** Append the hints to a response's `Link` header. */
95
+ applyTo(headers: Headers): void;
96
+ }
97
+
98
+ /** Resource hints (preconnect/preload) for the hosted SDK, the Whop API, and the scripts and connections elements use on your page. Render them into `<head>` to start the fetch before `loadWhop()` runs. */
99
+ export declare function preloadHints(options?: PreloadHintsOptions): WhopPreloadHints;
100
+
101
+ /** Which origins the policy allows. Unknown keys are ignored. */
102
+ export interface ContentSecurityPolicyOptions {
103
+ /** Which environment the policy allows: the Whop API, and each provider's sandbox hosts in sandbox. Defaults to production. */
104
+ environment?: NonNullable<WhopGlobalConfig["environment"]>;
105
+ /**
106
+ * Apple Pay, Google Pay, and PayPal: allows the origins it reaches from your page. Pass false on pages that don't show it.
107
+ */
108
+ wallets?: boolean;
109
+ }
110
+
111
+ /** The Content Security Policy sources elements need on your page. Merge them into your own policy; on their own they block everything else your page loads. */
112
+ export interface WhopContentSecurityPolicy {
113
+ /** The sources elements need, by directive. */
114
+ readonly directives: { readonly "frame-src": readonly string[]; readonly "script-src": readonly string[]; readonly "connect-src": readonly string[]; readonly "img-src": readonly string[] };
115
+ /** Only the sources elements need, as a policy string, leaving out a directive with none. Merge it into your own policy; used alone it blocks everything else. */
116
+ toString(): string;
117
+ /** Your policy with these sources added. A directive you don't set is left alone, except that one falling back to `default-src` starts from those values. */
118
+ merge(policy: string): string;
119
+ /** Your directives object, in helmet's shape, with these sources added. It assumes helmet's defaults (`useDefaults: true`), which fill every directive you leave out, so these directives are always written, starting from your value, else what helmet would send: `'self'` for `script-src`, `'self' data:` for `img-src`, and your `child-src` or `default-src` (else `'self'`) for the others. Other keys are returned as they were. */
120
+ merge<T extends Record<string, unknown>>(directives: T): T;
121
+ /** Merge these sources into the response's `Content-Security-Policy` (and `Content-Security-Policy-Report-Only`) headers. Does nothing to a response that sets neither. */
122
+ applyTo(headers: Headers): void;
123
+ /** Your policy, merged, as a `<meta http-equiv>` tag. A meta tag works only when it is your page's whole policy: the browser also enforces any header policy on top of it. */
124
+ toHtml(policy: string): string;
125
+ }
126
+
127
+ /** The Content Security Policy sources elements need on your page: the hosted frames and SDK, the Whop API, and the origins elements reach from your page. The scripts elements load for Apple Pay, Google Pay, and PayPal come from other companies and can load resources beyond these: check each provider's Content Security Policy guidance. A modal element dims the page behind it with an inline style, which a `style-src` without inline styles leaves out; the modal still works. */
128
+ export declare function contentSecurityPolicy(options?: ContentSecurityPolicyOptions): WhopContentSecurityPolicy;
66
129
 
67
130
  /** The locales this app's elements are built with, the default locale first: exactly the values `locale` accepts. `includes()` accepts any string and narrows it to `WhopElementsLocale`, so use it to validate dynamic values; anything unlisted falls back to the default locale. */
68
131
  export declare const WHOP_ELEMENTS_LOCALES: readonly WhopElementsLocale[] & {
package/index.d.ts CHANGED
@@ -61,8 +61,71 @@ export type WhopLoad = Promise<WhopConstructor> & { retry(): WhopLoad };
61
61
  /** Load the hosted SDK: injects the `<script data-whop-elements>` tag once and resolves the global constructor. Safe to call any number of times: one script, one shared promise. A failed load is held, so every later call returns the same rejected promise and nothing retries on its own; call `retry()` on it to load again. */
62
62
  export declare function loadWhop(): WhopLoad;
63
63
 
64
- /** Resource hints (preconnect/preload) for the hosted SDK. Render them into `<head>` to start the fetch before `loadWhop()` runs. */
65
- export declare function preloadHints(): { rel: string; href: string; as?: string }[];
64
+ /** One `<link>` hint: render it into `<head>`, or send the list as a `Link` header. */
65
+ export interface PreloadHint {
66
+ rel: string;
67
+ href: string;
68
+ as?: string;
69
+ crossOrigin?: "anonymous";
70
+ }
71
+
72
+ /** Which connections `preloadHints()` warms beyond the hosted SDK. Unknown keys are ignored. */
73
+ export interface PreloadHintsOptions {
74
+ /** Which environment to warm for: the Whop API, and each provider's sandbox hosts in sandbox. Defaults to production. */
75
+ environment?: NonNullable<WhopGlobalConfig["environment"]>;
76
+ /**
77
+ * Apple Pay, Google Pay, and PayPal: preloads its scripts and warms the connections it opens from your page. Pass false on pages that don't show it.
78
+ */
79
+ wallets?: boolean;
80
+ }
81
+
82
+ /** The hints `preloadHints()` returns: an array of {@link PreloadHint}, plus ways to apply it. `filter`, `slice`, `concat`, `toSorted` and `toReversed` keep these methods; `map` returns a plain array. */
83
+ export interface WhopPreloadHints<T extends PreloadHint = PreloadHint> extends Array<T> {
84
+ filter<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: unknown): WhopPreloadHints<S>;
85
+ filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: unknown): WhopPreloadHints<T>;
86
+ slice(start?: number, end?: number): WhopPreloadHints<T>;
87
+ concat(...items: (T | ConcatArray<T>)[]): WhopPreloadHints<T>;
88
+ toSorted(compareFn?: (a: T, b: T) => number): WhopPreloadHints<T>;
89
+ toReversed(): WhopPreloadHints<T>;
90
+ /** The hints as one `Link` header value (servers and CDNs can turn it into 103 Early Hints). */
91
+ toLinkHeader(): string;
92
+ /** The hints as `<link>` tags, for server-rendered templates. */
93
+ toHtml(): string;
94
+ /** Append the hints to a response's `Link` header. */
95
+ applyTo(headers: Headers): void;
96
+ }
97
+
98
+ /** Resource hints (preconnect/preload) for the hosted SDK, the Whop API, and the scripts and connections elements use on your page. Render them into `<head>` to start the fetch before `loadWhop()` runs. */
99
+ export declare function preloadHints(options?: PreloadHintsOptions): WhopPreloadHints;
100
+
101
+ /** Which origins the policy allows. Unknown keys are ignored. */
102
+ export interface ContentSecurityPolicyOptions {
103
+ /** Which environment the policy allows: the Whop API, and each provider's sandbox hosts in sandbox. Defaults to production. */
104
+ environment?: NonNullable<WhopGlobalConfig["environment"]>;
105
+ /**
106
+ * Apple Pay, Google Pay, and PayPal: allows the origins it reaches from your page. Pass false on pages that don't show it.
107
+ */
108
+ wallets?: boolean;
109
+ }
110
+
111
+ /** The Content Security Policy sources elements need on your page. Merge them into your own policy; on their own they block everything else your page loads. */
112
+ export interface WhopContentSecurityPolicy {
113
+ /** The sources elements need, by directive. */
114
+ readonly directives: { readonly "frame-src": readonly string[]; readonly "script-src": readonly string[]; readonly "connect-src": readonly string[]; readonly "img-src": readonly string[] };
115
+ /** Only the sources elements need, as a policy string, leaving out a directive with none. Merge it into your own policy; used alone it blocks everything else. */
116
+ toString(): string;
117
+ /** Your policy with these sources added. A directive you don't set is left alone, except that one falling back to `default-src` starts from those values. */
118
+ merge(policy: string): string;
119
+ /** Your directives object, in helmet's shape, with these sources added. It assumes helmet's defaults (`useDefaults: true`), which fill every directive you leave out, so these directives are always written, starting from your value, else what helmet would send: `'self'` for `script-src`, `'self' data:` for `img-src`, and your `child-src` or `default-src` (else `'self'`) for the others. Other keys are returned as they were. */
120
+ merge<T extends Record<string, unknown>>(directives: T): T;
121
+ /** Merge these sources into the response's `Content-Security-Policy` (and `Content-Security-Policy-Report-Only`) headers. Does nothing to a response that sets neither. */
122
+ applyTo(headers: Headers): void;
123
+ /** Your policy, merged, as a `<meta http-equiv>` tag. A meta tag works only when it is your page's whole policy: the browser also enforces any header policy on top of it. */
124
+ toHtml(policy: string): string;
125
+ }
126
+
127
+ /** The Content Security Policy sources elements need on your page: the hosted frames and SDK, the Whop API, and the origins elements reach from your page. The scripts elements load for Apple Pay, Google Pay, and PayPal come from other companies and can load resources beyond these: check each provider's Content Security Policy guidance. A modal element dims the page behind it with an inline style, which a `style-src` without inline styles leaves out; the modal still works. */
128
+ export declare function contentSecurityPolicy(options?: ContentSecurityPolicyOptions): WhopContentSecurityPolicy;
66
129
 
67
130
  /** The locales this app's elements are built with, the default locale first: exactly the values `locale` accepts. `includes()` accepts any string and narrows it to `WhopElementsLocale`, so use it to validate dynamic values; anything unlisted falls back to the default locale. */
68
131
  export declare const WHOP_ELEMENTS_LOCALES: readonly WhopElementsLocale[] & {
package/index.js CHANGED
@@ -1,10 +1,18 @@
1
+ // <define:__WHOP_ELEMENTS_API_ORIGINS__>
2
+ var define_WHOP_ELEMENTS_API_ORIGINS_default = { production: "https://api.whop.com", sandbox: "https://sandbox-api.whop.com" };
3
+
1
4
  // <define:__WHOP_ELEMENTS_LOCALES__>
2
5
  var define_WHOP_ELEMENTS_LOCALES_default = ["en", "es", "zh", "nl", "pt", "de", "hu", "it", "fr", "ja", "pl", "tr"];
3
6
 
7
+ // <define:__WHOP_ELEMENTS_PAGE_REQUIREMENTS__>
8
+ var define_WHOP_ELEMENTS_PAGE_REQUIREMENTS_default = { wallets: { production: { sources: { "frame-src": ["https://checkout.paypal.com", "https://assets.braintreegateway.com", "https://www.paypal.com"], "script-src": ["https://pay.google.com", "https://applepay.cdn-apple.com", "https://js.braintreegateway.com", "https://www.paypal.com", "https://c.paypal.com"], "connect-src": ["https://pay.google.com", "https://google.com", "https://account.google.com", "https://www.google.com", "https://api.basistheory.com", "https://payments.braintree-api.com", "https://api.braintreegateway.com", "https://api-m.paypal.com", "https://www.paypal.com"], "img-src": ["https://www.paypalobjects.com"] }, hints: [{ rel: "preload", href: "https://pay.google.com/gp/p/js/pay.js", as: "script" }, { rel: "preload", href: "https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js", as: "script", crossOrigin: "anonymous" }, { rel: "preload", href: "https://js.braintreegateway.com/web/3.146.0/js/paypal-checkout-v6.min.js", as: "script" }, { rel: "preload", href: "https://www.paypal.com/web-sdk/v6/core", as: "script" }, { rel: "preconnect", href: "https://api.basistheory.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://payments.braintree-api.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://api.braintreegateway.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://checkout.paypal.com" }, { rel: "preconnect", href: "https://assets.braintreegateway.com" }, { rel: "preconnect", href: "https://c.paypal.com" }, { rel: "preconnect", href: "https://api-m.paypal.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://www.paypal.com", crossOrigin: "anonymous" }] }, sandbox: { sources: { "frame-src": ["https://checkout.paypal.com", "https://assets.braintreegateway.com", "https://www.sandbox.paypal.com"], "script-src": ["https://pay.google.com", "https://applepay.cdn-apple.com", "https://js.braintreegateway.com", "https://www.sandbox.paypal.com", "https://c.paypal.com"], "connect-src": ["https://pay.google.com", "https://google.com", "https://account.google.com", "https://www.google.com", "https://api.basistheory.com", "https://payments.sandbox.braintree-api.com", "https://api.sandbox.braintreegateway.com", "https://api-m.sandbox.paypal.com", "https://www.sandbox.paypal.com"], "img-src": ["https://www.paypalobjects.com"] }, hints: [{ rel: "preload", href: "https://pay.google.com/gp/p/js/pay.js", as: "script" }, { rel: "preload", href: "https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js", as: "script", crossOrigin: "anonymous" }, { rel: "preload", href: "https://js.braintreegateway.com/web/3.146.0/js/paypal-checkout-v6.min.js", as: "script" }, { rel: "preload", href: "https://www.sandbox.paypal.com/web-sdk/v6/core", as: "script" }, { rel: "preconnect", href: "https://api.basistheory.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://payments.sandbox.braintree-api.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://api.sandbox.braintreegateway.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://checkout.paypal.com" }, { rel: "preconnect", href: "https://assets.braintreegateway.com" }, { rel: "preconnect", href: "https://c.paypal.com" }, { rel: "preconnect", href: "https://api-m.sandbox.paypal.com", crossOrigin: "anonymous" }, { rel: "preconnect", href: "https://www.sandbox.paypal.com", crossOrigin: "anonymous" }] } } };
9
+
4
10
  // ../../packages/elements-codegen/src/cli/build/npm-loader.ts
5
11
  var ORIGIN = "https://cdn.whop.com";
6
12
  var SDK_URL = "https://cdn.whop.com/elements/amber/elements.js";
7
13
  var WHOP_ELEMENTS_LOCALES = define_WHOP_ELEMENTS_LOCALES_default;
14
+ var API_ORIGINS = define_WHOP_ELEMENTS_API_ORIGINS_default;
15
+ var PAGE_REQUIREMENTS = define_WHOP_ELEMENTS_PAGE_REQUIREMENTS_default;
8
16
  var TAG = "script[data-whop-elements]";
9
17
  var resolved = null;
10
18
  function withRetry(promise) {
@@ -102,15 +110,194 @@ function watch(w, script, created) {
102
110
  }
103
111
  return script.__whopReady;
104
112
  }
105
- function preloadHints() {
106
- return [
107
- { rel: "preconnect", href: ORIGIN },
108
- { rel: "preload", href: SDK_URL, as: "script" }
109
- ];
113
+ function environmentOf(environment) {
114
+ if (environment === void 0 || environment === "production") return "production";
115
+ if (environment === "sandbox") return "sandbox";
116
+ throw new Error(`unknown environment ${JSON.stringify(environment)}: expected "production" or "sandbox"`);
117
+ }
118
+ function apiOrigin(environment) {
119
+ return API_ORIGINS[environmentOf(environment)];
120
+ }
121
+ function escapeAttribute(value) {
122
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
123
+ }
124
+ var PreloadHints = class extends Array {
125
+ // map/flatMap produce plain arrays: rendered <link> tags are not a hint list. The methods below
126
+ // keep hints as hints, so a narrowed list still has toLinkHeader() and friends.
127
+ static get [Symbol.species]() {
128
+ return Array;
129
+ }
130
+ filter(predicate, thisArg) {
131
+ return hintsFrom(super.filter(predicate, thisArg));
132
+ }
133
+ slice(start, end) {
134
+ return hintsFrom(super.slice(start, end));
135
+ }
136
+ concat(...items) {
137
+ return hintsFrom(super.concat(...items));
138
+ }
139
+ // copies sorted or reversed without Array.prototype.toSorted/toReversed, which older browsers lack.
140
+ toSorted(compareFn) {
141
+ return hintsFrom(this).sort(compareFn);
142
+ }
143
+ toReversed() {
144
+ const hints = hintsFrom(this);
145
+ hints.reverse();
146
+ return hints;
147
+ }
148
+ toLinkHeader() {
149
+ return this.map((h) => `<${h.href}>; rel=${h.rel}${h.as ? `; as=${h.as}` : ""}${h.crossOrigin ? `; crossorigin=${h.crossOrigin}` : ""}`).join(", ");
150
+ }
151
+ toHtml() {
152
+ return this.map((h) => `<link rel="${escapeAttribute(h.rel)}" href="${escapeAttribute(h.href)}"${h.as ? ` as="${escapeAttribute(h.as)}"` : ""}${h.crossOrigin ? ` crossorigin="${h.crossOrigin}"` : ""}>`).join("\n");
153
+ }
154
+ applyTo(headers) {
155
+ const value = this.toLinkHeader();
156
+ if (value) headers.append("Link", value);
157
+ }
158
+ };
159
+ function hintsFrom(items) {
160
+ const hints = new PreloadHints();
161
+ for (const item of items) hints.push(item);
162
+ return hints;
163
+ }
164
+ function preloadHints(options = {}) {
165
+ const hints = new PreloadHints();
166
+ const add = (hint) => {
167
+ if (!hints.some((h) => h.rel === hint.rel && h.href === hint.href && h.crossOrigin === hint.crossOrigin)) hints.push(hint);
168
+ };
169
+ add({ rel: "preconnect", href: ORIGIN });
170
+ add({ rel: "preload", href: SDK_URL, as: "script" });
171
+ add({ rel: "preconnect", href: apiOrigin(options.environment), crossOrigin: "anonymous" });
172
+ const environment = environmentOf(options.environment);
173
+ for (const [group, byEnvironment] of Object.entries(PAGE_REQUIREMENTS)) {
174
+ if (options[group] === false) continue;
175
+ for (const hint of byEnvironment[environment].hints) add(hint);
176
+ }
177
+ return hints;
178
+ }
179
+ var FALLBACKS = { "frame-src": ["child-src", "default-src"], "script-src": ["default-src"], "connect-src": ["default-src"], "img-src": ["default-src"] };
180
+ var POLICY_HEADERS = ["Content-Security-Policy", "Content-Security-Policy-Report-Only"];
181
+ var camelToKebab = (key) => key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
182
+ var kebabToCamel = (key) => key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
183
+ function addSources(values, sources) {
184
+ const out = values.filter((v) => typeof v !== "string" || v.toLowerCase() !== "'none'");
185
+ for (const source of sources) if (!out.includes(source)) out.push(source);
186
+ return out;
187
+ }
188
+ function mergeDirectives(directives, sources) {
189
+ for (const [name, list] of Object.entries(sources)) {
190
+ if (list.length === 0) continue;
191
+ const own = directives.find((d) => d.name === name);
192
+ const elem = name === "script-src" ? directives.find((d) => d.name === "script-src-elem") : void 0;
193
+ if (elem) elem.values = addSources(elem.values, list);
194
+ if (own) {
195
+ own.values = addSources(own.values, list);
196
+ continue;
197
+ }
198
+ const fallback = FALLBACKS[name].map((f) => directives.find((d) => d.name === f)).find((d) => d !== void 0);
199
+ if (fallback) directives.push({ name, values: addSources([...fallback.values], list) });
200
+ }
201
+ }
202
+ function parsePolicy(policy) {
203
+ const directives = [];
204
+ for (const part of policy.split(";")) {
205
+ const [name, ...values] = part.trim().split(/\s+/).filter(Boolean);
206
+ if (name && !directives.some((d) => d.name === name.toLowerCase())) directives.push({ name: name.toLowerCase(), values });
207
+ }
208
+ return directives;
209
+ }
210
+ var serializePolicy = (directives) => directives.map((d) => [d.name, ...d.values].join(" ")).join("; ");
211
+ var HELMET_DEFAULTS = { "script-src": ["'self'"], "img-src": ["'self'", "data:"] };
212
+ function mergeHelmetDirectives(policy, sources) {
213
+ const keys = Object.keys(policy);
214
+ const camel = keys.length > 0 && keys.every((k) => !k.includes("-"));
215
+ const keyOf = (name) => keys.find((k) => camelToKebab(k) === name);
216
+ const read = /* @__PURE__ */ new Map();
217
+ const valuesOf = (key) => {
218
+ if (key === void 0) return void 0;
219
+ const seen = read.get(key);
220
+ if (seen) return [...seen];
221
+ const value = policy[key];
222
+ const values = typeof value === "string" ? value.split(/\s+/).filter(Boolean) : value != null && typeof value[Symbol.iterator] === "function" ? [...value] : void 0;
223
+ if (values) read.set(key, values);
224
+ return values && [...values];
225
+ };
226
+ const isIterator = (value) => value != null && typeof value.next === "function" && value[Symbol.iterator]?.() === value;
227
+ const shaped = (key, values) => {
228
+ const value = policy[key];
229
+ if (typeof value === "string") return values.join(" ");
230
+ if (value instanceof Set) return new Set(values);
231
+ return isIterator(value) ? values.values() : values;
232
+ };
233
+ const out = { ...policy };
234
+ for (const [name, list] of Object.entries(sources)) {
235
+ if (list.length === 0) continue;
236
+ const ownKey = keyOf(name);
237
+ const own = valuesOf(ownKey);
238
+ const helmetDefault = ownKey !== void 0 && policy[ownKey] === null ? void 0 : HELMET_DEFAULTS[name];
239
+ const seed = own ?? (helmetDefault ? [...helmetDefault] : FALLBACKS[name].map((f) => valuesOf(keyOf(f))).find((v) => v !== void 0)) ?? ["'self'"];
240
+ const merged = addSources(seed, list);
241
+ const key = ownKey ?? (camel ? kebabToCamel(name) : name);
242
+ out[key] = shaped(key, merged);
243
+ if (name === "script-src") {
244
+ const elemKey = keyOf("script-src-elem");
245
+ const elem = valuesOf(elemKey);
246
+ if (elemKey !== void 0 && elem) out[elemKey] = shaped(elemKey, addSources(elem, list));
247
+ }
248
+ }
249
+ for (const [key, values] of read) if (out[key] === policy[key] && isIterator(policy[key])) out[key] = values.values();
250
+ return out;
251
+ }
252
+ var ContentSecurityPolicy = class {
253
+ directives;
254
+ constructor(directives) {
255
+ this.directives = Object.freeze({
256
+ "frame-src": Object.freeze([...directives["frame-src"]]),
257
+ "script-src": Object.freeze([...directives["script-src"]]),
258
+ "connect-src": Object.freeze([...directives["connect-src"]]),
259
+ "img-src": Object.freeze([...directives["img-src"]])
260
+ });
261
+ }
262
+ // A directive with no sources is left out: written bare, it would allow nothing.
263
+ toString() {
264
+ return serializePolicy(
265
+ Object.entries(this.directives).filter(([, values]) => values.length > 0).map(([name, values]) => ({ name, values: [...values] }))
266
+ );
267
+ }
268
+ merge(policy) {
269
+ if (typeof policy !== "string") return mergeHelmetDirectives(policy, this.directives);
270
+ return policy.split(",").map((one) => {
271
+ const directives = parsePolicy(one);
272
+ mergeDirectives(directives, this.directives);
273
+ return serializePolicy(directives);
274
+ }).join(", ");
275
+ }
276
+ applyTo(headers) {
277
+ for (const name of POLICY_HEADERS) {
278
+ const value = headers.get(name);
279
+ if (value !== null) headers.set(name, this.merge(value));
280
+ }
281
+ }
282
+ toHtml(policy) {
283
+ return `<meta http-equiv="Content-Security-Policy" content="${escapeAttribute(this.merge(policy))}">`;
284
+ }
285
+ };
286
+ function contentSecurityPolicy(options = {}) {
287
+ const directives = { "frame-src": [ORIGIN], "script-src": [ORIGIN], "connect-src": [apiOrigin(options.environment)], "img-src": [] };
288
+ const environment = environmentOf(options.environment);
289
+ for (const [group, byEnvironment] of Object.entries(PAGE_REQUIREMENTS)) {
290
+ if (options[group] === false) continue;
291
+ for (const [name, list] of Object.entries(byEnvironment[environment].sources)) {
292
+ for (const source of list) if (!directives[name].includes(source)) directives[name].push(source);
293
+ }
294
+ }
295
+ return new ContentSecurityPolicy(directives);
110
296
  }
111
297
  export {
112
298
  SDK_URL,
113
299
  WHOP_ELEMENTS_LOCALES,
300
+ contentSecurityPolicy,
114
301
  loadWhop,
115
302
  preloadHints
116
303
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whop/elements",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Whop Elements — vanilla-js loader + types. Loads the hosted SDK and exposes the typed WhopElements factory.",
5
5
  "keywords": [
6
6
  "checkout",
@@ -97,5 +97,5 @@
97
97
  }
98
98
  }
99
99
  },
100
- "whopElementsApiHash": "2b14d0c22a80bdf6595a304c0c3f516a3c4978e44a77ee608ad640a377b4bca8"
100
+ "whopElementsApiHash": "597d8fc784a33c8b760b0afab305d906f9d79cdaa04ebc7f7b3fc2f4de73400c"
101
101
  }
package/payments.d.cts CHANGED
@@ -39,10 +39,6 @@ export type PaymentsOptions = (
39
39
  * Wallet availability override for deterministic previews. Live checkouts probe the host.
40
40
  */
41
41
  previewWalletAvailability?: WalletAvailability;
42
- /**
43
- * Checkout session updated when a buyer signs in. Pass its `chs_`-prefixed `id` and `client_secret`. Without them, sign-in succeeds but the session does not record the buyer.
44
- */
45
- checkoutSession?: { id: string; clientSecret: string; };
46
42
  /**
47
43
  * Runs when the grouped loading state changes. The value is `true` while any mounted element is still loading.
48
44
  */
@@ -89,10 +85,6 @@ export type PaymentsOptions = (
89
85
  * Wallet availability override for deterministic previews. Live checkouts probe the host.
90
86
  */
91
87
  previewWalletAvailability?: WalletAvailability;
92
- /**
93
- * Checkout session updated when a buyer signs in. Pass its `chs_`-prefixed `id` and `client_secret`. Without them, sign-in succeeds but the session does not record the buyer.
94
- */
95
- checkoutSession?: { id: string; clientSecret: string; };
96
88
  /**
97
89
  * Runs when the grouped loading state changes. The value is `true` while any mounted element is still loading.
98
90
  */
@@ -143,10 +135,6 @@ export type PaymentsOptions = (
143
135
  * Wallet availability override for deterministic previews. Live checkouts probe the host.
144
136
  */
145
137
  previewWalletAvailability?: WalletAvailability;
146
- /**
147
- * Checkout session updated when a buyer signs in. Pass its `chs_`-prefixed `id` and `client_secret`. Without them, sign-in succeeds but the session does not record the buyer.
148
- */
149
- checkoutSession?: { id: string; clientSecret: string; };
150
138
  /**
151
139
  * Runs when the grouped loading state changes. The value is `true` while any mounted element is still loading.
152
140
  */
@@ -197,10 +185,6 @@ export type PaymentsOptions = (
197
185
  * Wallet availability override for deterministic previews. Live checkouts probe the host.
198
186
  */
199
187
  previewWalletAvailability?: WalletAvailability;
200
- /**
201
- * Checkout session updated when a buyer signs in. Pass its `chs_`-prefixed `id` and `client_secret`. Without them, sign-in succeeds but the session does not record the buyer.
202
- */
203
- checkoutSession?: { id: string; clientSecret: string; };
204
188
  /**
205
189
  * Runs when the grouped loading state changes. The value is `true` while any mounted element is still loading.
206
190
  */
@@ -1030,7 +1014,7 @@ export interface PaymentRequestResourceOptions {
1030
1014
  */
1031
1015
  requestPayerPhone?: boolean;
1032
1016
  /**
1033
- * Ask the sheet for a shipping address (and offer `shippingOptions` when given).
1017
+ * Ask the sheet for a shipping address (and offer `shippingOptions` when given). PayPal always asks for an address, since it is the address tax is computed on, and returns it as `shipping` only when this is set.
1034
1018
  * @default false
1035
1019
  */
1036
1020
  requestShipping?: boolean;
@@ -1049,34 +1033,34 @@ export interface PaymentRequestResourceOptions {
1049
1033
  }
1050
1034
 
1051
1035
  /**
1052
- * A minted "paymentRequest" resource — returned by `whop.payments.paymentRequest.create()`. Low-level Apple Pay or Google Pay sheet for custom buttons, express checkout, and shipping callbacks. Wallet tiles automate this flow through `payments.createConfirmationToken`. Await `canMakePayment()` to check availability and prime the sheet, then call `show(type)` synchronously in the user-interaction handler. The resolved `ctok` is a `ctok_`-prefixed confirmation token. Confirm it server-side like an element-minted token.
1036
+ * A minted "paymentRequest" resource — returned by `whop.payments.paymentRequest.create()`. Low-level Apple Pay, Google Pay, or PayPal sheet for custom buttons, express checkout, and shipping callbacks. Wallet tiles automate this flow through `payments.createConfirmationToken`. Await `canMakePayment()` to check availability and prime the wallet, then call `show(type)` synchronously in the user-interaction handler. The resolved `ctok` is a `ctok_`-prefixed confirmation token. Confirm it server-side like an element-minted token. A PayPal token charges at most the total PayPal showed the buyer.
1053
1037
  * @see https://docs.whop.com/elements/latest/payments/paymentRequest
1054
1038
  */
1055
1039
  export interface PaymentRequestResource {
1056
1040
  /**
1057
- * Returns availability for each wallet when the account offers it, the device supports it, and this page's origin is approved for the account (first-party whop.com pages are pre-approved; a merchant page needs its domain registered and verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain), and offering Google Pay there is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos)). Apple Pay on non-WebKit browsers is the desktop iPhone-handoff flow, so mobile devices there report it unavailable — native Apple Pay on iOS browsers is unaffected. `order` ranks the wallets by which sheet is native to the browser — Apple Pay first on Safari, Google Pay first elsewhere — so express buttons can stack best-first. This primes `show()`. Always await it first.
1041
+ * Returns availability for each wallet when the account offers it, the device supports it, and this page's origin is approved for the account (first-party whop.com pages are pre-approved; a merchant page needs its domain registered and verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain), and offering Google Pay there is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos)). Apple Pay on non-WebKit browsers is the desktop iPhone-handoff flow, so mobile devices there report it unavailable — native Apple Pay on iOS browsers is unaffected. `order` ranks the wallets by which sheet is native to the browser — Apple Pay first on Safari, Google Pay first elsewhere, PayPal last — so express buttons can stack best-first. `payPal` reports PayPal where the account offers it in this currency, the amount is within PayPal's limits, the request neither sets `setupFutureUsage` nor asks for the payer's phone, PayPal's scripts load on this page, and this page's origin is approved for the account like the other wallets'. A page with a Content Security Policy must allow PayPal's hosts, which `contentSecurityPolicy()` includes. A page that already loads PayPal's own v5 JavaScript SDK reports `payPal: false`. This primes `show()`. Always await it first.
1058
1042
  */
1059
1043
  canMakePayment: () => Promise<WalletAvailability>;
1060
1044
  /**
1061
- * Open the selected wallet sheet after awaiting `canMakePayment()`. Apple requires session creation within the gesture stack. Pass `email` when your page already collected one — the sheet takes it and never asks for a second. With `requestPayerEmail: false`, passing one is required, and the sheet refuses to open without it because confirmation tokens require an email.
1045
+ * Open the selected wallet after awaiting `canMakePayment()`, synchronously in the press: Apple requires session creation within the gesture stack, and PayPal opens its window there — a pop-up on desktop, PayPal's in-page window when the pop-up is blocked and on mobile. Pass `email` when your page already collected one — the wallet takes it and never asks for a second. With `requestPayerEmail: false`, passing one is required, and the wallet refuses to open without it because confirmation tokens require an email.
1062
1046
  * Call synchronously during user activation. Await prerequisites first because browsers revoke activation across asynchronous steps.
1063
1047
  */
1064
- show: (type: "apple_pay" | "google_pay", provided?: { email?: string | undefined; } | undefined) => Promise<PaymentRequestResult>;
1048
+ show: (type: "apple_pay" | "google_pay" | "paypal_express", provided?: { email?: string | undefined; } | undefined) => Promise<PaymentRequestResult>;
1065
1049
  /**
1066
- * The sheet's shipping address changed (redacted pre-authorization: city/state/postal/country only). An error in the answer that names a `field` (`city`, `state`, `postal_code`, `country`) marks that field in Apple Pay; Google Pay shows the message alone.
1067
- * Call `updateWith(…)` exactly once per event or the flow waits until the vendor times out.
1050
+ * The sheet's shipping address changed (redacted pre-authorization: city/state/postal/country only). An error in the answer that names a `field` (`city`, `state`, `postal_code`, `country`) marks that field in Apple Pay; Google Pay shows the message alone. PayPal fires it with the address the buyer picks, and an error naming a `field` refuses that part of the address inside PayPal.
1051
+ * Call `updateWith(…)` exactly once per event. A handler that throws or rejects before calling it fails that update right away.
1068
1052
  * Returns the unsubscribe function.
1069
1053
  */
1070
1054
  onShippingAddressChange(handler: (ev: ShippingAddressChangeEvent) => void): () => void;
1071
1055
  /**
1072
- * The buyer picked a shipping option. Amount-only contract: `updateWith` carries amount/lineItems/shippingOptions/errors.
1073
- * Call `updateWith(…)` exactly once per event or the flow waits until the vendor times out.
1056
+ * The buyer picked a shipping option. Amount-only contract: `updateWith` carries amount/lineItems/shippingOptions/errors. Apple Pay has no error slot for a shipping option: an answer with `errors` keeps the last accepted total, and the first error's message shows when the buyer taps Pay, until they pick an option your handler accepts. Google Pay shows the message when the option is picked.
1057
+ * Call `updateWith(…)` exactly once per event. A handler that throws or rejects before calling it fails that update right away.
1074
1058
  * Returns the unsubscribe function.
1075
1059
  */
1076
1060
  onShippingOptionChange(handler: (ev: ShippingOptionChangeEvent) => void): () => void;
1077
1061
  /**
1078
- * Fires at sheet open and on card switches with the selected card's redacted billing address (city/state/postal/country only) — reprice the total for the address the charge taxes off. Answer `updateWith({ amount })` in minor units, or `updateWith({})` to keep the current total. Every event is answered exactly once: a slow, thrown, or superseded handler answers keep-current.
1079
- * Call `updateWith(…)` exactly once per event or the flow waits until the vendor times out.
1062
+ * Fires at sheet open and on card switches with the selected card's redacted billing address (city/state/postal/country only) — reprice the total for the address the charge taxes off. Answer `updateWith({ amount })` in minor units, `updateWith({})` to keep the current total, or `updateWith({ errors })` to refuse the card. Every event is answered exactly once: a handler that throws, rejects, or doesn't answer within 10 seconds refuses the card, so the buyer picks another one. PayPal fires it with each shipping address the buyer picks in PayPal, which is the address its token bills and taxes on, and a refusal there refuses that address inside PayPal.
1063
+ * Call `updateWith(…)` exactly once per event. A handler that throws or rejects before calling it fails that update right away.
1080
1064
  * Returns the unsubscribe function.
1081
1065
  */
1082
1066
  onBillingAddressChange(handler: (ev: BillingAddressChangeEvent) => void): () => void;
@@ -1086,7 +1070,7 @@ export interface PaymentRequestResource {
1086
1070
  */
1087
1071
  onConfirmationToken(handler: (ev: PaymentRequestResult) => void): () => void;
1088
1072
  /**
1089
- * The buyer dismissed the sheet.
1073
+ * The buyer dismissed the sheet or closed PayPal.
1090
1074
  * Returns the unsubscribe function.
1091
1075
  */
1092
1076
  onCancel(handler: () => void): () => void;
@@ -1154,7 +1138,7 @@ export interface PaymentsStatics {
1154
1138
  */
1155
1139
  handleNextAction: (input: { clientSecret: string; returnUrl?: string | undefined; pollIntervalMs?: number | undefined; }) => Promise<{ status: string; redirected: boolean; lastPaymentError: { code?: string | null | undefined; decline_code?: string | null | undefined; message?: string | null | undefined; } | null; }>;
1156
1140
  /**
1157
- * Low-level Apple Pay or Google Pay sheet for custom buttons, express checkout, and shipping callbacks. Wallet tiles automate this flow through `payments.createConfirmationToken`. Await `canMakePayment()` to check availability and prime the sheet, then call `show(type)` synchronously in the user-interaction handler. The resolved `ctok` is a `ctok_`-prefixed confirmation token. Confirm it server-side like an element-minted token.
1141
+ * Low-level Apple Pay, Google Pay, or PayPal sheet for custom buttons, express checkout, and shipping callbacks. Wallet tiles automate this flow through `payments.createConfirmationToken`. Await `canMakePayment()` to check availability and prime the wallet, then call `show(type)` synchronously in the user-interaction handler. The resolved `ctok` is a `ctok_`-prefixed confirmation token. Confirm it server-side like an element-minted token. A PayPal token charges at most the total PayPal showed the buyer.
1158
1142
  * Mint with `whop.payments.paymentRequest.create(options)` — nothing needs to be mounted.
1159
1143
  */
1160
1144
  paymentRequest: { create(options: PaymentRequestResourceOptions): PaymentRequestResource };
@@ -1167,10 +1151,11 @@ export interface PaymentsStatics {
1167
1151
  export interface WalletAvailability {
1168
1152
  applePay: boolean;
1169
1153
  googlePay: boolean;
1154
+ payPal?: boolean | undefined;
1170
1155
  /**
1171
- * The wallets ranked by which sheet is native to this browser — Apple Pay first on WebKit, Google Pay first everywhere else — so express buttons can stack best-first. Absent on consumer-stated availability (previews) and on wires that predate it. An inline union, not payment-request's WalletSheetType: that module already imports this one.
1156
+ * The wallets ranked by which sheet is native to this browser — Apple Pay first on WebKit, Google Pay first everywhere else, PayPal last — so express buttons can stack best-first. Absent on consumer-stated availability (previews) and on wires that predate it. An inline union, not payment-request's WalletSheetType: that module already imports this one.
1172
1157
  */
1173
- order?: ("apple_pay" | "google_pay")[] | undefined;
1158
+ order?: ("apple_pay" | "google_pay" | "paypal_express")[] | undefined;
1174
1159
  }
1175
1160
 
1176
1161
  /**
@@ -1228,7 +1213,7 @@ export interface PaymentRequestResult {
1228
1213
  * Confirmation token ID, prefixed `ctok_`. Confirm it server-side with your secret key.
1229
1214
  */
1230
1215
  ctok: string;
1231
- type: "apple_pay" | "google_pay";
1216
+ type: "apple_pay" | "google_pay" | "paypal_express";
1232
1217
  payer: { email?: string | undefined; name?: string | undefined; phone?: string | undefined; country?: string | undefined; };
1233
1218
  shipping: { address?: PaymentRequestAddress | undefined; option?: PaymentRequestShippingOption | undefined; } | null;
1234
1219
  }
package/payments.d.ts CHANGED
@@ -39,10 +39,6 @@ export type PaymentsOptions = (
39
39
  * Wallet availability override for deterministic previews. Live checkouts probe the host.
40
40
  */
41
41
  previewWalletAvailability?: WalletAvailability;
42
- /**
43
- * Checkout session updated when a buyer signs in. Pass its `chs_`-prefixed `id` and `client_secret`. Without them, sign-in succeeds but the session does not record the buyer.
44
- */
45
- checkoutSession?: { id: string; clientSecret: string; };
46
42
  /**
47
43
  * Runs when the grouped loading state changes. The value is `true` while any mounted element is still loading.
48
44
  */
@@ -89,10 +85,6 @@ export type PaymentsOptions = (
89
85
  * Wallet availability override for deterministic previews. Live checkouts probe the host.
90
86
  */
91
87
  previewWalletAvailability?: WalletAvailability;
92
- /**
93
- * Checkout session updated when a buyer signs in. Pass its `chs_`-prefixed `id` and `client_secret`. Without them, sign-in succeeds but the session does not record the buyer.
94
- */
95
- checkoutSession?: { id: string; clientSecret: string; };
96
88
  /**
97
89
  * Runs when the grouped loading state changes. The value is `true` while any mounted element is still loading.
98
90
  */
@@ -143,10 +135,6 @@ export type PaymentsOptions = (
143
135
  * Wallet availability override for deterministic previews. Live checkouts probe the host.
144
136
  */
145
137
  previewWalletAvailability?: WalletAvailability;
146
- /**
147
- * Checkout session updated when a buyer signs in. Pass its `chs_`-prefixed `id` and `client_secret`. Without them, sign-in succeeds but the session does not record the buyer.
148
- */
149
- checkoutSession?: { id: string; clientSecret: string; };
150
138
  /**
151
139
  * Runs when the grouped loading state changes. The value is `true` while any mounted element is still loading.
152
140
  */
@@ -197,10 +185,6 @@ export type PaymentsOptions = (
197
185
  * Wallet availability override for deterministic previews. Live checkouts probe the host.
198
186
  */
199
187
  previewWalletAvailability?: WalletAvailability;
200
- /**
201
- * Checkout session updated when a buyer signs in. Pass its `chs_`-prefixed `id` and `client_secret`. Without them, sign-in succeeds but the session does not record the buyer.
202
- */
203
- checkoutSession?: { id: string; clientSecret: string; };
204
188
  /**
205
189
  * Runs when the grouped loading state changes. The value is `true` while any mounted element is still loading.
206
190
  */
@@ -1030,7 +1014,7 @@ export interface PaymentRequestResourceOptions {
1030
1014
  */
1031
1015
  requestPayerPhone?: boolean;
1032
1016
  /**
1033
- * Ask the sheet for a shipping address (and offer `shippingOptions` when given).
1017
+ * Ask the sheet for a shipping address (and offer `shippingOptions` when given). PayPal always asks for an address, since it is the address tax is computed on, and returns it as `shipping` only when this is set.
1034
1018
  * @default false
1035
1019
  */
1036
1020
  requestShipping?: boolean;
@@ -1049,34 +1033,34 @@ export interface PaymentRequestResourceOptions {
1049
1033
  }
1050
1034
 
1051
1035
  /**
1052
- * A minted "paymentRequest" resource — returned by `whop.payments.paymentRequest.create()`. Low-level Apple Pay or Google Pay sheet for custom buttons, express checkout, and shipping callbacks. Wallet tiles automate this flow through `payments.createConfirmationToken`. Await `canMakePayment()` to check availability and prime the sheet, then call `show(type)` synchronously in the user-interaction handler. The resolved `ctok` is a `ctok_`-prefixed confirmation token. Confirm it server-side like an element-minted token.
1036
+ * A minted "paymentRequest" resource — returned by `whop.payments.paymentRequest.create()`. Low-level Apple Pay, Google Pay, or PayPal sheet for custom buttons, express checkout, and shipping callbacks. Wallet tiles automate this flow through `payments.createConfirmationToken`. Await `canMakePayment()` to check availability and prime the wallet, then call `show(type)` synchronously in the user-interaction handler. The resolved `ctok` is a `ctok_`-prefixed confirmation token. Confirm it server-side like an element-minted token. A PayPal token charges at most the total PayPal showed the buyer.
1053
1037
  * @see https://docs.whop.com/elements/latest/payments/paymentRequest
1054
1038
  */
1055
1039
  export interface PaymentRequestResource {
1056
1040
  /**
1057
- * Returns availability for each wallet when the account offers it, the device supports it, and this page's origin is approved for the account (first-party whop.com pages are pre-approved; a merchant page needs its domain registered and verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain), and offering Google Pay there is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos)). Apple Pay on non-WebKit browsers is the desktop iPhone-handoff flow, so mobile devices there report it unavailable — native Apple Pay on iOS browsers is unaffected. `order` ranks the wallets by which sheet is native to the browser — Apple Pay first on Safari, Google Pay first elsewhere — so express buttons can stack best-first. This primes `show()`. Always await it first.
1041
+ * Returns availability for each wallet when the account offers it, the device supports it, and this page's origin is approved for the account (first-party whop.com pages are pre-approved; a merchant page needs its domain registered and verified as a [payment method domain](/api-reference/beta/payment-method-domains/payment-method-domain), and offering Google Pay there is subject to the [Google Pay API Terms of Service](https://payments.developers.google.com/terms/sellertos)). Apple Pay on non-WebKit browsers is the desktop iPhone-handoff flow, so mobile devices there report it unavailable — native Apple Pay on iOS browsers is unaffected. `order` ranks the wallets by which sheet is native to the browser — Apple Pay first on Safari, Google Pay first elsewhere, PayPal last — so express buttons can stack best-first. `payPal` reports PayPal where the account offers it in this currency, the amount is within PayPal's limits, the request neither sets `setupFutureUsage` nor asks for the payer's phone, PayPal's scripts load on this page, and this page's origin is approved for the account like the other wallets'. A page with a Content Security Policy must allow PayPal's hosts, which `contentSecurityPolicy()` includes. A page that already loads PayPal's own v5 JavaScript SDK reports `payPal: false`. This primes `show()`. Always await it first.
1058
1042
  */
1059
1043
  canMakePayment: () => Promise<WalletAvailability>;
1060
1044
  /**
1061
- * Open the selected wallet sheet after awaiting `canMakePayment()`. Apple requires session creation within the gesture stack. Pass `email` when your page already collected one — the sheet takes it and never asks for a second. With `requestPayerEmail: false`, passing one is required, and the sheet refuses to open without it because confirmation tokens require an email.
1045
+ * Open the selected wallet after awaiting `canMakePayment()`, synchronously in the press: Apple requires session creation within the gesture stack, and PayPal opens its window there — a pop-up on desktop, PayPal's in-page window when the pop-up is blocked and on mobile. Pass `email` when your page already collected one — the wallet takes it and never asks for a second. With `requestPayerEmail: false`, passing one is required, and the wallet refuses to open without it because confirmation tokens require an email.
1062
1046
  * Call synchronously during user activation. Await prerequisites first because browsers revoke activation across asynchronous steps.
1063
1047
  */
1064
- show: (type: "apple_pay" | "google_pay", provided?: { email?: string | undefined; } | undefined) => Promise<PaymentRequestResult>;
1048
+ show: (type: "apple_pay" | "google_pay" | "paypal_express", provided?: { email?: string | undefined; } | undefined) => Promise<PaymentRequestResult>;
1065
1049
  /**
1066
- * The sheet's shipping address changed (redacted pre-authorization: city/state/postal/country only). An error in the answer that names a `field` (`city`, `state`, `postal_code`, `country`) marks that field in Apple Pay; Google Pay shows the message alone.
1067
- * Call `updateWith(…)` exactly once per event or the flow waits until the vendor times out.
1050
+ * The sheet's shipping address changed (redacted pre-authorization: city/state/postal/country only). An error in the answer that names a `field` (`city`, `state`, `postal_code`, `country`) marks that field in Apple Pay; Google Pay shows the message alone. PayPal fires it with the address the buyer picks, and an error naming a `field` refuses that part of the address inside PayPal.
1051
+ * Call `updateWith(…)` exactly once per event. A handler that throws or rejects before calling it fails that update right away.
1068
1052
  * Returns the unsubscribe function.
1069
1053
  */
1070
1054
  onShippingAddressChange(handler: (ev: ShippingAddressChangeEvent) => void): () => void;
1071
1055
  /**
1072
- * The buyer picked a shipping option. Amount-only contract: `updateWith` carries amount/lineItems/shippingOptions/errors.
1073
- * Call `updateWith(…)` exactly once per event or the flow waits until the vendor times out.
1056
+ * The buyer picked a shipping option. Amount-only contract: `updateWith` carries amount/lineItems/shippingOptions/errors. Apple Pay has no error slot for a shipping option: an answer with `errors` keeps the last accepted total, and the first error's message shows when the buyer taps Pay, until they pick an option your handler accepts. Google Pay shows the message when the option is picked.
1057
+ * Call `updateWith(…)` exactly once per event. A handler that throws or rejects before calling it fails that update right away.
1074
1058
  * Returns the unsubscribe function.
1075
1059
  */
1076
1060
  onShippingOptionChange(handler: (ev: ShippingOptionChangeEvent) => void): () => void;
1077
1061
  /**
1078
- * Fires at sheet open and on card switches with the selected card's redacted billing address (city/state/postal/country only) — reprice the total for the address the charge taxes off. Answer `updateWith({ amount })` in minor units, or `updateWith({})` to keep the current total. Every event is answered exactly once: a slow, thrown, or superseded handler answers keep-current.
1079
- * Call `updateWith(…)` exactly once per event or the flow waits until the vendor times out.
1062
+ * Fires at sheet open and on card switches with the selected card's redacted billing address (city/state/postal/country only) — reprice the total for the address the charge taxes off. Answer `updateWith({ amount })` in minor units, `updateWith({})` to keep the current total, or `updateWith({ errors })` to refuse the card. Every event is answered exactly once: a handler that throws, rejects, or doesn't answer within 10 seconds refuses the card, so the buyer picks another one. PayPal fires it with each shipping address the buyer picks in PayPal, which is the address its token bills and taxes on, and a refusal there refuses that address inside PayPal.
1063
+ * Call `updateWith(…)` exactly once per event. A handler that throws or rejects before calling it fails that update right away.
1080
1064
  * Returns the unsubscribe function.
1081
1065
  */
1082
1066
  onBillingAddressChange(handler: (ev: BillingAddressChangeEvent) => void): () => void;
@@ -1086,7 +1070,7 @@ export interface PaymentRequestResource {
1086
1070
  */
1087
1071
  onConfirmationToken(handler: (ev: PaymentRequestResult) => void): () => void;
1088
1072
  /**
1089
- * The buyer dismissed the sheet.
1073
+ * The buyer dismissed the sheet or closed PayPal.
1090
1074
  * Returns the unsubscribe function.
1091
1075
  */
1092
1076
  onCancel(handler: () => void): () => void;
@@ -1154,7 +1138,7 @@ export interface PaymentsStatics {
1154
1138
  */
1155
1139
  handleNextAction: (input: { clientSecret: string; returnUrl?: string | undefined; pollIntervalMs?: number | undefined; }) => Promise<{ status: string; redirected: boolean; lastPaymentError: { code?: string | null | undefined; decline_code?: string | null | undefined; message?: string | null | undefined; } | null; }>;
1156
1140
  /**
1157
- * Low-level Apple Pay or Google Pay sheet for custom buttons, express checkout, and shipping callbacks. Wallet tiles automate this flow through `payments.createConfirmationToken`. Await `canMakePayment()` to check availability and prime the sheet, then call `show(type)` synchronously in the user-interaction handler. The resolved `ctok` is a `ctok_`-prefixed confirmation token. Confirm it server-side like an element-minted token.
1141
+ * Low-level Apple Pay, Google Pay, or PayPal sheet for custom buttons, express checkout, and shipping callbacks. Wallet tiles automate this flow through `payments.createConfirmationToken`. Await `canMakePayment()` to check availability and prime the wallet, then call `show(type)` synchronously in the user-interaction handler. The resolved `ctok` is a `ctok_`-prefixed confirmation token. Confirm it server-side like an element-minted token. A PayPal token charges at most the total PayPal showed the buyer.
1158
1142
  * Mint with `whop.payments.paymentRequest.create(options)` — nothing needs to be mounted.
1159
1143
  */
1160
1144
  paymentRequest: { create(options: PaymentRequestResourceOptions): PaymentRequestResource };
@@ -1167,10 +1151,11 @@ export interface PaymentsStatics {
1167
1151
  export interface WalletAvailability {
1168
1152
  applePay: boolean;
1169
1153
  googlePay: boolean;
1154
+ payPal?: boolean | undefined;
1170
1155
  /**
1171
- * The wallets ranked by which sheet is native to this browser — Apple Pay first on WebKit, Google Pay first everywhere else — so express buttons can stack best-first. Absent on consumer-stated availability (previews) and on wires that predate it. An inline union, not payment-request's WalletSheetType: that module already imports this one.
1156
+ * The wallets ranked by which sheet is native to this browser — Apple Pay first on WebKit, Google Pay first everywhere else, PayPal last — so express buttons can stack best-first. Absent on consumer-stated availability (previews) and on wires that predate it. An inline union, not payment-request's WalletSheetType: that module already imports this one.
1172
1157
  */
1173
- order?: ("apple_pay" | "google_pay")[] | undefined;
1158
+ order?: ("apple_pay" | "google_pay" | "paypal_express")[] | undefined;
1174
1159
  }
1175
1160
 
1176
1161
  /**
@@ -1228,7 +1213,7 @@ export interface PaymentRequestResult {
1228
1213
  * Confirmation token ID, prefixed `ctok_`. Confirm it server-side with your secret key.
1229
1214
  */
1230
1215
  ctok: string;
1231
- type: "apple_pay" | "google_pay";
1216
+ type: "apple_pay" | "google_pay" | "paypal_express";
1232
1217
  payer: { email?: string | undefined; name?: string | undefined; phone?: string | undefined; country?: string | undefined; };
1233
1218
  shipping: { address?: PaymentRequestAddress | undefined; option?: PaymentRequestShippingOption | undefined; } | null;
1234
1219
  }
package/wallet.d.cts CHANGED
@@ -1730,7 +1730,7 @@ export interface BreakdownElementCreateOptions {
1730
1730
  /**
1731
1731
  * A balance row or its matching bar segment was clicked. The host can open details for `available`, `pending`, `reserve`, or `debt`.
1732
1732
  */
1733
- onBreakdownSelected?: ((payload: { category: "available" | "pending" | "reserve" | "debt"; }) => void);
1733
+ onBreakdownSelected?: ((payload: { category: "available" | "debt" | "pending" | "reserve"; }) => void);
1734
1734
  /**
1735
1735
  * Runs after the loading skeleton first paints and before `onReady`.
1736
1736
  */
@@ -2828,6 +2828,38 @@ export interface Money {
2828
2828
  display_decimals: number;
2829
2829
  }
2830
2830
 
2831
+ /**
2832
+ * Declared from the app's `UserProfilePicture`, referenced by a host-capability surface.
2833
+ */
2834
+ export interface UserProfilePicture {
2835
+ /**
2836
+ * Avatar image URL. Always present — a generated placeholder when the user set no picture.
2837
+ */
2838
+ url: string;
2839
+ }
2840
+
2841
+ /**
2842
+ * Declared from the app's `UserSummary`, referenced by a host-capability surface.
2843
+ */
2844
+ export interface UserSummary {
2845
+ /**
2846
+ * User ID, prefixed `user_`.
2847
+ */
2848
+ id: string;
2849
+ /**
2850
+ * Public username.
2851
+ */
2852
+ username: string;
2853
+ /**
2854
+ * Display name.
2855
+ */
2856
+ name: string | null;
2857
+ /**
2858
+ * Avatar wrapper; its `url` is always present, using a generated placeholder when the user set no picture.
2859
+ */
2860
+ profile_picture: UserProfilePicture;
2861
+ }
2862
+
2831
2863
  /**
2832
2864
  * Declared from the app's `LedgerActivity`, referenced by a host-capability surface.
2833
2865
  */
@@ -2840,7 +2872,7 @@ export interface LedgerActivity {
2840
2872
  /**
2841
2873
  * The ledger line category this activity was posted under.
2842
2874
  */
2843
- line_type: "platform_balance_transfer_outgoing" | "payment_gross" | "withdrawal" | "topup" | "payment_dispute" | "payment_revshare" | "card_spend_authorization" | "airdrop" | "onchain_swap_target" | "cashback" | "account_settlement" | "ad_budget_release" | "ad_campaign_budget" | "ad_publisher_payout" | "ad_publisher_payout_received" | "ad_spend_charge" | "affiliate_fee" | "airdrop_link_created" | "airdrop_link_redeemed" | "airdrop_link_returned" | "airdrop_reversal" | "application_fee" | "application_fee_payout" | "balance_reservation" | "balance_reservation_reversal" | "bank_transfer" | "billing_percentage_fee" | "buyer_fee" | "card_interchange" | "card_load_deposit" | "card_load_transfer" | "card_spend_authorization_void" | "card_spend_refund" | "card_unload_deposit" | "card_unload_transfer" | "cashback_funding" | "company_referral" | "connected_account_negative_balance" | "cross_border_percentage_fee" | "currency_conversion_incoming" | "currency_conversion_outgoing" | "dispute_alert_fee" | "dispute_hold_adjustment" | "dispute_representment_fee" | "economic_intelligence_percentage_fee" | "external_card_load_deposit" | "fraud_prevention_fee" | "fx_percentage_fee" | "high_risk_merchant_fee" | "installment_default" | "internal_balance_transfer_incoming" | "internal_balance_transfer_outgoing" | "internal_withdrawal" | "internal_withdrawal_complete" | "internal_withdrawal_fee" | "internal_withdrawal_fee_reversal" | "internal_withdrawal_in_transit" | "internal_withdrawal_in_transit_reversal" | "internal_withdrawal_markup_fee" | "internal_withdrawal_markup_fee_payout" | "internal_withdrawal_markup_fee_payout_reversal" | "internal_withdrawal_markup_fee_reversal" | "internal_withdrawal_reversal" | "legacy_crypto_payment" | "legacy_payment" | "legacy_payment_refund" | "license_sale" | "license_sale_commission" | "license_sale_revenue" | "marketplace_affiliate_fee" | "misc_purchase" | "misc_refund" | "misc_reversal" | "onboarding_reward" | "onchain_deposit" | "onchain_swap_source" | "onchain_wallet_transfer_incoming" | "onchain_wallet_transfer_outgoing" | "onchain_withdrawal" | "orchestration_percentage_fee" | "partner_grant_reward" | "passthrough_gmv" | "payment_dispute_adjustment" | "payment_dispute_fee" | "payment_dispute_reversal" | "payment_gross_reversal" | "payment_processing_fixed_fee" | "payment_processing_percentage_fee" | "payment_referral" | "payment_referral_refund" | "payment_referral_reversal" | "payment_refund" | "payment_refund_reversal" | "payment_revshare_payout" | "payment_revshare_refund" | "payment_revshare_reversal" | "payout_fee" | "platform_affiliate_payment" | "platform_affiliate_payment_reversal" | "platform_balance_payment" | "platform_balance_payment_refund" | "platform_balance_transfer_fee" | "platform_balance_transfer_incoming" | "platform_covered_dispute" | "platform_credits_granted" | "platform_credits_payment" | "platform_credits_payment_refund" | "platform_earning" | "platform_markup_fee" | "platform_markup_fee_payout" | "promo_reversal" | "referral_bonus" | "resolution_center_refund" | "revshare_percentage_fee" | "sales_tax_fee" | "sales_tax_remittance" | "sales_tax_remittance_reversal" | "software_rental_revshare" | "software_rental_transaction" | "stripe_domestic_processing_fee" | "stripe_international_processing_fee" | "swap_fee" | "three_ds_fixed_fee" | "topup_fee" | "topup_reversal" | "treasury_payin" | "whop_processing_fee" | "withdrawal_clawback" | "withdrawal_clawback_reversal" | "withdrawal_fee" | "withdrawal_fee_reversal" | "withdrawal_markup_fee" | "withdrawal_markup_fee_payout" | "withdrawal_markup_fee_payout_reversal" | "withdrawal_markup_fee_reversal" | "withdrawal_reclassification" | "withdrawal_reversal" | "withdrawal_topup_adjustment" | "deposit" | "wallet_transfer_incoming" | "wallet_transfer_outgoing" | "swap_source" | "swap_target";
2875
+ line_type: "platform_balance_transfer_outgoing" | "payment_gross" | "withdrawal" | "topup" | "payment_dispute" | "payment_revshare" | "card_spend_authorization" | "airdrop" | "onchain_swap_target" | "cashback" | "account_settlement" | "ad_budget_release" | "ad_campaign_budget" | "ad_publisher_payout" | "ad_publisher_payout_received" | "ad_spend_charge" | "affiliate_fee" | "airdrop_link_created" | "airdrop_link_redeemed" | "airdrop_link_returned" | "airdrop_reversal" | "application_fee" | "application_fee_payout" | "balance_reservation" | "balance_reservation_reversal" | "bank_transfer" | "billing_percentage_fee" | "buyer_fee" | "card_interchange" | "card_load_deposit" | "card_load_transfer" | "card_spend_authorization_void" | "card_spend_refund" | "card_unload_deposit" | "card_unload_transfer" | "cashback_funding" | "company_referral" | "connected_account_negative_balance" | "cross_border_percentage_fee" | "currency_conversion_incoming" | "currency_conversion_outgoing" | "dispute_alert_fee" | "dispute_hold_adjustment" | "dispute_representment_fee" | "economic_intelligence_percentage_fee" | "external_card_load_deposit" | "fraud_prevention_fee" | "fx_percentage_fee" | "high_risk_merchant_fee" | "installment_default" | "internal_balance_transfer_incoming" | "internal_balance_transfer_outgoing" | "internal_withdrawal" | "internal_withdrawal_complete" | "internal_withdrawal_fee" | "internal_withdrawal_fee_reversal" | "internal_withdrawal_in_transit" | "internal_withdrawal_in_transit_reversal" | "internal_withdrawal_markup_fee" | "internal_withdrawal_markup_fee_payout" | "internal_withdrawal_markup_fee_payout_reversal" | "internal_withdrawal_markup_fee_reversal" | "internal_withdrawal_reversal" | "legacy_crypto_payment" | "legacy_payment" | "legacy_payment_refund" | "license_sale" | "license_sale_commission" | "license_sale_revenue" | "marketplace_affiliate_fee" | "misc_purchase" | "misc_refund" | "misc_reversal" | "onboarding_reward" | "onchain_deposit" | "onchain_swap_source" | "onchain_wallet_transfer_incoming" | "onchain_wallet_transfer_outgoing" | "onchain_withdrawal" | "orchestration_percentage_fee" | "partner_grant_reward" | "passthrough_gmv" | "payment_dispute_adjustment" | "payment_dispute_fee" | "payment_dispute_reversal" | "payment_gross_reversal" | "payment_processing_fixed_fee" | "payment_processing_percentage_fee" | "payment_referral" | "payment_referral_refund" | "payment_referral_reversal" | "payment_refund" | "payment_refund_reversal" | "payment_revshare_payout" | "payment_revshare_refund" | "payment_revshare_reversal" | "payout_fee" | "platform_affiliate_payment" | "platform_affiliate_payment_reversal" | "platform_balance_payment" | "platform_balance_payment_refund" | "platform_balance_transfer_fee" | "platform_balance_transfer_incoming" | "platform_covered_dispute" | "platform_credits_granted" | "platform_credits_payment" | "platform_credits_payment_refund" | "platform_earning" | "platform_markup_fee" | "platform_markup_fee_payout" | "promo_reversal" | "referral_bonus" | "resolution_center_refund" | "revshare_percentage_fee" | "sales_tax_fee" | "sales_tax_remittance" | "sales_tax_remittance_reversal" | "software_rental_revshare" | "software_rental_transaction" | "stripe_domestic_processing_fee" | "stripe_international_processing_fee" | "swap_fee" | "three_ds_fixed_fee" | "topup_fee" | "topup_reversal" | "treasury_payin" | "whop_processing_fee" | "withdrawal_clawback" | "withdrawal_clawback_reversal" | "withdrawal_fee" | "withdrawal_fee_reversal" | "withdrawal_markup_fee" | "withdrawal_markup_fee_payout" | "withdrawal_markup_fee_payout_reversal" | "withdrawal_markup_fee_reversal" | "withdrawal_reclassification" | "withdrawal_reversal" | "withdrawal_topup_adjustment" | "psp_payment_receivable" | "ad_spend_purchase" | "psp_refund_payable" | "passthrough_gmv_offset" | "aggregated_fee" | "sales_tax_collected" | "sales_tax_collected_reversal" | "sales_tax_remitted" | "dispute_hold_blocked" | "psp_dispute_payable" | "withdrawal_payable" | "withdrawal_payable_reversal" | "payout_receivable" | "clawback_receivable" | "clawback_receivable_reversal" | "treasury_payin_receivable" | "onchain_deposit_offset" | "onchain_withdrawal_offset" | "trading_account_withdrawal" | "trading_account_withdrawal_offset" | "trading_account_deposit" | "trading_account_deposit_offset" | "onchain_swap_offset" | "whop_swap_fee_received" | "internal_withdrawal_payable" | "internal_withdrawal_payable_reversal" | "external_card_load_offset" | "card_spend_authorization_hold" | "card_spend_capture" | "card_spend_capture_offset" | "card_spend_authorization_void_release" | "card_spend_refund_offset" | "cashback_expense" | "card_interchange_receivable" | "balance_reservation_hold" | "balance_reservation_release" | "bad_debt_offset" | "bad_debt_expense" | "connected_account_clawback" | "payment_referral_payable" | "application_fee_payable" | "airdrop_expense" | "airdrop_expense_reversal" | "airdrop_link_funded" | "airdrop_link_canceled" | "airdrop_link_claimed" | "ad_funding_disbursement" | "ads_card_spread" | "ad_network_cost" | "ad_spend_margin" | "ad_income_expense" | "ad_income_receipt" | "ad_network_settlement" | "ad_balance_funding_receipt" | "fx_gain_loss" | "fx_markup" | "payment_receivable_settlement" | "platform_earning_settlement" | "platform_earning_unreconciled_clearing" | "payment_receivable_clearing" | "payment_receivable_reversal" | "payment_unreconciled_clearing" | "payment_unreconciled_reversal" | "settlement_shortfall_loss" | "fx_settlement_gain_loss" | "settlement_rounding_variance" | "psp_payout_settlement" | "psp_payout_deposit" | "psp_payout_receivable" | "psp_payout_receivable_clearing" | "payout_unreconciled_clearing" | "psp_currency_conversion_outgoing" | "psp_currency_conversion_incoming" | "psp_currency_conversion_receivable" | "psp_currency_conversion_receivable_clearing" | "psp_fx_adjustment" | "refund_settlement" | "refund_settlement_reversal" | "refund_payable_clearing" | "refund_payable_reversal" | "refund_unreconciled_clearing" | "refund_unreconciled_reversal" | "psp_account_debit" | "payment_refund_fee" | "dispute_settlement" | "dispute_settlement_reversal" | "dispute_payable_clearing" | "dispute_payable_reversal" | "dispute_unreconciled_clearing" | "dispute_unreconciled_reversal" | "psp_reserve_hold" | "psp_reserve_release" | "psp_dispute_fee" | "psp_dispute_alert_fee" | "psp_refund_fee" | "psp_account_updater_fee" | "psp_adjusted_processing_fee" | "psp_authentication_fee" | "psp_commission_fee" | "psp_fixed_fee" | "psp_gateway_fee" | "psp_interchange_fee" | "psp_invoice_tax_fee" | "psp_markup_fee" | "psp_network_token_fee" | "psp_optimization_fee" | "psp_payment_method_fee" | "psp_payout_fee" | "psp_processing_fee" | "psp_risk_fee" | "psp_scheme_fee" | "psp_variable_fee" | "psp_billing_fee" | "psp_tax_service_fee" | "psp_connect_fee" | "external_account_connection_fee" | "psp_external_account_connection_fee" | "psp_invoicing_fee" | "psp_terminal_fee" | "treasury_fee" | "psp_treasury_fee" | "accelerated_settlement_fee" | "psp_accelerated_settlement_fee" | "psp_receivable_pooled" | "psp_pool_settlement" | "psp_pool_clearing" | "psp_pool_refund_settlement" | "psp_pool_refund_clearing" | "psp_correction" | "psp_payin_settlement" | "psp_payin_clearing" | "psp_bank_pull_settlement" | "psp_bank_pull_clearing" | "psp_pool_dispute_settlement" | "psp_payout_consolidation" | "psp_withholding_tax" | "psp_payout_settlement_reversal" | "psp_transfer_settlement" | "psp_transfer_settlement_reversal" | "psp_collection_settlement" | "payout_subsidy" | "withdrawal_payable_clearing" | "withdrawal_payable_clearing_reversal" | "identity_verification_fee" | "psp_identity_verification_fee" | "tax_filing_fee" | "psp_tax_filing_fee" | "psp_service_fee" | "recipient_wallet_load" | "psp_recipient_wallet_load" | "dispute_management_fee" | "psp_dispute_management_fee" | "psp_clawback_settlement" | "psp_clawback_settlement_reversal" | "clawback_receivable_settlement" | "clawback_receivable_settlement_reversal" | "clawback_fee" | "psp_clawback_fee" | "deposit" | "wallet_transfer_incoming" | "wallet_transfer_outgoing" | "swap_source" | "swap_target";
2844
2876
  /**
2845
2877
  * Signed amount in the currency's smallest precision units.
2846
2878
  */
@@ -2868,7 +2900,7 @@ export interface LedgerActivity {
2868
2900
  /**
2869
2901
  * Source of this ledger activity. Platform markup fees use object platform_fee and the ledger activity ID.
2870
2902
  */
2871
- source: { object: string; id: string; fee_kind?: "transfer" | "card_spend" | "deposit" | "swap" | "payout" | null | undefined; status?: string | null | undefined; payment_amount?: Money | null | undefined; payment_method_type?: string | null | undefined; payment_processor?: string | null | undefined; card_brand?: string | null | undefined; reason?: string | null | undefined; notes?: string | null | undefined; risk_review_hold?: boolean | null | undefined; claim_url?: string | null | undefined; amount_float?: number | null | undefined; created_at?: string | null | undefined; estimated_arrival?: string | null | undefined; payer_name?: string | null | undefined; payout_token_nickname?: string | null | undefined; tx_hash?: string | null | undefined; sender_address?: string | null | undefined; chain?: string | null | undefined; from_amount?: string | null | undefined; from_currency?: string | null | undefined; to_amount?: string | null | undefined; to_currency?: string | null | undefined; payout_destination?: { payer_name?: string | null | undefined; icon_url?: string | null | undefined; } | null | undefined; [key: string]: unknown; } | null;
2903
+ source: { object: string; id: string; fee_kind?: "transfer" | "card_spend" | "deposit" | "swap" | "payout" | null | undefined; status?: string | null | undefined; payment_amount?: Money | null | undefined; payment_method_type?: string | null | undefined; payment_processor?: string | null | undefined; card_brand?: string | null | undefined; reason?: string | null | undefined; notes?: string | null | undefined; risk_review_hold?: boolean | null | undefined; claim_url?: string | null | undefined; amount_float?: number | null | undefined; payout_amount?: Money | null | undefined; fee_amount?: Money | null | undefined; fee_type?: "exclusive" | "inclusive" | null | undefined; created_by_user?: UserSummary | null | undefined; trace_code?: string | null | undefined; created_at?: string | null | undefined; estimated_arrival?: string | null | undefined; payer_name?: string | null | undefined; payout_token_nickname?: string | null | undefined; tx_hash?: string | null | undefined; sender_address?: string | null | undefined; chain?: string | null | undefined; from_amount?: string | null | undefined; from_currency?: string | null | undefined; to_amount?: string | null | undefined; to_currency?: string | null | undefined; payout_destination?: { payer_name?: string | null | undefined; icon_url?: string | null | undefined; } | null | undefined; [key: string]: unknown; } | null;
2872
2904
  /**
2873
2905
  * Payment related to this ledger activity. Included when rich resource hydration is enabled and the movement is tied to a payment.
2874
2906
  */
package/wallet.d.ts CHANGED
@@ -1730,7 +1730,7 @@ export interface BreakdownElementCreateOptions {
1730
1730
  /**
1731
1731
  * A balance row or its matching bar segment was clicked. The host can open details for `available`, `pending`, `reserve`, or `debt`.
1732
1732
  */
1733
- onBreakdownSelected?: ((payload: { category: "available" | "pending" | "reserve" | "debt"; }) => void);
1733
+ onBreakdownSelected?: ((payload: { category: "available" | "debt" | "pending" | "reserve"; }) => void);
1734
1734
  /**
1735
1735
  * Runs after the loading skeleton first paints and before `onReady`.
1736
1736
  */
@@ -2828,6 +2828,38 @@ export interface Money {
2828
2828
  display_decimals: number;
2829
2829
  }
2830
2830
 
2831
+ /**
2832
+ * Declared from the app's `UserProfilePicture`, referenced by a host-capability surface.
2833
+ */
2834
+ export interface UserProfilePicture {
2835
+ /**
2836
+ * Avatar image URL. Always present — a generated placeholder when the user set no picture.
2837
+ */
2838
+ url: string;
2839
+ }
2840
+
2841
+ /**
2842
+ * Declared from the app's `UserSummary`, referenced by a host-capability surface.
2843
+ */
2844
+ export interface UserSummary {
2845
+ /**
2846
+ * User ID, prefixed `user_`.
2847
+ */
2848
+ id: string;
2849
+ /**
2850
+ * Public username.
2851
+ */
2852
+ username: string;
2853
+ /**
2854
+ * Display name.
2855
+ */
2856
+ name: string | null;
2857
+ /**
2858
+ * Avatar wrapper; its `url` is always present, using a generated placeholder when the user set no picture.
2859
+ */
2860
+ profile_picture: UserProfilePicture;
2861
+ }
2862
+
2831
2863
  /**
2832
2864
  * Declared from the app's `LedgerActivity`, referenced by a host-capability surface.
2833
2865
  */
@@ -2840,7 +2872,7 @@ export interface LedgerActivity {
2840
2872
  /**
2841
2873
  * The ledger line category this activity was posted under.
2842
2874
  */
2843
- line_type: "platform_balance_transfer_outgoing" | "payment_gross" | "withdrawal" | "topup" | "payment_dispute" | "payment_revshare" | "card_spend_authorization" | "airdrop" | "onchain_swap_target" | "cashback" | "account_settlement" | "ad_budget_release" | "ad_campaign_budget" | "ad_publisher_payout" | "ad_publisher_payout_received" | "ad_spend_charge" | "affiliate_fee" | "airdrop_link_created" | "airdrop_link_redeemed" | "airdrop_link_returned" | "airdrop_reversal" | "application_fee" | "application_fee_payout" | "balance_reservation" | "balance_reservation_reversal" | "bank_transfer" | "billing_percentage_fee" | "buyer_fee" | "card_interchange" | "card_load_deposit" | "card_load_transfer" | "card_spend_authorization_void" | "card_spend_refund" | "card_unload_deposit" | "card_unload_transfer" | "cashback_funding" | "company_referral" | "connected_account_negative_balance" | "cross_border_percentage_fee" | "currency_conversion_incoming" | "currency_conversion_outgoing" | "dispute_alert_fee" | "dispute_hold_adjustment" | "dispute_representment_fee" | "economic_intelligence_percentage_fee" | "external_card_load_deposit" | "fraud_prevention_fee" | "fx_percentage_fee" | "high_risk_merchant_fee" | "installment_default" | "internal_balance_transfer_incoming" | "internal_balance_transfer_outgoing" | "internal_withdrawal" | "internal_withdrawal_complete" | "internal_withdrawal_fee" | "internal_withdrawal_fee_reversal" | "internal_withdrawal_in_transit" | "internal_withdrawal_in_transit_reversal" | "internal_withdrawal_markup_fee" | "internal_withdrawal_markup_fee_payout" | "internal_withdrawal_markup_fee_payout_reversal" | "internal_withdrawal_markup_fee_reversal" | "internal_withdrawal_reversal" | "legacy_crypto_payment" | "legacy_payment" | "legacy_payment_refund" | "license_sale" | "license_sale_commission" | "license_sale_revenue" | "marketplace_affiliate_fee" | "misc_purchase" | "misc_refund" | "misc_reversal" | "onboarding_reward" | "onchain_deposit" | "onchain_swap_source" | "onchain_wallet_transfer_incoming" | "onchain_wallet_transfer_outgoing" | "onchain_withdrawal" | "orchestration_percentage_fee" | "partner_grant_reward" | "passthrough_gmv" | "payment_dispute_adjustment" | "payment_dispute_fee" | "payment_dispute_reversal" | "payment_gross_reversal" | "payment_processing_fixed_fee" | "payment_processing_percentage_fee" | "payment_referral" | "payment_referral_refund" | "payment_referral_reversal" | "payment_refund" | "payment_refund_reversal" | "payment_revshare_payout" | "payment_revshare_refund" | "payment_revshare_reversal" | "payout_fee" | "platform_affiliate_payment" | "platform_affiliate_payment_reversal" | "platform_balance_payment" | "platform_balance_payment_refund" | "platform_balance_transfer_fee" | "platform_balance_transfer_incoming" | "platform_covered_dispute" | "platform_credits_granted" | "platform_credits_payment" | "platform_credits_payment_refund" | "platform_earning" | "platform_markup_fee" | "platform_markup_fee_payout" | "promo_reversal" | "referral_bonus" | "resolution_center_refund" | "revshare_percentage_fee" | "sales_tax_fee" | "sales_tax_remittance" | "sales_tax_remittance_reversal" | "software_rental_revshare" | "software_rental_transaction" | "stripe_domestic_processing_fee" | "stripe_international_processing_fee" | "swap_fee" | "three_ds_fixed_fee" | "topup_fee" | "topup_reversal" | "treasury_payin" | "whop_processing_fee" | "withdrawal_clawback" | "withdrawal_clawback_reversal" | "withdrawal_fee" | "withdrawal_fee_reversal" | "withdrawal_markup_fee" | "withdrawal_markup_fee_payout" | "withdrawal_markup_fee_payout_reversal" | "withdrawal_markup_fee_reversal" | "withdrawal_reclassification" | "withdrawal_reversal" | "withdrawal_topup_adjustment" | "deposit" | "wallet_transfer_incoming" | "wallet_transfer_outgoing" | "swap_source" | "swap_target";
2875
+ line_type: "platform_balance_transfer_outgoing" | "payment_gross" | "withdrawal" | "topup" | "payment_dispute" | "payment_revshare" | "card_spend_authorization" | "airdrop" | "onchain_swap_target" | "cashback" | "account_settlement" | "ad_budget_release" | "ad_campaign_budget" | "ad_publisher_payout" | "ad_publisher_payout_received" | "ad_spend_charge" | "affiliate_fee" | "airdrop_link_created" | "airdrop_link_redeemed" | "airdrop_link_returned" | "airdrop_reversal" | "application_fee" | "application_fee_payout" | "balance_reservation" | "balance_reservation_reversal" | "bank_transfer" | "billing_percentage_fee" | "buyer_fee" | "card_interchange" | "card_load_deposit" | "card_load_transfer" | "card_spend_authorization_void" | "card_spend_refund" | "card_unload_deposit" | "card_unload_transfer" | "cashback_funding" | "company_referral" | "connected_account_negative_balance" | "cross_border_percentage_fee" | "currency_conversion_incoming" | "currency_conversion_outgoing" | "dispute_alert_fee" | "dispute_hold_adjustment" | "dispute_representment_fee" | "economic_intelligence_percentage_fee" | "external_card_load_deposit" | "fraud_prevention_fee" | "fx_percentage_fee" | "high_risk_merchant_fee" | "installment_default" | "internal_balance_transfer_incoming" | "internal_balance_transfer_outgoing" | "internal_withdrawal" | "internal_withdrawal_complete" | "internal_withdrawal_fee" | "internal_withdrawal_fee_reversal" | "internal_withdrawal_in_transit" | "internal_withdrawal_in_transit_reversal" | "internal_withdrawal_markup_fee" | "internal_withdrawal_markup_fee_payout" | "internal_withdrawal_markup_fee_payout_reversal" | "internal_withdrawal_markup_fee_reversal" | "internal_withdrawal_reversal" | "legacy_crypto_payment" | "legacy_payment" | "legacy_payment_refund" | "license_sale" | "license_sale_commission" | "license_sale_revenue" | "marketplace_affiliate_fee" | "misc_purchase" | "misc_refund" | "misc_reversal" | "onboarding_reward" | "onchain_deposit" | "onchain_swap_source" | "onchain_wallet_transfer_incoming" | "onchain_wallet_transfer_outgoing" | "onchain_withdrawal" | "orchestration_percentage_fee" | "partner_grant_reward" | "passthrough_gmv" | "payment_dispute_adjustment" | "payment_dispute_fee" | "payment_dispute_reversal" | "payment_gross_reversal" | "payment_processing_fixed_fee" | "payment_processing_percentage_fee" | "payment_referral" | "payment_referral_refund" | "payment_referral_reversal" | "payment_refund" | "payment_refund_reversal" | "payment_revshare_payout" | "payment_revshare_refund" | "payment_revshare_reversal" | "payout_fee" | "platform_affiliate_payment" | "platform_affiliate_payment_reversal" | "platform_balance_payment" | "platform_balance_payment_refund" | "platform_balance_transfer_fee" | "platform_balance_transfer_incoming" | "platform_covered_dispute" | "platform_credits_granted" | "platform_credits_payment" | "platform_credits_payment_refund" | "platform_earning" | "platform_markup_fee" | "platform_markup_fee_payout" | "promo_reversal" | "referral_bonus" | "resolution_center_refund" | "revshare_percentage_fee" | "sales_tax_fee" | "sales_tax_remittance" | "sales_tax_remittance_reversal" | "software_rental_revshare" | "software_rental_transaction" | "stripe_domestic_processing_fee" | "stripe_international_processing_fee" | "swap_fee" | "three_ds_fixed_fee" | "topup_fee" | "topup_reversal" | "treasury_payin" | "whop_processing_fee" | "withdrawal_clawback" | "withdrawal_clawback_reversal" | "withdrawal_fee" | "withdrawal_fee_reversal" | "withdrawal_markup_fee" | "withdrawal_markup_fee_payout" | "withdrawal_markup_fee_payout_reversal" | "withdrawal_markup_fee_reversal" | "withdrawal_reclassification" | "withdrawal_reversal" | "withdrawal_topup_adjustment" | "psp_payment_receivable" | "ad_spend_purchase" | "psp_refund_payable" | "passthrough_gmv_offset" | "aggregated_fee" | "sales_tax_collected" | "sales_tax_collected_reversal" | "sales_tax_remitted" | "dispute_hold_blocked" | "psp_dispute_payable" | "withdrawal_payable" | "withdrawal_payable_reversal" | "payout_receivable" | "clawback_receivable" | "clawback_receivable_reversal" | "treasury_payin_receivable" | "onchain_deposit_offset" | "onchain_withdrawal_offset" | "trading_account_withdrawal" | "trading_account_withdrawal_offset" | "trading_account_deposit" | "trading_account_deposit_offset" | "onchain_swap_offset" | "whop_swap_fee_received" | "internal_withdrawal_payable" | "internal_withdrawal_payable_reversal" | "external_card_load_offset" | "card_spend_authorization_hold" | "card_spend_capture" | "card_spend_capture_offset" | "card_spend_authorization_void_release" | "card_spend_refund_offset" | "cashback_expense" | "card_interchange_receivable" | "balance_reservation_hold" | "balance_reservation_release" | "bad_debt_offset" | "bad_debt_expense" | "connected_account_clawback" | "payment_referral_payable" | "application_fee_payable" | "airdrop_expense" | "airdrop_expense_reversal" | "airdrop_link_funded" | "airdrop_link_canceled" | "airdrop_link_claimed" | "ad_funding_disbursement" | "ads_card_spread" | "ad_network_cost" | "ad_spend_margin" | "ad_income_expense" | "ad_income_receipt" | "ad_network_settlement" | "ad_balance_funding_receipt" | "fx_gain_loss" | "fx_markup" | "payment_receivable_settlement" | "platform_earning_settlement" | "platform_earning_unreconciled_clearing" | "payment_receivable_clearing" | "payment_receivable_reversal" | "payment_unreconciled_clearing" | "payment_unreconciled_reversal" | "settlement_shortfall_loss" | "fx_settlement_gain_loss" | "settlement_rounding_variance" | "psp_payout_settlement" | "psp_payout_deposit" | "psp_payout_receivable" | "psp_payout_receivable_clearing" | "payout_unreconciled_clearing" | "psp_currency_conversion_outgoing" | "psp_currency_conversion_incoming" | "psp_currency_conversion_receivable" | "psp_currency_conversion_receivable_clearing" | "psp_fx_adjustment" | "refund_settlement" | "refund_settlement_reversal" | "refund_payable_clearing" | "refund_payable_reversal" | "refund_unreconciled_clearing" | "refund_unreconciled_reversal" | "psp_account_debit" | "payment_refund_fee" | "dispute_settlement" | "dispute_settlement_reversal" | "dispute_payable_clearing" | "dispute_payable_reversal" | "dispute_unreconciled_clearing" | "dispute_unreconciled_reversal" | "psp_reserve_hold" | "psp_reserve_release" | "psp_dispute_fee" | "psp_dispute_alert_fee" | "psp_refund_fee" | "psp_account_updater_fee" | "psp_adjusted_processing_fee" | "psp_authentication_fee" | "psp_commission_fee" | "psp_fixed_fee" | "psp_gateway_fee" | "psp_interchange_fee" | "psp_invoice_tax_fee" | "psp_markup_fee" | "psp_network_token_fee" | "psp_optimization_fee" | "psp_payment_method_fee" | "psp_payout_fee" | "psp_processing_fee" | "psp_risk_fee" | "psp_scheme_fee" | "psp_variable_fee" | "psp_billing_fee" | "psp_tax_service_fee" | "psp_connect_fee" | "external_account_connection_fee" | "psp_external_account_connection_fee" | "psp_invoicing_fee" | "psp_terminal_fee" | "treasury_fee" | "psp_treasury_fee" | "accelerated_settlement_fee" | "psp_accelerated_settlement_fee" | "psp_receivable_pooled" | "psp_pool_settlement" | "psp_pool_clearing" | "psp_pool_refund_settlement" | "psp_pool_refund_clearing" | "psp_correction" | "psp_payin_settlement" | "psp_payin_clearing" | "psp_bank_pull_settlement" | "psp_bank_pull_clearing" | "psp_pool_dispute_settlement" | "psp_payout_consolidation" | "psp_withholding_tax" | "psp_payout_settlement_reversal" | "psp_transfer_settlement" | "psp_transfer_settlement_reversal" | "psp_collection_settlement" | "payout_subsidy" | "withdrawal_payable_clearing" | "withdrawal_payable_clearing_reversal" | "identity_verification_fee" | "psp_identity_verification_fee" | "tax_filing_fee" | "psp_tax_filing_fee" | "psp_service_fee" | "recipient_wallet_load" | "psp_recipient_wallet_load" | "dispute_management_fee" | "psp_dispute_management_fee" | "psp_clawback_settlement" | "psp_clawback_settlement_reversal" | "clawback_receivable_settlement" | "clawback_receivable_settlement_reversal" | "clawback_fee" | "psp_clawback_fee" | "deposit" | "wallet_transfer_incoming" | "wallet_transfer_outgoing" | "swap_source" | "swap_target";
2844
2876
  /**
2845
2877
  * Signed amount in the currency's smallest precision units.
2846
2878
  */
@@ -2868,7 +2900,7 @@ export interface LedgerActivity {
2868
2900
  /**
2869
2901
  * Source of this ledger activity. Platform markup fees use object platform_fee and the ledger activity ID.
2870
2902
  */
2871
- source: { object: string; id: string; fee_kind?: "transfer" | "card_spend" | "deposit" | "swap" | "payout" | null | undefined; status?: string | null | undefined; payment_amount?: Money | null | undefined; payment_method_type?: string | null | undefined; payment_processor?: string | null | undefined; card_brand?: string | null | undefined; reason?: string | null | undefined; notes?: string | null | undefined; risk_review_hold?: boolean | null | undefined; claim_url?: string | null | undefined; amount_float?: number | null | undefined; created_at?: string | null | undefined; estimated_arrival?: string | null | undefined; payer_name?: string | null | undefined; payout_token_nickname?: string | null | undefined; tx_hash?: string | null | undefined; sender_address?: string | null | undefined; chain?: string | null | undefined; from_amount?: string | null | undefined; from_currency?: string | null | undefined; to_amount?: string | null | undefined; to_currency?: string | null | undefined; payout_destination?: { payer_name?: string | null | undefined; icon_url?: string | null | undefined; } | null | undefined; [key: string]: unknown; } | null;
2903
+ source: { object: string; id: string; fee_kind?: "transfer" | "card_spend" | "deposit" | "swap" | "payout" | null | undefined; status?: string | null | undefined; payment_amount?: Money | null | undefined; payment_method_type?: string | null | undefined; payment_processor?: string | null | undefined; card_brand?: string | null | undefined; reason?: string | null | undefined; notes?: string | null | undefined; risk_review_hold?: boolean | null | undefined; claim_url?: string | null | undefined; amount_float?: number | null | undefined; payout_amount?: Money | null | undefined; fee_amount?: Money | null | undefined; fee_type?: "exclusive" | "inclusive" | null | undefined; created_by_user?: UserSummary | null | undefined; trace_code?: string | null | undefined; created_at?: string | null | undefined; estimated_arrival?: string | null | undefined; payer_name?: string | null | undefined; payout_token_nickname?: string | null | undefined; tx_hash?: string | null | undefined; sender_address?: string | null | undefined; chain?: string | null | undefined; from_amount?: string | null | undefined; from_currency?: string | null | undefined; to_amount?: string | null | undefined; to_currency?: string | null | undefined; payout_destination?: { payer_name?: string | null | undefined; icon_url?: string | null | undefined; } | null | undefined; [key: string]: unknown; } | null;
2872
2904
  /**
2873
2905
  * Payment related to this ledger activity. Included when rich resource hydration is enabled and the movement is tied to a payment.
2874
2906
  */