Let Lens Protocol profiles receive U.CASH Pay links (tip / collect). Non-custodial.
This is a small Next.js app that, given a Lens profile handle, resolves the profile and renders a
Pay-with-U.CASH button. Clicking it opens a hosted U.CASH checkout prefilled with the profile as
the title and the Lens profileId/handle as the external_reference. Funds always settle to the
creator's own receive addresses configured in their pay.u.cash store.
The store Cloud Token is publishable: it is safe to embed in the browser/app and does not give anyone custody of funds. No keys, no signing, no custodial rails.
app/page.js- resolve a Lens handle (public Lens API) and show a U.CASH tip button.components/UcashTipButton.js- the reusable Pay button + modal checkout (client-side hosted link).app/embed/route.js- optional server-side tracked checkout (idempotent perexternal_reference).lib/ucashpay.js- the pay.u.cash contract in one place (buildEmbedUrl,createTrackedCheckout).public/embed.example.html- a zero-dependency static page showing the hosted pay link.
# 1. Install
npm install
# 2. Set your publishable store Cloud Token (see "Set up your pay.u.cash account" below)
export NEXT_PUBLIC_UCASH_CLOUD_TOKEN=your_store_cloud_token_here
# 3. Run
npm run dev
# open http://localhost:3000Enter a Lens handle (e.g. lens/stani), pick an amount, and click Tip with U.CASH.
The simplest path. Build a hosted link with the publishable Cloud Token and open it in a tab or iframe. No server needed:
import { buildEmbedUrl } from "@/lib/ucashpay";
const url = buildEmbedUrl({
cloud: "YOUR_STORE_CLOUD_TOKEN", // publishable, non-custodial
amount: "1",
currency: "USD",
title: "Tip lens/stani",
external_reference: "lens:stani",
redirect: "https://your-app.example/thanks",
});
window.open(url, "_blank", "noopener");Contract:
GET https://pay.u.cash/embed.php
?cloud=<store cloud token>
&amount=<amount>
¤cy=USD
&title=<title>
&external_reference=<optional ref>
&redirect=<optional redirect url>
<UcashTipButton
cloud={process.env.NEXT_PUBLIC_UCASH_CLOUD_TOKEN}
lensProfile={{ handle: "stani", profileId: "0x...", displayName: "Stani" }}
amount="1"
currency="USD"
/>When you want a tracked transaction (e.g. a "collect" action behind a login), call the server route.
It posts to https://pay.u.cash/payment/ajax.php with function=create-transaction and
idempotent=1, so repeated requests for the same external_reference return the same payment URL
rather than creating duplicates.
curl "http://localhost:3000/embed?cloud=YOUR_STORE_CLOUD_TOKEN&handle=stani&amount=1¤cy=USD"
# -> { "success": true, "paymentUrl": "https://pay.u.cash/...", "transactionId": "...", ... }The route also accepts POST with a JSON body. You may instead set UCASH_CLOUD_TOKEN (server env)
so callers never need to pass the token at all.
Response handling: the pay.u.cash response is { success: true, response: [paymentUrl, transactionId, ...] }.
The payment URL is the array element that starts with http(s)://.
Profile resolution in app/page.js uses the public Lens GraphQL API (https://api.lens.dev/graphql)
for a read-only lookup of the profile's handle, id, and metadata.displayName. No wallet, no
signing, no collect module interaction happens here, which keeps the flow non-custodial.
For richer integrations (collecting a Lens post, session-based profile context, or interacting with
Lens publications), use the official Lens SDK (@lens-protocol/react-web + @lens-protocol/client)
or the Lens React Native SDK. Those packages give you authenticated sessions and typed access to
publications, follow modules, and collect/open-action modules. They are optional for this starter,
which intentionally stays dependency-light so the pay link works anywhere a link can be opened.
Install if you want them:
npm install @lens-protocol/react-web @lens-protocol/client viem- Sign up at pay.u.cash, then click the verification link in the email.
- Set receive addresses under Settings -> Addresses (raw address, ENS, Unstoppable Domains, or FIO).
- Create a store under Account -> Stores and copy its Store Cloud Token (use the store-level token, not the account-wide one).
- For fiat cards, connect your own Stripe under Settings -> Payment processors.
| Variable | Where | Purpose |
|---|---|---|
NEXT_PUBLIC_UCASH_CLOUD_TOKEN |
client | Publishable store Cloud Token for the hosted embed button. |
UCASH_CLOUD_TOKEN |
server only | Optional: lets the /embed route own the token so callers don't pass it. |
- Recurring crypto billing is not automatic. pay.u.cash checkouts are one-time payments; there is no on-chain recurring crypto subscription. Recurring revenue needs a membership/collect schedule on your side that opens a fresh one-time pay link each cycle.
- This app never holds funds or keys. The Cloud Token is publishable and only authorizes creating checkouts that pay to the merchant's configured receive addresses.
npm run build
npm start # production server
# or deploy on Vercel / any Node host; set the env vars in the host dashboard.This repo is an app, not a published package. If you extract lib/ucashpay.js into a library later:
npm login
npm version patch
npm publish --access publicMIT - see LICENSE.