@aglyn/plugins-marketplace 1.2.0 → 1.3.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 +1 -1
- package/package.json +14 -14
- package/src/lib/components/listing-content.component.js +11 -3
- package/src/lib/components/listing-content.component.js.map +1 -1
- package/src/lib/components/marketplace-pay-panel.component.d.ts +67 -0
- package/src/lib/components/marketplace-pay-panel.component.js +228 -0
- package/src/lib/components/marketplace-pay-panel.component.js.map +1 -0
- package/src/lib/components/publish-artifact-dialog.component.d.ts +1 -1
- package/src/lib/components/publish-artifact-dialog.component.js +144 -20
- package/src/lib/components/publish-artifact-dialog.component.js.map +1 -1
- package/src/lib/components/publish-starter-fields.component.d.ts +35 -0
- package/src/lib/components/publish-starter-fields.component.js +176 -0
- package/src/lib/components/publish-starter-fields.component.js.map +1 -0
- package/src/lib/components/starter-picker-shelf.component.d.ts +2 -2
- package/src/lib/components/starter-picker-shelf.component.js +28 -6
- package/src/lib/components/starter-picker-shelf.component.js.map +1 -1
- package/src/lib/hooks/use-marketplace-actions.d.ts +9 -3
- package/src/lib/hooks/use-marketplace-actions.js +47 -89
- package/src/lib/hooks/use-marketplace-actions.js.map +1 -1
- package/src/lib/hooks/use-marketplace-pay.d.ts +70 -0
- package/src/lib/hooks/use-marketplace-pay.js +359 -0
- package/src/lib/hooks/use-marketplace-pay.js.map +1 -0
- package/src/lib/hooks/use-starter-publish-facts.d.ts +25 -0
- package/src/lib/hooks/use-starter-publish-facts.js +116 -0
- package/src/lib/hooks/use-starter-publish-facts.js.map +1 -0
- package/src/lib/hooks/use-starter-purchase.d.ts +17 -22
- package/src/lib/hooks/use-starter-purchase.js +26 -73
- package/src/lib/hooks/use-starter-purchase.js.map +1 -1
- package/src/lib/model/marketplace-invoice.d.ts +52 -0
- package/src/lib/model/marketplace-invoice.js +75 -0
- package/src/lib/model/marketplace-invoice.js.map +1 -0
- package/src/lib/model/marketplace-pay.d.ts +42 -0
- package/src/lib/model/marketplace-pay.js +33 -0
- package/src/lib/model/marketplace-pay.js.map +1 -0
- package/src/lib/model/platform-seller.d.ts +37 -0
- package/src/lib/model/platform-seller.js +36 -0
- package/src/lib/model/platform-seller.js.map +1 -0
- package/src/lib/model/starter-publish.d.ts +159 -0
- package/src/lib/model/starter-publish.js +189 -0
- package/src/lib/model/starter-publish.js.map +1 -0
- package/src/lib/model/starter-query.d.ts +0 -8
- package/src/lib/model/starter-query.js +0 -10
- package/src/lib/model/starter-query.js.map +1 -1
- package/src/lib/server/billing-webhook.d.ts +6 -0
- package/src/lib/server/billing-webhook.js +269 -211
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/checkout.d.ts +2 -48
- package/src/lib/server/checkout.js +21 -241
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/pay.d.ts +18 -0
- package/src/lib/server/pay.js +540 -0
- package/src/lib/server/pay.js.map +1 -0
- package/src/lib/server/platform-org.d.ts +23 -0
- package/src/lib/server/platform-org.js +27 -0
- package/src/lib/server/platform-org.js.map +1 -0
- package/src/lib/server/publish-preconditions.js +4 -1
- package/src/lib/server/publish-preconditions.js.map +1 -1
- package/src/lib/server/publish-starter.js +5 -2
- package/src/lib/server/publish-starter.js.map +1 -1
- package/src/lib/server/publisher-profile.d.ts +5 -0
- package/src/lib/server/publisher-profile.js +20 -1
- package/src/lib/server/publisher-profile.js.map +1 -1
- package/src/lib/server/publisher-standing.d.ts +26 -0
- package/src/lib/server/publisher-standing.js +66 -0
- package/src/lib/server/publisher-standing.js.map +1 -0
- package/src/lib/server/sale.d.ts +119 -0
- package/src/lib/server/sale.js +304 -0
- package/src/lib/server/sale.js.map +1 -0
- package/src/lib/server/starter-publish-facts.d.ts +36 -0
- package/src/lib/server/starter-publish-facts.js +135 -0
- package/src/lib/server/starter-publish-facts.js.map +1 -0
- package/src/lib/server.js +8 -0
- package/src/lib/server.js.map +1 -1
|
@@ -14,87 +14,40 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ 'use client';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { useUser } from "@aglyn/tenant-feature-instance";
|
|
21
|
-
import { useCallback, useRef, useState } from "react";
|
|
22
|
-
import { STARTER_CHECKOUT_PATH, starterCheckoutBody } from "../model/starter-query.js";
|
|
23
|
-
/** Where the browser goes to pay: replaced in a spec. */ export const starterPurchaseNavigation = {
|
|
24
|
-
assign: (url)=>window.location.assign(url)
|
|
25
|
-
};
|
|
17
|
+
import { buildRoute, Route } from "@aglyn/aglyn/app-utils/console-routes";
|
|
18
|
+
import { useCallback, useState } from "react";
|
|
19
|
+
import { useMarketplacePay } from "./use-marketplace-pay.js";
|
|
26
20
|
/**
|
|
27
|
-
* "Buy and continue" for a priced starter in Create site (AGL-4049):
|
|
28
|
-
* the
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const userRef = useRef(user);
|
|
39
|
-
userRef.current = user;
|
|
40
|
-
const attempts = useRef(new Map());
|
|
41
|
-
const [busy, setBusy] = useState(false);
|
|
21
|
+
* "Buy and continue" for a priced starter in Create site (AGL-4049): pays in
|
|
22
|
+
* the console, licensed to the workspace the site is for, so the person stays
|
|
23
|
+
* on the same pick with the site still to be created. The purchase is on
|
|
24
|
+
* record when it answers, so Create is ready on the next click.
|
|
25
|
+
*/ export function useStarterPurchase(orgId, orgSlug) {
|
|
26
|
+
const pay = useMarketplacePay({
|
|
27
|
+
orgId,
|
|
28
|
+
billingHref: orgSlug ? buildRoute(Route.MANAGE_BILLING, {
|
|
29
|
+
orgSlug
|
|
30
|
+
}) : null
|
|
31
|
+
});
|
|
42
32
|
const [error, setError] = useState(null);
|
|
43
|
-
const buy = useCallback(async (
|
|
44
|
-
var _attempts_current_get;
|
|
45
|
-
if (!orgId || !listingId) return 'failed';
|
|
46
|
-
const key = (_attempts_current_get = attempts.current.get(listingId)) != null ? _attempts_current_get : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
47
|
-
attempts.current.set(listingId, key);
|
|
48
|
-
setBusy(true);
|
|
33
|
+
const buy = useCallback(async (listing)=>{
|
|
49
34
|
setError(null);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
method: 'POST',
|
|
56
|
-
headers: {
|
|
57
|
-
'Content-Type': 'application/json',
|
|
58
|
-
'Idempotency-Key': key
|
|
59
|
-
},
|
|
60
|
-
body: JSON.stringify(starterCheckoutBody({
|
|
61
|
-
listingId,
|
|
62
|
-
orgId,
|
|
63
|
-
gaClientId: await readGaClientId(process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID)
|
|
64
|
-
}))
|
|
65
|
-
});
|
|
66
|
-
const payload = await response.json().catch(()=>null);
|
|
67
|
-
if (response.status === 409 && (payload == null ? void 0 : payload.code) === 'already_purchased') return 'owned';
|
|
68
|
-
const locked = parseLockdownRefusal(response.status, payload);
|
|
69
|
-
if (locked) {
|
|
70
|
-
setError(lockdownRefusalText(locked));
|
|
71
|
-
return 'failed';
|
|
72
|
-
}
|
|
73
|
-
if (response.status === 501) {
|
|
74
|
-
setError('Purchases are not set up here yet, so a starter with a price cannot be bought.');
|
|
75
|
-
return 'failed';
|
|
76
|
-
}
|
|
77
|
-
if (!response.ok || typeof (payload == null ? void 0 : payload.url) !== 'string' || !payload.url) {
|
|
78
|
-
setError(typeof (payload == null ? void 0 : payload.error) === 'string' && payload.error ? payload.error : 'The payment could not open. Try again.');
|
|
79
|
-
return 'failed';
|
|
80
|
-
}
|
|
81
|
-
opening = true;
|
|
82
|
-
starterPurchaseNavigation.assign(payload.url);
|
|
83
|
-
return 'opening';
|
|
84
|
-
} catch (caught) {
|
|
85
|
-
console.error(caught);
|
|
86
|
-
setError('The payment could not open. Check your connection and try again.');
|
|
87
|
-
return 'failed';
|
|
88
|
-
} finally{
|
|
89
|
-
if (!opening) setBusy(false);
|
|
35
|
+
const result = await pay.buy(listing);
|
|
36
|
+
if (result.outcome === 'paid' || result.outcome === 'owned') return 'owned';
|
|
37
|
+
if (result.outcome === 'pending') {
|
|
38
|
+
setError('Your payment is going through. You can create this site as soon as it clears.');
|
|
39
|
+
return 'pending';
|
|
90
40
|
}
|
|
41
|
+
setError(result.error);
|
|
42
|
+
return 'stopped';
|
|
91
43
|
}, [
|
|
92
|
-
|
|
44
|
+
pay.buy
|
|
93
45
|
]);
|
|
94
46
|
return {
|
|
95
47
|
buy,
|
|
96
|
-
busy,
|
|
97
|
-
error
|
|
48
|
+
busy: pay.busy,
|
|
49
|
+
error,
|
|
50
|
+
panel: pay.panel
|
|
98
51
|
};
|
|
99
52
|
}
|
|
100
53
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/hooks/use-starter-purchase.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/hooks/use-starter-purchase.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { useCallback, useState, type ReactNode } from 'react'\nimport { useMarketplacePay, type PayListing } from './use-marketplace-pay'\n\n/** How one \"Buy and continue\" ended. */\nexport type StarterPurchaseOutcome =\n /** Paid just now, or owned already: the site can be created. */\n | 'owned'\n /** Paid and still clearing: the site can be created once it has. */\n | 'pending'\n /** Closed, or it could not open; {@link StarterPurchase.error} says why. */\n | 'stopped'\n\nexport interface StarterPurchase {\n buy: (listing: PayListing) => Promise<StarterPurchaseOutcome>\n /** A purchase is opening or paying. */\n busy: boolean\n /** Why the last one did not open, or `null`. */\n error: string | null\n /** The payment, in place under the pick, or `null`. */\n panel: ReactNode\n}\n\n/**\n * \"Buy and continue\" for a priced starter in Create site (AGL-4049): pays in\n * the console, licensed to the workspace the site is for, so the person stays\n * on the same pick with the site still to be created. The purchase is on\n * record when it answers, so Create is ready on the next click.\n */\nexport function useStarterPurchase(orgId: string | undefined, orgSlug: string): StarterPurchase {\n const pay = useMarketplacePay({\n orgId,\n billingHref: orgSlug ? buildRoute(Route.MANAGE_BILLING, { orgSlug }) : null,\n })\n const [error, setError] = useState<string | null>(null)\n\n const buy = useCallback(\n async (listing: PayListing): Promise<StarterPurchaseOutcome> => {\n setError(null)\n const result = await pay.buy(listing)\n if (result.outcome === 'paid' || result.outcome === 'owned') return 'owned'\n if (result.outcome === 'pending') {\n setError('Your payment is going through. You can create this site as soon as it clears.')\n return 'pending'\n }\n setError(result.error)\n return 'stopped'\n },\n [pay.buy],\n )\n\n return { buy, busy: pay.busy, error, panel: pay.panel }\n}\n"],"names":["buildRoute","Route","useCallback","useState","useMarketplacePay","useStarterPurchase","orgId","orgSlug","pay","billingHref","MANAGE_BILLING","error","setError","buy","listing","result","outcome","busy","panel"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAEA,SAASA,UAAU,EAAEC,KAAK,QAAQ,wCAAuC;AACzE,SAASC,WAAW,EAAEC,QAAQ,QAAwB,QAAO;AAC7D,SAASC,iBAAiB,QAAyB,2BAAuB;AAqB1E;;;;;CAKC,GACD,OAAO,SAASC,mBAAmBC,KAAyB,EAAEC,OAAe;IAC3E,MAAMC,MAAMJ,kBAAkB;QAC5BE;QACAG,aAAaF,UAAUP,WAAWC,MAAMS,cAAc,EAAE;YAAEH;QAAQ,KAAK;IACzE;IACA,MAAM,CAACI,OAAOC,SAAS,GAAGT,SAAwB;IAElD,MAAMU,MAAMX,YACV,OAAOY;QACLF,SAAS;QACT,MAAMG,SAAS,MAAMP,IAAIK,GAAG,CAACC;QAC7B,IAAIC,OAAOC,OAAO,KAAK,UAAUD,OAAOC,OAAO,KAAK,SAAS,OAAO;QACpE,IAAID,OAAOC,OAAO,KAAK,WAAW;YAChCJ,SAAS;YACT,OAAO;QACT;QACAA,SAASG,OAAOJ,KAAK;QACrB,OAAO;IACT,GACA;QAACH,IAAIK,GAAG;KAAC;IAGX,OAAO;QAAEA;QAAKI,MAAMT,IAAIS,IAAI;QAAEN;QAAOO,OAAOV,IAAIU,KAAK;IAAC;AACxD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/** The discriminator every Stripe object of a marketplace sale carries. */
|
|
18
|
+
export declare const MARKETPLACE_PURCHASE_TYPE = "marketplace-purchase";
|
|
19
|
+
/** The facts a marketplace sale carries across Stripe, as the webhook reads them. */
|
|
20
|
+
export interface MarketplaceSaleFacts {
|
|
21
|
+
listingId: string;
|
|
22
|
+
buyerUid: string;
|
|
23
|
+
buyerOrgId: string;
|
|
24
|
+
sellerOrgId: string;
|
|
25
|
+
feeCents: number;
|
|
26
|
+
transferCents: number;
|
|
27
|
+
/** The buyer's GA client id, so the server's `purchase` joins their session. */
|
|
28
|
+
gaClientId?: string | null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A sale's metadata as Stripe form params (AGL-4049): the same fields a
|
|
32
|
+
* hosted checkout session carries, so the purchase record cannot tell the two
|
|
33
|
+
* doors apart, plus the marker that keeps the sale out of the platform's own
|
|
34
|
+
* revenue.
|
|
35
|
+
*/
|
|
36
|
+
export declare function marketplaceSaleMetadataParams(facts: MarketplaceSaleFacts): Record<string, string>;
|
|
37
|
+
/** Whether a Stripe invoice is a marketplace sale's. */
|
|
38
|
+
export declare function isMarketplaceInvoice(invoice: unknown): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The tax on an invoice, in cents.
|
|
41
|
+
*
|
|
42
|
+
* The breakdown first, the scalar only when no array said anything: measured
|
|
43
|
+
* on the live account, a computed invoice can send `tax: 0` or `tax: null`
|
|
44
|
+
* beside a populated `total_taxes[]` (AGL-1811).
|
|
45
|
+
*/
|
|
46
|
+
export declare function invoiceTaxCents(invoice: any): number;
|
|
47
|
+
/**
|
|
48
|
+
* The PaymentIntent an invoice settles through, whichever generation of the
|
|
49
|
+
* field it carries: an id or an expanded object at the top level, or under
|
|
50
|
+
* `payments[]` on newer API versions.
|
|
51
|
+
*/
|
|
52
|
+
export declare function invoicePaymentIntentId(invoice: any): string;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { PLUGIN_SALE_METADATA_KEY, PLUGIN_SALE_METADATA_VALUE } from "@aglyn/aglyn/app-utils/plugin-sale";
|
|
18
|
+
/** The discriminator every Stripe object of a marketplace sale carries. */ export const MARKETPLACE_PURCHASE_TYPE = 'marketplace-purchase';
|
|
19
|
+
/**
|
|
20
|
+
* A sale's metadata as Stripe form params (AGL-4049): the same fields a
|
|
21
|
+
* hosted checkout session carries, so the purchase record cannot tell the two
|
|
22
|
+
* doors apart, plus the marker that keeps the sale out of the platform's own
|
|
23
|
+
* revenue.
|
|
24
|
+
*/ export function marketplaceSaleMetadataParams(facts) {
|
|
25
|
+
return _extends({
|
|
26
|
+
'metadata[type]': MARKETPLACE_PURCHASE_TYPE,
|
|
27
|
+
[`metadata[${PLUGIN_SALE_METADATA_KEY}]`]: PLUGIN_SALE_METADATA_VALUE,
|
|
28
|
+
'metadata[listingId]': facts.listingId,
|
|
29
|
+
'metadata[buyerUid]': facts.buyerUid,
|
|
30
|
+
'metadata[buyerOrgId]': facts.buyerOrgId,
|
|
31
|
+
'metadata[sellerOrgId]': facts.sellerOrgId,
|
|
32
|
+
'metadata[feeCents]': String(facts.feeCents),
|
|
33
|
+
'metadata[transferCents]': String(facts.transferCents)
|
|
34
|
+
}, facts.gaClientId && /^\d+\.\d+$/.test(facts.gaClientId) ? {
|
|
35
|
+
'metadata[ga_client_id]': facts.gaClientId
|
|
36
|
+
} : {});
|
|
37
|
+
}
|
|
38
|
+
/** Whether a Stripe invoice is a marketplace sale's. */ export function isMarketplaceInvoice(invoice) {
|
|
39
|
+
var _invoice_metadata;
|
|
40
|
+
return (invoice == null ? void 0 : (_invoice_metadata = invoice.metadata) == null ? void 0 : _invoice_metadata.type) === MARKETPLACE_PURCHASE_TYPE;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The tax on an invoice, in cents.
|
|
44
|
+
*
|
|
45
|
+
* The breakdown first, the scalar only when no array said anything: measured
|
|
46
|
+
* on the live account, a computed invoice can send `tax: 0` or `tax: null`
|
|
47
|
+
* beside a populated `total_taxes[]` (AGL-1811).
|
|
48
|
+
*/ export function invoiceTaxCents(invoice) {
|
|
49
|
+
var _ref;
|
|
50
|
+
const entries = Array.isArray(invoice == null ? void 0 : invoice.total_taxes) ? invoice.total_taxes : Array.isArray(invoice == null ? void 0 : invoice.total_tax_amounts) ? invoice.total_tax_amounts : [];
|
|
51
|
+
if (entries.length > 0) {
|
|
52
|
+
return entries.reduce((sum, entry)=>{
|
|
53
|
+
var _ref;
|
|
54
|
+
const amount = Number((_ref = entry == null ? void 0 : entry.amount) != null ? _ref : 0);
|
|
55
|
+
return sum + (Number.isFinite(amount) ? Math.round(amount) : 0);
|
|
56
|
+
}, 0);
|
|
57
|
+
}
|
|
58
|
+
const scalar = Number((_ref = invoice == null ? void 0 : invoice.tax) != null ? _ref : NaN);
|
|
59
|
+
return Number.isFinite(scalar) && scalar > 0 ? Math.round(scalar) : 0;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The PaymentIntent an invoice settles through, whichever generation of the
|
|
63
|
+
* field it carries: an id or an expanded object at the top level, or under
|
|
64
|
+
* `payments[]` on newer API versions.
|
|
65
|
+
*/ export function invoicePaymentIntentId(invoice) {
|
|
66
|
+
var _invoice_payments_data__payment, _invoice_payments_data_, _invoice_payments_data, _invoice_payments;
|
|
67
|
+
const intent = invoice == null ? void 0 : invoice.payment_intent;
|
|
68
|
+
if (typeof intent === 'string' && intent) return intent;
|
|
69
|
+
if (typeof (intent == null ? void 0 : intent.id) === 'string' && intent.id) return intent.id;
|
|
70
|
+
const nested = invoice == null ? void 0 : (_invoice_payments = invoice.payments) == null ? void 0 : (_invoice_payments_data = _invoice_payments.data) == null ? void 0 : (_invoice_payments_data_ = _invoice_payments_data[0]) == null ? void 0 : (_invoice_payments_data__payment = _invoice_payments_data_.payment) == null ? void 0 : _invoice_payments_data__payment.payment_intent;
|
|
71
|
+
if (typeof nested === 'string' && nested) return nested;
|
|
72
|
+
return typeof (nested == null ? void 0 : nested.id) === 'string' ? nested.id : '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=marketplace-invoice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/model/marketplace-invoice.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n PLUGIN_SALE_METADATA_KEY,\n PLUGIN_SALE_METADATA_VALUE,\n} from '@aglyn/aglyn/app-utils/plugin-sale'\n\n/** The discriminator every Stripe object of a marketplace sale carries. */\nexport const MARKETPLACE_PURCHASE_TYPE = 'marketplace-purchase'\n\n/** The facts a marketplace sale carries across Stripe, as the webhook reads them. */\nexport interface MarketplaceSaleFacts {\n listingId: string\n buyerUid: string\n buyerOrgId: string\n sellerOrgId: string\n feeCents: number\n transferCents: number\n /** The buyer's GA client id, so the server's `purchase` joins their session. */\n gaClientId?: string | null\n}\n\n/**\n * A sale's metadata as Stripe form params (AGL-4049): the same fields a\n * hosted checkout session carries, so the purchase record cannot tell the two\n * doors apart, plus the marker that keeps the sale out of the platform's own\n * revenue.\n */\nexport function marketplaceSaleMetadataParams(facts: MarketplaceSaleFacts): Record<string, string> {\n return {\n 'metadata[type]': MARKETPLACE_PURCHASE_TYPE,\n [`metadata[${PLUGIN_SALE_METADATA_KEY}]`]: PLUGIN_SALE_METADATA_VALUE,\n 'metadata[listingId]': facts.listingId,\n 'metadata[buyerUid]': facts.buyerUid,\n 'metadata[buyerOrgId]': facts.buyerOrgId,\n 'metadata[sellerOrgId]': facts.sellerOrgId,\n 'metadata[feeCents]': String(facts.feeCents),\n 'metadata[transferCents]': String(facts.transferCents),\n ...(facts.gaClientId && /^\\d+\\.\\d+$/.test(facts.gaClientId)\n ? { 'metadata[ga_client_id]': facts.gaClientId }\n : {}),\n }\n}\n\n/** Whether a Stripe invoice is a marketplace sale's. */\nexport function isMarketplaceInvoice(invoice: unknown): boolean {\n return (invoice as { metadata?: { type?: unknown } } | null | undefined)?.metadata?.type ===\n MARKETPLACE_PURCHASE_TYPE\n}\n\n/**\n * The tax on an invoice, in cents.\n *\n * The breakdown first, the scalar only when no array said anything: measured\n * on the live account, a computed invoice can send `tax: 0` or `tax: null`\n * beside a populated `total_taxes[]` (AGL-1811).\n */\nexport function invoiceTaxCents(invoice: any): number {\n const entries: any[] = Array.isArray(invoice?.total_taxes)\n ? invoice.total_taxes\n : Array.isArray(invoice?.total_tax_amounts)\n ? invoice.total_tax_amounts\n : []\n if (entries.length > 0) {\n return entries.reduce((sum: number, entry: any) => {\n const amount = Number(entry?.amount ?? 0)\n return sum + (Number.isFinite(amount) ? Math.round(amount) : 0)\n }, 0)\n }\n const scalar = Number(invoice?.tax ?? NaN)\n return Number.isFinite(scalar) && scalar > 0 ? Math.round(scalar) : 0\n}\n\n/**\n * The PaymentIntent an invoice settles through, whichever generation of the\n * field it carries: an id or an expanded object at the top level, or under\n * `payments[]` on newer API versions.\n */\nexport function invoicePaymentIntentId(invoice: any): string {\n const intent = invoice?.payment_intent\n if (typeof intent === 'string' && intent) return intent\n if (typeof intent?.id === 'string' && intent.id) return intent.id\n const nested = invoice?.payments?.data?.[0]?.payment?.payment_intent\n if (typeof nested === 'string' && nested) return nested\n return typeof nested?.id === 'string' ? nested.id : ''\n}\n"],"names":["PLUGIN_SALE_METADATA_KEY","PLUGIN_SALE_METADATA_VALUE","MARKETPLACE_PURCHASE_TYPE","marketplaceSaleMetadataParams","facts","listingId","buyerUid","buyerOrgId","sellerOrgId","String","feeCents","transferCents","gaClientId","test","isMarketplaceInvoice","invoice","metadata","type","invoiceTaxCents","entries","Array","isArray","total_taxes","total_tax_amounts","length","reduce","sum","entry","amount","Number","isFinite","Math","round","scalar","tax","NaN","invoicePaymentIntentId","intent","payment_intent","id","nested","payments","data","payment"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,wBAAwB,EACxBC,0BAA0B,QACrB,qCAAoC;AAE3C,yEAAyE,GACzE,OAAO,MAAMC,4BAA4B,uBAAsB;AAc/D;;;;;CAKC,GACD,OAAO,SAASC,8BAA8BC,KAA2B;IACvE,OAAO;QACL,kBAAkBF;QAClB,CAAC,CAAC,SAAS,EAAEF,yBAAyB,CAAC,CAAC,CAAC,EAAEC;QAC3C,uBAAuBG,MAAMC,SAAS;QACtC,sBAAsBD,MAAME,QAAQ;QACpC,wBAAwBF,MAAMG,UAAU;QACxC,yBAAyBH,MAAMI,WAAW;QAC1C,sBAAsBC,OAAOL,MAAMM,QAAQ;QAC3C,2BAA2BD,OAAOL,MAAMO,aAAa;OACjDP,MAAMQ,UAAU,IAAI,aAAaC,IAAI,CAACT,MAAMQ,UAAU,IACtD;QAAE,0BAA0BR,MAAMQ,UAAU;IAAC,IAC7C,CAAC;AAET;AAEA,sDAAsD,GACtD,OAAO,SAASE,qBAAqBC,OAAgB;QAC5C;IAAP,OAAO,CAACA,4BAAD,oBAAA,AAACA,QAAkEC,QAAQ,qBAA3E,kBAA6EC,IAAI,MACtFf;AACJ;AAEA;;;;;;CAMC,GACD,OAAO,SAASgB,gBAAgBH,OAAY;;IAC1C,MAAMI,UAAiBC,MAAMC,OAAO,CAACN,2BAAAA,QAASO,WAAW,IACrDP,QAAQO,WAAW,GACnBF,MAAMC,OAAO,CAACN,2BAAAA,QAASQ,iBAAiB,IACtCR,QAAQQ,iBAAiB,GACzB,EAAE;IACR,IAAIJ,QAAQK,MAAM,GAAG,GAAG;QACtB,OAAOL,QAAQM,MAAM,CAAC,CAACC,KAAaC;;YAClC,MAAMC,SAASC,eAAOF,yBAAAA,MAAOC,MAAM,mBAAI;YACvC,OAAOF,MAAOG,CAAAA,OAAOC,QAAQ,CAACF,UAAUG,KAAKC,KAAK,CAACJ,UAAU,CAAA;QAC/D,GAAG;IACL;IACA,MAAMK,SAASJ,eAAOd,2BAAAA,QAASmB,GAAG,mBAAIC;IACtC,OAAON,OAAOC,QAAQ,CAACG,WAAWA,SAAS,IAAIF,KAAKC,KAAK,CAACC,UAAU;AACtE;AAEA;;;;CAIC,GACD,OAAO,SAASG,uBAAuBrB,OAAY;QAIlCA,iCAAAA,yBAAAA,wBAAAA;IAHf,MAAMsB,SAAStB,2BAAAA,QAASuB,cAAc;IACtC,IAAI,OAAOD,WAAW,YAAYA,QAAQ,OAAOA;IACjD,IAAI,QAAOA,0BAAAA,OAAQE,EAAE,MAAK,YAAYF,OAAOE,EAAE,EAAE,OAAOF,OAAOE,EAAE;IACjE,MAAMC,SAASzB,4BAAAA,oBAAAA,QAAS0B,QAAQ,sBAAjB1B,yBAAAA,kBAAmB2B,IAAI,sBAAvB3B,0BAAAA,sBAAyB,CAAC,EAAE,sBAA5BA,kCAAAA,wBAA8B4B,OAAO,qBAArC5B,gCAAuCuB,cAAc;IACpE,IAAI,OAAOE,WAAW,YAAYA,QAAQ,OAAOA;IACjD,OAAO,QAAOA,0BAAAA,OAAQD,EAAE,MAAK,WAAWC,OAAOD,EAAE,GAAG;AACtD"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/** The card a quote names, as far as this person may see it. */
|
|
18
|
+
export interface PayQuoteCard {
|
|
19
|
+
type?: string | null;
|
|
20
|
+
brand?: string | null;
|
|
21
|
+
last4?: string | null;
|
|
22
|
+
email?: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** What the pay door answers a `quote` with: what will be charged, before it is. */
|
|
25
|
+
export interface PayQuote {
|
|
26
|
+
listingName: string;
|
|
27
|
+
amountCents: number;
|
|
28
|
+
taxCents: number;
|
|
29
|
+
totalCents: number;
|
|
30
|
+
/** `null` for someone who may buy but may not see billing. */
|
|
31
|
+
card: PayQuoteCard | null;
|
|
32
|
+
/** Whether this person holds `billing.manage`, which paying takes. */
|
|
33
|
+
canPay: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** The two things a workspace needs before it can be charged. */
|
|
36
|
+
export type PayPrerequisite = 'card_required' | 'billing_address_required';
|
|
37
|
+
/** Reads the prerequisite a refusal names, or `null` when it names none. */
|
|
38
|
+
export declare function payPrerequisiteOf(status: number, payload: unknown): PayPrerequisite | null;
|
|
39
|
+
/** Dollars and cents, for a line a buyer is about to agree to. */
|
|
40
|
+
export declare function formatPayCents(cents: number): string;
|
|
41
|
+
/** The saved method in words, wallets included: they carry an email, not a number. */
|
|
42
|
+
export declare function payCardLabel(card: PayQuoteCard | null): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /** The card a quote names, as far as this person may see it. */ /** Reads the prerequisite a refusal names, or `null` when it names none. */ export function payPrerequisiteOf(status, payload) {
|
|
17
|
+
const code = payload == null ? void 0 : payload.code;
|
|
18
|
+
return status === 412 && (code === 'card_required' || code === 'billing_address_required') ? code : null;
|
|
19
|
+
}
|
|
20
|
+
/** Dollars and cents, for a line a buyer is about to agree to. */ export function formatPayCents(cents) {
|
|
21
|
+
return new Intl.NumberFormat('en-US', {
|
|
22
|
+
style: 'currency',
|
|
23
|
+
currency: 'USD'
|
|
24
|
+
}).format(cents / 100);
|
|
25
|
+
}
|
|
26
|
+
/** The saved method in words, wallets included: they carry an email, not a number. */ export function payCardLabel(card) {
|
|
27
|
+
var _ref, _card_brand, _card_type;
|
|
28
|
+
if (card == null ? void 0 : card.last4) return `${((_ref = (_card_brand = card.brand) != null ? _card_brand : card.type) != null ? _ref : 'card').toUpperCase()} •••• ${card.last4}`;
|
|
29
|
+
if (card == null ? void 0 : card.email) return `${(_card_type = card.type) != null ? _card_type : 'wallet'} · ${card.email}`;
|
|
30
|
+
return 'the card on file';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=marketplace-pay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/model/marketplace-pay.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/** The card a quote names, as far as this person may see it. */\nexport interface PayQuoteCard {\n type?: string | null\n brand?: string | null\n last4?: string | null\n email?: string | null\n}\n\n/** What the pay door answers a `quote` with: what will be charged, before it is. */\nexport interface PayQuote {\n listingName: string\n amountCents: number\n taxCents: number\n totalCents: number\n /** `null` for someone who may buy but may not see billing. */\n card: PayQuoteCard | null\n /** Whether this person holds `billing.manage`, which paying takes. */\n canPay: boolean\n}\n\n/** The two things a workspace needs before it can be charged. */\nexport type PayPrerequisite = 'card_required' | 'billing_address_required'\n\n/** Reads the prerequisite a refusal names, or `null` when it names none. */\nexport function payPrerequisiteOf(status: number, payload: unknown): PayPrerequisite | null {\n const code = (payload as { code?: unknown } | null | undefined)?.code\n return status === 412 && (code === 'card_required' || code === 'billing_address_required')\n ? code\n : null\n}\n\n/** Dollars and cents, for a line a buyer is about to agree to. */\nexport function formatPayCents(cents: number): string {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(cents / 100)\n}\n\n/** The saved method in words, wallets included: they carry an email, not a number. */\nexport function payCardLabel(card: PayQuoteCard | null): string {\n if (card?.last4) return `${(card.brand ?? card.type ?? 'card').toUpperCase()} •••• ${card.last4}`\n if (card?.email) return `${card.type ?? 'wallet'} · ${card.email}`\n return 'the card on file'\n}\n"],"names":["payPrerequisiteOf","status","payload","code","formatPayCents","cents","Intl","NumberFormat","style","currency","format","payCardLabel","card","last4","brand","type","toUpperCase","email"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,8DAA8D,GAuB9D,0EAA0E,GAC1E,OAAO,SAASA,kBAAkBC,MAAc,EAAEC,OAAgB;IAChE,MAAMC,OAAQD,2BAAD,AAACA,QAAmDC,IAAI;IACrE,OAAOF,WAAW,OAAQE,CAAAA,SAAS,mBAAmBA,SAAS,0BAAyB,IACpFA,OACA;AACN;AAEA,gEAAgE,GAChE,OAAO,SAASC,eAAeC,KAAa;IAC1C,OAAO,IAAIC,KAAKC,YAAY,CAAC,SAAS;QAAEC,OAAO;QAAYC,UAAU;IAAM,GAAGC,MAAM,CAACL,QAAQ;AAC/F;AAEA,oFAAoF,GACpF,OAAO,SAASM,aAAaC,IAAyB;QACxBA,MAAAA,aACDA;IAD3B,IAAIA,wBAAAA,KAAMC,KAAK,EAAE,OAAO,GAAG,EAACD,QAAAA,cAAAA,KAAKE,KAAK,YAAVF,cAAcA,KAAKG,IAAI,YAAvBH,OAA2B,QAAQI,WAAW,GAAG,MAAM,EAAEJ,KAAKC,KAAK,EAAE;IACjG,IAAID,wBAAAA,KAAMK,KAAK,EAAE,OAAO,IAAGL,aAAAA,KAAKG,IAAI,YAATH,aAAa,SAAS,GAAG,EAAEA,KAAKK,KAAK,EAAE;IAClE,OAAO;AACT"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Whether a seller is the platform itself, selling at a waived fee (AGL-4049).
|
|
19
|
+
*
|
|
20
|
+
* The platform's own org sells through the marketplace the same way any
|
|
21
|
+
* publisher does, with one difference that is not a privilege: there is nobody
|
|
22
|
+
* to pay. Its money already lands on the platform's account, so a Connect
|
|
23
|
+
* transfer would be the platform paying itself, and it never onboards Connect.
|
|
24
|
+
*
|
|
25
|
+
* BOTH HALVES, ALWAYS. The waiver is the existing staff override
|
|
26
|
+
* (`marketplaceFeePct: 0`) and the org is a server-side marker
|
|
27
|
+
* (`AGLYN_PLATFORM_ORG_ID`, read in `server/platform-org.ts`). A waiver on any
|
|
28
|
+
* other seller still pays that seller, and the platform's org at a real rate
|
|
29
|
+
* still needs a payout account. No client-writable field decides this.
|
|
30
|
+
*
|
|
31
|
+
* Pure, so the sale, the publish gate and their specs all ask one question.
|
|
32
|
+
*/
|
|
33
|
+
export declare function sellsAsPlatform(input: {
|
|
34
|
+
sellerOrgId: string;
|
|
35
|
+
feePercent: number;
|
|
36
|
+
platformOrgId: string | null | undefined;
|
|
37
|
+
}): boolean;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* Whether a seller is the platform itself, selling at a waived fee (AGL-4049).
|
|
18
|
+
*
|
|
19
|
+
* The platform's own org sells through the marketplace the same way any
|
|
20
|
+
* publisher does, with one difference that is not a privilege: there is nobody
|
|
21
|
+
* to pay. Its money already lands on the platform's account, so a Connect
|
|
22
|
+
* transfer would be the platform paying itself, and it never onboards Connect.
|
|
23
|
+
*
|
|
24
|
+
* BOTH HALVES, ALWAYS. The waiver is the existing staff override
|
|
25
|
+
* (`marketplaceFeePct: 0`) and the org is a server-side marker
|
|
26
|
+
* (`AGLYN_PLATFORM_ORG_ID`, read in `server/platform-org.ts`). A waiver on any
|
|
27
|
+
* other seller still pays that seller, and the platform's org at a real rate
|
|
28
|
+
* still needs a payout account. No client-writable field decides this.
|
|
29
|
+
*
|
|
30
|
+
* Pure, so the sale, the publish gate and their specs all ask one question.
|
|
31
|
+
*/ export function sellsAsPlatform(input) {
|
|
32
|
+
const { sellerOrgId, feePercent, platformOrgId } = input;
|
|
33
|
+
return feePercent === 0 && !!platformOrgId && sellerOrgId === platformOrgId;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=platform-seller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/model/platform-seller.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Whether a seller is the platform itself, selling at a waived fee (AGL-4049).\n *\n * The platform's own org sells through the marketplace the same way any\n * publisher does, with one difference that is not a privilege: there is nobody\n * to pay. Its money already lands on the platform's account, so a Connect\n * transfer would be the platform paying itself, and it never onboards Connect.\n *\n * BOTH HALVES, ALWAYS. The waiver is the existing staff override\n * (`marketplaceFeePct: 0`) and the org is a server-side marker\n * (`AGLYN_PLATFORM_ORG_ID`, read in `server/platform-org.ts`). A waiver on any\n * other seller still pays that seller, and the platform's org at a real rate\n * still needs a payout account. No client-writable field decides this.\n *\n * Pure, so the sale, the publish gate and their specs all ask one question.\n */\nexport function sellsAsPlatform(input: {\n sellerOrgId: string\n feePercent: number\n platformOrgId: string | null | undefined\n}): boolean {\n const { sellerOrgId, feePercent, platformOrgId } = input\n return feePercent === 0 && !!platformOrgId && sellerOrgId === platformOrgId\n}\n"],"names":["sellsAsPlatform","input","sellerOrgId","feePercent","platformOrgId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASA,gBAAgBC,KAI/B;IACC,MAAM,EAAEC,WAAW,EAAEC,UAAU,EAAEC,aAAa,EAAE,GAAGH;IACnD,OAAOE,eAAe,KAAK,CAAC,CAACC,iBAAiBF,gBAAgBE;AAChE"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { SiteStarterBuiltinReading } from '@aglyn/aglyn/app-utils/site-starter-theme';
|
|
18
|
+
/** The pages a starter publish captures, as counted before the click. */
|
|
19
|
+
export interface StarterPublishPages {
|
|
20
|
+
/** Pages in the site's routing that were saved at least once and not deleted. */
|
|
21
|
+
count: number;
|
|
22
|
+
/** Whether one of them is the Home page, at `/`. */
|
|
23
|
+
hasHome: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** One screen document, as the dialog reads it. */
|
|
26
|
+
export interface StarterPublishScreen {
|
|
27
|
+
$id: string;
|
|
28
|
+
versionId?: unknown;
|
|
29
|
+
deletedAt?: unknown;
|
|
30
|
+
[field: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The pages the door will capture: every page in the host's routing map
|
|
34
|
+
* (`screens`, id → slug) with a published version and not deleted, as the
|
|
35
|
+
* door walks them. An email screen is never in the routing map.
|
|
36
|
+
*/
|
|
37
|
+
export declare function starterPublishPages(routing: unknown, screens: readonly StarterPublishScreen[]): StarterPublishPages;
|
|
38
|
+
export type StarterPublishStepId = 'plan' | 'profile' | 'payouts' | 'home' | 'pages';
|
|
39
|
+
/** One thing that stands between this site and a starter publish. */
|
|
40
|
+
export interface StarterPublishStep {
|
|
41
|
+
id: StarterPublishStepId;
|
|
42
|
+
/** What is missing, in one sentence. */
|
|
43
|
+
text: string;
|
|
44
|
+
/** Where it is fixed, or `null` until the address is known. */
|
|
45
|
+
href: string | null;
|
|
46
|
+
/** The link's label: the page it opens. */
|
|
47
|
+
actionLabel: string;
|
|
48
|
+
}
|
|
49
|
+
/** What the dialog has read, each `undefined`/`null` while it is pending. */
|
|
50
|
+
export interface StarterPublishFacts {
|
|
51
|
+
/** The owning org's plan, as `usePlanFeature(hostId, 'marketplaceSelling')` resolves it. */
|
|
52
|
+
plan: {
|
|
53
|
+
ready: boolean;
|
|
54
|
+
locked: boolean;
|
|
55
|
+
planLabel?: string;
|
|
56
|
+
upgradeHref?: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* `publisherProfiles/{orgId}`: `undefined` while it loads or when it cannot
|
|
60
|
+
* be read, `null` when the org has none.
|
|
61
|
+
*/
|
|
62
|
+
profile: Record<string, unknown> | null | undefined;
|
|
63
|
+
/** The pages it will capture, or `null` while they load. */
|
|
64
|
+
pages: StarterPublishPages | null;
|
|
65
|
+
/** The price typed in the form, in whole dollars. */
|
|
66
|
+
priceUsd: number;
|
|
67
|
+
/**
|
|
68
|
+
* The server says this org is the platform at the staff fee waiver, so a
|
|
69
|
+
* price needs no payout account (AGL-4049). Only the server can say.
|
|
70
|
+
*/
|
|
71
|
+
sellsAsPlatform?: boolean;
|
|
72
|
+
/** `/{org}/marketplace`, or `null` until the org's address is known. */
|
|
73
|
+
marketplacePath: string | null;
|
|
74
|
+
/** The site's Pages list, or `null` until its address is known. */
|
|
75
|
+
pagesPath: string | null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Whether a profile's payout account can take a sale, as far as the console
|
|
79
|
+
* can tell: an account that takes charges. The door also checks which Stripe
|
|
80
|
+
* world the account belongs to, which only the server knows, and says so if
|
|
81
|
+
* that is what is missing.
|
|
82
|
+
*/
|
|
83
|
+
export declare const payoutsReady: (profile: Record<string, unknown>) => boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Every step that stands between this site and a starter publish, the setup
|
|
86
|
+
* steps first, then the site's own. A read still pending refuses nothing.
|
|
87
|
+
*/
|
|
88
|
+
export declare function starterPublishSteps(facts: StarterPublishFacts): StarterPublishStep[];
|
|
89
|
+
/**
|
|
90
|
+
* Where a setup refusal from the door (412) is fixed. The door's words are
|
|
91
|
+
* shown as they are; this picks the link beside them from what the dialog
|
|
92
|
+
* read: the plan, the profile, or payouts for a priced starter. With nothing
|
|
93
|
+
* read to say which, the publisher profile, where the other two are set up.
|
|
94
|
+
*/
|
|
95
|
+
export declare function starterRefusalLink(facts: StarterPublishFacts): {
|
|
96
|
+
href: string | null;
|
|
97
|
+
actionLabel: string;
|
|
98
|
+
};
|
|
99
|
+
/** The two theme shapes a publisher chooses between. */
|
|
100
|
+
export type StarterThemeChoice = 'builtin' | 'full';
|
|
101
|
+
/** What a starter publish adds to the listing form's body. */
|
|
102
|
+
export interface StarterPublishBody {
|
|
103
|
+
theme: SiteStarterBuiltinReading['theme'] | {
|
|
104
|
+
kind: 'full';
|
|
105
|
+
};
|
|
106
|
+
/** Always a base starter: publishers' AI starters are not offered yet. */
|
|
107
|
+
mode: 'base';
|
|
108
|
+
facets: {
|
|
109
|
+
kind: string | null;
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The starter half of the publish body, or `null` until it can be sent: no
|
|
114
|
+
* theme chosen (neither is chosen for the publisher), or the built-in chosen
|
|
115
|
+
* on a site that wears none. The full theme is the site's own, read by the
|
|
116
|
+
* door off the site itself.
|
|
117
|
+
*/
|
|
118
|
+
export declare function starterPublishBody(input: {
|
|
119
|
+
choice: StarterThemeChoice | null;
|
|
120
|
+
builtin: SiteStarterBuiltinReading | null;
|
|
121
|
+
kind: string | null;
|
|
122
|
+
}): StarterPublishBody | null;
|
|
123
|
+
/** What a publish captures, in a sentence, from the pages counted before the click. */
|
|
124
|
+
export declare function starterCapturesSentence(pages: StarterPublishPages): string;
|
|
125
|
+
/**
|
|
126
|
+
* The line under the built-in theme choice: what the starter would carry, or
|
|
127
|
+
* why this site has no built-in to name. `hostReady` is false while the site
|
|
128
|
+
* is still being read.
|
|
129
|
+
*/
|
|
130
|
+
export declare function starterBuiltinDetail(builtin: SiteStarterBuiltinReading | null, hostReady?: boolean): string;
|
|
131
|
+
/** What the built-in choice leaves behind, for a site whose fonts it cannot name, or `null`. */
|
|
132
|
+
export declare function starterFontsNote(builtin: SiteStarterBuiltinReading | null): string | null;
|
|
133
|
+
/**
|
|
134
|
+
* What "Publish as a starter" reads before the click, for a surface that
|
|
135
|
+
* cannot read it (AGL-4049): the native apps ask
|
|
136
|
+
* `GET /api/marketplace/starter-publish-facts`, and show these words.
|
|
137
|
+
*/
|
|
138
|
+
export interface StarterPublishFactsAnswer {
|
|
139
|
+
/** What the publish captures, in a sentence. */
|
|
140
|
+
captures: string;
|
|
141
|
+
/** The site's built-in theme as the publish body sends it, or `null` when it wears none. */
|
|
142
|
+
builtin: SiteStarterBuiltinReading['theme'] | null;
|
|
143
|
+
/** The line under the built-in choice. */
|
|
144
|
+
builtinDetail: string;
|
|
145
|
+
/** What the built-in choice leaves behind, or `null`. */
|
|
146
|
+
fontsNote: string | null;
|
|
147
|
+
/**
|
|
148
|
+
* What stands in the way, by step: a step left out is one that is done.
|
|
149
|
+
* `payouts` says what a price would need, whether or not one is typed yet.
|
|
150
|
+
*/
|
|
151
|
+
steps: Partial<Record<StarterPublishStepId, string>>;
|
|
152
|
+
/** Whether a step stands in the way of a free publish too, so Publish is off. */
|
|
153
|
+
blocked: boolean;
|
|
154
|
+
}
|
|
155
|
+
/** The answer, from what the door would read. Pure: the route hands in what it read. */
|
|
156
|
+
export declare function starterPublishFactsAnswer(input: {
|
|
157
|
+
facts: Omit<StarterPublishFacts, 'priceUsd'>;
|
|
158
|
+
builtin: SiteStarterBuiltinReading | null;
|
|
159
|
+
}): StarterPublishFactsAnswer;
|