Edit on CodePen
https://codepen.io/nano2dev/pen/VwRQypE
Introduction
Inspired by Apple Pay ™, NanoPay is a open source, non-custodial javascript library for the Nano blockchain.
Easily accept crypto payments on your website(s) with this package.
Installation
First, add NanoPay to your project's index.html.
CDN:
<script src="https://cdn.nano.to/pay.js"></script>
Local:
<script src="/NanoPay.js"></script>
Download latest version.
Simple Usage
Simple payments can be achieved with basic HTML. No javascript required.
<a
data-title="Tip @Nano2Dev"
data-amount="0.133"
data-address="@development"
data-button="Open Natrium"
data-position="bottom">
Tip 0.133 NANO</a>
Javascript API
Configure and open popup programmatically with NanoPay.open() method.
NanoPay.open({
title: "Demo",
address: '@development', // Username or Nano Address
notify: '[email protected]', // get an email receipt
contact: true, // user's email
shipping: 10, // user's shipping address, use 'true' for free shipping
currency: 'EUR', // converts prices EUR, default USD
// amount: 30, // not used if using line_items
line_items: [
{ name: "Shirt (X-Small)", price: 50 },
{ name: "Mens Shoes (9.5)", price: 20 }
],
success: (block) => {
console.log(block)
// {
// "hash": "D16FF348B634CDB3DF8...9D6F5B180CCD3B93F99A4D15203",
// "amount": "NANO_AMOUNT_PAID",
// "address": "PAYEE_NANO_ADDRESS",
// "username": "PAYEE_USERNAME",
// "height": "PAYEE_BLOCK_HEIGHT",
// "shipping": "PAYEE_SHIPPING_ADDRESS",
// "email": "PAYEE_EMAIL_ADDRESS",
// "nanolooker": "https://nanolooker.com/block/D16FF348B634CDB3DF8...9D6F5B180CCD3B93F99A4D15203"
// "checkout": "https://api.nano.to/checkout/93be1ab9",
// }
},
cancel: () => {
console.log("User cancelled")
},
})
All Options
- title: (string) Show custom title shown in popup.
- address: (string) Nano address or Nano.to @Username.
- contact: (string) User's email address.
- shipping: (number or bool) User's mailing address. 'true' for free shipping.
- position: (string) Control popup position (top, center, bottom).
- wallet: (string) Custom wallet icon: natrium, nault, nautilus, cake
- button: (string) Custom button text, default "Open Natrium".
- symbol: (string) Custom NANO symbol, default "NANO".
- random: (bool) Use unique payment amounts, default true.
- notify: (string) Send email notification to admin, default false.
- amount: (string) Custom popup amount, used for simple payments.
- line_items: (array) List of products user is buying, replaces amount.
- currency: (string) Fiat currency for Nano price conversion, default "USD".
- note: (string) Custom note shown in admin email, default false.
- qrcode: (string) Always show QR Code, default desktop only.
- success: (function) Called when payment is successful.
- cancel: (function) Called when popup is cancelled.
- checkout: (string) Use custom Nano.to Checkout. Read More.
- email: (string) Pre-configure user's email
- mailing_address: (string) Pre-configure user's shipping address
Notifications
NanoPay offers email notifications for your convenience.
NanoPay.open({
address: '@bank', // Username or Nano Address
notify: '[email protected]',
contact: true,
shipping: true,
amount: 0.133
})

Login with Nano
NanoPay makes Login with Nano easy.
NanoPay.open({
title: "Login",
address: '@bank', // Your App's Address
amount: 0.0001, // Small Amount
success: (block) => {
// {
// "hash": "D16FF348B634CDB3DF8...9D6F5B180CCD3B93F99A4D15203",
// "address": "PAYEE_NANO_ADDRESS",
// "username": "PAYEE_USERNAME",
// ...
// }
console.log("Hello:", block.username || block.address)
}
})
HTML Content Paywall

