Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
604d6aa1f4 | ||
|
|
acac49969d | ||
|
|
651e0d7ab0 | ||
|
|
e2b6684dcb | ||
|
|
6149b52b42 | ||
|
|
e7674475cf | ||
|
|
3d917d386f | ||
|
|
e1d9fcbf28 | ||
|
|
365871c077 | ||
|
|
a5515a65f6 | ||
|
|
554286edfa | ||
|
|
487b5e9ec8 | ||
|
|
67a27f8f59 | ||
|
|
de1b7b1074 |
19
Caddyfile
19
Caddyfile
@@ -1,10 +1,8 @@
|
|||||||
{$DOMAIN:localhost} {
|
(common) {
|
||||||
root * /srv
|
root * /srv
|
||||||
encode gzip
|
encode gzip
|
||||||
|
|
||||||
# Security headers
|
|
||||||
header {
|
header {
|
||||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
|
||||||
X-Content-Type-Options "nosniff"
|
X-Content-Type-Options "nosniff"
|
||||||
X-Frame-Options "DENY"
|
X-Frame-Options "DENY"
|
||||||
Referrer-Policy "no-referrer"
|
Referrer-Policy "no-referrer"
|
||||||
@@ -12,13 +10,20 @@
|
|||||||
Content-Security-Policy "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; form-action 'none'; frame-ancestors 'none'; base-uri 'none'"
|
Content-Security-Policy "default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; form-action 'none'; frame-ancestors 'none'; base-uri 'none'"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Short URL rewrite: /s/CODE -> s.php?c=CODE
|
|
||||||
@shorturl path_regexp short ^/s/([a-zA-Z0-9]+)$
|
@shorturl path_regexp short ^/s/([a-zA-Z0-9]+)$
|
||||||
rewrite @shorturl /s.php?c={re.short.1}
|
rewrite @shorturl /s.php?c={re.short.1}
|
||||||
|
|
||||||
# PHP via FPM
|
|
||||||
php_fastcgi 127.0.0.1:9000
|
php_fastcgi 127.0.0.1:9000
|
||||||
|
|
||||||
# Static files
|
|
||||||
file_server
|
file_server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Clearnet (auto-HTTPS)
|
||||||
|
{$DOMAIN:localhost} {
|
||||||
|
import common
|
||||||
|
header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Tor hidden service (HTTP only, no TLS needed)
|
||||||
|
:8080 {
|
||||||
|
import common
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
FROM caddy:2-alpine AS caddy
|
||||||
|
|
||||||
FROM php:8.3-fpm-alpine AS base
|
FROM php:8.3-fpm-alpine AS base
|
||||||
|
|
||||||
|
# Copy Caddy binary from official image (avoids stale Alpine package)
|
||||||
|
COPY --from=caddy /usr/bin/caddy /usr/sbin/caddy
|
||||||
|
|
||||||
# Install PHP curl extension (needed for API proxies)
|
# Install PHP curl extension (needed for API proxies)
|
||||||
RUN apk add --no-cache caddy curl-dev \
|
RUN apk add --no-cache curl-dev \
|
||||||
&& docker-php-ext-install curl \
|
&& docker-php-ext-install curl \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
@@ -31,7 +36,7 @@ COPY Caddyfile /etc/caddy/Caddyfile
|
|||||||
COPY docker-entrypoint.sh /usr/local/bin/
|
COPY docker-entrypoint.sh /usr/local/bin/
|
||||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
EXPOSE 80 443
|
EXPOSE 80 443 8080
|
||||||
|
|
||||||
VOLUME ["/srv/data", "/data/caddy"]
|
VOLUME ["/srv/data", "/data/caddy"]
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ You need a VPS with a domain pointing to it. Then:
|
|||||||
curl -sL https://xmrpay.link/install.sh | sh -s your-domain.com
|
curl -sL https://xmrpay.link/install.sh | sh -s your-domain.com
|
||||||
```
|
```
|
||||||
|
|
||||||
Done. HTTPS is automatic (via Caddy + Let's Encrypt).
|
Done. HTTPS is automatic (via Caddy + Let's Encrypt). A **Tor hidden service** (.onion) is included — the installer shows your onion address after setup.
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
@@ -66,6 +66,9 @@ XMRPAY_IMAGE=schmidt1024/xmrpay:latest
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
docker compose pull && docker compose up -d
|
docker compose pull && docker compose up -d
|
||||||
|
|
||||||
|
# Show your onion address
|
||||||
|
docker exec xmrpay-tor cat /var/lib/tor/hidden_service/hostname
|
||||||
```
|
```
|
||||||
|
|
||||||
### Uninstall
|
### Uninstall
|
||||||
@@ -94,7 +97,7 @@ The public instance at [xmrpay.link](https://xmrpay.link) exists as a demo and f
|
|||||||
- **Payment verification** — sender provides TX Hash + TX Key, cryptographic verification in browser
|
- **Payment verification** — sender provides TX Hash + TX Key, cryptographic verification in browser
|
||||||
- **Fiat conversion** — EUR/USD/CHF/GBP/JPY/RUB/BRL via CoinGecko, auto-detected from locale
|
- **Fiat conversion** — EUR/USD/CHF/GBP/JPY/RUB/BRL via CoinGecko, auto-detected from locale
|
||||||
- **Short URLs** — optional, with explicit trust trade-off warning
|
- **Short URLs** — optional, with explicit trust trade-off warning
|
||||||
- **i18n** — English, German, French, Italian, Spanish, Portuguese, Russian
|
- **i18n** — English, German, French, Italian, Spanish, Portuguese, Russian, Turkish
|
||||||
- **Offline-capable** — Service Worker for offline use
|
- **Offline-capable** — Service Worker for offline use
|
||||||
- **Privacy** — zero cookies, no analytics, no external scripts, self-hosted fonts
|
- **Privacy** — zero cookies, no analytics, no external scripts, self-hosted fonts
|
||||||
|
|
||||||
|
|||||||
4
app.js
4
app.js
@@ -215,7 +215,7 @@
|
|||||||
paymentStatus.innerHTML = '';
|
paymentStatus.innerHTML = '';
|
||||||
paymentStatus.className = 'payment-status';
|
paymentStatus.className = 'payment-status';
|
||||||
paymentSummary.innerHTML = '';
|
paymentSummary.innerHTML = '';
|
||||||
document.title = 'xmrpay.link \u2014 Monero Invoice Generator';
|
document.title = 'xmrpay \u2014 Monero Invoice Generator';
|
||||||
history.replaceState(null, '', location.pathname);
|
history.replaceState(null, '', location.pathname);
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
addrInput.focus();
|
addrInput.focus();
|
||||||
@@ -517,7 +517,7 @@
|
|||||||
if (xmrAmount) parts.push(xmrAmount.toFixed(4) + ' XMR');
|
if (xmrAmount) parts.push(xmrAmount.toFixed(4) + ' XMR');
|
||||||
if (desc) parts.push(desc);
|
if (desc) parts.push(desc);
|
||||||
if (parts.length) {
|
if (parts.length) {
|
||||||
document.title = parts.join(' — ') + ' | xmrpay.link';
|
document.title = parts.join(' — ') + ' | xmrpay';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
app.min.js
vendored
2
app.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -12,6 +12,29 @@ services:
|
|||||||
- xmrpay-data:/srv/data
|
- xmrpay-data:/srv/data
|
||||||
- caddy-data:/data/caddy
|
- caddy-data:/data/caddy
|
||||||
|
|
||||||
|
tor:
|
||||||
|
image: alpine:latest
|
||||||
|
container_name: xmrpay-tor
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- xmrpay
|
||||||
|
entrypoint: /bin/sh
|
||||||
|
command:
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
apk add --no-cache tor > /dev/null 2>&1
|
||||||
|
mkdir -p /var/lib/tor/hidden_service
|
||||||
|
chmod 700 /var/lib/tor/hidden_service
|
||||||
|
cat > /etc/tor/torrc <<EOF
|
||||||
|
SocksPort 0
|
||||||
|
HiddenServiceDir /var/lib/tor/hidden_service
|
||||||
|
HiddenServicePort 80 xmrpay:8080
|
||||||
|
EOF
|
||||||
|
echo "Starting Tor..."
|
||||||
|
tor -f /etc/tor/torrc
|
||||||
|
volumes:
|
||||||
|
- tor-keys:/var/lib/tor/hidden_service
|
||||||
|
|
||||||
watchtower:
|
watchtower:
|
||||||
image: containrrr/watchtower
|
image: containrrr/watchtower
|
||||||
container_name: watchtower
|
container_name: watchtower
|
||||||
@@ -25,3 +48,4 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
xmrpay-data:
|
xmrpay-data:
|
||||||
caddy-data:
|
caddy-data:
|
||||||
|
tor-keys:
|
||||||
|
|||||||
72
i18n.js
72
i18n.js
@@ -8,10 +8,11 @@ var I18n = (function () {
|
|||||||
it: { name: 'Italiano' },
|
it: { name: 'Italiano' },
|
||||||
es: { name: 'Español' },
|
es: { name: 'Español' },
|
||||||
pt: { name: 'Português' },
|
pt: { name: 'Português' },
|
||||||
ru: { name: 'Русский' }
|
ru: { name: 'Русский' },
|
||||||
|
tr: { name: 'Türkçe' }
|
||||||
};
|
};
|
||||||
|
|
||||||
var VERSION = '1.0.0';
|
var VERSION = '1.2.1';
|
||||||
|
|
||||||
var footer = 'Open Source · No Tracking · No KYC<br /><a href="https://github.com/schmidt1024/xmrpay" target="_blank" rel="noopener noreferrer">Source</a> · <a href="http://mc6wfeaqc7oijgdcudrr5zsotmwok3jzk3tu2uezzyjisn7nzzjjizyd.onion" title="Tor Hidden Service">Onion</a> · <a href="/privacy.html">Privacy & Terms</a><br /><span class="version">v' + VERSION + '</span>';
|
var footer = 'Open Source · No Tracking · No KYC<br /><a href="https://github.com/schmidt1024/xmrpay" target="_blank" rel="noopener noreferrer">Source</a> · <a href="http://mc6wfeaqc7oijgdcudrr5zsotmwok3jzk3tu2uezzyjisn7nzzjjizyd.onion" title="Tor Hidden Service">Onion</a> · <a href="/privacy.html">Privacy & Terms</a><br /><span class="version">v' + VERSION + '</span>';
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ var I18n = (function () {
|
|||||||
pdf_deadline_days: '{d} days',
|
pdf_deadline_days: '{d} days',
|
||||||
pdf_date: 'Date',
|
pdf_date: 'Date',
|
||||||
pdf_scan_qr: 'Scan QR code to pay',
|
pdf_scan_qr: 'Scan QR code to pay',
|
||||||
pdf_footer: 'Created with xmrpay.link',
|
pdf_footer: 'Created with xmrpay',
|
||||||
qr_hint: 'Click QR to save',
|
qr_hint: 'Click QR to save',
|
||||||
self_host_banner: 'This is a public demo. For real payments, <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">host your own instance</a> — it takes 60 seconds.',
|
self_host_banner: 'This is a public demo. For real payments, <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">host your own instance</a> — it takes 60 seconds.',
|
||||||
footer: footer,
|
footer: footer,
|
||||||
@@ -92,7 +93,7 @@ var I18n = (function () {
|
|||||||
pdf_deadline_days: '{d} Tage',
|
pdf_deadline_days: '{d} Tage',
|
||||||
pdf_date: 'Datum',
|
pdf_date: 'Datum',
|
||||||
pdf_scan_qr: 'QR-Code scannen zum Bezahlen',
|
pdf_scan_qr: 'QR-Code scannen zum Bezahlen',
|
||||||
pdf_footer: 'Erstellt mit xmrpay.link',
|
pdf_footer: 'Erstellt mit xmrpay',
|
||||||
self_host_banner: 'Dies ist eine öffentliche Demo. Für echte Zahlungen <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">eigene Instanz hosten</a> — dauert 60 Sekunden.',
|
self_host_banner: 'Dies ist eine öffentliche Demo. Für echte Zahlungen <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">eigene Instanz hosten</a> — dauert 60 Sekunden.',
|
||||||
qr_hint: 'Klick auf QR zum Speichern',
|
qr_hint: 'Klick auf QR zum Speichern',
|
||||||
footer: footer,
|
footer: footer,
|
||||||
@@ -145,7 +146,7 @@ var I18n = (function () {
|
|||||||
pdf_deadline_days: '{d} jours',
|
pdf_deadline_days: '{d} jours',
|
||||||
pdf_date: 'Date',
|
pdf_date: 'Date',
|
||||||
pdf_scan_qr: 'Scanner le QR code pour payer',
|
pdf_scan_qr: 'Scanner le QR code pour payer',
|
||||||
pdf_footer: 'Créé avec xmrpay.link',
|
pdf_footer: 'Créé avec xmrpay',
|
||||||
self_host_banner: 'Ceci est une démo publique. Pour de vrais paiements, <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">hébergez votre propre instance</a> — ça prend 60 secondes.',
|
self_host_banner: 'Ceci est une démo publique. Pour de vrais paiements, <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">hébergez votre propre instance</a> — ça prend 60 secondes.',
|
||||||
qr_hint: 'Cliquez sur le QR pour enregistrer',
|
qr_hint: 'Cliquez sur le QR pour enregistrer',
|
||||||
footer: footer,
|
footer: footer,
|
||||||
@@ -198,7 +199,7 @@ var I18n = (function () {
|
|||||||
pdf_deadline_days: '{d} giorni',
|
pdf_deadline_days: '{d} giorni',
|
||||||
pdf_date: 'Data',
|
pdf_date: 'Data',
|
||||||
pdf_scan_qr: 'Scansiona il QR per pagare',
|
pdf_scan_qr: 'Scansiona il QR per pagare',
|
||||||
pdf_footer: 'Creato con xmrpay.link',
|
pdf_footer: 'Creato con xmrpay',
|
||||||
self_host_banner: 'Questa è una demo pubblica. Per pagamenti reali, <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">ospita la tua istanza</a> — ci vogliono 60 secondi.',
|
self_host_banner: 'Questa è una demo pubblica. Per pagamenti reali, <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">ospita la tua istanza</a> — ci vogliono 60 secondi.',
|
||||||
qr_hint: 'Clicca sul QR per salvare',
|
qr_hint: 'Clicca sul QR per salvare',
|
||||||
footer: footer,
|
footer: footer,
|
||||||
@@ -251,7 +252,7 @@ var I18n = (function () {
|
|||||||
pdf_deadline_days: '{d} días',
|
pdf_deadline_days: '{d} días',
|
||||||
pdf_date: 'Fecha',
|
pdf_date: 'Fecha',
|
||||||
pdf_scan_qr: 'Escanear QR para pagar',
|
pdf_scan_qr: 'Escanear QR para pagar',
|
||||||
pdf_footer: 'Creado con xmrpay.link',
|
pdf_footer: 'Creado con xmrpay',
|
||||||
self_host_banner: 'Esta es una demo pública. Para pagos reales, <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">aloja tu propia instancia</a> — toma 60 segundos.',
|
self_host_banner: 'Esta es una demo pública. Para pagos reales, <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">aloja tu propia instancia</a> — toma 60 segundos.',
|
||||||
qr_hint: 'Clic en QR para guardar',
|
qr_hint: 'Clic en QR para guardar',
|
||||||
footer: footer,
|
footer: footer,
|
||||||
@@ -304,7 +305,7 @@ var I18n = (function () {
|
|||||||
pdf_deadline_days: '{d} dias',
|
pdf_deadline_days: '{d} dias',
|
||||||
pdf_date: 'Data',
|
pdf_date: 'Data',
|
||||||
pdf_scan_qr: 'Digitalizar QR para pagar',
|
pdf_scan_qr: 'Digitalizar QR para pagar',
|
||||||
pdf_footer: 'Criado com xmrpay.link',
|
pdf_footer: 'Criado com xmrpay',
|
||||||
self_host_banner: 'Esta é uma demo pública. Para pagamentos reais, <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">hospede sua própria instância</a> — leva 60 segundos.',
|
self_host_banner: 'Esta é uma demo pública. Para pagamentos reais, <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">hospede sua própria instância</a> — leva 60 segundos.',
|
||||||
qr_hint: 'Clique no QR para guardar',
|
qr_hint: 'Clique no QR para guardar',
|
||||||
footer: footer,
|
footer: footer,
|
||||||
@@ -357,7 +358,7 @@ var I18n = (function () {
|
|||||||
pdf_deadline_days: '{d} дней',
|
pdf_deadline_days: '{d} дней',
|
||||||
pdf_date: 'Дата',
|
pdf_date: 'Дата',
|
||||||
pdf_scan_qr: 'Сканируйте QR для оплаты',
|
pdf_scan_qr: 'Сканируйте QR для оплаты',
|
||||||
pdf_footer: 'Создано с помощью xmrpay.link',
|
pdf_footer: 'Создано с помощью xmrpay',
|
||||||
self_host_banner: 'Это публичная демо-версия. Для реальных платежей <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">разверните свой экземпляр</a> — это займёт 60 секунд.',
|
self_host_banner: 'Это публичная демо-версия. Для реальных платежей <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">разверните свой экземпляр</a> — это займёт 60 секунд.',
|
||||||
qr_hint: 'Нажмите на QR для сохранения',
|
qr_hint: 'Нажмите на QR для сохранения',
|
||||||
footer: footer,
|
footer: footer,
|
||||||
@@ -386,6 +387,59 @@ var I18n = (function () {
|
|||||||
status_pending: 'Ожидание',
|
status_pending: 'Ожидание',
|
||||||
proof_confirmed_pending: 'Выход найден: {amount} XMR — {n}/10 подтверждений. Авт. обновление…',
|
proof_confirmed_pending: 'Выход найден: {amount} XMR — {n}/10 подтверждений. Авт. обновление…',
|
||||||
toast_integrity_warning: 'Предупреждение: обнаружено несоответствие подписи'
|
toast_integrity_warning: 'Предупреждение: обнаружено несоответствие подписи'
|
||||||
|
},
|
||||||
|
tr: {
|
||||||
|
subtitle: 'Saniyeler içerisinde Monero ödeme talebi',
|
||||||
|
label_addr: 'XMR Adresi',
|
||||||
|
placeholder_addr: '8...',
|
||||||
|
label_amount: 'Tutar',
|
||||||
|
label_desc: 'Açıklama (isteğe bağlı)',
|
||||||
|
placeholder_desc: 'örn. 42 numaralı Fatura, freelance iş için...',
|
||||||
|
label_timer: 'Ödeme için son tarih (isteğe bağlı)',
|
||||||
|
days: 'gün',
|
||||||
|
placeholder_timer_custom: 'Gün',
|
||||||
|
btn_generate: 'Ödeme talebi oluştur',
|
||||||
|
btn_open_wallet: 'Cüzdanda aç',
|
||||||
|
btn_copy_uri: 'URI kopyala',
|
||||||
|
btn_copy_addr: 'Adresi kopyala',
|
||||||
|
btn_download_pdf: 'Fatura (PDF)',
|
||||||
|
pdf_title: 'Ödeme Talebi',
|
||||||
|
pdf_address: 'XMR Adresi',
|
||||||
|
pdf_amount: 'Tutar',
|
||||||
|
pdf_desc: 'Açıklama',
|
||||||
|
pdf_deadline: 'Ödeme için son tarih',
|
||||||
|
pdf_deadline_days: '{d} gün',
|
||||||
|
pdf_date: 'Tarih',
|
||||||
|
pdf_scan_qr: 'Ödeme için QR kodu tara',
|
||||||
|
pdf_footer: 'xmrpay ile oluşturulmuştur',
|
||||||
|
qr_hint: 'QR kodu kaydetmek için tıkla',
|
||||||
|
self_host_banner: 'Bu bir herkese açık demodur. Gerçek ödemeler için <a href="https://github.com/schmidt1024/xmrpay#self-host-in-60-seconds">kendi sunucunuzu kurun</a> — sadece 60 saniye sürer.',
|
||||||
|
footer: footer,
|
||||||
|
aria_currency: 'Para Birimi',
|
||||||
|
label_share_link: 'Paylaşılabilir bağlantı',
|
||||||
|
shortlink_toggle_label: 'Kısaltılmış bağlantı kullan (sunucuya güven gerektirir)',
|
||||||
|
shortlink_toggle_hint: 'Olası dezavantaj: kısaltılmış bağlantılar kullanışlıdır, fakat ilgili sunucu güvende değil ise ilk erişimde fatura verileri değiştirilebilir.',
|
||||||
|
btn_new_request: 'Yeni ödeme talebi',
|
||||||
|
toast_copied: 'Kopyalandı!',
|
||||||
|
countdown_expired: 'Ödeme için son tarih süresi doldu',
|
||||||
|
countdown_remaining_days: 'Son Tarih: {d} gün, {h} saat',
|
||||||
|
countdown_remaining_hours: 'Son Tarih: {h}:{m} saat',
|
||||||
|
rates_offline: 'Kurlar mevcut değil — Yalnızca XMR tutarı',
|
||||||
|
btn_prove_payment: 'Ödemeyi doğrula',
|
||||||
|
label_tx_hash: 'İşlem kimliği (TX Hash)',
|
||||||
|
placeholder_tx_hash: '64 hex karakteri...',
|
||||||
|
label_tx_key: 'İşlem Anahtarı (TX Key)',
|
||||||
|
placeholder_tx_key: '64 hex karakteri...',
|
||||||
|
btn_verify_proof: 'Ödemeyi onayla',
|
||||||
|
proof_verifying: 'Onaylanıyor...',
|
||||||
|
proof_verified: 'Ödeme onaylandı: {amount} XMR',
|
||||||
|
proof_no_match: 'Çıktılar eşleşmiyor — TX anahtarı ya da adresi eşleşmiyor',
|
||||||
|
proof_tx_not_found: 'İşlem bulunamadı',
|
||||||
|
proof_error: 'Onaylama hatası',
|
||||||
|
status_paid: 'Ödeme yapıldı',
|
||||||
|
status_pending: 'Beklemede',
|
||||||
|
proof_confirmed_pending: 'Bulunan çıktı: {amount} XMR — {n}/10 tamamlanan. Otomatik yenileniyor…',
|
||||||
|
toast_integrity_warning: 'Uyarı: eşleşmeyen imza tespit edildi'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
2
i18n.min.js
vendored
2
i18n.min.js
vendored
File diff suppressed because one or more lines are too long
14
index.html
14
index.html
@@ -3,12 +3,12 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>xmrpay.link — Monero Invoice Generator</title>
|
<title>xmrpay — Monero Invoice Generator</title>
|
||||||
<meta name="description" content="Create Monero payment requests in seconds. No account registration, no KYC. Minimal backend for short URLs only.">
|
<meta name="description" content="Self-hosted Monero payment requests in seconds. No accounts, no KYC, no tracking. Generate QR codes, PDF invoices, and verify payments.">
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; form-action 'none'; base-uri 'none'">
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self'; connect-src 'self'; form-action 'none'; base-uri 'none'">
|
||||||
<link rel="icon" id="favicon" href="favicon.svg" type="image/svg+xml">
|
<link rel="icon" id="favicon" href="favicon.svg" type="image/svg+xml">
|
||||||
<link rel="preload" href="fonts/inter-400.woff2" as="font" type="font/woff2" crossorigin>
|
<link rel="preload" href="fonts/inter-400.woff2" as="font" type="font/woff2" crossorigin>
|
||||||
<link rel="stylesheet" href="style.css?v=20260326-3" integrity="sha384-TLao5+UFp5VS0Vn+LamdOYwxjGy1ZB0dNemTi7Za0HpPsnA+koCWOmVM0Szwaf3n" crossorigin="anonymous">
|
<link rel="stylesheet" href="style.css?v=20260326-3" integrity="sha384-HrVyafi6sY5wzJh/jPfdCAq5WytRoWDiUnZ/Y05Xt2Oz1C+kLZLO47euo7q3fv46" crossorigin="anonymous">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1><a href="/" id="homeLink">xmr<span>pay</span>.link</a></h1>
|
<h1><a href="/" id="homeLink">xmr<span>pay</span></a></h1>
|
||||||
<p data-i18n="subtitle">Monero payment request in seconds</p>
|
<p data-i18n="subtitle">Monero payment request in seconds</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p data-i18n-html="footer">Open Source · No Tracking · No KYC<br /><a href="https://github.com/schmidt1024/xmrpay" target="_blank" rel="noopener noreferrer">Source</a> · <a href="http://mc6wfeaqc7oijgdcudrr5zsotmwok3jzk3tu2uezzyjisn7nzzjjizyd.onion" title="Tor Hidden Service">Onion</a> · <a href="/privacy.html">Privacy & Terms</a><br /><span class="version">v1.0.0</span></p>
|
<p data-i18n-html="footer">Open Source · No Tracking · No KYC<br /><a href="https://github.com/schmidt1024/xmrpay" target="_blank" rel="noopener noreferrer">Source</a> · <a href="http://mc6wfeaqc7oijgdcudrr5zsotmwok3jzk3tu2uezzyjisn7nzzjjizyd.onion" title="Tor Hidden Service">Onion</a> · <a href="/privacy.html">Privacy & Terms</a><br /><span class="version">v1.2.1</span></p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<div class="lang-picker" id="langPicker">
|
<div class="lang-picker" id="langPicker">
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
<div class="toast" id="toast"></div>
|
<div class="toast" id="toast"></div>
|
||||||
|
|
||||||
<script src="lib/qrcode.min.js?v=20260326-3" integrity="sha384-3zSEDfvllQohrq0PHL1fOXJuC/jSOO34H46t6UQfobFOmxE5BpjjaIJY5F2/bMnU" crossorigin="anonymous" defer></script>
|
<script src="lib/qrcode.min.js?v=20260326-3" integrity="sha384-3zSEDfvllQohrq0PHL1fOXJuC/jSOO34H46t6UQfobFOmxE5BpjjaIJY5F2/bMnU" crossorigin="anonymous" defer></script>
|
||||||
<script src="i18n.min.js?v=20260326-3" integrity="sha384-mKqQalrpTWCzSD1ErLtp+GqHpzJDm7D1jzHDMuhCW7ql2v9YkEzSfE4PNuTj4dqU" crossorigin="anonymous" defer></script>
|
<script src="i18n.min.js?v=20260326-3" integrity="sha384-GS62r/FP1LcB9Ec+ow+45oUWdQsjZKKwtPT6D/YXBfgGjUCjtpuxeLE3GMtbItgx" crossorigin="anonymous" defer></script>
|
||||||
<script src="app.min.js?v=20260326-3" integrity="sha384-tdgiaUZYJ6E+/EqlbzOvxRvySKQZNdxjNktRV3K75fitMLdkR5DXuVU9XTppNku5" crossorigin="anonymous" defer></script>
|
<script src="app.min.js?v=20260326-3" integrity="sha384-Y8cPBLtvKkMhHUuD+ElA1hWJHo86yO5MRs8HTUvhuK9h+lwo9WT9eBvRM7mRgtCr" crossorigin="anonymous" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
18
install.sh
18
install.sh
@@ -52,7 +52,23 @@ docker compose up -d
|
|||||||
|
|
||||||
ok "xmrpay is running!"
|
ok "xmrpay is running!"
|
||||||
echo ""
|
echo ""
|
||||||
echo " https://$DOMAIN"
|
echo " Clearnet: https://$DOMAIN"
|
||||||
|
|
||||||
|
# Wait for Tor to generate the onion address (up to 30s)
|
||||||
|
info "Waiting for Tor hidden service..."
|
||||||
|
ONION=""
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
ONION=$(docker exec xmrpay-tor cat /var/lib/tor/hidden_service/hostname 2>/dev/null || true)
|
||||||
|
[ -n "$ONION" ] && break
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
if [ -n "$ONION" ]; then
|
||||||
|
ok "Tor hidden service ready"
|
||||||
|
echo " Onion: http://$ONION"
|
||||||
|
else
|
||||||
|
echo " Onion: (still starting — run: docker exec xmrpay-tor cat /var/lib/tor/hidden_service/hostname)"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo " Watchtower checks for updates every 6 hours."
|
echo " Watchtower checks for updates every 6 hours."
|
||||||
echo " Data stored in Docker volume: xmrpay-data"
|
echo " Data stored in Docker volume: xmrpay-data"
|
||||||
|
|||||||
33
privacy.html
33
privacy.html
@@ -3,11 +3,11 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>xmrpay.link — Privacy & Terms</title>
|
<title>xmrpay — Privacy & Terms</title>
|
||||||
<meta name="description" content="Privacy policy and terms of use for xmrpay.link.">
|
<meta name="description" content="Privacy policy and terms of use for xmrpay.">
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self'; base-uri 'none'">
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self'; base-uri 'none'">
|
||||||
<link rel="icon" href="favicon.svg" type="image/svg+xml">
|
<link rel="icon" href="favicon.svg" type="image/svg+xml">
|
||||||
<link rel="stylesheet" href="style.css?v=20260326-3" integrity="sha384-TLao5+UFp5VS0Vn+LamdOYwxjGy1ZB0dNemTi7Za0HpPsnA+koCWOmVM0Szwaf3n" crossorigin="anonymous">
|
<link rel="stylesheet" href="style.css?v=20260326-3" integrity="sha384-HrVyafi6sY5wzJh/jPfdCAq5WytRoWDiUnZ/Y05Xt2Oz1C+kLZLO47euo7q3fv46" crossorigin="anonymous">
|
||||||
<style>
|
<style>
|
||||||
main.legal-main {
|
main.legal-main {
|
||||||
max-width: 920px;
|
max-width: 920px;
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<h1><a href="/" id="homeLink">xmr<span>pay</span>.link</a></h1>
|
<h1><a href="/" id="homeLink">xmr<span>pay</span></a></h1>
|
||||||
<p>Privacy & Terms</p>
|
<p>Privacy & Terms</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -193,6 +193,25 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="legal-lang" data-lang="tr">
|
||||||
|
<h2>Türkçe</h2>
|
||||||
|
<h3>Mahremiyet Politikası</h3>
|
||||||
|
<p>xmrpay.link veri toplama işlemini asgari seviyeye indirecek şekilde tasarlanmıştır. Hesap oluşturmaya gerek yoktur.</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Hız sınırlaması:</strong> kötüye kullanım karşıtı koruma mekanizması, IP'lerden gelen istekleri kısa süreli dosyalarda tutar. İlgili IP adresleri ham hali ile değil, hash'lenmiş olarak barındırılır.</li>
|
||||||
|
<li><strong>Kısaltılmış bağlantılar:</strong> fatura hash verileri, oluşturulan kısa URL'ler için saklanır.</li>
|
||||||
|
<li><strong>Ödeme onayı:</strong> eğer kullanıldılar ise; tx hash değeri, tutar, onaylamalar ve zaman mührü saklanır. Onay veritabanında hiçbir Monero adresi saklanmaz.</li>
|
||||||
|
<li><strong>Hiçbir şey takip edilmez:</strong> istatistik ve reklam olmadığı gibi herhangi bir profilleme eylemi barındırmaz.</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Kullanım Koşulları</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Tüm servis "olduğu gibi" herhangi bir garanti olmadan sunulmaktadır.</li>
|
||||||
|
<li>Tâbi olduğunuz yasalara uyum hususunda tüm sorumluluk size aittir.</li>
|
||||||
|
<li>Servisin herhangi bir nedenle istismarı, yasadışı alanda kullanımı ve/veya servise yönelik saldırılar yasaklı eylem statüsündedir.</li>
|
||||||
|
<li>İlgili kullanılabilirlik garanti edilmemiştir, tüm özellikler herhangi bir zaman aralığında değişebilir.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
<p style="margin-top:1rem;color:var(--text-muted);font-size:0.82rem;">Last updated: 2026-03-26</p>
|
<p style="margin-top:1rem;color:var(--text-muted);font-size:0.82rem;">Last updated: 2026-03-26</p>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
@@ -212,10 +231,10 @@
|
|||||||
<div class="lang-dropdown" id="langDropdown"></div>
|
<div class="lang-dropdown" id="langDropdown"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="i18n.min.js?v=20260326-3" defer></script>
|
<script src="i18n.min.js?v=20260326-3" integrity="sha384-GS62r/FP1LcB9Ec+ow+45oUWdQsjZKKwtPT6D/YXBfgGjUCjtpuxeLE3GMtbItgx" crossorigin="anonymous" defer></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
var supported = ['en', 'de', 'fr', 'it', 'es', 'pt', 'ru'];
|
var supported = ['en', 'de', 'fr', 'it', 'es', 'pt', 'ru', 'tr'];
|
||||||
var sections = document.querySelectorAll('.legal-lang');
|
var sections = document.querySelectorAll('.legal-lang');
|
||||||
|
|
||||||
function applyLang(lang) {
|
function applyLang(lang) {
|
||||||
|
|||||||
Reference in New Issue
Block a user