Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fc97bc8
feat(connectkit): correct native-token (SOL/ETH/XLM) payment amounts …
akbarsaputrait Jul 9, 2026
b9123ce
chore(release): v0.1.35-beta.1
akbarsaputrait Jul 9, 2026
eedf083
chore: update dependencies to @rozoai/intent-common v0.1.22-beta.1 an…
akbarsaputrait Jul 9, 2026
6314758
refactor(usePaymentState): streamline token handling and optimize pay…
akbarsaputrait Jul 9, 2026
82089ec
feat: normalize evm address and fix deposit options native token
akbarsaputrait Jul 9, 2026
f8cc050
fix: deposit address and exactly amount
akbarsaputrait Jul 9, 2026
99f2577
chore: update dependencies to @rozoai/intent-common v0.1.22-beta.2 an…
akbarsaputrait Jul 9, 2026
e8b7b23
fix(connectkit): native source deposit + structured errors
akbarsaputrait Jul 10, 2026
06aa510
test(e2e): native token merchant flows + example refactor
akbarsaputrait Jul 10, 2026
f8cdd3a
feat(connectkit): dual-chain connect, query migration, stellar deeplink
akbarsaputrait Jul 12, 2026
4f0e9d5
chore(deps): workspace:* deps and oxfmt config
akbarsaputrait Jul 12, 2026
9f8b87d
fix(payment): correct fee quote, Stellar deep links, and token normal…
akbarsaputrait Jul 12, 2026
f3aef36
docs(AGENTS): clarify pre-commit hook runs lint only, not format
akbarsaputrait Jul 12, 2026
031a186
test(e2e): add missing native specs, pay-to-address mode, unified runner
akbarsaputrait Jul 13, 2026
23fc1be
Merge remote-tracking branch 'origin/master' into feat/native-token-p…
akbarsaputrait Jul 14, 2026
b88614a
fix(sdk): code review P0/P1 fixes, mobile flow, UX improvements
akbarsaputrait Jul 14, 2026
e742e50
fix: force EVM connect on Phantom deeplink, safe disconnectAll
akbarsaputrait Jul 15, 2026
0a9eaab
Merge branch 'master' into feat/native-token-payments
akbarsaputrait Jul 17, 2026
6e11a53
fix(connectkit): re-enable switch native token for merchant payment
akbarsaputrait Jul 17, 2026
2850c4f
fix(solana): use payment.source.amount instead from walletOptions
akbarsaputrait Jul 18, 2026
cf82b2a
Merge remote-tracking branch 'origin/master' into feat/native-token-p…
akbarsaputrait Jul 18, 2026
960cab9
fix(example): stub @x402/* missing transitive deps from @coinbase/cdp…
akbarsaputrait Jul 18, 2026
3eefdec
chore: upgrade sdk version
akbarsaputrait Jul 18, 2026
92f39b5
Merge remote-tracking branch 'origin/master' into feat/native-token-p…
akbarsaputrait Jul 29, 2026
25f354a
chore: upgrade sdk version
akbarsaputrait Jul 31, 2026
d8eaeac
bumpp to beta version
akbarsaputrait Aug 3, 2026
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ dist/
.serena
config.bat
.impeccable
.github/skills/
.github/skills/
docs/superpowers/
104 changes: 104 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# AGENTS.md

Tooling and repo-setup reference for AI agents. For architecture, payment FSM,
multi-chain flows, and coding conventions, see [CLAUDE.md](./CLAUDE.md).

## Toolchain summary

- **Package manager**: pnpm (`packageManager` pinned to `pnpm@11.10.0`). Use `pnpm`, never npm/yarn.
- **Monorepo**: pnpm workspaces. See `pnpm-workspace.yaml`.
- `packages/connectkit` → `@rozoai/intent-pay` (main SDK; built with Rollup)
- `packages/pay-common` → `@rozoai/intent-common` (shared types/utils; built with `tsc`)
- `examples/nextjs-app` → Next.js demo (built with `next build`)

## Linting — oxlint (not ESLint)

oxlint is the linter. Config is **per-package**: `oxlint.json` in each package root.
Plugins differ by package:

- `packages/pay-common/oxlint.json` — `typescript` plugin only (node env).
- `packages/connectkit/oxlint.json` — `react`, `react-hooks`, `typescript` (browser env).
- `examples/nextjs-app/oxlint.json` — `react`, `react-hooks`, `nextjs`, `typescript`.

Run:
```bash
pnpm lint # all packages (root)
pnpm --filter @rozoai/intent-pay run lint # single package
```

Key rule differences to respect:
- `pay-common`: `no-explicit-any` and `no-unused-vars` are **`error`**.
- `connectkit` / `nextjs-app`: those two are **`warn`**; `react-hooks/rules-of-hooks` is **`error`**.
- Don't add an ESLint config. The repo deliberately uses oxlint + oxfmt.

## Formatting — oxfmt (oxc formatter)

Formatting uses **oxfmt**, the oxc formatter. There is **no** `.prettierrc` in the
SDK packages.

```bash
pnpm format # runs oxfmt per package (root script)
pnpm --filter @rozoai/intent-pay run format
```

- `packages/connectkit` and `packages/pay-common` → `oxfmt src/`.
- `examples/nextjs-app` → `oxfmt src/ app/ components/ lib/` **plus** `prettier`
(with `prettier-plugin-tailwindcss`). The example's `.prettierrc` sets
`semi: false`, `singleQuote: false`, `printWidth: 80`, `endOfLine: lf`.

When editing the example app, run both formats. When editing SDK packages, use
oxfmt only — do not introduce Prettier there.

Check mode (CI-safe): `pnpm --filter <pkg> run format:check` (maps to `oxfmt --check`).

## Type checking

- `packages/pay-common`: `tsc` (strict) — runs as part of `pnpm build`.
- `packages/connectkit`: `tsc` is invoked inside the Rollup build (`pnpm build`).
- `examples/nextjs-app`: `pnpm typecheck` → `tsc --noEmit`.

## Build & dev

```bash
pnpm build # build:common → build:pay → build:example
pnpm dev # watch all three in parallel
pnpm --filter @rozoai/intent-common run dev # tsc --watch
pnpm --filter @rozoai/intent-pay run dev # rollup -w
```

Example app uses local packages via workspace symlinks.

## Tests

- `packages/pay-common`: `pnpm test` → `tape -r ts-node/register/transpile-only test/**/*.test.ts`.
- `examples/nextjs-app`: Playwright E2E — `pnpm test:e2e` and the many `test:e2e:*` matrix scripts
(per route direction: evm-to-stellar, solana-to-evm, etc.). Config: `e2e/playwright.config.ts`.

## Dead code / dependency hygiene

- `knip.json` (root) drives `knip` for unused exports/deps. Entry points configured there.
- `ts-prune` and `depcheck` are devDependencies (pay-common lint runs `depcheck`).
- Run `npx knip` from root to audit.

## Git hooks & CI

- **Husky** `pre-commit` runs `pnpm lint-staged` (runs oxlint on changed files
only; does not run format).
- **CI** (`.github/workflows/`):
- `ai-pr-review.yml` — runs `.github/ai_pr_review.py` on PRs (comments P0/P1/P2, labels `ai-review-passed`).
- `release.yml` — on `v*` tag: build + `pnpm publish` to npm.
- `security-scan.yml` — runs `scripts/security.sh` (code-injection + secret-leak gate) on every push/PR.

Target branch for PRs: `master`.

## Quick reference

| Task | Command |
|------|---------|
| Lint everything | `pnpm lint` |
| Format everything | `pnpm format` |
| Typecheck example | `pnpm --filter examples/nextjs-app typecheck` |
| Build all | `pnpm build` |
| pay-common tests | `pnpm --filter @rozoai/intent-common test` |
| E2E (example) | `pnpm --filter examples/nextjs-app test:e2e` |
| Dead-code audit | `npx knip` |
167 changes: 167 additions & 0 deletions docs/NEXTJS_BEST_PRACTICES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Next.js Best Practices — `RozoPayProvider`

A short, opinionated checklist for integrating `@rozoai/intent-pay` in a Next.js app.
For the full walkthrough (cookie flash-fix, `dynamic` fallback, Pages Router, non-Next.js apps),
see [`PROVIDER_SETUP.md`](./PROVIDER_SETUP.md). This page is the "what should I actually do" summary.

---

## TL;DR

1. Put `RozoPayProvider` + `WagmiProvider` in a **client component** (`"use client"`), never in a Server Component.
2. Build `wagmi`'s `config` with `createConfig` **inside `useState(() => ...)`**, not at module scope.
3. Pass `ssr: true` to `getDefaultConfig` — required for any App Router usage.
4. Only **one** `RozoPayProvider` per app, mounted once near the root layout.
5. If you're embedded in a wallet's in-app browser (Base App, MetaMask, Phantom), add cookie-based `initialState` too — `ssr: true` alone won't stop the reconnect flash there.

---

## 1. Client boundary

`RozoPayProvider`, `WagmiProvider`, and `getDefaultConfig`/`createConfig` all rely on browser-only wallet SDKs. Isolate them behind `"use client"` in a dedicated `providers.tsx`, and keep your root `layout.tsx` a plain Server Component that just renders `<Providers>{children}</Providers>`.

```tsx
// app/providers.tsx
"use client";