NanoPay includes an easy way to monetize any website client-side.
Please note, this method does not require a back-end, as such it's not for keeping secrets from public. Google bots can still crawl content.
<style>
/* This css prevents flash of content on page load */
.premium { display: none; }
</style>
<article class="premium">
Premium content is hidden until the payment is completed.
</article>
NanoPay.wall({
element: '.premium',
title: 'Read Story',
button: 'Unlock Story',
free: true, // Allow free reading
amount: 0.001, // Cost of Article
address: '@development', // Your Nano Address or Username
success: (block) => {
// do stuff like render highlight.js
console.log("Thanks for reading:", block.username || block.address)
}
})
I work on Nano part-time. Please consider helping fund this kind of work. Anything helps.
Protecting Premium Content with a Backend
The client-side wall above is useful for demos and simple donations, but it is not content protection. The article HTML is already in the browser, so a reader can inspect the source without paying.
The source repository is part of the security boundary too. Do not keep paid
Markdown in a public repository or public build context. Use a private
repository, a private content store, or an encrypted content workflow whose
decryption key is available only to the server-side build/runtime. Removing the
body from docs/ does not protect a source file that anyone can read on GitHub.
For real premium content, keep the article body on the server and use NanoPay only as the payment interface. A secure flow has four parts:
- The backend selects the article and its fixed price. Never accept the price or article body from the browser.
- The backend creates a checkout at
https://rpc.nano.toand returns the checkout object plus a short-lived, signed capability tied to that checkout and article. - The frontend opens NanoPay with the returned checkout. Its success callback is only a signal to ask the backend for access; it is not proof of payment.
- The backend independently checks the checkout and returns the article only when the response is confirmed complete and contains a block.
The checkout endpoint should use server-owned values and metadata:
const checkout = await fetch('https://rpc.nano.to', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
action: 'checkout',
address: env.PREMIUM_ADDRESS,
amount: article.amount,
payment_monitor: true,
metadata: { type: 'article', slug: article.slug }
})
})
Do not put PREMIUM_SECRET, a private key, or an unredacted checkout token in
frontend code, Markdown, screenshots, or source control. Store signing keys in
the platform secret manager. On Cloudflare Pages, for example:
npx wrangler pages secret put PREMIUM_SECRET --project-name your-project
The content endpoint verifies the signed capability, asks the checkout API for the current payment state, and rejects pending or detected payments:
const capability = verifyCapability(request, env.PREMIUM_SECRET)
if (!capability || capability.slug !== params.slug) return new Response(null, { status: 401 })
const payment = await fetch(`https://rpc.nano.to/check/${capability.checkoutId}`)
.then(response => response.json())
if (payment.status !== 'complete' || !payment.block) {
return Response.json({ status: payment.status || 'pending' }, { status: 402 })
}
return Response.json({ content: await loadPrivateArticle(params.slug) }, {
headers: { 'cache-control': 'no-store' }
})
The browser can then combine that backend with NanoPay without treating local storage, a success callback, a block hash supplied by the browser, or a hidden HTML element as an entitlement:
fetch(`/api/articles/${slug}/checkout`, { method: 'POST' })
.then(response => response.json())
.then(({ checkout, token }) => NanoPay.open({
title: 'Unlock Article',
checkout,
success: () => fetch(`/api/articles/${slug}/content`, {
headers: { authorization: `Bearer ${token}` }
})
}))
Use cache-control: no-store for checkout capabilities and unlocked content.
Add platform rate limiting to checkout creation, keep premium source files out
of public assets and public source repositories, and make every fulfillment
check idempotent. A public checkout URL is not an entitlement by itself; only
the server-side confirmed payment check should grant access.
Nano.to Support
Bugs, Questions & Installation Support:
- Github: Source Code
- Issues: Issues
- Support: [email protected]
- Twitter: @nano2dev
- Mastodon: Xno.Social
- @nano2dev on Nano's Discord