Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions apps/www/src/app/products/hosted-checkout/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { ProductStoryLayout } from "@/components/stories/ProductStoryLayout";

const features = [
"Embed via URL or iframe — no SDK required",
"Auto-applies your logo, brand color, company name",
"Customer picks payment method; you settle in one asset",
"Idempotent re-render: customer can refresh during sign without losing state",
"Webhook on every state change",
];

const apiRequest = `POST /v1/checkout/sessions
{
"amount": 100.00,
"currency": "USD",
"success_url": "https://yourapp.com/thanks",
"metadata": { "order_id": "ord_123" }
}`;

const apiResponse = `→ { "url": "https://checkout.useroutr.com/cs_abc123" }`;

export const metadata = {
title: "Hosted Checkout — Useroutr",
description:
"A drop-in payment page that accepts USDC on five chains, cards via Stripe, bank transfers via ACH or SEPA, and fiat on-ramp through MoneyGram.",
};

export default function HostedCheckoutPage() {
return (
<ProductStoryLayout title="Hosted Checkout" category="Payments">
{/* Hero */}
<section className="min-h-screen flex items-center px-6 md:px-20 pt-32 pb-20">
<div className="max-w-3xl space-y-8">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-zinc-600">
Hosted Checkout
</p>
<h1 className="font-display text-5xl md:text-7xl font-bold italic tracking-tighter text-white leading-[1.0]">
Branded checkout.
<br />
One URL.
</h1>
<p className="text-xl text-zinc-400 leading-relaxed font-light max-w-xl">
A drop-in payment page that accepts USDC on five chains, cards via
Stripe, bank transfers via ACH or SEPA, and fiat on-ramp through
MoneyGram. The customer never leaves your brand.
</p>
</div>
</section>

{/* Features */}
<section className="px-6 md:px-20 py-24 border-t border-white/5">
<div className="max-w-3xl space-y-12">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-zinc-600">
What&apos;s included
</p>
<ul className="space-y-5">
{features.map((f) => (
<li key={f} className="flex items-start gap-4">
<span className="mt-1 w-1.5 h-1.5 rounded-full bg-white/30 shrink-0" />
<span className="text-[17px] text-zinc-300 leading-relaxed">{f}</span>
</li>
))}
</ul>
</div>
</section>

{/* API example */}
<section className="px-6 md:px-20 py-24 border-t border-white/5">
<div className="max-w-3xl space-y-6">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-zinc-600">
API example
</p>
<pre className="rounded-xl bg-white/3 border border-white/8 p-6 overflow-x-auto text-sm text-zinc-300 font-mono leading-relaxed whitespace-pre">
{apiRequest}
</pre>
<pre className="rounded-xl bg-white/3 border border-white/8 p-6 overflow-x-auto text-sm text-emerald-400 font-mono leading-relaxed whitespace-pre">
{apiResponse}
</pre>
</div>
</section>
</ProductStoryLayout>
);
}
92 changes: 92 additions & 0 deletions apps/www/src/app/products/invoicing/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { ProductStoryLayout } from "@/components/stories/ProductStoryLayout";

const features = [
"PDF + hosted page generated from the same JSON",
"Line items, taxes, discounts, partial payments",
"Multi-currency: invoice in EUR, settle in USDC",
"Automatic dunning: reminders at +3, +7, +14 days overdue",
"Webhook on invoice.paid, invoice.overdue, invoice.cancelled",
"CSV export for accountants",
];

const apiRequest = `POST /v1/invoices
{
"customer_email": "ap@acme.com",
"customer_name": "Acme Inc.",
"line_items": [
{ "description": "April retainer", "qty": 1, "unit_price": 5000, "amount": 5000 }
],
"currency": "USD",
"due_date": "2026-05-31"
}`;

const apiResponse = `→ {
"id": "inv_abc123",
"invoice_number": "INV-2026-042",
"pdf_url": "https://...",
"pay_url": "https://pay.useroutr.com/inv_abc123"
}`;

export const metadata = {
title: "Invoicing — Useroutr",
description:
"Send a PDF invoice with a payment URL baked in. Customer pays in USDC, card, or bank transfer — you reconcile automatically.",
};

export default function InvoicingPage() {
return (
<ProductStoryLayout title="Invoicing" category="Pay-by-Link">
{/* Hero */}
<section className="min-h-screen flex items-center px-6 md:px-20 pt-32 pb-20">
<div className="max-w-3xl space-y-8">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-zinc-600">
Invoicing
</p>
<h1 className="font-display text-5xl md:text-7xl font-bold italic tracking-tighter text-white leading-[1.0]">
Invoices that get paid.
<br />
In minutes, not weeks.
</h1>
<p className="text-xl text-zinc-400 leading-relaxed font-light max-w-xl">
Send a PDF invoice with a payment URL baked in. Customer pays in
USDC, card, or bank transfer — you reconcile automatically. Built
for B2B teams that hate Stripe-style &ldquo;click here to enable
Treasury.&rdquo;
</p>
</div>
</section>

{/* Features */}
<section className="px-6 md:px-20 py-24 border-t border-white/5">
<div className="max-w-3xl space-y-12">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-zinc-600">
What&apos;s included
</p>
<ul className="space-y-5">
{features.map((f) => (
<li key={f} className="flex items-start gap-4">
<span className="mt-1 w-1.5 h-1.5 rounded-full bg-white/30 shrink-0" />
<span className="text-[17px] text-zinc-300 leading-relaxed">{f}</span>
</li>
))}
</ul>
</div>
</section>

{/* API example */}
<section className="px-6 md:px-20 py-24 border-t border-white/5">
<div className="max-w-3xl space-y-6">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-zinc-600">
API example
</p>
<pre className="rounded-xl bg-white/3 border border-white/8 p-6 overflow-x-auto text-sm text-zinc-300 font-mono leading-relaxed whitespace-pre">
{apiRequest}
</pre>
<pre className="rounded-xl bg-white/3 border border-white/8 p-6 overflow-x-auto text-sm text-emerald-400 font-mono leading-relaxed whitespace-pre">
{apiResponse}
</pre>
</div>
</section>
</ProductStoryLayout>
);
}
90 changes: 90 additions & 0 deletions apps/www/src/app/products/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import Link from "next/link";
import { ArrowRight } from "lucide-react";
import { PageShell } from "@/components/site/PageShell";
import type { Metadata } from "next";

const products = [
{
label: "Hosted checkout",
description: "Stripe-style branded checkout. USDC + card + bank, one URL.",
href: "/products/hosted-checkout",
},
{
label: "Pay by link",
description: "One-link payment requests. No code, no checkout to build.",
href: "/products/pay-by-link",
},
{
label: "Invoicing",
description: "Send a PDF invoice, get paid in USDC or fiat.",
href: "/products/invoicing",
},
{
label: "Global payouts",
description: "Bulk disbursements to 174 countries.",
href: "/products/payouts",
},
];

export const metadata: Metadata = {
title: "Products — Useroutr",
description:
"One engine. Four ways to use it. Accept, route, settle, and notify — pick the surface that fits your business.",
alternates: { canonical: "/products" },
};

export default function ProductsIndexPage() {
return (
<PageShell>
<div className="min-h-screen bg-black text-white">
{/* Hero */}
<section className="pt-40 pb-20 px-6 max-w-5xl mx-auto">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-zinc-500 mb-6">
Products
</p>
<h1 className="font-display text-5xl md:text-7xl font-bold italic tracking-tighter text-white leading-[1.0]">
One engine.
<br />
Four ways to use it.
</h1>
<p className="mt-8 max-w-xl text-lg text-zinc-400 leading-relaxed font-light">
The same primitives — accept, route, settle, notify — power every
product on Useroutr. Pick the surface that fits your business.
</p>
</section>

{/* Product cards */}
<section className="pb-32 px-6 max-w-5xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{products.map((p) => (
<Link
key={p.href}
href={p.href}
className="group relative flex flex-col justify-between p-8 rounded-2xl border border-white/8 bg-white/2 hover:bg-white/5 hover:border-white/15 transition-all duration-200"
>
<div>
<p className="font-mono text-[10px] uppercase tracking-widest text-zinc-600 mb-3">
Useroutr
</p>
<h2 className="font-display text-2xl font-bold text-white tracking-tight">
{p.label}
</h2>
<p className="mt-3 text-[15px] text-zinc-400 leading-relaxed">
{p.description}
</p>
</div>
<div className="mt-8 flex items-center gap-2 text-zinc-500 group-hover:text-white transition-colors text-sm font-mono uppercase tracking-widest">
<span>Learn more</span>
<ArrowRight
size={14}
className="group-hover:translate-x-1 transition-transform"
/>
</div>
</Link>
))}
</div>
</section>
</div>
</PageShell>
);
}
88 changes: 88 additions & 0 deletions apps/www/src/app/products/pay-by-link/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { ProductStoryLayout } from "@/components/stories/ProductStoryLayout";

