mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-14 06:47:28 +00:00
feat(web): move Hire Us pricing to $400/hour metered billing (PRD 0002)
Replaces the $250/week retainer with a $400/hour rate billed against actual hours, invoiced through CoinPay after the client approves them. A 10-hour minimum engagement replaces the week as the unit of commitment. The weekly price lived in 12 places, not the 3 the PRD listed: the front-page Hire Us section, the Top-Level Pages list, /hire-us metadata, /pricing (metadata, two FAQ answers, rate bullet), /about, llms.txt, skill.md, and the Hire Us form success message. Metered billing rather than a committed weekly block, because the old "recurring CoinPay invoice" copy documented a mechanic that never existed: /api/payments/create makes a single one-shot payment, not a subscription. - coinpay-checkout derives amount_usd from hours x 400 instead of a hardcoded 250, validates hours as quarter-hour increments at or above the minimum, and returns 422 before calling CoinPay on bad input. Payment metadata carries billing/hours/rate_usd_per_hour in place of interval. - project-request returns a rate, billing mode, and minimum; no amount exists until hours are approved. - CoinPay config block documents COINPAY_RATE_USD_PER_HOUR / COINPAY_BILLING / COINPAY_MINIMUM_HOURS instead of a weekly amount and interval. - New real /terms route replacing the SPA stub: what is billable, the approve-then-invoice flow, the minimum, cancellation on one week's notice, and an explicit clause that existing engagements keep their terms until both sides agree in writing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
da5f6f8381
commit
5aae78bfbc
15 changed files with 444 additions and 42 deletions
|
|
@ -173,18 +173,20 @@ describe("POST /api/hire-us/coinpay-checkout", () => {
|
||||||
expect(createCall?.[1]?.headers).toMatchObject({ authorization: "Bearer cp_test_key" });
|
expect(createCall?.[1]?.headers).toMatchObject({ authorization: "Bearer cp_test_key" });
|
||||||
expect(createBody).toMatchObject({
|
expect(createBody).toMatchObject({
|
||||||
business_id: "business-123",
|
business_id: "business-123",
|
||||||
amount_usd: 250,
|
amount_usd: 4000,
|
||||||
payment_method: "both",
|
payment_method: "both",
|
||||||
currency: "usdc_pol",
|
currency: "usdc_pol",
|
||||||
blockchain: "USDC_POL",
|
blockchain: "USDC_POL",
|
||||||
description: "LogicSRC Hire Us - $250/week",
|
description: "LogicSRC Hire Us - 10h @ $400/hour",
|
||||||
success_url: "https://logicsrc.test/hire-us?payment=success",
|
success_url: "https://logicsrc.test/hire-us?payment=success",
|
||||||
cancel_url: "https://logicsrc.test/hire-us?payment=cancelled",
|
cancel_url: "https://logicsrc.test/hire-us?payment=cancelled",
|
||||||
redirect_url: "https://logicsrc.test/hire-us?payment=coinpay",
|
redirect_url: "https://logicsrc.test/hire-us?payment=coinpay",
|
||||||
webhook_url: "https://logicsrc.test/api/webhooks/coinpay",
|
webhook_url: "https://logicsrc.test/api/webhooks/coinpay",
|
||||||
metadata: {
|
metadata: {
|
||||||
product: "logicsrc-hire-us",
|
product: "logicsrc-hire-us",
|
||||||
interval: "week",
|
billing: "metered_hours",
|
||||||
|
hours: 10,
|
||||||
|
rate_usd_per_hour: 400,
|
||||||
source: "logicsrc.com/hire-us",
|
source: "logicsrc.com/hire-us",
|
||||||
buyer_email: "buyer@example.com"
|
buyer_email: "buyer@example.com"
|
||||||
}
|
}
|
||||||
|
|
@ -241,7 +243,71 @@ describe("POST /api/hire-us/coinpay-checkout", () => {
|
||||||
const body = await response.json();
|
const body = await response.json();
|
||||||
|
|
||||||
expect(response.status).toBe(201);
|
expect(response.status).toBe(201);
|
||||||
expect(body.payment.amount_usd).toBe(250);
|
expect(body.payment.amount_usd).toBe(4000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prices the checkout from the approved hours", async () => {
|
||||||
|
process.env.COINPAY_API_KEY = "cp_test_key";
|
||||||
|
process.env.COINPAY_API_URL = "https://coinpayportal.example";
|
||||||
|
process.env.COINPAY_BUSINESS_ID = "business-123";
|
||||||
|
process.env.COINPAY_ELIGIBILITY_MERCHANT_ID = "merchant-123";
|
||||||
|
process.env.COINPAY_HIRE_US_BLOCKCHAIN = "USDC_POL";
|
||||||
|
|
||||||
|
const fetchMock = vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => {
|
||||||
|
const url = typeof input === "string" ? input : input.toString();
|
||||||
|
if (url.includes("/api/payments/merchant-eligibility")) {
|
||||||
|
return jsonResponse({ success: true, accepts_card: true, accepts_crypto: true, chains: ["USDC_POL"] });
|
||||||
|
}
|
||||||
|
return jsonResponse({ success: true, payment: { id: "pay_123", amount_usd: 10100 } }, 201);
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await coinpayCheckout(
|
||||||
|
new NextRequest("http://localhost/api/hire-us/coinpay-checkout", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
body: JSON.stringify({ hours: 25.25 })
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const body = await response.json();
|
||||||
|
|
||||||
|
const createCall = fetchMock.mock.calls.find(([input]) =>
|
||||||
|
(typeof input === "string" ? input : input.toString()).includes("/api/payments/create")
|
||||||
|
);
|
||||||
|
const createBody = JSON.parse((createCall?.[1]?.body as string) ?? "{}");
|
||||||
|
|
||||||
|
expect(response.status).toBe(201);
|
||||||
|
expect(createBody.amount_usd).toBe(10100);
|
||||||
|
expect(createBody.description).toBe("LogicSRC Hire Us - 25.25h @ $400/hour");
|
||||||
|
expect(createBody.metadata).toMatchObject({ billing: "metered_hours", hours: 25.25, rate_usd_per_hour: 400 });
|
||||||
|
expect(body.payment).toMatchObject({ amount_usd: 10100, hours: 25.25, rate_usd_per_hour: 400 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects hours below the minimum engagement or off the quarter-hour", async () => {
|
||||||
|
process.env.COINPAY_API_KEY = "cp_test_key";
|
||||||
|
process.env.COINPAY_API_URL = "https://coinpayportal.example";
|
||||||
|
process.env.COINPAY_BUSINESS_ID = "business-123";
|
||||||
|
process.env.COINPAY_ELIGIBILITY_MERCHANT_ID = "merchant-123";
|
||||||
|
|
||||||
|
const fetchMock = vi.spyOn(globalThis, "fetch");
|
||||||
|
|
||||||
|
for (const hours of [9.75, 12.3, "many", -40]) {
|
||||||
|
const response = await coinpayCheckout(
|
||||||
|
new NextRequest("http://localhost/api/hire-us/coinpay-checkout", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
body: JSON.stringify({ hours })
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const body = await response.json();
|
||||||
|
|
||||||
|
expect(response.status).toBe(422);
|
||||||
|
expect(body).toEqual({
|
||||||
|
success: false,
|
||||||
|
error: "Approved hours must be a quarter-hour increment of at least 10"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(fetchMock).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not create checkout when no payment rail is available", async () => {
|
it("does not create checkout when no payment rail is available", async () => {
|
||||||
|
|
@ -290,7 +356,13 @@ describe("POST /api/hire-us/project-request", () => {
|
||||||
expect(response.status).toBe(202);
|
expect(response.status).toBe(202);
|
||||||
expect(body).toMatchObject({
|
expect(body).toMatchObject({
|
||||||
success: true,
|
success: true,
|
||||||
request: { status: "pending_acceptance", amount_usd: 250, interval: "week", invoice: "created_after_acceptance" }
|
request: {
|
||||||
|
status: "pending_acceptance",
|
||||||
|
rate_usd_per_hour: 400,
|
||||||
|
billing: "metered_hours",
|
||||||
|
minimum_hours: 10,
|
||||||
|
invoice: "created_after_acceptance"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
expect(body.request.id).toMatch(/^hire_/);
|
expect(body.request.id).toMatch(/^hire_/);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,17 @@ test.describe("LogicSRC PWA", () => {
|
||||||
await page.goto("/hire-us");
|
await page.goto("/hire-us");
|
||||||
|
|
||||||
await expect(page.getByRole("heading", { name: "Hire Us", exact: true })).toBeVisible();
|
await expect(page.getByRole("heading", { name: "Hire Us", exact: true })).toBeVisible();
|
||||||
await expect(page.locator(".price-row strong", { hasText: "$250" })).toBeVisible();
|
await expect(page.locator(".price-row strong", { hasText: "$400" })).toBeVisible();
|
||||||
await expect(page.getByText("per week")).toBeVisible();
|
await expect(page.getByText("per hour")).toBeVisible();
|
||||||
|
await expect(page.getByText("Ten-hour minimum engagement")).toBeVisible();
|
||||||
await expect(page.getByText("open infrastructure and open specs for AI agent systems")).toBeVisible();
|
await expect(page.getByText("open infrastructure and open specs for AI agent systems")).toBeVisible();
|
||||||
await expect(page.getByRole("button", { name: "Request review" })).toBeVisible();
|
await expect(page.getByRole("button", { name: "Request review" })).toBeVisible();
|
||||||
await expect(page.getByPlaceholder("you@example.com")).toBeVisible();
|
await expect(page.getByPlaceholder("you@example.com")).toBeVisible();
|
||||||
await expect(page.getByPlaceholder("Describe the agent workflow")).toBeVisible();
|
await expect(page.getByPlaceholder("Describe the agent workflow")).toBeVisible();
|
||||||
await expect(page.getByText("without exposing merchant credentials to the browser")).toBeVisible();
|
await expect(page.getByText("without exposing merchant credentials to the browser")).toBeVisible();
|
||||||
await expect(page.getByText("COINPAY_PRODUCT=logicsrc-hire-us")).toBeVisible();
|
await expect(page.getByText("COINPAY_PRODUCT=logicsrc-hire-us")).toBeVisible();
|
||||||
|
await expect(page.getByText("COINPAY_RATE_USD_PER_HOUR=400")).toBeVisible();
|
||||||
|
await expect(page.getByText("COINPAY_BILLING=metered_hours")).toBeVisible();
|
||||||
await expect(page.getByText("COINPAY_STATUS=pending_acceptance")).toBeVisible();
|
await expect(page.getByText("COINPAY_STATUS=pending_acceptance")).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,9 @@ import { HomeInteractivity } from "@/components/home-interactivity";
|
||||||
// scrolled to the matching section. We preserve those URLs (they are canonical
|
// scrolled to the matching section. We preserve those URLs (they are canonical
|
||||||
// in sitemap.xml) by rendering the same page for each known route and 404ing
|
// in sitemap.xml) by rendering the same page for each known route and 404ing
|
||||||
// anything else.
|
// anything else.
|
||||||
// /about and /docs are now real routes (app/about, app/docs); the rest still
|
// /about, /docs, /pricing, and /terms are now real routes (app/about, app/docs,
|
||||||
// render the homepage SPA scrolled to their section.
|
// app/pricing, app/terms); the rest still render the homepage SPA scrolled to
|
||||||
|
// their section.
|
||||||
const ROUTE_META: Record<string, { title: string; description: string }> = {
|
const ROUTE_META: Record<string, { title: string; description: string }> = {
|
||||||
openspec: {
|
openspec: {
|
||||||
title: "LogicSRC vs OpenSpec.dev · LogicSRC",
|
title: "LogicSRC vs OpenSpec.dev · LogicSRC",
|
||||||
|
|
@ -21,9 +22,8 @@ const ROUTE_META: Record<string, { title: string; description: string }> = {
|
||||||
},
|
},
|
||||||
"hire-us": {
|
"hire-us": {
|
||||||
title: "Hire Us · LogicSRC",
|
title: "Hire Us · LogicSRC",
|
||||||
description: "Implementation help for LogicSRC, AgentSwarm, and Credential Sharing at $250/week for accepted work, paid via CoinPay.",
|
description: "Implementation help for LogicSRC, AgentSwarm, and Credential Sharing at $400/hour for accepted work, paid via CoinPay.",
|
||||||
},
|
},
|
||||||
terms: { title: "Terms · LogicSRC", description: "LogicSRC terms of use." },
|
|
||||||
privacy: { title: "Privacy · LogicSRC", description: "LogicSRC privacy notes." },
|
privacy: { title: "Privacy · LogicSRC", description: "LogicSRC privacy notes." },
|
||||||
"agent-swarm": {
|
"agent-swarm": {
|
||||||
title: "AgentSwarm · LogicSRC",
|
title: "AgentSwarm · LogicSRC",
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ export default function AboutPage(): ReactNode {
|
||||||
|
|
||||||
<h3>Work with us</h3>
|
<h3>Work with us</h3>
|
||||||
<p>
|
<p>
|
||||||
Profullstack implements LogicSRC-based systems at $250/week for
|
Profullstack implements LogicSRC-based systems at $400/hour for
|
||||||
accepted work, paid via CoinPay. See <a href="/hire-us">Hire Us</a>.
|
accepted work, paid via CoinPay. See <a href="/hire-us">Hire Us</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,23 @@ import { choosePaymentRail, fetchMerchantEligibility, parseJson } from "@/lib/co
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
// POST /api/hire-us/coinpay-checkout — create a $250/week CoinPay checkout for
|
// POST /api/hire-us/coinpay-checkout — create a CoinPay checkout for approved
|
||||||
// the Hire Us plan, choosing card/crypto/both based on merchant eligibility.
|
// Hire Us hours at $400/hour, choosing card/crypto/both based on merchant
|
||||||
|
// eligibility. Billing is metered: the caller supplies the approved hours and the
|
||||||
|
// amount is derived from them, never a fixed recurring figure.
|
||||||
|
const RATE_USD_PER_HOUR = 400;
|
||||||
|
const MINIMUM_HOURS = 10;
|
||||||
|
|
||||||
|
// Hours are quoted in quarter-hour increments; anything finer is a rounding
|
||||||
|
// artifact rather than a real billing unit.
|
||||||
|
function parseHours(value: unknown): number | null {
|
||||||
|
const hours = typeof value === "number" ? value : Number(value);
|
||||||
|
if (!Number.isFinite(hours) || hours < MINIMUM_HOURS) return null;
|
||||||
|
const quarters = Math.round(hours * 4);
|
||||||
|
if (Math.abs(hours * 4 - quarters) > 1e-9) return null;
|
||||||
|
return quarters / 4;
|
||||||
|
}
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
const apiKey = process.env.COINPAY_API_KEY;
|
const apiKey = process.env.COINPAY_API_KEY;
|
||||||
const eligibilityApiKey = process.env.COINPAY_ELIGIBILITY_API_KEY || process.env.COINPAY_AGENT_API_KEY || apiKey;
|
const eligibilityApiKey = process.env.COINPAY_ELIGIBILITY_API_KEY || process.env.COINPAY_AGENT_API_KEY || apiKey;
|
||||||
|
|
@ -22,6 +37,19 @@ export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const body = (await request.json().catch(() => ({}))) as Record<string, unknown>;
|
const body = (await request.json().catch(() => ({}))) as Record<string, unknown>;
|
||||||
const buyerEmail = typeof body.email === "string" ? body.email.trim().slice(0, 160) : "";
|
const buyerEmail = typeof body.email === "string" ? body.email.trim().slice(0, 160) : "";
|
||||||
|
const hours = body.hours === undefined ? MINIMUM_HOURS : parseHours(body.hours);
|
||||||
|
|
||||||
|
if (hours === null) {
|
||||||
|
return json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
error: `Approved hours must be a quarter-hour increment of at least ${MINIMUM_HOURS}`
|
||||||
|
},
|
||||||
|
422
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const amountUsdDue = Math.round(hours * RATE_USD_PER_HOUR * 100) / 100;
|
||||||
const eligibility = await fetchMerchantEligibility(apiUrl, eligibilityApiKey, eligibilityMerchantId);
|
const eligibility = await fetchMerchantEligibility(apiUrl, eligibilityApiKey, eligibilityMerchantId);
|
||||||
const paymentRail = choosePaymentRail(eligibility, blockchain);
|
const paymentRail = choosePaymentRail(eligibility, blockchain);
|
||||||
|
|
||||||
|
|
@ -37,18 +65,20 @@ export async function POST(request: NextRequest) {
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
business_id: businessId,
|
business_id: businessId,
|
||||||
amount_usd: 250,
|
amount_usd: amountUsdDue,
|
||||||
payment_method: paymentRail.method,
|
payment_method: paymentRail.method,
|
||||||
currency: paymentRail.currency,
|
currency: paymentRail.currency,
|
||||||
...(paymentRail.blockchain ? { blockchain: paymentRail.blockchain } : {}),
|
...(paymentRail.blockchain ? { blockchain: paymentRail.blockchain } : {}),
|
||||||
description: "LogicSRC Hire Us - $250/week",
|
description: `LogicSRC Hire Us - ${hours}h @ $${RATE_USD_PER_HOUR}/hour`,
|
||||||
success_url: `${publicUrl}/hire-us?payment=success`,
|
success_url: `${publicUrl}/hire-us?payment=success`,
|
||||||
cancel_url: `${publicUrl}/hire-us?payment=cancelled`,
|
cancel_url: `${publicUrl}/hire-us?payment=cancelled`,
|
||||||
redirect_url: `${publicUrl}/hire-us?payment=coinpay`,
|
redirect_url: `${publicUrl}/hire-us?payment=coinpay`,
|
||||||
webhook_url: `${publicUrl}/api/webhooks/coinpay`,
|
webhook_url: `${publicUrl}/api/webhooks/coinpay`,
|
||||||
metadata: {
|
metadata: {
|
||||||
product: "logicsrc-hire-us",
|
product: "logicsrc-hire-us",
|
||||||
interval: "week",
|
billing: "metered_hours",
|
||||||
|
hours,
|
||||||
|
rate_usd_per_hour: RATE_USD_PER_HOUR,
|
||||||
source: "logicsrc.com/hire-us",
|
source: "logicsrc.com/hire-us",
|
||||||
...(buyerEmail ? { buyer_email: buyerEmail } : {})
|
...(buyerEmail ? { buyer_email: buyerEmail } : {})
|
||||||
}
|
}
|
||||||
|
|
@ -70,13 +100,15 @@ export async function POST(request: NextRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const payment = (payload.payment as Record<string, unknown>) || {};
|
const payment = (payload.payment as Record<string, unknown>) || {};
|
||||||
const amountUsd = Number(payment.amount_usd ?? payment.amount ?? 250);
|
const amountUsd = Number(payment.amount_usd ?? payment.amount ?? amountUsdDue);
|
||||||
return json(
|
return json(
|
||||||
{
|
{
|
||||||
success: true,
|
success: true,
|
||||||
payment: {
|
payment: {
|
||||||
id: payment.id,
|
id: payment.id,
|
||||||
amount_usd: Number.isFinite(amountUsd) ? amountUsd : 250,
|
amount_usd: Number.isFinite(amountUsd) ? amountUsd : amountUsdDue,
|
||||||
|
hours,
|
||||||
|
rate_usd_per_hour: RATE_USD_PER_HOUR,
|
||||||
payment_method: payment.stripe_checkout_url ? "card" : paymentRail.method,
|
payment_method: payment.stripe_checkout_url ? "card" : paymentRail.method,
|
||||||
currency: payment.currency ?? payment.blockchain ?? paymentRail.blockchain ?? paymentRail.currency,
|
currency: payment.currency ?? payment.blockchain ?? paymentRail.blockchain ?? paymentRail.currency,
|
||||||
crypto_amount: payment.amount_crypto ?? payment.crypto_amount ?? null,
|
crypto_amount: payment.amount_crypto ?? payment.crypto_amount ?? null,
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,12 @@ import { json } from "@/lib/http";
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
// POST /api/hire-us/project-request — accept a Hire Us project request before a
|
// POST /api/hire-us/project-request — accept a Hire Us project request before any
|
||||||
// recurring CoinPay invoice is created (invoice is created after acceptance).
|
// CoinPay invoice is created. Hire Us bills metered hours at $400/hour, so there is
|
||||||
|
// no amount until we accept the project and hours are approved.
|
||||||
|
const RATE_USD_PER_HOUR = 400;
|
||||||
|
const MINIMUM_HOURS = 10;
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const body = (await request.json().catch(() => ({}))) as Record<string, unknown>;
|
const body = (await request.json().catch(() => ({}))) as Record<string, unknown>;
|
||||||
|
|
@ -20,7 +24,7 @@ export async function POST(request: NextRequest) {
|
||||||
id: requestId,
|
id: requestId,
|
||||||
contact,
|
contact,
|
||||||
project_length: project.length,
|
project_length: project.length,
|
||||||
plan: "250/week",
|
plan: "400/hour",
|
||||||
invoice: "pending_acceptance"
|
invoice: "pending_acceptance"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -30,8 +34,9 @@ export async function POST(request: NextRequest) {
|
||||||
request: {
|
request: {
|
||||||
id: requestId,
|
id: requestId,
|
||||||
status: "pending_acceptance",
|
status: "pending_acceptance",
|
||||||
amount_usd: 250,
|
rate_usd_per_hour: RATE_USD_PER_HOUR,
|
||||||
interval: "week",
|
billing: "metered_hours",
|
||||||
|
minimum_hours: MINIMUM_HOURS,
|
||||||
invoice: "created_after_acceptance"
|
invoice: "created_after_acceptance"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export function GET(): Response {
|
||||||
## Company & legal
|
## Company & legal
|
||||||
|
|
||||||
- [About](${SITE_URL}/about): What LogicSRC is and who maintains it (Profullstack, Inc.).
|
- [About](${SITE_URL}/about): What LogicSRC is and who maintains it (Profullstack, Inc.).
|
||||||
- [Hire Us](${SITE_URL}/hire-us): Implementation help at $250/week for accepted LogicSRC work.
|
- [Hire Us](${SITE_URL}/hire-us): Implementation help at $400/hour for accepted LogicSRC work.
|
||||||
- [Terms](${SITE_URL}/terms)
|
- [Terms](${SITE_URL}/terms)
|
||||||
- [Privacy](${SITE_URL}/privacy)
|
- [Privacy](${SITE_URL}/privacy)
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { SiteShell } from "@/components/site-shell";
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Pricing · LogicSRC",
|
title: "Pricing · LogicSRC",
|
||||||
description:
|
description:
|
||||||
"LogicSRC the open specification, schemas, SDKs, and CLI are free and open source. Implementation help is $250/week for accepted work, paid via CoinPay.",
|
"LogicSRC the open specification, schemas, SDKs, and CLI are free and open source. Implementation help is $400/hour for accepted work, paid via CoinPay.",
|
||||||
alternates: { canonical: "/pricing" },
|
alternates: { canonical: "/pricing" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -16,7 +16,7 @@ const FAQ: Array<{ q: string; a: string }> = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
q: "How does pricing work?",
|
q: "How does pricing work?",
|
||||||
a: "The standard is free. If you want Profullstack to build a LogicSRC-based system for you, implementation work is billed at $250/week for accepted work, paid via a CoinPay recurring invoice.",
|
a: "The standard is free. If you want Profullstack to build a LogicSRC-based system for you, implementation work is billed at $400/hour against actual hours worked, invoiced through CoinPay after you approve them. The minimum engagement is 10 hours.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
q: "Who is LogicSRC for?",
|
q: "Who is LogicSRC for?",
|
||||||
|
|
@ -24,7 +24,7 @@ const FAQ: Array<{ q: string; a: string }> = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
q: "How do I pay or get started?",
|
q: "How do I pay or get started?",
|
||||||
a: "Read the docs and adopt the schemas for free, or submit a project through the Hire Us form. Accepted projects are invoiced weekly via CoinPay.",
|
a: "Read the docs and adopt the schemas for free, or submit a project through the Hire Us form. Accepted projects are invoiced for approved hours via CoinPay.",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -61,9 +61,11 @@ export default function PricingPage(): ReactNode {
|
||||||
CLI, TUI, and reference plugins are open source.
|
CLI, TUI, and reference plugins are open source.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Implementation — $250/week.</strong> Profullstack builds
|
<strong>Implementation — $400/hour.</strong> Profullstack builds
|
||||||
LogicSRC-based systems for accepted projects, billed weekly via
|
LogicSRC-based systems for accepted projects, billed against actual
|
||||||
CoinPay. See <a href="/hire-us">Hire Us</a>.
|
hours worked and invoiced via CoinPay once you approve them.
|
||||||
|
Ten-hour minimum engagement. See <a href="/hire-us">Hire Us</a> and{" "}
|
||||||
|
<a href="/terms">Terms</a>.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ Base URL: ${SITE_URL}
|
||||||
- Read the LogicSRC coordination schemas and conventions.
|
- Read the LogicSRC coordination schemas and conventions.
|
||||||
- Compare LogicSRC with OpenSpec.dev.
|
- Compare LogicSRC with OpenSpec.dev.
|
||||||
- Request paid implementation help via the Hire Us flow (${SITE_URL}/hire-us),
|
- Request paid implementation help via the Hire Us flow (${SITE_URL}/hire-us),
|
||||||
billed at $250/week for accepted work and paid through CoinPay.
|
billed at $400/hour for accepted work and paid through CoinPay.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|
|
||||||
128
apps/logicsrc-web/src/app/terms/page.tsx
Normal file
128
apps/logicsrc-web/src/app/terms/page.tsx
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import type { Metadata } from "next";
|
||||||
|
import { SiteShell } from "@/components/site-shell";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Terms · LogicSRC",
|
||||||
|
description:
|
||||||
|
"Terms of engagement for LogicSRC: the specification and tooling are open source and free; Profullstack implementation work is billed at $400/hour against approved hours, with a 10-hour minimum.",
|
||||||
|
alternates: { canonical: "/terms" },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function TermsPage(): ReactNode {
|
||||||
|
return (
|
||||||
|
<SiteShell active="Terms">
|
||||||
|
<article className="band" style={{ maxWidth: "48rem" }}>
|
||||||
|
<div className="section-head">
|
||||||
|
<h2>Terms</h2>
|
||||||
|
<p>
|
||||||
|
Two separate things live on this site: an open standard anyone may
|
||||||
|
use, and paid implementation work from Profullstack, Inc. These terms
|
||||||
|
cover both.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="blog-content" style={{ lineHeight: 1.7, marginTop: "1.5rem" }}>
|
||||||
|
<h3>The standard is free</h3>
|
||||||
|
<p>
|
||||||
|
The LogicSRC specification, JSON schemas, SDKs, CLI, TUI, and
|
||||||
|
reference plugins are open source under the project license. There is
|
||||||
|
no license fee, no per-seat charge, and no obligation to hire us in
|
||||||
|
order to implement the standard. Nothing on this page restricts your
|
||||||
|
use of the spec.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Rate</h3>
|
||||||
|
<p>
|
||||||
|
Profullstack implementation work is billed at{" "}
|
||||||
|
<strong>$400 per hour</strong>. One rate applies to all
|
||||||
|
implementation work — specs, CLIs, SDKs, MCP resources, APIs, PWAs,
|
||||||
|
and provider-neutral plugin surfaces. There are no tiers, role-based
|
||||||
|
rates, or volume discounts.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>What is billable</h3>
|
||||||
|
<p>
|
||||||
|
Billable hours are hours spent on your project: design, spec work,
|
||||||
|
implementation, review, debugging, integration, and deployment, plus
|
||||||
|
meetings and written communication about the work. Time is recorded
|
||||||
|
in quarter-hour increments.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The following are not billed: the initial scoping conversation,
|
||||||
|
preparing your invoice, and time spent fixing defects in work we have
|
||||||
|
already delivered and you have already paid for.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>How you are invoiced</h3>
|
||||||
|
<p>
|
||||||
|
Billing is metered against actual hours worked, not a subscription.
|
||||||
|
After we accept a project, we send you a record of hours worked. Once
|
||||||
|
you approve those hours, we issue a CoinPay invoice for exactly that
|
||||||
|
amount at $400/hour. You are never charged for hours you have not
|
||||||
|
seen and approved.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Invoices are payable on receipt via CoinPay, by card or by supported
|
||||||
|
cryptocurrency. Work may pause on invoices unpaid after 14 days.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Minimum engagement</h3>
|
||||||
|
<p>
|
||||||
|
The minimum engagement is <strong>10 hours</strong>. Engagements
|
||||||
|
smaller than this do not cover the cost of scoping and context, so we
|
||||||
|
will decline them rather than quote them.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Cancellation</h3>
|
||||||
|
<p>
|
||||||
|
Either side may end an engagement at any time with one week's
|
||||||
|
written notice. You pay for hours already worked and approved,
|
||||||
|
including hours worked during the notice period; nothing further is
|
||||||
|
owed. We do not bill a cancellation fee and we do not hold unused
|
||||||
|
committed hours. Work product produced by hours you have paid for is
|
||||||
|
yours to keep.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Existing engagements</h3>
|
||||||
|
<p>
|
||||||
|
Clients engaged under a prior pricing model keep their existing terms
|
||||||
|
until both sides agree in writing to move to the hourly rate. This
|
||||||
|
page does not reprice work already underway.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Estimates</h3>
|
||||||
|
<p>
|
||||||
|
Any estimate of total hours is an estimate, not a fixed-price quote.
|
||||||
|
If the work looks likely to exceed an estimate we will tell you
|
||||||
|
before the additional hours are worked, so you can rescope or stop.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Acceptable use</h3>
|
||||||
|
<p>
|
||||||
|
We build auditable, portable systems in the open. We decline work
|
||||||
|
intended to deceive users, evade legal obligations, or produce
|
||||||
|
systems that cannot be inspected by the people they affect.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Reference implementations</h3>
|
||||||
|
<p>
|
||||||
|
Reference implementations published under the LogicSRC project exist
|
||||||
|
to prove the specification is usable. They are provided as-is,
|
||||||
|
without warranty, and are not a hosted product or a support
|
||||||
|
commitment. Paid engagements are governed by the terms above, not by
|
||||||
|
the license of any reference implementation.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Questions</h3>
|
||||||
|
<p>
|
||||||
|
Submit a project through the <a href="/hire-us">Hire Us</a> form, or
|
||||||
|
see <a href="/pricing">Pricing</a> for a summary. These terms may
|
||||||
|
change; the version on this page at the time your engagement starts
|
||||||
|
is the one that applies to it.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</SiteShell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -45,7 +45,7 @@ export function HomeInteractivity(): null {
|
||||||
}
|
}
|
||||||
|
|
||||||
result.replaceChildren(
|
result.replaceChildren(
|
||||||
buildParagraph("Request received. If it is a fit, we will send a $250/week recurring CoinPay invoice.")
|
buildParagraph("Request received. If it is a fit, we will scope the work and invoice approved hours at $400/hour via CoinPay.")
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
result.replaceChildren(
|
result.replaceChildren(
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,9 @@ const pages = [
|
||||||
{ id: "blog", title: "Blog", detail: "Project notes for LogicSRC, AgentSwarm, AgentByte, OpenSpec workflows, and reference implementations." },
|
{ id: "blog", title: "Blog", detail: "Project notes for LogicSRC, AgentSwarm, AgentByte, OpenSpec workflows, and reference implementations." },
|
||||||
{ id: "openspec", title: "OpenSpec", detail: "Comparison and compatibility notes for OpenSpec.dev-style repo-local specs, proposals, tasks, and deltas." },
|
{ id: "openspec", title: "OpenSpec", detail: "Comparison and compatibility notes for OpenSpec.dev-style repo-local specs, proposals, tasks, and deltas." },
|
||||||
{ id: "credential-sharing", title: "Credential Sharing", detail: "Open replacement architecture for portable secret sync across .env, Doppler, Railway variables, GitHub Secrets, and future providers." },
|
{ id: "credential-sharing", title: "Credential Sharing", detail: "Open replacement architecture for portable secret sync across .env, Doppler, Railway variables, GitHub Secrets, and future providers." },
|
||||||
{ id: "hire-us", title: "Hire Us", detail: "$250/week LogicSRC work on open infrastructure, specs, AI agent workflows, and reference implementations paid through CoinPay after project acceptance." },
|
{ id: "hire-us", title: "Hire Us", detail: "$400/hour LogicSRC work on open infrastructure, specs, AI agent workflows, and reference implementations paid through CoinPay after project acceptance." },
|
||||||
{ id: "about", title: "About", detail: "LogicSRC is the Profullstack open specification project for human and AI agent coordination." },
|
{ id: "about", title: "About", detail: "LogicSRC is the Profullstack open specification project for human and AI agent coordination." },
|
||||||
{ id: "terms", title: "Terms", detail: "Draft terms will cover acceptable use, reference implementation boundaries, and hosted-product responsibilities." },
|
{ id: "terms", title: "Terms", detail: "Terms of engagement: the $400/hour rate, what is billable, how approved hours are invoiced, the 10-hour minimum, cancellation, acceptable use, and reference implementation boundaries." },
|
||||||
{ id: "privacy", title: "Privacy", detail: "Draft privacy notes will cover telemetry, audit events, identity data, and hosted-product data boundaries." }
|
{ id: "privacy", title: "Privacy", detail: "Draft privacy notes will cover telemetry, audit events, identity data, and hosted-product data boundaries." }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -358,7 +358,7 @@ logicsrc credentials rollback --run <runId></code></pre>
|
||||||
<section id="hire-us" class="band hire-us">
|
<section id="hire-us" class="band hire-us">
|
||||||
<div class="section-head">
|
<div class="section-head">
|
||||||
<h2>Hire Us</h2>
|
<h2>Hire Us</h2>
|
||||||
<p>$250/week for accepted LogicSRC work using open infrastructure and open specs for AI agent systems.</p>
|
<p>$400/hour for accepted LogicSRC work using open infrastructure and open specs for AI agent systems.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="hire-layout">
|
<div class="hire-layout">
|
||||||
<article class="hire-panel">
|
<article class="hire-panel">
|
||||||
|
|
@ -366,9 +366,10 @@ logicsrc credentials rollback --run <runId></code></pre>
|
||||||
<h3>Open-spec AI agent implementation help</h3>
|
<h3>Open-spec AI agent implementation help</h3>
|
||||||
<p>Hire us to turn agent ideas into portable LogicSRC specs, CLIs, SDKs, MCP resources, PWAs, APIs, and provider-neutral plugin workflows. We prioritize auditable contracts, repo-local artifacts, and integrations that can move between model providers and infrastructure.</p>
|
<p>Hire us to turn agent ideas into portable LogicSRC specs, CLIs, SDKs, MCP resources, PWAs, APIs, and provider-neutral plugin workflows. We prioritize auditable contracts, repo-local artifacts, and integrations that can move between model providers and infrastructure.</p>
|
||||||
<div class="price-row">
|
<div class="price-row">
|
||||||
<strong>$250</strong>
|
<strong>$400</strong>
|
||||||
<span>per week</span>
|
<span>per hour</span>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="price-terms">Billed against actual hours worked, invoiced after you approve them. Ten-hour minimum engagement; cancel any time with one week’s notice and pay only for hours already approved. See <a href="/terms">Terms</a>.</p>
|
||||||
<form id="project-request-form" class="project-request-form">
|
<form id="project-request-form" class="project-request-form">
|
||||||
<label>
|
<label>
|
||||||
<span>Contact</span>
|
<span>Contact</span>
|
||||||
|
|
@ -395,12 +396,13 @@ logicsrc credentials rollback --run <runId></code></pre>
|
||||||
`).join("")}
|
`).join("")}
|
||||||
</div>
|
</div>
|
||||||
<article id="coinpay-setup" class="coinpay-panel">
|
<article id="coinpay-setup" class="coinpay-panel">
|
||||||
<h3>CoinPay recurring invoice</h3>
|
<h3>CoinPay hourly invoice</h3>
|
||||||
<p>After we accept the project, we create a recurring CoinPay invoice for the weekly plan without exposing merchant credentials to the browser.</p>
|
<p>After we accept the project, we invoice approved hours at $400/hour through CoinPay without exposing merchant credentials to the browser.</p>
|
||||||
<pre><code>COINPAY_ORG=profullstack
|
<pre><code>COINPAY_ORG=profullstack
|
||||||
COINPAY_PRODUCT=logicsrc-hire-us
|
COINPAY_PRODUCT=logicsrc-hire-us
|
||||||
COINPAY_AMOUNT_USD=250
|
COINPAY_RATE_USD_PER_HOUR=400
|
||||||
COINPAY_INTERVAL=week
|
COINPAY_BILLING=metered_hours
|
||||||
|
COINPAY_MINIMUM_HOURS=10
|
||||||
COINPAY_STATUS=pending_acceptance</code></pre>
|
COINPAY_STATUS=pending_acceptance</code></pre>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -484,6 +484,17 @@ pre {
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.price-terms {
|
||||||
|
color: #b5beb2;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: -0.5rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-terms a {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.cta-row {
|
.cta-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
|
||||||
146
prd/0002-hourly-hire-us-rate.md
Normal file
146
prd/0002-hourly-hire-us-rate.md
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
---
|
||||||
|
openprd: "0.2"
|
||||||
|
id: "0002"
|
||||||
|
title: Move Hire Us pricing from a weekly retainer to an hourly rate
|
||||||
|
status: Accepted
|
||||||
|
authors:
|
||||||
|
- anthony@profullstack.com
|
||||||
|
created: 2026-07-28
|
||||||
|
updated: 2026-07-28
|
||||||
|
repo: https://github.com/profullstack/logicsrc
|
||||||
|
implementation: apps/logicsrc-web
|
||||||
|
tags: [pricing, site, billing]
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
The Hire Us surface on logicsrc.com prices standards work at $250/week, paid through a
|
||||||
|
recurring CoinPay invoice after project acceptance. That number reads as a token retainer
|
||||||
|
rather than a rate for senior open-spec implementation work — schemas, CLIs, SDKs, MCP
|
||||||
|
resources, and provider-neutral plugin surfaces. It anchors every inbound conversation at a
|
||||||
|
price that cannot cover the work, and it selects for clients who are shopping on price rather
|
||||||
|
than on the standard.
|
||||||
|
|
||||||
|
The replacement is $400/hour. This is not a bump to an existing hourly number; it is a change
|
||||||
|
of pricing *model*, which touches page copy, the payment configuration, and the invoicing
|
||||||
|
mechanics that currently assume a fixed weekly amount.
|
||||||
|
|
||||||
|
## Goals
|
||||||
|
|
||||||
|
- Every public price on logicsrc.com states one rate, in one unit, with no stale $250/week
|
||||||
|
copy left behind on any surface.
|
||||||
|
- Inbound Hire Us inquiries arrive already anchored to a senior rate, so the pricing
|
||||||
|
conversation is about scope rather than about the number.
|
||||||
|
- Billing can actually execute the new model: an accepted project produces a correct invoice
|
||||||
|
without manual repair.
|
||||||
|
- The change is reversible and auditable — the reasoning survives in the repo, not in a Slack
|
||||||
|
thread.
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- Repricing the standard itself. LogicSRC schemas, specs, and reference implementations stay
|
||||||
|
open and free; this covers implementation services only.
|
||||||
|
- Building time tracking. Hourly billing needs hours captured, but that is an operational
|
||||||
|
process for now, not a product to build.
|
||||||
|
- Replacing CoinPay or adding a second payment provider.
|
||||||
|
- Publishing a rate card with tiers, discounts, or role-based pricing. One rate, one line.
|
||||||
|
- Migrating anyone currently engaged at $250/week. Handled case by case, not by this PRD.
|
||||||
|
|
||||||
|
## Users
|
||||||
|
|
||||||
|
- **Prospective clients** evaluating whether to hire Profullstack for LogicSRC work — mostly
|
||||||
|
founders and engineering leads arriving from the spec pages, who read the price before they
|
||||||
|
read anything else.
|
||||||
|
- **Profullstack**, as the party quoting, invoicing, and collecting.
|
||||||
|
- **Existing clients** on the weekly plan, who must not be silently repriced.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
Two questions blocked this PRD at drafting time. Both were resolved before implementation:
|
||||||
|
|
||||||
|
- **The rate is $400/hour, confirmed deliberately.** Against $250/week this is roughly a 64x
|
||||||
|
change at a 40-hour week. The magnitude is the point: the weekly figure was a token retainer,
|
||||||
|
not a rate, and the new number is intended to filter out engagements too small to scope.
|
||||||
|
- **Billing is metered against actual hours, not a committed weekly block.** This matches how
|
||||||
|
the code already works — `/api/payments/create` creates a single one-shot payment, never a
|
||||||
|
recurring subscription, so the previous "recurring invoice" copy documented a mechanic that
|
||||||
|
did not exist. Metered billing also avoids the committed-block failure mode, which is a
|
||||||
|
weekly rate wearing an hourly label. A **10-hour minimum engagement** replaces the week as
|
||||||
|
the unit of commitment.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- R1 [P0] The Hire Us section heading states the new rate and unit, replacing "$250/week for
|
||||||
|
accepted LogicSRC work".
|
||||||
|
- R2 [P0] The price display block shows $400 with the unit "per hour".
|
||||||
|
- R3 [P0] The `/hire-us` entry in the Top-Level Pages list is updated; it repeated the weekly
|
||||||
|
figure independently of the section above it.
|
||||||
|
- R4 [P0] The `/hire-us` page body and its meta description are updated.
|
||||||
|
- R5 [P0] The CoinPay configuration block reflects the new model. `COINPAY_AMOUNT_USD=250` and
|
||||||
|
`COINPAY_INTERVAL=week` are both wrong under hourly pricing and are replaced by a rate, a
|
||||||
|
billing mode, and a minimum rather than renumbered.
|
||||||
|
- R6 [P0] A repo-wide search for `250`, `per week`, and `/week` returns no remaining pricing
|
||||||
|
references before the change is considered done.
|
||||||
|
- R7 [P1] The site states what an hour is billed against — metered actual hours, invoiced after
|
||||||
|
the client approves them — so the invoice mechanic is legible before a client asks.
|
||||||
|
- R8 [P1] Minimum engagement and cancellation terms are stated, since removing the weekly
|
||||||
|
cadence also removes the implicit unit of commitment.
|
||||||
|
- R9 [P2] Terms of engagement are documented at `/terms` rather than only in marketing copy.
|
||||||
|
|
||||||
|
Surfaces the original draft did not list, but which carried the weekly price and were therefore
|
||||||
|
in scope for R6: `/pricing` (metadata, two FAQ answers, and the rate bullet), `/about`,
|
||||||
|
`llms.txt`, `skill.md`, the Hire Us form's success message, and both the contract and e2e tests.
|
||||||
|
|
||||||
|
## UX Notes
|
||||||
|
|
||||||
|
The price appears in several places across the front page, `/pricing`, `/about`, and the
|
||||||
|
machine-readable surfaces; they are separate strings and will drift if edited one at a time.
|
||||||
|
Treat the set as one change.
|
||||||
|
|
||||||
|
The CoinPay block is rendered as example configuration, so it reads as documentation of how
|
||||||
|
billing actually works. A weekly interval next to an hourly rate is worse than a stale price —
|
||||||
|
it looks like the system does not do what the copy says.
|
||||||
|
|
||||||
|
The stated rate sits next to what it buys. The existing four capability cards (workflow specs,
|
||||||
|
reference implementations, integration hardening, open infrastructure) already do that work and
|
||||||
|
are unchanged.
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
- `src/lib/page-markup.ts` — section heading, price row, a new `.price-terms` line carrying the
|
||||||
|
minimum and cancellation summary, the CoinPay config block, and the Top-Level Pages entries
|
||||||
|
for both Hire Us and Terms.
|
||||||
|
- `src/app/api/hire-us/coinpay-checkout/route.ts` — the amount is now derived as
|
||||||
|
`hours × $400` rather than hardcoded. Hours are validated as quarter-hour increments at or
|
||||||
|
above the 10-hour minimum, defaulting to the minimum when omitted; invalid hours return 422
|
||||||
|
before any call to CoinPay. Payment metadata carries `billing`, `hours`, and
|
||||||
|
`rate_usd_per_hour` in place of `interval`.
|
||||||
|
- `src/app/api/hire-us/project-request/route.ts` — returns a rate, billing mode, and minimum
|
||||||
|
instead of a fixed `amount_usd`/`interval` pair, since no amount exists until hours are
|
||||||
|
approved.
|
||||||
|
- `src/app/terms/page.tsx` — new real route, replacing the `/terms` stub that rendered the
|
||||||
|
homepage SPA. The stub's entry was removed from the catch-all's `ROUTE_META`.
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
- Zero occurrences of the weekly price across the site and repo after the change, verified by
|
||||||
|
grep rather than by reading.
|
||||||
|
- First accepted project under the new model invoices correctly on the first attempt, with no
|
||||||
|
manual adjustment to the CoinPay invoice.
|
||||||
|
- Inbound inquiries that reach a scoping conversation do not open by disputing the rate.
|
||||||
|
- No existing engagement is repriced without explicit agreement.
|
||||||
|
|
||||||
|
## Risks & Open Questions
|
||||||
|
|
||||||
|
- **Hours are not currently tracked.** Metered billing won, and there is no capture mechanism.
|
||||||
|
`/terms` now defines what is billable (project work, meetings, and written communication in
|
||||||
|
quarter-hour increments; not scoping calls, invoicing, or warranty fixes), so the definition
|
||||||
|
is settled even though the tooling is not. Capturing hours is an operational process until it
|
||||||
|
is worth building.
|
||||||
|
- **Client mix will change.** An hourly rate at this level filters out the small experimental
|
||||||
|
engagements the weekly price attracted. This is the intent, recorded here as a decision
|
||||||
|
rather than left to be discovered.
|
||||||
|
- **Existing weekly clients** keep their terms until both sides agree in writing to move, per
|
||||||
|
`/terms`. Whether any active weekly engagements exist is still unknown.
|
||||||
|
- **Author attribution** is assumed from the CoinPay org configuration and should be corrected
|
||||||
|
if wrong.
|
||||||
|
|
@ -12,3 +12,4 @@ Status lives in each file's front-matter and is the source of truth:
|
||||||
| ID | Title | Status | Tags |
|
| ID | Title | Status | Tags |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| [0001](./0001-add-logicsrc-openontology-spec.md) | Add the LogicSRC OpenOntology specification | Draft | openontology, ontology, knowledge-graph, agents, mcp, schemas |
|
| [0001](./0001-add-logicsrc-openontology-spec.md) | Add the LogicSRC OpenOntology specification | Draft | openontology, ontology, knowledge-graph, agents, mcp, schemas |
|
||||||
|
| [0002](./0002-hourly-hire-us-rate.md) | Move Hire Us pricing from a weekly retainer to an hourly rate | Accepted | pricing, site, billing |
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue