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
170 changes: 48 additions & 122 deletions auth-demo/app/api-demo/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use client';

import { Protect, useHyperyAuth } from '@hyperyai/sdk';
import Link from 'next/link';
import { Terminal, TriangleAlert } from 'lucide-react';
import { BackLink, C, Card, CodeBlock, Note, Page, PageHeader, button } from '@/components/ui';
import { useState } from 'react';

export default function ApiDemoPage() {
Expand Down Expand Up @@ -64,144 +65,69 @@ function ApiDemoContent() {
};

return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-purple-50">
<div className="max-w-4xl mx-auto px-4 py-12">
<Link
href="/"
className="inline-flex items-center text-blue-600 hover:text-blue-700 mb-8"
>
<svg
className="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 19l-7-7 7-7"
/>
</svg>
Back to Home
</Link>

<div className="bg-white rounded-2xl shadow-xl p-8">
<div className="flex items-center space-x-2 mb-6">
<div className="w-10 h-10 bg-purple-100 rounded-full flex items-center justify-center">
<svg
className="w-6 h-6 text-purple-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
</div>
<h1 className="text-3xl font-bold">API Demo</h1>
</div>

<p className="text-gray-600 mb-6">
Test authenticated API requests using the <code className="bg-gray-100 px-2 py-1 rounded text-sm">getAccessToken()</code> hook.
</p>

{/* Actions */}
<div className="space-y-4 mb-8">
<div>
<button
onClick={handleGetToken}
disabled={loading}
className="w-full bg-blue-600 text-white py-3 px-6 rounded-lg font-semibold hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition"
>
{loading ? 'Loading...' : '1. Get Access Token'}
</button>
<p className="text-xs text-gray-500 mt-2">
Retrieves your current access token (auto-refreshes if expired)
</p>
</div>

<div>
<button
onClick={handleMakeRequest}
disabled={loading}
className="w-full bg-purple-600 text-white py-3 px-6 rounded-lg font-semibold hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed transition"
>
{loading ? 'Loading...' : '2. Make Authenticated Request'}
<Page narrow>
<BackLink />
<PageHeader
icon={<Terminal />}
title="API demo"
description={<>Make authenticated requests with the token from <C>getAccessToken()</C>.</>}
/>
<div className="space-y-6">
<Card className="divide-y divide-border">
{[
{ n: 1, label: 'Get access token', hint: 'Returns your current token, refreshed if it expired.', run: handleGetToken, primary: false },
{ n: 2, label: 'Make authenticated request', hint: 'Calls /api/user/me with a Bearer token.', run: handleMakeRequest, primary: true },
].map((step) => (
<div key={step.n} className="flex flex-wrap items-center gap-4 p-5">
<span className="flex size-7 shrink-0 items-center justify-center rounded-full bg-primary-subtle text-xs font-semibold text-primary-subtle-foreground">
{step.n}
</span>
<div className="min-w-0 flex-1">
<p className="text-sm font-semibold">{step.label}</p>
<p className="text-sm text-muted-foreground">{step.hint}</p>
</div>
<button onClick={step.run} disabled={loading} className={step.primary ? button.primary : button.secondary}>
{loading ? 'Loading…' : 'Run'}
</button>
<p className="text-xs text-gray-500 mt-2">
Calls <code className="bg-gray-100 px-1 rounded">/api/user/me</code> with Bearer token
</p>
</div>
))}
</Card>

{token && (
<div>
<h3 className="mb-2 text-sm font-semibold">Access token</h3>
<div className="break-all rounded-xl bg-surface-sunken p-4 font-mono text-xs text-muted-foreground">{token}</div>
<p className="mt-2 text-xs text-subtle-foreground">Send it in the <C>Authorization</C> header.</p>
</div>
)}

{/* Token Display */}
{token && (
<div className="mb-6">
<h3 className="font-semibold mb-2">Access Token:</h3>
<div className="bg-gray-50 rounded-lg p-4">
<code className="text-xs break-all">{token}</code>
</div>
<p className="text-xs text-gray-500 mt-2">
This token is automatically included in the <code>Authorization</code> header
</p>
</div>
)}

{/* Response Display */}
{response && (
<div className="mb-6">
<h3 className="font-semibold mb-2">API Response:</h3>
<div className="bg-green-50 rounded-lg p-4">
<pre className="text-xs overflow-x-auto">
{JSON.stringify(response, null, 2)}
</pre>
</div>
</div>
)}

{/* Error Display */}
{error && (
<div className="mb-6">
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
<h3 className="font-semibold text-red-900 mb-2">Error:</h3>
<p className="text-sm text-red-800">{error}</p>
</div>
</div>
)}
{response && <CodeBlock title="Response" code={JSON.stringify(response, null, 2)} />}

{/* Code Example */}
<div className="bg-gray-50 rounded-lg p-4">
<h3 className="font-semibold mb-3">Example Code:</h3>
<pre className="text-xs overflow-x-auto">
{`import { useHyperyAuth } from '@hyperyai/sdk';
{error && (
<Note tone="warning" icon={<TriangleAlert />} title="Request failed">
{error}
</Note>
)}

<CodeBlock
title="Example"
code={`import { useHyperyAuth } from '@hyperyai/sdk';

function MyComponent() {
const { getAccessToken } = useHyperyAuth();

const makeRequest = async () => {
// Get valid token (auto-refreshes if needed)
const token = await getAccessToken();

// Make authenticated request

const response = await fetch('/api/endpoint', {
headers: {
Authorization: \`Bearer \${token}\`,
},
headers: { Authorization: \`Bearer \${token}\` },
});

return response.json();
};
}`}
</pre>
</div>
</div>
/>
</div>
</div>
</Page>
);
}

35 changes: 12 additions & 23 deletions auth-demo/app/callback/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import { useEffect } from 'react';
import { useHyperyAuth } from '@hyperyai/sdk';
import { useRouter } from 'next/navigation';
import { X } from 'lucide-react';
import { Spinner } from '@/components/ui';

export default function CallbackPage() {
const { isAuthenticated, isLoading, error } = useHyperyAuth();
Expand Down Expand Up @@ -31,38 +33,25 @@ export default function CallbackPage() {
}, [isAuthenticated, isLoading, error, router]);

return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-purple-50 flex items-center justify-center">
<div className="bg-white rounded-2xl shadow-xl p-8 max-w-md w-full text-center">
<div className="flex min-h-screen items-center justify-center p-4">
<div className="w-full max-w-sm rounded-2xl bg-card p-8 text-center shadow-card">
{error ? (
<>
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg
className="w-8 h-8 text-red-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<div className="mx-auto mb-4 flex size-11 items-center justify-center rounded-full bg-danger-subtle text-danger-foreground">
<X className="size-5" />
</div>
<h2 className="text-2xl font-bold mb-2">Authentication Failed</h2>
<p className="text-gray-600 mb-4">{error}</p>
<p className="text-sm text-gray-500">Redirecting back to home...</p>
<h2 className="text-xl font-semibold tracking-tight">Authentication failed</h2>
<p className="mt-2 text-sm text-muted-foreground">{error}</p>
<p className="mt-4 text-xs text-subtle-foreground">Redirecting back to home…</p>
</>
) : (
<>
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4" />
<h2 className="text-2xl font-bold mb-2">Completing Sign In</h2>
<p className="text-gray-600">Please wait while we authenticate you...</p>
<Spinner className="mx-auto mb-4 block size-8" />
<h2 className="text-xl font-semibold tracking-tight">Completing sign in</h2>
<p className="mt-2 text-sm text-muted-foreground">One moment while we authenticate you…</p>
</>
)}
</div>
</div>
);
}

92 changes: 46 additions & 46 deletions auth-demo/app/checkout/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import { BuyButton, useCheckout, useHyperyAuth } from '@hyperyai/sdk';
import { useState } from 'react';
import { CreditCard } from 'lucide-react';
import { C, Card, Page, PageHeader, button } from '@/components/ui';

export default function CheckoutDemoPage() {
const { isAuthenticated, interactionMode } = useHyperyAuth();
Expand All @@ -31,51 +33,49 @@ export default function CheckoutDemoPage() {
};

return (
<main style={{ maxWidth: 560, margin: '48px auto', padding: '0 20px', fontFamily: 'system-ui' }}>
<h1 style={{ fontSize: 24, fontWeight: 700 }}>Auth + charge demo</h1>
<p style={{ color: '#555', marginTop: 8 }}>
Signed in: <b>{isAuthenticated ? 'yes' : 'no'}</b> · interaction mode:{' '}
<b>{interactionMode}</b>
</p>

<section style={{ marginTop: 32 }}>
<h2 style={{ fontSize: 16, fontWeight: 600 }}>Marketplace purchase (&lt;BuyButton&gt;)</h2>
<p style={{ color: '#777', fontSize: 14, margin: '4px 0 12px' }}>
Charges a seller app via Stripe Connect. Logs you in and adds a card first if needed.
</p>
<BuyButton
appId={process.env.NEXT_PUBLIC_DEMO_SELLER_APP_ID || 'app_demo_seller'}
amountCents={499}
description="Pro upgrade (demo)"
onSuccess={(r) => alert(`Paid! paymentIntent=${r.paymentIntentId}`)}
onError={(e) => alert(`Purchase failed: ${e.message}`)}
/>
</section>

<section style={{ marginTop: 40 }}>
<h2 style={{ fontSize: 16, fontWeight: 600 }}>Buy AI credits (useCheckout topup)</h2>
<p style={{ color: '#777', fontSize: 14, margin: '4px 0 12px' }}>
Tops up your own credit balance. Same login → add-card → charge chain.
</p>
<button
type="button"
onClick={buyCredits}
disabled={isRunning}
style={{
padding: '10px 20px',
borderRadius: 8,
border: 'none',
background: '#111',
color: '#fff',
fontWeight: 600,
cursor: isRunning ? 'not-allowed' : 'pointer',
opacity: isRunning ? 0.6 : 1,
}}
>
{isRunning ? `${status}…` : 'Add $5 credits'}
</button>
{topupResult && <p style={{ marginTop: 12, fontSize: 14 }}>{topupResult}</p>}
</section>
</main>
<Page narrow>
<PageHeader
icon={<CreditCard />}
title="Auth + charge"
description="Neither button needs you signed in: the SDK walks you through sign-in, adding a card and the charge."
/>
<div className="mb-6 flex flex-wrap gap-2 text-xs">
<span className="rounded-md bg-muted px-2 py-1 text-muted-foreground">
Signed in: <b className="text-foreground">{isAuthenticated ? 'yes' : 'no'}</b>
</span>
<span className="rounded-md bg-muted px-2 py-1 text-muted-foreground">
Interaction mode: <b className="text-foreground">{interactionMode}</b>
</span>
</div>
<div className="grid gap-5 sm:grid-cols-2">
<Card className="flex flex-col p-6">
<h2 className="font-semibold">Marketplace purchase</h2>
<p className="mt-1 flex-1 text-sm text-muted-foreground">
<C>{'<BuyButton>'}</C> charges a seller app via Stripe Connect.
</p>
<div className="mt-5">
<BuyButton
appId={process.env.NEXT_PUBLIC_DEMO_SELLER_APP_ID || 'app_demo_seller'}
amountCents={499}
description="Pro upgrade (demo)"
onSuccess={(r) => alert(`Paid! paymentIntent=${r.paymentIntentId}`)}
onError={(e) => alert(`Purchase failed: ${e.message}`)}
/>
</div>
</Card>
<Card className="flex flex-col p-6">
<h2 className="font-semibold">Buy AI credits</h2>
<p className="mt-1 flex-1 text-sm text-muted-foreground">
<C>useCheckout()</C> tops up your own balance.
</p>
<div className="mt-5">
<button type="button" onClick={buyCredits} disabled={isRunning} className={button.secondary}>
{isRunning ? `${status}…` : 'Add $5 credits'}
</button>
</div>
{topupResult && <p className="mt-3 text-sm text-muted-foreground">{topupResult}</p>}
</Card>
</div>
</Page>
);
}
Loading
Loading