aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/names.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-05 19:38:10 -0700
committerAdam Soltys <[email protected]>2026-09-05 19:38:10 -0700
commitae85dc0afba70f40b333cf03c64abbbf4c8dfea0 (patch)
tree869f84166f0ad9589ef85504d75376ef7446598d /src/features/names.js
parentefc4355b015a20fc346f3c9d985b75ae8dfd45c0 (diff)
downloadcoinosv3-ae85dc0afba70f40b333cf03c64abbbf4c8dfea0.tar.gz
coinosv3-ae85dc0afba70f40b333cf03c64abbbf4c8dfea0.zip
Point of sale: a coinos-pos terminal works against a name
Registrar: the owner mints a bearer POS token from Settings (NIP-98 POST /pos/token, DELETE revokes; /admin/pos/token for operators). With it the terminal gets /pos/me, rings up sales at /pos/invoice (fiat via coinos.io rates, or sats), reads /pos/payments, and holds a websocket at /pos/ws that gets a payment message when the sale settles. Sales are always minted on our node so the settle loop sees them and the ordinary forward path delivers the sats to the name's ark address. The name's lnurlp pins the newest unpaid sale under five minutes old — coinos.io's /p/<name> rule — so an NFC tap or a scan pays exactly that invoice. Wallet: a "Point of sale" section on the names card creates, copies and revokes tokens. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01VMi2Y1oKPZ3TeeFmLej47y
Diffstat (limited to 'src/features/names.js')
-rw-r--r--src/features/names.js25
1 files changed, 25 insertions, 0 deletions
diff --git a/src/features/names.js b/src/features/names.js
index bd4ab4d..dc3426e 100644
--- a/src/features/names.js
+++ b/src/features/names.js
@@ -437,6 +437,10 @@ export function namesFeature(ctx) {
copyBtn(code, t('namesZapCodeCopy'))) : null;
})(),
h('details', { class: 'small faint' },
+ h('summary', {}, t('namesPos')),
+ h('p', { style: 'margin:4px 0' }, t('namesPosHow')),
+ posSection(st)),
+ h('details', { class: 'small faint' },
h('summary', {}, t('namesOwnNode')),
h('p', { style: 'margin:4px 0' }, t('namesOwnNodeMenuHow')),
ownNodeSection()),
@@ -452,6 +456,27 @@ export function namesFeature(ctx) {
claimForm(false));
}
+ // A coinos POS terminal (coinos-pos) takes payments to this name with a
+ // bearer token the registrar mints for the owner: the terminal can ring up
+ // sales and see them settle, never spend. Shown once, never stored — the
+ // token goes straight into the terminal's config at coinos.io/pos.
+ function posSection(st) {
+ const tok = ui.posToken;
+ const body = () => ({ name: st.name, domain: st.domain || DOMAIN() });
+ return h('div', { class: 'col', style: 'gap:6px' },
+ tok ? h('div', { class: 'addr-box break', style: 'font-size:10px' }, tok) : null,
+ h('div', { class: 'row gap6', style: 'flex-wrap:wrap' },
+ h('button', { class: 'btn-ghost btn-sm', onClick: async () => {
+ try { ui.posToken = (await post('/pos/token', 'POST', body())).token; render(); }
+ catch (e) { toast(e.message); }
+ } }, t('namesPosCreate')),
+ tok ? copyBtn(tok, t('namesPosCopy')) : null,
+ h('button', { class: 'btn-ghost btn-sm', onClick: async () => {
+ try { await post('/pos/token', 'DELETE', body()); ui.posToken = null; toast(t('namesPosRevoked')); render(); }
+ catch (e) { toast(e.message); }
+ } }, t('namesPosRevoke'))));
+ }
+
// The Receive tab's default pane: your name, big and scannable.
function namePane(seg) {
const st = load();