const features = [
"Fixed amount, open amount, or pick-from-list",
"Single-use or multi-use links",
"Custom expiry windows",
"Per-link conversion stats (views vs. payments)",
"QR code generated automatically",
"Works with any payment method the customer picks",
];

const apiRequest = `POST /v1/payment-links
{
"amount": 25,
"currency": "USD",
"description": "Coffee subscription · April",
"single_use": false,
"expires_at": "2026-06-01T00:00:00Z"
}`;

const apiResponse = `→ {
"id": "lnk_abc123",
"url": "https://pay.useroutr.com/abc12345",
"qr_code_url": "data:image/png;base64,..."
}`;

export const metadata = {
title: "Pay by Link — Useroutr",
description:
"Generate a payment URL with one API call. Share it on email, Slack, WhatsApp, an invoice, or a QR code.",
};

export default function PayByLinkPage() {
return (
<ProductStoryLayout title="Pay by Link" category="Payments">
{/* Hero */}
<section className="min-h-screen flex items-center px-6 md:px-20 pt-32 pb-20">
<div className="max-w-3xl space-y-8">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-zinc-600">
Pay by Link
</p>
<h1 className="font-display text-5xl md:text-7xl font-bold italic tracking-tighter text-white leading-[1.0]">
The link is
<br />
the integration.
</h1>
<p className="text-xl text-zinc-400 leading-relaxed font-light max-w-xl">
Generate a payment URL with one API call (or one button in the
dashboard). Share it on email, Slack, WhatsApp, an invoice, or a QR
code. Customers pay — you get a webhook.
</p>
</div>
</section>

{/* Features */}
<section className="px-6 md:px-20 py-24 border-t border-white/5">
<div className="max-w-3xl space-y-12">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-zinc-600">
What&apos;s included
</p>
<ul className="space-y-5">
{features.map((f) => (
<li key={f} className="flex items-start gap-4">
<span className="mt-1 w-1.5 h-1.5 rounded-full bg-white/30 shrink-0" />
<span className="text-[17px] text-zinc-300 leading-relaxed">{f}</span>
</li>
))}
</ul>
</div>
</section>

{/* API example */}
<section className="px-6 md:px-20 py-24 border-t border-white/5">
<div className="max-w-3xl space-y-6">
<p className="font-mono text-[11px] uppercase tracking-[0.18em] text-zinc-600">
API example
</p>
<pre className="rounded-xl bg-white/3 border border-white/8 p-6 overflow-x-auto text-sm text-zinc-300 font-mono leading-relaxed whitespace-pre">
{apiRequest}
</pre>
<pre className="rounded-xl bg-white/3 border border-white/8 p-6 overflow-x-auto text-sm text-emerald-400 font-mono leading-relaxed whitespace-pre">
{apiResponse}
</pre>
</div>
</section>
</ProductStoryLayout>
);
}
19 changes: 19 additions & 0 deletions apps/www/src/app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ const baseUrl = "https://useroutr.com";

const useCases = ["marketplaces", "fintech", "ecommerce", "payouts"];

const productPages = [
"hosted-checkout",
"pay-by-link",
"invoicing",
"payouts",
];

export default function sitemap(): MetadataRoute.Sitemap {
const now = new Date();

Expand All @@ -14,6 +21,18 @@ export default function sitemap(): MetadataRoute.Sitemap {
changeFrequency: "weekly",
priority: 1,
},
{
url: `${baseUrl}/products`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.9,
},
...productPages.map((slug) => ({
url: `${baseUrl}/products/${slug}`,
lastModified: now,
changeFrequency: "monthly" as const,
priority: 0.85,
})),
{
url: `${baseUrl}/use-cases`,
lastModified: now,
Expand Down
Loading