Nostr Wallet Connect (NIP-47) implementation in PHP (nostr-php)

July 7, 2025 ยท View on GitHub

CI Packagist PHP Version

This project contains a complete client-side implementation of NIP-47 Nostr Wallet Connect for PHP, building upon nostr-php.

This project applied for the NWC Hackathon Grant on Geyser (Geyser project)

Async websocket communication is implemented using valtzu/guzzle-websocket-middleware, which is an opinionated decision and therefore the NWC functionality is provided as a separate library instead of added to the core functionality.

Overview

Nostr Wallet Connect (NWC) allows applications to connect to Lightning wallets over the Nostr protocol in a secure, decentralized way. This implementation provides:

  • Client-side functionality for connecting to NWC wallet services
  • Complete command support for all major Lightning operations
  • Secure encryption using NIP-04 (deprecated) or NIP-44 (recommended) for wallet communications
  • Comprehensive error handling with specific exception types
  • Event-driven architecture following Nostr patterns

Key Features

๐Ÿ”— URI Parsing & Validation

  • Parse and validate NWC connection URIs
  • Support for multiple relays and optional Lightning addresses
  • Secure parameter validation and format checking

๐Ÿ’ฐ Lightning Operations

  • Get Balance - Check wallet balance
  • Pay Invoice - Pay Lightning invoices
  • Make Invoice - Create Lightning invoices
  • Lookup Invoice - Query invoice status
  • Get Info - Retrieve wallet capabilities

๐Ÿ” Security

  • NIP-04/NIP-44 encryption for wallet communications
  • Proper key management and validation
  • Secure event signing and verification

โšก Events

  • Request Events (kind 23194) - Encrypted commands to wallets
  • Response Events (kind 23195) - Encrypted responses from wallets
  • Info Events (kind 13194) - Public wallet capability announcements

Quick Start

1. Basic Connection

use dsbaars\nostr\Nip47\NwcClient;
use dsbaars\nostr\Nip47\NwcUri;

// Parse NWC URI
$nwcUri = 'nostr+walletconnect://wallet_pubkey?relay=wss://relay.com&secret=client_secret';
$client = new NwcClient($nwcUri);

// Check wallet capabilities
$info = $client->getWalletInfo();
echo "Supported methods: " . implode(', ', $info->getMethods());

2. Balance Check

$balance = $client->getBalance();
if ($balance->isSuccess()) {
    echo "Balance: " . $balance->getBalance() . " msats";
    echo "(" . $balance->getBalanceInSats() . " sats)";
}

3. Create Invoice

$invoice = $client->makeInvoice(
    amount: 1000,        // 1000 msats = 1 sat
    description: "Test payment",
    expiry: 3600         // 1 hour
);

if ($invoice->isSuccess()) {
    echo "Invoice: " . $invoice->getInvoice();
    echo "Payment hash: " . $invoice->getPaymentHash();
}

4. Pay Invoice

$payment = $client->payInvoice("lnbc1000n1...");
if ($payment->isPaymentSuccessful()) {
    echo "Payment successful!";
    echo "Preimage: " . $payment->getPreimage();
}

NIP-47 Implementation Status

This implementation provides complete coverage of the NIP-47 specification. Below are detailed tables showing all functionality and implementation status.

๐Ÿ“ก Event Kinds

KindDescriptionPurposeImplementationClass
13194Info EventWallet capability announcementโœ…InfoEvent.php
23194Request EventEncrypted commands to walletโœ…RequestEvent.php
23195Response EventEncrypted responses from walletโœ…ResponseEvent.php
23196Notification EventReal-time wallet notificationsโœ…NotificationEvent.php

๐Ÿš€ Commands

CommandDescriptionParametersImplementationClass
get_infoGet wallet capabilitiesNoneโœ…GetInfoCommand.php
get_balanceGet wallet balanceNoneโœ…GetBalanceCommand.php
pay_invoicePay Lightning invoiceinvoice, amount?โœ…PayInvoiceCommand.php
make_invoiceCreate Lightning invoiceamount, description?, description_hash?, expiry?โœ…MakeInvoiceCommand.php
lookup_invoiceLookup invoice detailspayment_hash?, invoice?โœ…LookupInvoiceCommand.php
list_transactionsList wallet transactionsfrom?, until?, limit?, offset?, unpaid?, type?โœ…ListTransactionsCommand.php
pay_keysendSend keysend paymentamount, pubkey, preimage?, tlv_records?โœ…PayKeysendCommand.php
multi_pay_invoicePay multiple invoicesinvoices[]โœ…MultiPayInvoiceCommand.php
multi_pay_keysendSend multiple keysendskeysends[]โœ…MultiPayKeysendCommand.php