import { getDefaultConfig, RozoPayProvider } from "@rozoai/intent-pay";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState, type ReactNode } from "react";
import { createConfig, WagmiProvider } from "wagmi";

export function Providers({ children }: { children: ReactNode }) {
const [config] = useState(() =>
createConfig(getDefaultConfig({ appName: "Your App", ssr: true }))
);
const [queryClient] = useState(() => new QueryClient());

return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<RozoPayProvider>{children}</RozoPayProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
```

```tsx
// app/layout.tsx — stays a Server Component
import { Providers } from "./providers";

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
```

## 2. Don't build `config` at module scope

```tsx
// ❌ evaluated during SSR module load — some wallet connectors touch
// window/localStorage at construction time and will throw on the server
const config = createConfig(getDefaultConfig({ appName: "My App" }));

// ✅ deferred to client render via useState initializer
const [config] = useState(() =>
createConfig(getDefaultConfig({ appName: "My App", ssr: true }))
);
```

`"use client"` marks a module boundary, but Next.js still evaluates that module on the server once (for the RSC payload). Module-scope `createConfig` runs there too — `useState(() => ...)` guarantees it only runs on the client, on first render.

If a wallet SDK still throws on import even inside `"use client"`, fall back to lazy-loading the whole provider tree with `next/dynamic({ ssr: false })` (see [PROVIDER_SETUP.md § Alternative Pattern](./PROVIDER_SETUP.md#alternative-pattern--dynamic-with-ssr-false)).

## 3. `ssr: true` is required, but it's not the whole story

`ssr: true` tells wagmi to render a deterministic "disconnected" state on the server so client hydration matches — it prevents the classic React hydration mismatch error. It does **not** make wagmi know a wallet was already connected before first paint.

If your users mostly open the app from a normal browser tab, `ssr: true` alone is enough — they'll see a brief "disconnected" flash for a few hundred ms while wagmi's `reconnect()` resolves, which is normal and unavoidable client-side.

If your users open the app **inside a wallet's in-app browser** (Base App, MetaMask, Phantom), that flash is more noticeable because the SDK waits for reconnect to settle before auto-navigating to the token list. Fix it with cookie-persisted `initialState`:

```tsx
// app/providers.tsx
"use client";
import { cookieStorage, createConfig, createStorage, WagmiProvider, type State } from "wagmi";

export function Providers({
children,
initialState,
}: {
children: ReactNode;
initialState?: State;
}) {
const [config] = useState(() =>
createConfig(
getDefaultConfig({
appName: "Your App",
ssr: true,
storage: createStorage({ storage: cookieStorage }),
})
)
);

return <WagmiProvider config={config} initialState={initialState}>{/* ... */}</WagmiProvider>;
}
```

```tsx
// app/layout.tsx (Server Component)
import { cookieToInitialState } from "wagmi";
import { headers } from "next/headers";
import { Providers } from "./providers";
import { config } from "./wagmi-config"; // same config shape as above

export default async function RootLayout({ children }: { children: React.ReactNode }) {
const initialState = cookieToInitialState(config, (await headers()).get("cookie"));
return (
<html>
<body>
<Providers initialState={initialState}>{children}</Providers>
</body>
</html>
);
}
```

This is a consumer-app configuration choice — cookies are read on your server, so the SDK can't do it for you. See [PROVIDER_SETUP.md § Minimizing the Wallet Reconnect Flash](./PROVIDER_SETUP.md#minimizing-the-wallet-reconnect-flash-in-app-browsers) for the full explanation of the race condition this closes.

## 4. Mount `RozoPayProvider` exactly once

The provider throws at render time if it detects a second, nested instance:

```
Error: Multiple, nested usages of RozoPayProvider detected. Please use only one.
```

Mount it once, at (or near) the root layout. Don't wrap individual pages or route groups in their own `RozoPayProvider` — compose all pages under the single root instance instead.

It also requires a `WagmiProvider` ancestor; if missing, it logs a warning and renders `children` without payment functionality rather than crashing:

```
[RozoPay] RozoPayProvider must be within a WagmiProvider
```

## 5. Hooks and components only work inside the provider tree

`useRozoPayUI()`, `useRozoPayStatus()`, and `<RozoPayButton />` all read from `RozoPayProvider`'s context and throw (`useRozoPayUI must be used within a RozoPayProvider`) or fail to render correctly if used outside it. Keep them in components rendered under `<Providers>` in your layout tree — this is automatic for anything inside `app/**/page.tsx` once `Providers` wraps `{children}` at the root.

## Quick checklist before shipping

- [ ] `providers.tsx` has `"use client"` at the top
- [ ] `createConfig(...)` is inside `useState(() => ...)`, not at module scope
- [ ] `getDefaultConfig({ ssr: true, ... })`
- [ ] Exactly one `<RozoPayProvider>` in the whole app
- [ ] `layout.tsx` (or equivalent root) stays a Server Component and only renders `<Providers>{children}</Providers>`
- [ ] If targeting in-app wallet browsers: cookie `storage` + `initialState` wired through `layout.tsx`
- [ ] Swapped `appId="rozoSandbox"` for your own production `appId` before launch

---

For prop-level reference (`apiVersion`, `payApiUrl`, `stellarKit`, `debugMode`, theme/mode, etc.), see the [Props Reference table in PROVIDER_SETUP.md](./PROVIDER_SETUP.md#props-reference). For `RozoPayButton` props, see [ROZO_PAY_BUTTON_PROPS.md](./ROZO_PAY_BUTTON_PROPS.md).
Loading
Loading