@colixsystems/widget-sdk 0.280.0 → 0.281.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/README.md CHANGED
@@ -88,7 +88,11 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
88
88
 
89
89
  ## Status
90
90
 
91
- `v0.280.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
91
+ `v0.281.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
92
+
93
+ ### What's new in 0.281.0 (contract 1.230.0)
94
+
95
+ **Paid page access in the store builds.** A workspace can sell a `useAccessPlans()` plan in its iOS and Android apps by naming a RevenueCat entitlement for it. In those builds `purchase(planId)` then opens the store's own in-app purchase sheet, and access arrives when RevenueCat confirms the purchase — call `refresh()` a moment later. Each plan now carries `purchase_available`; `purchaseAvailable` is `false` only when no plan can be bought here, so check the plan's own flag before showing its button. A plan or paid-app access bought on the web also grants its RevenueCat entitlement, so `usePurchases().entitlements` in the app includes it. `usePurchases()` itself stays native-only. The payments client's `purchaseAccessPlan()` may answer with `store_product_id` (`@colixsystems/payments-client` 0.4.0).
92
96
 
93
97
  ### What's new in 0.280.0 (contract 1.229.0)
94
98
 
package/dist/contract.cjs CHANGED
@@ -2343,13 +2343,13 @@ const HOOKS = [
2343
2343
  signature: "useAccessPlans()",
2344
2344
  returnShape: {
2345
2345
  plans:
2346
- "Array<{ id, name, price_cents, currency, billing: \"one_time\" | \"monthly\", owned, renews, access_ends_at }> // owned: the user holds it; renews: a held monthly plan will charge again; access_ends_at: end of held access without a renewal (null for one-time)",
2346
+ "Array<{ id, name, price_cents, currency, billing: \"one_time\" | \"monthly\", owned, renews, access_ends_at, purchase_available }> // owned: the user holds it; renews: a held monthly plan will charge again; access_ends_at: end of held access without a renewal (null for one-time); purchase_available: this plan can be bought here — false in an iOS/Android build for a plan the workspace does not sell in-app",
2347
2347
  loading: "boolean",
2348
2348
  error: "PaymentError | null",
2349
2349
  purchaseAvailable:
2350
- "boolean // false in an iOS/Android build: page access is digital and needs store billing — render a visible not-available state, never a dead button or a blank",
2350
+ "boolean // false when no plan can be bought here (an iOS/Android build where the workspace sells no plan in-app) — render a visible not-available state, never a dead button or a blank; check each plan's purchase_available too",
2351
2351
  purchase:
2352
- "(planId, { return_path? }?) => Promise<{ plan_id, status: \"ACTIVE\" | \"PENDING\" }> // pass ONLY the plan id; the server sets price and group; host opens hosted Checkout; access arrives with the payment confirmation",
2352
+ "(planId, { return_path? }?) => Promise<{ plan_id, status: \"ACTIVE\" | \"PENDING\" }> // pass ONLY the plan id; the server sets price and group; the host opens hosted Checkout on the web and the store's in-app purchase sheet in an iOS/Android build; access arrives with the payment confirmation, so refresh() a moment later",
2353
2353
  cancel:
2354
2354
  "(planId) => Promise<{ plan_id, renews: false, access_ends_at }> // stop a renewing monthly plan; access runs to access_ends_at — offer it beside every plan with renews: true",
2355
2355
  refresh: "() => Promise<void> // re-read after returning from Checkout",
@@ -3171,7 +3171,9 @@ const HOOKS = [
3171
3171
  "the buyer closed the sheet, so do not show it as a failure. A purchase needs a signed-in app user. NATIVE-ONLY: a " +
3172
3172
  "browser has no App Store or Google Play billing, so the web Player (and the Studio canvas) report supported:false " +
3173
3173
  "— ALWAYS render a visible 'available in the app' state there, never a blank or a dead button. The Expo export also " +
3174
- "reports supported:false until the workspace connects RevenueCat in Payments and rebuilds the app.",
3174
+ "reports supported:false until the workspace connects RevenueCat in Payments and rebuilds the app. On the web, sell " +
3175
+ "page access with useAccessPlans() instead: a web purchase of a plan (or of paid-app access) also grants its " +
3176
+ "RevenueCat entitlement, so `entitlements` in the app includes what the user bought on the web.",
3175
3177
  returnShape: {
3176
3178
  supported: "boolean // false on the web Player and in an app built without RevenueCat",
3177
3179
  loading: "boolean",
@@ -6662,7 +6664,10 @@ const CONTRACT = deepFreeze({
6662
6664
  // `kind` (physical | service | digital); three new PaymentError codes.
6663
6665
  // 1.229.0: additive (sc-15167) — `useAccessPlans()`, and the payments slice
6664
6666
  // gains `listAccessPlans` / `purchaseAccessPlan` / `cancelAccessPlan`.
6665
- version: "1.229.0",
6667
+ // 1.230.0: additive (sc-15164) — each access plan carries
6668
+ // `purchase_available`; a store build buys a plan the workspace sells
6669
+ // in-app through the store, and web purchases grant RevenueCat entitlements.
6670
+ version: "1.230.0",
6666
6671
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
6667
6672
  hooks: HOOKS,
6668
6673
  primitives: PRIMITIVES,
package/dist/contract.js CHANGED
@@ -2339,13 +2339,13 @@ const HOOKS = [
2339
2339
  signature: "useAccessPlans()",
2340
2340
  returnShape: {
2341
2341
  plans:
2342
- "Array<{ id, name, price_cents, currency, billing: \"one_time\" | \"monthly\", owned, renews, access_ends_at }> // owned: the user holds it; renews: a held monthly plan will charge again; access_ends_at: end of held access without a renewal (null for one-time)",
2342
+ "Array<{ id, name, price_cents, currency, billing: \"one_time\" | \"monthly\", owned, renews, access_ends_at, purchase_available }> // owned: the user holds it; renews: a held monthly plan will charge again; access_ends_at: end of held access without a renewal (null for one-time); purchase_available: this plan can be bought here — false in an iOS/Android build for a plan the workspace does not sell in-app",
2343
2343
  loading: "boolean",
2344
2344
  error: "PaymentError | null",
2345
2345
  purchaseAvailable:
2346
- "boolean // false in an iOS/Android build: page access is digital and needs store billing — render a visible not-available state, never a dead button or a blank",
2346
+ "boolean // false when no plan can be bought here (an iOS/Android build where the workspace sells no plan in-app) — render a visible not-available state, never a dead button or a blank; check each plan's purchase_available too",
2347
2347
  purchase:
2348
- "(planId, { return_path? }?) => Promise<{ plan_id, status: \"ACTIVE\" | \"PENDING\" }> // pass ONLY the plan id; the server sets price and group; host opens hosted Checkout; access arrives with the payment confirmation",
2348
+ "(planId, { return_path? }?) => Promise<{ plan_id, status: \"ACTIVE\" | \"PENDING\" }> // pass ONLY the plan id; the server sets price and group; the host opens hosted Checkout on the web and the store's in-app purchase sheet in an iOS/Android build; access arrives with the payment confirmation, so refresh() a moment later",
2349
2349
  cancel:
2350
2350
  "(planId) => Promise<{ plan_id, renews: false, access_ends_at }> // stop a renewing monthly plan; access runs to access_ends_at — offer it beside every plan with renews: true",
2351
2351
  refresh: "() => Promise<void> // re-read after returning from Checkout",
@@ -3167,7 +3167,9 @@ const HOOKS = [
3167
3167
  "the buyer closed the sheet, so do not show it as a failure. A purchase needs a signed-in app user. NATIVE-ONLY: a " +
3168
3168
  "browser has no App Store or Google Play billing, so the web Player (and the Studio canvas) report supported:false " +
3169
3169
  "— ALWAYS render a visible 'available in the app' state there, never a blank or a dead button. The Expo export also " +
3170
- "reports supported:false until the workspace connects RevenueCat in Payments and rebuilds the app.",
3170
+ "reports supported:false until the workspace connects RevenueCat in Payments and rebuilds the app. On the web, sell " +
3171
+ "page access with useAccessPlans() instead: a web purchase of a plan (or of paid-app access) also grants its " +
3172
+ "RevenueCat entitlement, so `entitlements` in the app includes what the user bought on the web.",
3171
3173
  returnShape: {
3172
3174
  supported: "boolean // false on the web Player and in an app built without RevenueCat",
3173
3175
  loading: "boolean",
@@ -6658,7 +6660,10 @@ const CONTRACT = deepFreeze({
6658
6660
  // `kind` (physical | service | digital); three new PaymentError codes.
6659
6661
  // 1.229.0: additive (sc-15167) — `useAccessPlans()`, and the payments slice
6660
6662
  // gains `listAccessPlans` / `purchaseAccessPlan` / `cancelAccessPlan`.
6661
- version: "1.229.0",
6663
+ // 1.230.0: additive (sc-15164) — each access plan carries
6664
+ // `purchase_available`; a store build buys a plan the workspace sells
6665
+ // in-app through the store, and web purchases grant RevenueCat entitlements.
6666
+ version: "1.230.0",
6662
6667
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
6663
6668
  hooks: HOOKS,
6664
6669
  primitives: PRIMITIVES,
package/dist/hooks.js CHANGED
@@ -7075,21 +7075,24 @@ export function usePayments() {
7075
7075
  * `{ plans, loading, error, purchaseAvailable, purchase, cancel, refresh }`:
7076
7076
  *
7077
7077
  * plans → [{ id, name, price_cents, currency, billing, owned, renews,
7078
- * access_ends_at }] — `billing` is "one_time" or "monthly"; `owned` is
7079
- * true once the user holds the plan; `renews` while a held monthly plan
7080
- * will charge again; `access_ends_at` is when held access ends without a
7081
- * renewal (null for one-time access).
7078
+ * access_ends_at, purchase_available }] — `billing` is "one_time" or
7079
+ * "monthly"; `owned` is true once the user holds the plan; `renews` while
7080
+ * a held monthly plan will charge again; `access_ends_at` is when held
7081
+ * access ends without a renewal (null for one-time access);
7082
+ * `purchase_available` is whether this plan can be bought here.
7082
7083
  * Render the price with `useWorkspaceCurrency().formatMoney(price_cents)`.
7083
- * purchaseAvailable → false where buying cannot work here: page access is a
7084
- * digital good, and an iOS / Android build must use store billing. Render a
7085
- * visible "not available in this app" state then — never a dead button and
7086
- * never a blank render.
7084
+ * purchaseAvailable → false where no plan can be bought here: page access is
7085
+ * a digital good, and an iOS / Android build sells only the plans the
7086
+ * workspace sells in-app (through store billing). Render a visible "not
7087
+ * available in this app" state for a plan whose `purchase_available` is
7088
+ * false — never a dead button and never a blank render.
7087
7089
  * purchase(planId, { return_path? }?) → Promise<{ plan_id, status }>. Pass
7088
7090
  * ONLY the plan id: the server sets the price and the group, so a widget can
7089
- * never name an amount. The HOST opens hosted Checkout (same-tab on web, the
7090
- * in-app browser on native). `status` is "ACTIVE" when access was granted at
7091
- * once (the mock provider) and "PENDING" while payment is confirmed; access
7092
- * arrives with the confirmation, never from the client.
7091
+ * never name an amount. The HOST takes the payment: hosted Checkout on the
7092
+ * web, the store's in-app purchase sheet in an iOS / Android build. `status`
7093
+ * is "ACTIVE" when access was granted at once (the mock provider) and
7094
+ * "PENDING" while payment is confirmed; access arrives with the
7095
+ * confirmation, never from the client.
7093
7096
  * cancel(planId) → Promise<{ plan_id, renews: false, access_ends_at }> —
7094
7097
  * stop a held monthly plan renewing (`renews` true). Access continues
7095
7098
  * until `access_ends_at`; offer it beside every renewing plan.
package/dist/index.d.ts CHANGED
@@ -1527,6 +1527,8 @@ export interface AccessPlan {
1527
1527
  renews: boolean;
1528
1528
  /** ISO time held access ends without a renewal; null for one-time access. */
1529
1529
  access_ends_at: string | null;
1530
+ /** Whether this plan can be bought here (false in a store build that does not sell it in-app). */
1531
+ purchase_available: boolean;
1530
1532
  }
1531
1533
 
1532
1534
  export interface AccessPlanPurchase {
@@ -1540,8 +1542,8 @@ export interface AccessPlansApi {
1540
1542
  loading: boolean;
1541
1543
  error: PaymentError | null;
1542
1544
  /**
1543
- * False where buying cannot work here (an iOS / Android build, where page
1544
- * access must use store billing): render a visible not-available state.
1545
+ * False where no plan can be bought here (an iOS / Android build sells only
1546
+ * the plans the workspace sells in-app): render a visible not-available state.
1545
1547
  */
1546
1548
  purchaseAvailable: boolean;
1547
1549
  /** Pass only the plan id; the server sets price and group. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colixsystems/widget-sdk",
3
- "version": "0.280.0",
3
+ "version": "0.281.0",
4
4
  "description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
5
5
  "homepage": "https://github.com/Colix-AB/AppStudio",
6
6
  "type": "module",
package/src/contract.js CHANGED
@@ -2339,13 +2339,13 @@ const HOOKS = [
2339
2339
  signature: "useAccessPlans()",
2340
2340
  returnShape: {
2341
2341
  plans:
2342
- "Array<{ id, name, price_cents, currency, billing: \"one_time\" | \"monthly\", owned, renews, access_ends_at }> // owned: the user holds it; renews: a held monthly plan will charge again; access_ends_at: end of held access without a renewal (null for one-time)",
2342
+ "Array<{ id, name, price_cents, currency, billing: \"one_time\" | \"monthly\", owned, renews, access_ends_at, purchase_available }> // owned: the user holds it; renews: a held monthly plan will charge again; access_ends_at: end of held access without a renewal (null for one-time); purchase_available: this plan can be bought here — false in an iOS/Android build for a plan the workspace does not sell in-app",
2343
2343
  loading: "boolean",
2344
2344
  error: "PaymentError | null",
2345
2345
  purchaseAvailable:
2346
- "boolean // false in an iOS/Android build: page access is digital and needs store billing — render a visible not-available state, never a dead button or a blank",
2346
+ "boolean // false when no plan can be bought here (an iOS/Android build where the workspace sells no plan in-app) — render a visible not-available state, never a dead button or a blank; check each plan's purchase_available too",
2347
2347
  purchase:
2348
- "(planId, { return_path? }?) => Promise<{ plan_id, status: \"ACTIVE\" | \"PENDING\" }> // pass ONLY the plan id; the server sets price and group; host opens hosted Checkout; access arrives with the payment confirmation",
2348
+ "(planId, { return_path? }?) => Promise<{ plan_id, status: \"ACTIVE\" | \"PENDING\" }> // pass ONLY the plan id; the server sets price and group; the host opens hosted Checkout on the web and the store's in-app purchase sheet in an iOS/Android build; access arrives with the payment confirmation, so refresh() a moment later",
2349
2349
  cancel:
2350
2350
  "(planId) => Promise<{ plan_id, renews: false, access_ends_at }> // stop a renewing monthly plan; access runs to access_ends_at — offer it beside every plan with renews: true",
2351
2351
  refresh: "() => Promise<void> // re-read after returning from Checkout",
@@ -3167,7 +3167,9 @@ const HOOKS = [
3167
3167
  "the buyer closed the sheet, so do not show it as a failure. A purchase needs a signed-in app user. NATIVE-ONLY: a " +
3168
3168
  "browser has no App Store or Google Play billing, so the web Player (and the Studio canvas) report supported:false " +
3169
3169
  "— ALWAYS render a visible 'available in the app' state there, never a blank or a dead button. The Expo export also " +
3170
- "reports supported:false until the workspace connects RevenueCat in Payments and rebuilds the app.",
3170
+ "reports supported:false until the workspace connects RevenueCat in Payments and rebuilds the app. On the web, sell " +
3171
+ "page access with useAccessPlans() instead: a web purchase of a plan (or of paid-app access) also grants its " +
3172
+ "RevenueCat entitlement, so `entitlements` in the app includes what the user bought on the web.",
3171
3173
  returnShape: {
3172
3174
  supported: "boolean // false on the web Player and in an app built without RevenueCat",
3173
3175
  loading: "boolean",
@@ -6658,7 +6660,10 @@ const CONTRACT = deepFreeze({
6658
6660
  // `kind` (physical | service | digital); three new PaymentError codes.
6659
6661
  // 1.229.0: additive (sc-15167) — `useAccessPlans()`, and the payments slice
6660
6662
  // gains `listAccessPlans` / `purchaseAccessPlan` / `cancelAccessPlan`.
6661
- version: "1.229.0",
6663
+ // 1.230.0: additive (sc-15164) — each access plan carries
6664
+ // `purchase_available`; a store build buys a plan the workspace sells
6665
+ // in-app through the store, and web purchases grant RevenueCat entitlements.
6666
+ version: "1.230.0",
6662
6667
  sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
6663
6668
  hooks: HOOKS,
6664
6669
  primitives: PRIMITIVES,