๐Ÿ“จ Responses

ResponseDescriptionFieldsImplementationClass
get_infoWallet info responsealias, color, pubkey, network, block_height, block_hash, methods[], notifications[]โœ…GetInfoResponse.php
get_balanceBalance responsebalanceโœ…GetBalanceResponse.php
pay_invoicePayment responsepreimage, fees_paid?โœ…PayInvoiceResponse.php
make_invoiceInvoice creation responsetype, invoice?, description?, description_hash?, preimage?, payment_hash, amount, fees_paid, created_at, expires_at?, metadata?โœ…MakeInvoiceResponse.php
lookup_invoiceInvoice lookup responsetype, invoice?, description?, description_hash?, preimage?, payment_hash, amount, fees_paid, created_at, expires_at?, settled_at?, metadata?โœ…LookupInvoiceResponse.php
list_transactionsTransaction list responsetransactions[]โœ…ListTransactionsResponse.php
pay_keysendKeysend payment responsepreimage, fees_paid?โœ…PayKeysendResponse.php
multi_pay_invoiceMulti-payment responseMultiple individual responsesโœ…MultiPayInvoiceResponse.php
multi_pay_keysendMulti-keysend responseMultiple individual responsesโœ…MultiPayKeysendResponse.php

๐Ÿ”” Notifications

TypeDescriptionFieldsImplementationClass
payment_receivedPayment successfully receivedtype, invoice, description?, description_hash?, preimage, payment_hash, amount, fees_paid, created_at, expires_at?, settled_at, metadata?โœ…PaymentReceivedNotification.php
payment_sentPayment successfully senttype, invoice, description?, description_hash?, preimage, payment_hash, amount, fees_paid, created_at, expires_at?, settled_at, metadata?โœ…PaymentSentNotification.php

โŒ Error Codes

CodeDescriptionWhen UsedImplementation
RATE_LIMITEDClient sending commands too fastRate limiting exceededโœ…
NOT_IMPLEMENTEDCommand not known/implementedUnsupported methodsโœ…
INSUFFICIENT_BALANCENot enough funds availablePayment amount > balanceโœ…
QUOTA_EXCEEDEDSpending quota exceededBudget limits reachedโœ…
RESTRICTEDOperation not allowedPermission deniedโœ…
UNAUTHORIZEDNo wallet connectedInvalid authorizationโœ…
INTERNALInternal wallet errorServer-side issuesโœ…
OTHEROther unspecified errorCatch-all errorโœ…
PAYMENT_FAILEDPayment processing failedRouting/capacity issuesโœ…
NOT_FOUNDInvoice not foundInvalid payment hash/invoiceโœ…

๐Ÿ”— URI Components

ComponentDescriptionRequiredFormatImplementation
ProtocolNWC protocol identifierโœ…nostr+walletconnect://โœ…
PubkeyWallet service public keyโœ…32-byte hexโœ…
relayRelay URL(s) for communicationโœ…WebSocket URLโœ…
secretClient private keyโœ…32-byte hexโœ…
lud16Lightning addressโŒLightning address formatโœ…

๐Ÿ” Security Features

FeatureDescriptionImplementationNotes
NIP-04 EncryptionEnd-to-end encryption of commands/responsesโœ…Deprecated, using NIP-44 recommended
NIP-44 EncryptionEnd-to-end encryption of commands/responsesโœ…All wallet communications encrypted
Event SigningCryptographic signatures on all eventsโœ…Prevents tampering
Key IsolationUnique keys per wallet connectionโœ…Improves privacy
Relay AuthenticationOptional relay-level authโœ…Metadata protection
Request ExpirationTime-bounded request validityโœ…Prevents replay attacks

๐ŸŽฏ Advanced Features

FeatureDescriptionImplementationClass
WebSocket CommunicationReal-time relay communicationโœ…NwcClient.php
Notification ListenerReal-time payment notificationsโœ…NwcNotificationListener.php
Multi-Command SupportBatch payment operationsโœ…MultiPay*Command.php
Filter ManagementSubscription filteringโœ…NwcClient.php
Connection ValidationURI and capability validationโœ…NwcUri.php
Error HandlingComprehensive exception systemโœ…Exception/ namespace
Logging SupportConfigurable loggingโœ…PSR-3 compatible

Directory Structure

