@colixsystems/widget-sdk 0.61.0 → 0.63.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 +15 -1
- package/dist/contract.cjs +15 -4
- package/dist/contract.js +15 -4
- package/dist/hooks.js +39 -12
- package/dist/index.d.ts +16 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,7 +53,19 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
53
53
|
|
|
54
54
|
## Status
|
|
55
55
|
|
|
56
|
-
`v0.
|
|
56
|
+
`v0.63.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**.
|
|
57
|
+
|
|
58
|
+
### What's new in 0.63.0
|
|
59
|
+
|
|
60
|
+
**New optional manifest field `rendersOwnChrome` (sc-3331).** A boolean (default `false`) that declares whether your widget renders its OWN section header — a heading (and optional subtitle), plus any primary action for its section — making it a self-contained section. Set it `true` when your widget draws its own title (from a `title`/`subtitle` prop with a real default, so the author can still retitle it in the Properties Panel), and the AppStudio app-builder will place the widget as the WHOLE section: it will NOT add a standalone heading or a duplicate action button above it, so the section is never double-titled. Leave it `false`/omitted for a content-only widget whose heading the page supplies. `CONTRACT.version` → `1.42.0`. Additive — existing manifests omit it and read `false`, so no widget needs changing.
|
|
61
|
+
|
|
62
|
+
### What's new in 0.62.0
|
|
63
|
+
|
|
64
|
+
**`usePayments()` — the host owns the hosted-checkout redirect, and the documented contract is corrected to snake_case (sc-3290).** Two things that had drifted are now aligned with the runtime:
|
|
65
|
+
|
|
66
|
+
- **The host opens Checkout; the widget never does.** When a hosted-checkout provider (Mollie) is active, `requestPayment(...)` now makes the host open Checkout itself — a **same-tab** redirect on web, the **in-app browser** on native (the identical model the paid-signup flow uses). The result no longer carries a `checkout_url`, and widgets must **not** call `Linking.openURL(...)` for payments: `react-native-web`'s `Linking.openURL` opens a `_blank`, `noopener` tab, which left the app user stranded on an orphan tab after paying. `return_path` now defaults to the current page, so the web return lands the user back where they started. Confirm completion from server-authoritative state (the Mollie webhook flips your datastore record) or by polling `getPayment(id)`.
|
|
67
|
+
- **`PaymentRequest` / `PaymentResult` types are snake_case.** The TypeScript types and the `CONTRACT` return-shape strings described `amountCents` / `checkoutUrl` (plus a `metadata` field the client never forwarded), but the wire — and the runtime client — have always been snake_case. They now read `amount_cents`, `currency?`, `description`, `return_path?` in, `{ id, status, amount_cents, ... }` out. A TS widget that passed `amountCents` was silently sending `undefined`; update to `amount_cents`.
|
|
68
|
+
- No `CONTRACT.version` change — the `ctx.payments` context shape (`{ requestPayment, getPayment }`) is unchanged; only its documented request/return shape and the host-owned redirect behaviour changed.
|
|
57
69
|
|
|
58
70
|
### What's new in 0.60.0
|
|
59
71
|
|
|
@@ -394,6 +406,8 @@ The "split-implementation + vetted package list" pivot.
|
|
|
394
406
|
|
|
395
407
|
### What's new in 0.9.0
|
|
396
408
|
|
|
409
|
+
> **Superseded in 0.62.0** — the wire was always snake_case (`amount_cents`, `return_path`), never the camelCase shown below, and the HOST now opens hosted Checkout itself (the widget does not). See the 0.62.0 entry for the current contract.
|
|
410
|
+
|
|
397
411
|
- **`usePayments()` — incoming app-user payments.** Returns `{ requestPayment, getPayment }`. `requestPayment({ amountCents, currency?, description, metadata?, returnPath? })` triggers a one-time charge from the signed-in app user and resolves to `{ id, status, checkoutUrl? }`: when `checkoutUrl` is present the widget opens it (web: navigate; native: `expo-web-browser`) — the user pays in the provider's **hosted checkout**; when absent (the platform's built-in **mock** provider, the default until a real provider is configured) the charge auto-confirms (`status: "PAID"`). `getPayment(id)` polls the terminal status. Backed by a new `WidgetContext.payments` slice and gated by the new `payments.charge:appUser` scope. **No card data ever touches the widget** — never collect card fields yourself. The charge settles to the workspace owner; the amount is bounded by a platform per-charge cap. Rejections are a structured `PaymentError` (also a new named export) with a stable `.code`.
|
|
398
412
|
- **`CONTRACT.version` → `1.2.0`** (additive: one new hook, one new context slice, one new scope, one new error class). No existing export changed signature.
|
|
399
413
|
|
package/dist/contract.cjs
CHANGED
|
@@ -469,9 +469,9 @@ const HOOKS = [
|
|
|
469
469
|
signature: "usePayments()",
|
|
470
470
|
returnShape: {
|
|
471
471
|
requestPayment:
|
|
472
|
-
"({
|
|
472
|
+
"({ amount_cents, currency?, description, return_path? }) => Promise<{ id, status }> // host opens hosted Checkout; rejects with PaymentError",
|
|
473
473
|
getPayment:
|
|
474
|
-
"(paymentId) => Promise<{ id, status,
|
|
474
|
+
"(paymentId) => Promise<{ id, status, amount_cents, currency, description }>",
|
|
475
475
|
},
|
|
476
476
|
requiredContextSlice: ["payments.requestPayment"],
|
|
477
477
|
scopes: ["payments.charge:appUser"],
|
|
@@ -971,6 +971,13 @@ const MANIFEST_SCHEMA = {
|
|
|
971
971
|
"never auto-applies style — the widget owns placement.",
|
|
972
972
|
default: {},
|
|
973
973
|
},
|
|
974
|
+
rendersOwnChrome: {
|
|
975
|
+
type: "boolean",
|
|
976
|
+
required: false,
|
|
977
|
+
description:
|
|
978
|
+
"Optional (default false). TRUE when the widget renders its OWN section header — a heading (and optional subtitle), plus any primary action for its section — making it a self-contained section. The AppStudio app-builder reads this when it places an already-installed widget: it lays the widget out as the WHOLE section and does NOT add a standalone heading or a duplicate action button above it, so the section is never double-titled. Set it to match what the component actually renders; leave it false (or omit) for a content-only widget whose heading the page supplies.",
|
|
979
|
+
default: false,
|
|
980
|
+
},
|
|
974
981
|
events: {
|
|
975
982
|
type: "object[]",
|
|
976
983
|
required: true,
|
|
@@ -1119,7 +1126,7 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
1119
1126
|
},
|
|
1120
1127
|
payments: {
|
|
1121
1128
|
description:
|
|
1122
|
-
"Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status
|
|
1129
|
+
"Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status }>, getPayment(id) -> Promise<payment> }; wire is snake_case (amount_cents, return_path). Backs usePayments(); requires the payments.charge:appUser scope. The host opens hosted Checkout itself — same-tab on web, in-app browser on native (or auto-confirms under the mock provider); the charge settles to the workspace owner.",
|
|
1123
1130
|
required: true,
|
|
1124
1131
|
fields: { requestPayment: "function", getPayment: "function" },
|
|
1125
1132
|
},
|
|
@@ -1908,7 +1915,11 @@ const CONTRACT = deepFreeze({
|
|
|
1908
1915
|
// no in-code `|| "fallback"`. Backed by a host-only subpath export
|
|
1909
1916
|
// (`@colixsystems/widget-sdk/host` -> resolveProps); the author-facing entry
|
|
1910
1917
|
// is unchanged. No existing behaviour changes — minor bump.
|
|
1911
|
-
|
|
1918
|
+
// 1.42.0 (sc-3331) — new OPTIONAL manifest field `rendersOwnChrome` (boolean,
|
|
1919
|
+
// default false): declares that the widget renders its own section header,
|
|
1920
|
+
// so the app-builder places it as a whole section without stacking a
|
|
1921
|
+
// heading above it. Additive — existing manifests omit it and read false.
|
|
1922
|
+
version: "1.42.0",
|
|
1912
1923
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
1913
1924
|
hooks: HOOKS,
|
|
1914
1925
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -469,9 +469,9 @@ const HOOKS = [
|
|
|
469
469
|
signature: "usePayments()",
|
|
470
470
|
returnShape: {
|
|
471
471
|
requestPayment:
|
|
472
|
-
"({
|
|
472
|
+
"({ amount_cents, currency?, description, return_path? }) => Promise<{ id, status }> // host opens hosted Checkout; rejects with PaymentError",
|
|
473
473
|
getPayment:
|
|
474
|
-
"(paymentId) => Promise<{ id, status,
|
|
474
|
+
"(paymentId) => Promise<{ id, status, amount_cents, currency, description }>",
|
|
475
475
|
},
|
|
476
476
|
requiredContextSlice: ["payments.requestPayment"],
|
|
477
477
|
scopes: ["payments.charge:appUser"],
|
|
@@ -971,6 +971,13 @@ const MANIFEST_SCHEMA = {
|
|
|
971
971
|
"never auto-applies style — the widget owns placement.",
|
|
972
972
|
default: {},
|
|
973
973
|
},
|
|
974
|
+
rendersOwnChrome: {
|
|
975
|
+
type: "boolean",
|
|
976
|
+
required: false,
|
|
977
|
+
description:
|
|
978
|
+
"Optional (default false). TRUE when the widget renders its OWN section header — a heading (and optional subtitle), plus any primary action for its section — making it a self-contained section. The AppStudio app-builder reads this when it places an already-installed widget: it lays the widget out as the WHOLE section and does NOT add a standalone heading or a duplicate action button above it, so the section is never double-titled. Set it to match what the component actually renders; leave it false (or omit) for a content-only widget whose heading the page supplies.",
|
|
979
|
+
default: false,
|
|
980
|
+
},
|
|
974
981
|
events: {
|
|
975
982
|
type: "object[]",
|
|
976
983
|
required: true,
|
|
@@ -1119,7 +1126,7 @@ const WIDGET_CONTEXT_SHAPE = {
|
|
|
1119
1126
|
},
|
|
1120
1127
|
payments: {
|
|
1121
1128
|
description:
|
|
1122
|
-
"Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status
|
|
1129
|
+
"Injected @colixsystems/payments-client instance (REQ-BILL-07-WIDGETPAY). { requestPayment(body) -> Promise<{ id, status }>, getPayment(id) -> Promise<payment> }; wire is snake_case (amount_cents, return_path). Backs usePayments(); requires the payments.charge:appUser scope. The host opens hosted Checkout itself — same-tab on web, in-app browser on native (or auto-confirms under the mock provider); the charge settles to the workspace owner.",
|
|
1123
1130
|
required: true,
|
|
1124
1131
|
fields: { requestPayment: "function", getPayment: "function" },
|
|
1125
1132
|
},
|
|
@@ -1908,7 +1915,11 @@ const CONTRACT = deepFreeze({
|
|
|
1908
1915
|
// no in-code `|| "fallback"`. Backed by a host-only subpath export
|
|
1909
1916
|
// (`@colixsystems/widget-sdk/host` -> resolveProps); the author-facing entry
|
|
1910
1917
|
// is unchanged. No existing behaviour changes — minor bump.
|
|
1911
|
-
|
|
1918
|
+
// 1.42.0 (sc-3331) — new OPTIONAL manifest field `rendersOwnChrome` (boolean,
|
|
1919
|
+
// default false): declares that the widget renders its own section header,
|
|
1920
|
+
// so the app-builder places it as a whole section without stacking a
|
|
1921
|
+
// heading above it. Additive — existing manifests omit it and read false.
|
|
1922
|
+
version: "1.42.0",
|
|
1912
1923
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
1913
1924
|
hooks: HOOKS,
|
|
1914
1925
|
primitives: PRIMITIVES,
|
package/dist/hooks.js
CHANGED
|
@@ -2813,20 +2813,47 @@ function toPaymentError(err) {
|
|
|
2813
2813
|
|
|
2814
2814
|
/**
|
|
2815
2815
|
* Incoming app-user payments (REQ-BILL-07-WIDGETPAY). Returns
|
|
2816
|
-
* `{ requestPayment, getPayment }`.
|
|
2817
|
-
*
|
|
2818
|
-
*
|
|
2819
|
-
*
|
|
2820
|
-
*
|
|
2821
|
-
*
|
|
2822
|
-
*
|
|
2823
|
-
* `
|
|
2824
|
-
*
|
|
2816
|
+
* `{ requestPayment, getPayment }`. The wire is snake_case (REQ-GEN-09) —
|
|
2817
|
+
* pass and read snake_case keys VERBATIM.
|
|
2818
|
+
*
|
|
2819
|
+
* requestPayment({ amount_cents, currency?, description, return_path? })
|
|
2820
|
+
* → Promise<{ id, status, ... }>. When a hosted-checkout provider
|
|
2821
|
+
* (Mollie) is active the HOST opens Checkout for you — a same-tab
|
|
2822
|
+
* redirect on web, the in-app browser on native — so you do NOT open
|
|
2823
|
+
* anything yourself (no `Linking.openURL`, no `checkout_url` to
|
|
2824
|
+
* handle). The mock provider auto-confirms (`status: "PAID"`, no
|
|
2825
|
+
* redirect). Rejects with a `PaymentError`.
|
|
2826
|
+
* getPayment(paymentId) → Promise<payment> — read the current status
|
|
2827
|
+
* (`PENDING` | `PAID` | `FAILED`), scoped server-side to the caller.
|
|
2828
|
+
*
|
|
2829
|
+
* THE PAYER IS ALWAYS A SIGNED-IN APP USER — charging requires it, and a paid
|
|
2830
|
+
* widget lives on a page behind login. So read the buyer's identity from
|
|
2831
|
+
* `useUser()` (`{ id, email, displayName }`) and connect the record you write
|
|
2832
|
+
* to `user.id`. Do NOT re-collect the name / email `useUser()` already gives
|
|
2833
|
+
* you as required inputs; pre-filling an editable field from it, or asking for
|
|
2834
|
+
* genuinely new details (phone, address, notes), is fine.
|
|
2835
|
+
*
|
|
2836
|
+
* CONFIRM FROM SERVER STATE, NOT MEMORY — and handle success, failure, AND
|
|
2837
|
+
* cancel. On web `requestPayment` triggers a full-page redirect, so the app
|
|
2838
|
+
* reloads on return and any in-memory "done" flag is gone. One pattern works on
|
|
2839
|
+
* both platforms:
|
|
2840
|
+
* 1. On mount, read `useRouteParams()`. After the web redirect it carries
|
|
2841
|
+
* `payment` (`"success"` | `"cancel"`) and, on success, `payment_id`.
|
|
2842
|
+
* `"cancel"` → the user backed out: show a "payment cancelled" state, do
|
|
2843
|
+
* NOT treat it as paid. `"success"` + `payment_id` → poll `getPayment(id)`
|
|
2844
|
+
* until it leaves `PENDING`, then branch: `PAID` → confirm / write the
|
|
2845
|
+
* record; `FAILED` → show a failure state.
|
|
2846
|
+
* 2. Also poll the `id` that `requestPayment` RESOLVES with — the mock
|
|
2847
|
+
* provider and the native in-app-browser flow return here without a reload,
|
|
2848
|
+
* so route params are absent and this branch reconciles instead.
|
|
2849
|
+
* Poll a bounded number of times (e.g. every ~1.5s for up to ~30s) because the
|
|
2850
|
+
* Mollie webhook may land a moment after the redirect; if it is still `PENDING`
|
|
2851
|
+
* when you give up, show a "still processing" state rather than "paid".
|
|
2825
2852
|
*
|
|
2826
2853
|
* Requires the `payments.charge:appUser` scope in the manifest's
|
|
2827
|
-
* `requestedScopes`. The charge settles to the workspace owner; the app
|
|
2828
|
-
*
|
|
2829
|
-
*
|
|
2854
|
+
* `requestedScopes`. The charge settles to the workspace owner; the app user
|
|
2855
|
+
* confirms the amount in hosted Checkout. No card data touches the widget —
|
|
2856
|
+
* never collect card fields yourself.
|
|
2830
2857
|
*/
|
|
2831
2858
|
export function usePayments() {
|
|
2832
2859
|
const ctx = useWidgetContextOrThrow("usePayments");
|
package/dist/index.d.ts
CHANGED
|
@@ -614,32 +614,27 @@ export function useDirectory(query?: DirectoryQuery): DirectoryResult;
|
|
|
614
614
|
export function useWidgetEvent(name: string): (payload?: unknown) => void;
|
|
615
615
|
|
|
616
616
|
/**
|
|
617
|
-
* Arguments for `usePayments().requestPayment(...)`.
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
* widget's own reconciliation (never used to derive the amount).
|
|
617
|
+
* Arguments for `usePayments().requestPayment(...)`. snake_case VERBATIM —
|
|
618
|
+
* this is the wire contract (REQ-GEN-09). `amount_cents` is the charge in the
|
|
619
|
+
* currency's minor unit; the app user confirms it in hosted Checkout.
|
|
621
620
|
*/
|
|
622
621
|
export interface PaymentRequest {
|
|
623
|
-
|
|
622
|
+
amount_cents: number;
|
|
624
623
|
currency?: string;
|
|
625
624
|
description: string;
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
625
|
+
/**
|
|
626
|
+
* Site-relative path to return to after Checkout (e.g. "/cart"). Defaults
|
|
627
|
+
* to the current page, so the user lands back where they started.
|
|
628
|
+
*/
|
|
629
|
+
return_path?: string;
|
|
629
630
|
}
|
|
630
631
|
|
|
631
632
|
export interface PaymentResult {
|
|
632
633
|
id: string;
|
|
633
634
|
status: "PENDING" | "PAID" | "FAILED" | "REFUNDED" | "CANCELLED";
|
|
634
|
-
|
|
635
|
+
amount_cents?: number;
|
|
635
636
|
currency?: string;
|
|
636
637
|
description?: string;
|
|
637
|
-
/**
|
|
638
|
-
* Present (Mollie provider) when the app user must complete a hosted
|
|
639
|
-
* checkout: the widget should open this URL. Absent under the mock
|
|
640
|
-
* provider, where the charge auto-confirms (`status: "PAID"`).
|
|
641
|
-
*/
|
|
642
|
-
checkoutUrl?: string | null;
|
|
643
638
|
}
|
|
644
639
|
|
|
645
640
|
export interface PaymentsApi {
|
|
@@ -649,8 +644,12 @@ export interface PaymentsApi {
|
|
|
649
644
|
|
|
650
645
|
/**
|
|
651
646
|
* Incoming app-user payments (REQ-BILL-07-WIDGETPAY). Requires the
|
|
652
|
-
* `payments.charge:appUser` scope in the widget manifest.
|
|
653
|
-
*
|
|
647
|
+
* `payments.charge:appUser` scope in the widget manifest. When a hosted-
|
|
648
|
+
* checkout provider is active the HOST opens Checkout for you (a same-tab
|
|
649
|
+
* redirect on web, the in-app browser on native) — you never open a URL and
|
|
650
|
+
* never collect card data. Confirm completion from server state (the webhook
|
|
651
|
+
* flips your record) or poll `getPayment(id)`. The charge settles to the
|
|
652
|
+
* workspace owner.
|
|
654
653
|
*/
|
|
655
654
|
export function usePayments(): PaymentsApi;
|
|
656
655
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colixsystems/widget-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.0",
|
|
4
4
|
"description": "Common widget interface for AppStudio. Implements WidgetManifest, WidgetContext, property schema, and helper hooks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|