'handleNostrLoggedIn', 'nostrLoggedOut' => 'handleNostrLoggedOut', ]; public function mount(): void { if (NostrAuth::check()) { $this->currentPubkey = NostrAuth::pubkey(); $this->currentPleb = EinundzwanzigPleb::query() ->with([ 'paymentEvents' => fn ($query) => $query->where('year', date('Y')), 'profile', ]) ->where('pubkey', $this->currentPubkey)->first(); if ($this->currentPleb) { $this->email = $this->currentPleb->email; if ($this->currentPleb->nip05_handle) { $this->nip05Handle = $this->currentPleb->nip05_handle; // Verify NIP-05 handle against nostr.json try { $response = \Illuminate\Support\Facades\Http::get( 'https://einundzwanzig.space/.well-known/nostr.json', ); $data = $response->json(); if (isset($data['names'])) { foreach ($data['names'] as $handle => $pubkey) { if ($pubkey === $this->currentPubkey) { $this->nip05Verified = true; $this->nip05VerifiedHandle = $handle; // Check if verified handle differs from database handle if ($handle !== $this->nip05Handle) { $this->nip05HandleMismatch = true; } break; } } } } catch (\Exception) { // Silently fail if nostr.json is not accessible } } $this->no = $this->currentPleb->no_email; $this->showEmail = ! $this->no; $this->amountToPay = config('app.env') === 'production' ? 21000 : 1; if ($this->currentPleb->paymentEvents->count() < 1) { $this->createPaymentEvent(); $this->currentPleb->load('paymentEvents'); } $this->loadEvents(); $this->listenForPayment(); } } } public function updatedNo(): void { $this->showEmail = ! $this->no; $this->currentPleb->update([ 'no_email' => $this->no, ]); } public function updatedFax(): void { $this->js('alert("Markus Turm wird sich per Fax melden!")'); } public function updatedNip05Handle(): void { $this->nip05Handle = strtolower($this->nip05Handle); } public function saveEmail(): void { $this->validate([ 'email' => 'required|email', ]); $this->currentPleb->update([ 'email' => $this->email, ]); Flux::toast('E-Mail Adresse gespeichert.'); } public function saveNip05Handle(): void { $this->validate([ 'nip05Handle' => 'required|string|max:255|regex:/^[a-z0-9_-]+$/|unique:einundzwanzig_plebs,nip05_handle', ]); $nip05Handle = strtolower($this->nip05Handle); $this->currentPleb->update([ 'nip05_handle' => $nip05Handle, ]); Flux::toast('NIP-05 Handle gespeichert.'); } public function pay($comment): mixed { $paymentEvent = $this->currentPleb ->paymentEvents() ->where('year', date('Y')) ->first(); if ($paymentEvent->btc_pay_invoice) { return redirect()->away('https://pay.einundzwanzig.space/i/'.$paymentEvent->btc_pay_invoice); } try { $response = \Illuminate\Support\Facades\Http::withHeaders([ 'Authorization' => 'token '.config('services.btc_pay.api_key'), ])->post( 'https://pay.einundzwanzig.space/api/v1/stores/98PF86BoMd3C8P1nHHyFdoeznCwtcm5yehcAgoCYDQ2a/invoices', [ 'amount' => $this->amountToPay, 'metadata' => [ 'orderId' => $comment, 'orderUrl' => url()->route('association.profile'), 'itemDesc' => 'Mitgliedsbeitrag '.date('Y').' von nostr:'.$this->currentPleb->npub, 'posData' => [ 'event' => $paymentEvent->event_id, 'pubkey' => $this->currentPleb->pubkey, 'npub' => $this->currentPleb->npub, ], ], 'checkout' => [ 'expirationMinutes' => 60 * 24, 'redirectURL' => url()->route('association.profile'), 'redirectAutomatically' => true, 'defaultLanguage' => 'de', ], ], )->throw(); $paymentEvent->btc_pay_invoice = $response->json()['id']; $paymentEvent->save(); return redirect()->away($response->json()['checkoutLink']); } catch (\Exception $e) { Flux::toast( 'Fehler beim Erstellen der Rechnung. Bitte versuche es später erneut: '.$e->getMessage(), variant: 'danger', ); return redirect()->route('association.profile'); } } public function listenForPayment(): void { $paymentEvent = $this->currentPleb ->paymentEvents() ->where('year', date('Y')) ->first(); if ($paymentEvent->btc_pay_invoice) { $response = \Illuminate\Support\Facades\Http::withHeaders([ 'Authorization' => 'token '.config('services.btc_pay.api_key'), ]) ->get( 'https://pay.einundzwanzig.space/api/v1/stores/98PF86BoMd3C8P1nHHyFdoeznCwtcm5yehcAgoCYDQ2a/invoices/'.$paymentEvent->btc_pay_invoice, ); if ($response->json()['status'] === 'Expired') { $paymentEvent->btc_pay_invoice = null; $paymentEvent->paid = false; $paymentEvent->save(); } if ($response->json()['status'] === 'Settled') { $paymentEvent->paid = true; $paymentEvent->save(); $this->currentYearIsPaid = true; } } if ($paymentEvent->paid) { $this->currentYearIsPaid = true; } $paymentEvent = $paymentEvent->refresh(); $this->payments = $this->currentPleb ->paymentEvents() ->where('paid', true) ->get(); } public function save($type): void { $this->form->validate(); if (! $this->form->check) { $this->js('alert("Du musst den Statuten zustimmen.")'); return; } $this->currentPleb ->update([ 'association_status' => $type, ]); } public function createPaymentEvent(): void { $note = new NostrEvent; $note->setKind(32121); $note->setContent( 'Dieses Event dient der Zahlung des Mitgliedsbeitrags für das Jahr '.date( 'Y', ).'. Bitte bezahle den Betrag von '.number_format($this->amountToPay, 0, ',', '.').' Satoshis.', ); $note->setTags([ ['d', $this->currentPleb->pubkey.','.date('Y')], ['zap', 'daf83d92768b5d0005373f83e30d4203c0b747c170449e02fea611a0da125ee6', config('services.relay'), '1'], ]); $signer = new Sign; $signer->signEvent($note, config('services.nostr')); $eventMessage = new EventMessage($note); $relayUrl = config('services.relay'); $relay = new Relay($relayUrl); $relay->setMessage($eventMessage); $result = $relay->send(); $this->currentPleb->paymentEvents()->create([ 'year' => date('Y'), 'event_id' => $result->eventId, 'amount' => $this->amountToPay, ]); } public function loadEvents(): void { $subscription = new Subscription; $subscriptionId = $subscription->setId(); $filter1 = new Filter; $filter1->setKinds([32121]); $filter1->setAuthors(['daf83d92768b5d0005373f83e30d4203c0b747c170449e02fea611a0da125ee6']); $filters = [$filter1]; $requestMessage = new RequestMessage($subscriptionId, $filters); $relays = [ new Relay(config('services.relay')), ]; $relaySet = new RelaySet; $relaySet->setRelays($relays); $request = new Request($relaySet, $requestMessage); $response = $request->send(); $this->events = collect($response[config('services.relay')]) ->map(function ($event) { if (! isset($event->event)) { return false; } return [ 'id' => $event->event->id, 'kind' => $event->event->kind, 'content' => $event->event->content, 'pubkey' => $event->event->pubkey, 'tags' => $event->event->tags, 'created_at' => $event->event->created_at, ]; }) ->filter() ->unique('id') ->toArray(); } public function copyRelayUrl(): void { $relayUrl = 'wss://nostr.einundzwanzig.space'; $this->js("navigator.clipboard.writeText('{$relayUrl}')"); Flux::toast('Relay-Adresse in die Zwischenablage kopiert!'); } } ?>
Exklusive Schreib-Rechte auf Premium Nostr Relay von Einundzwanzig.
Ein Relay ist wie ein Postbote für deine Nostr-Nachrichten. Es speichert und verteilt deine Posts. Um unser Relay nutzen zu können, musst du es in deinem Nostr-Client hinzufügen.
Gehe in deinem Nostr-Client zu den Einstellungen (meistens "Settings" oder "Relays") und füge folgende Relay-Adresse hinzu:
wss://nostr.einundzwanzig.space
Wichtige Hinweise: Du kannst deine Posts auf mehreren Relays gleichzeitig veröffentlichen. So stellst du sicher, dass deine Inhalte auch über unser Relay erreichbar sind.
Verifiziere deine Identität mit einem menschenlesbaren Nostr-Namen.
Regeln für dein Handle: Nur Kleinbuchstaben (a-z), Zahlen (0-9) und die Zeichen "-" und "_" sind erlaubt. Dein Handle wird automatisch kleingeschrieben.
Dein aktiviertes Handle ist {{ $nip05VerifiedHandle }}@einundzwanzig.space
Die Synchronisation zu {{ $nip05Handle }}@einundzwanzig.space wird automatisch im Hintergrund durchgeführt.
Dein Handle {{ $nip05VerifiedHandle }}@einundzwanzig.space ist aktiv verifiziert!
Dein Handle ist in der NIP-05 Konfiguration eingetragen und bereit für die Verwendung.
Dein Handle {{ $nip05Handle }}@einundzwanzig.space ist noch nicht aktiv.
Das Handle ist gespeichert, aber noch nicht in der NIP-05 Konfiguration veröffentlicht. Der Vorstand wird dies bald aktivieren.
Mehr Vorteile kommen bald!
Wir arbeiten ständig daran, unsere Mitglieder-Vorteile auszubauen. Bleib dran für neue exklusive Services und Kooperationen.
@if($currentPleb->profile?->name) {{ $currentPleb->profile->name }} @endif
{{ $currentPleb->pubkey }}
{{ $currentPleb->npub }}
{{ $currentPleb->nip05_handle }}
Perfekt für mobile Android Geräte. Eine App, in der man alle Keys/nsecs verwalten kann.
Browser-Erweiterung in die man seinen Key/nsec eingeben kann. Pro Alby-Konto ein nsec.
Browser-Erweiterung für Chrome Browser. Multi-Key fähig.
Browser-Erweiterung für Firefox Browser. Multi-Key fähig.
Profil in der Datenbank vorhanden.
Nur Personen können Mitglied werden und zahlen 21.000 Satoshis im Jahr. Firmen melden sich bitte direkt an den Vorstand.
Falls du möchtest, kannst du hier eine E-Mail Adresse hinterlegen, damit der Verein dich darüber informieren kann, wenn es Neuigkeiten gibt.
Am besten eine anonymisierte E-Mail Adresse verwenden. Wir sichern diese Adresse AES-256 verschlüsselt in der Datenbank ab.
@if($currentYearIsPaid) Du bist derzeit ein Mitglied des Vereins. Das aktuelle Jahr ist bezahlt. @else Du wirst nach Zahlung des Vereinsbeitrages zum Mitglied. Das aktuelle Jahr ist noch nicht bezahlt. @endif
Nostr Event für die Zahlung des Mitgliedsbeitrags: {{ $currentPleb->paymentEvents->last()->event_id }}
{{ $latestEvent['content'] }}
Unser Nostr-Relay konnte derzeit nicht erreicht werden, um eine Zahlung zu initialisieren. Bitte versuche es später noch einmal.
|
Satoshis
|
Jahr
|
Event-ID
|
Quittung
|
|---|---|---|---|
|
{{ $payment->amount }}
|
{{ $payment->year }}
|
{{ $payment->event_id }}
|
@if($payment->btc_pay_invoice)
|