src/Nip47/
โ”œโ”€โ”€ NwcClient.php                    # Main client implementation
โ”œโ”€โ”€ NwcNotificationListener.php      # Real-time notification listener  
โ”œโ”€โ”€ NwcUri.php                       # URI parsing and validation
โ”œโ”€โ”€ NwcUriInterface.php              # URI interface
โ”œโ”€โ”€ ErrorCode.php                    # NWC error codes enum
โ”‚
โ”œโ”€โ”€ Command/                         # Command implementations
โ”‚   โ”œโ”€โ”€ CommandInterface.php
โ”‚   โ”œโ”€โ”€ AbstractCommand.php
โ”‚   โ”œโ”€โ”€ GetBalanceCommand.php        # โœ… get_balance
โ”‚   โ”œโ”€โ”€ GetInfoCommand.php           # โœ… get_info
โ”‚   โ”œโ”€โ”€ PayInvoiceCommand.php        # โœ… pay_invoice
โ”‚   โ”œโ”€โ”€ MakeInvoiceCommand.php       # โœ… make_invoice
โ”‚   โ”œโ”€โ”€ LookupInvoiceCommand.php     # โœ… lookup_invoice
โ”‚   โ”œโ”€โ”€ ListTransactionsCommand.php  # โœ… list_transactions
โ”‚   โ”œโ”€โ”€ PayKeysendCommand.php        # โœ… pay_keysend
โ”‚   โ”œโ”€โ”€ MultiPayInvoiceCommand.php   # โœ… multi_pay_invoice
โ”‚   โ””โ”€โ”€ MultiPayKeysendCommand.php   # โœ… multi_pay_keysend
โ”‚
โ”œโ”€โ”€ Response/                        # Response implementations
โ”‚   โ”œโ”€โ”€ ResponseInterface.php
โ”‚   โ”œโ”€โ”€ AbstractResponse.php
โ”‚   โ”œโ”€โ”€ GetBalanceResponse.php       # โœ… Balance data + conversions
โ”‚   โ”œโ”€โ”€ GetInfoResponse.php          # โœ… Wallet info + capability checks
โ”‚   โ”œโ”€โ”€ PayInvoiceResponse.php       # โœ… Payment confirmation
โ”‚   โ”œโ”€โ”€ MakeInvoiceResponse.php      # โœ… Invoice creation
โ”‚   โ”œโ”€โ”€ LookupInvoiceResponse.php    # โœ… Invoice details + status
โ”‚   โ”œโ”€โ”€ ListTransactionsResponse.php # โœ… Transaction history
โ”‚   โ”œโ”€โ”€ PayKeysendResponse.php       # โœ… Keysend confirmation
โ”‚   โ”œโ”€โ”€ MultiPayInvoiceResponse.php  # โœ… Batch payment results
โ”‚   โ””โ”€โ”€ MultiPayKeysendResponse.php  # โœ… Batch keysend results
โ”‚
โ”œโ”€โ”€ Event/                           # Nostr event implementations
โ”‚   โ”œโ”€โ”€ RequestEvent.php             # โœ… kind 23194
โ”‚   โ”œโ”€โ”€ ResponseEvent.php            # โœ… kind 23195
โ”‚   โ”œโ”€โ”€ InfoEvent.php                # โœ… kind 13194
โ”‚   โ””โ”€โ”€ NotificationEvent.php        # โœ… kind 23196
โ”‚
โ”œโ”€โ”€ Notification/                    # Notification system
โ”‚   โ”œโ”€โ”€ NotificationInterface.php
โ”‚   โ”œโ”€โ”€ NotificationFactory.php      # โœ… Factory for creating notifications
โ”‚   โ”œโ”€โ”€ PaymentReceivedNotification.php # โœ… payment_received
โ”‚   โ””โ”€โ”€ PaymentSentNotification.php     # โœ… payment_sent
โ”‚
โ””โ”€โ”€ Exception/                       # Exception hierarchy
    โ”œโ”€โ”€ NwcException.php
    โ”œโ”€โ”€ InvalidUriException.php
    โ”œโ”€โ”€ CommandException.php
    โ”œโ”€โ”€ PaymentFailedException.php
    โ”œโ”€โ”€ InsufficientBalanceException.php
    โ”œโ”€โ”€ UnauthorizedException.php
    โ””โ”€โ”€ RateLimitedException.php

Examples

This project includes working examples demonstrating all NIP-47 functionality with a centralized configuration system.

Quick Start

First, configure your wallet connection:

cd examples/
cp config.php.example config.php
# Edit config.php with your actual NWC URI

Running Examples

cd examples/

# Complete wallet functionality demo
php client-example.php

# URI parsing and validation
php uri-parsing-example.php

# End-to-end payment workflow
php payment-flow-example.php

# Real-time notification listening
php notification-listener.php

# Simple wallet info and balance check
php get-info-command.php

Configuration

The examples use environment variables for easy configuration:

# Set your NWC URI
export NWC_URI="nostr+walletconnect://your-wallet-details"

# Enable verbose output
export NWC_VERBOSE=true

# Run any example
php client-example.php

Example Output

Nostr Wallet Connect Client Example
===================================

1. Parsing NWC URI...
   โœ“ Wallet Pubkey: b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4
   โœ“ Relays: wss://relay.getalby.com/v1
   โœ“ Secret: 71a8c14c...
   โœ“ Lightning Address: wallet@example.com

2. Creating NWC client...
   โœ“ Client created with pubkey: a1b2c3d4e5f6789...

3. Getting wallet info...
   โœ“ Wallet connected successfully!
     - Alias: My Lightning Wallet
     - Network: bitcoin
     - Supported methods: get_balance, pay_invoice, make_invoice, lookup_invoice
     - Supported notifications: payment_received, payment_sent

4. Getting wallet balance...
   โœ“ Balance: 100000 msats
     - In sats: 100 sats
     - In BTC: 0.00000100 BTC

5. Creating invoice...
   โœ“ Invoice created!
     - Amount: 21000 msats
     - Description: Test invoice from NWC client
     - Invoice: lnbc210n1pjw8...

6. List transactions...
   โœ“ Found 5 transactions
   - Incoming: 3
   - Outgoing: 2
   - Total amount: 150000 msats
   - Total fees: 2100 msats

Example completed successfully!

Error Handling

The implementation includes comprehensive error handling:

use dsbaars\nostr\Nip47\Exception\PaymentFailedException;
use dsbaars\nostr\Nip47\Exception\InsufficientBalanceException;

try {
    $payment = $client->payInvoice($invoice);
} catch (PaymentFailedException $e) {
    echo "Payment failed: " . $e->getMessage();
} catch (InsufficientBalanceException $e) {
    echo "Insufficient balance: " . $e->getMessage();
} catch (NwcException $e) {
    echo "General NWC error: " . $e->getMessage();
}

NIP-47 Compliance

This implementation supports:

  • โœ… All required event kinds (23194, 23195, 13194, 23196)
  • โœ… All standard commands (get_info, get_balance, pay_invoice, make_invoice, lookup_invoice, list_transactions, pay_keysend, multi_pay_invoice, multi_pay_keysend)
  • โœ… All notification types (payment_received, payment_sent)
  • โœ… All error codes per specification
  • โœ… Proper NIP-04/NIP-44 encryption
  • โœ… URI format validation
  • โœ… Relay communication
  • โœ… Event signing and verification
  • โœ… Real-time WebSocket notifications

Security Considerations

  • Encryption: All communications are encrypted using NIP-04/NIP-44
  • Authentication: Events are signed with client private keys
  • Validation: All inputs are validated before processing
  • Error Handling: Sensitive information is not leaked in errors
  • Timeouts: Commands have reasonable timeout limits
  • Rate Limiting: Built-in support for rate limit handling

Testing

โš ๏ธ Important: Be careful when testing with real wallets and real Bitcoin!

Running Tests

# Run all tests
php vendor/bin/phpunit tests/

# Run specific test file
php vendor/bin/phpunit tests/Nip47Test.php

Testing with Examples

  1. Use testnet wallets for development
  2. Start with small amounts - examples use 1-100 sats by default
  3. Configure safely - set NWC_VERBOSE=true for detailed output
  4. Verify parameters before executing payments
  5. Check capabilities with get-info-command.php first

Test Configuration

# Use testnet amounts
export NWC_TEST_AMOUNT=1000      # 1 sat
export NWC_SMALL_AMOUNT=21000    # 21 sats  
export NWC_MEDIUM_AMOUNT=100000  # 100 sats

# Enable verbose mode
export NWC_VERBOSE=true

# Test basic functionality
cd examples/
php get-info-command.php
php client-example.php

Integration

To integrate NWC into your application:

  1. Parse the NWC URI from user input or configuration
  2. Create an NwcClient instance
  3. Check wallet capabilities with getWalletInfo()
  4. Implement your payment flow using the available commands
  5. Handle errors gracefully with proper exception handling