@unicitylabs/nostr-js-sdk 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.js +220 -7
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.min.js +7 -7
- package/dist/browser/index.min.js.map +1 -1
- package/dist/browser/index.umd.js +220 -6
- package/dist/browser/index.umd.js.map +1 -1
- package/dist/browser/index.umd.min.js +7 -7
- package/dist/browser/index.umd.min.js.map +1 -1
- package/dist/cjs/client/NostrClient.js +42 -5
- package/dist/cjs/client/NostrClient.js.map +1 -1
- package/dist/cjs/payment/PaymentRequestProtocol.js +170 -0
- package/dist/cjs/payment/PaymentRequestProtocol.js.map +1 -1
- package/dist/cjs/protocol/EventKinds.js +5 -1
- package/dist/cjs/protocol/EventKinds.js.map +1 -1
- package/dist/esm/client/NostrClient.js +42 -5
- package/dist/esm/client/NostrClient.js.map +1 -1
- package/dist/esm/payment/PaymentRequestProtocol.js +162 -0
- package/dist/esm/payment/PaymentRequestProtocol.js.map +1 -1
- package/dist/esm/protocol/EventKinds.js +4 -0
- package/dist/esm/protocol/EventKinds.js.map +1 -1
- package/dist/types/client/NostrClient.d.ts +23 -0
- package/dist/types/client/NostrClient.d.ts.map +1 -1
- package/dist/types/payment/PaymentRequestProtocol.d.ts +105 -0
- package/dist/types/payment/PaymentRequestProtocol.d.ts.map +1 -1
- package/dist/types/protocol/EventKinds.d.ts +2 -0
- package/dist/types/protocol/EventKinds.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.js
CHANGED
|
@@ -5600,6 +5600,8 @@ const TOKEN_TRANSFER = 31113;
|
|
|
5600
5600
|
const FILE_METADATA = 31114;
|
|
5601
5601
|
/** Unicity: Payment request */
|
|
5602
5602
|
const PAYMENT_REQUEST = 31115;
|
|
5603
|
+
/** Unicity: Payment request response (accept/decline) */
|
|
5604
|
+
const PAYMENT_REQUEST_RESPONSE = 31116;
|
|
5603
5605
|
// ============================================================================
|
|
5604
5606
|
// Event Kind Classification Functions
|
|
5605
5607
|
// ============================================================================
|
|
@@ -5675,6 +5677,8 @@ function getName(kind) {
|
|
|
5675
5677
|
return 'File Metadata';
|
|
5676
5678
|
case PAYMENT_REQUEST:
|
|
5677
5679
|
return 'Payment Request';
|
|
5680
|
+
case PAYMENT_REQUEST_RESPONSE:
|
|
5681
|
+
return 'Payment Request Response';
|
|
5678
5682
|
default:
|
|
5679
5683
|
if (isReplaceable(kind)) {
|
|
5680
5684
|
return `Replaceable (${kind})`;
|
|
@@ -5701,6 +5705,7 @@ var EventKinds = /*#__PURE__*/Object.freeze({
|
|
|
5701
5705
|
FILE_METADATA: FILE_METADATA,
|
|
5702
5706
|
GIFT_WRAP: GIFT_WRAP,
|
|
5703
5707
|
PAYMENT_REQUEST: PAYMENT_REQUEST,
|
|
5708
|
+
PAYMENT_REQUEST_RESPONSE: PAYMENT_REQUEST_RESPONSE,
|
|
5704
5709
|
PROFILE: PROFILE,
|
|
5705
5710
|
REACTION: REACTION,
|
|
5706
5711
|
READ_RECEIPT: READ_RECEIPT,
|
|
@@ -6254,14 +6259,16 @@ class NostrClient {
|
|
|
6254
6259
|
return;
|
|
6255
6260
|
}
|
|
6256
6261
|
// Send a subscription request as a ping (relays respond with EOSE)
|
|
6257
|
-
//
|
|
6262
|
+
// Use a single fixed subscription ID per relay to avoid accumulating subscriptions
|
|
6263
|
+
// Note: limit:1 is used because some relays don't respond to limit:0
|
|
6258
6264
|
try {
|
|
6259
|
-
const pingSubId = `ping
|
|
6260
|
-
|
|
6261
|
-
relay.socket.send(pingMessage);
|
|
6262
|
-
// Immediately close the subscription
|
|
6265
|
+
const pingSubId = `ping`;
|
|
6266
|
+
// First close any existing ping subscription to ensure we don't accumulate
|
|
6263
6267
|
const closeMessage = JSON.stringify(['CLOSE', pingSubId]);
|
|
6264
6268
|
relay.socket.send(closeMessage);
|
|
6269
|
+
// Then send the new ping request (limit:1 ensures relay sends EOSE)
|
|
6270
|
+
const pingMessage = JSON.stringify(['REQ', pingSubId, { limit: 1 }]);
|
|
6271
|
+
relay.socket.send(pingMessage);
|
|
6265
6272
|
}
|
|
6266
6273
|
catch {
|
|
6267
6274
|
// Send failed, connection likely dead
|
|
@@ -6561,6 +6568,41 @@ class NostrClient {
|
|
|
6561
6568
|
const event = await PaymentRequestProtocol$1.createPaymentRequestEvent(this.keyManager, targetPubkeyHex, request);
|
|
6562
6569
|
return this.publishEvent(event);
|
|
6563
6570
|
}
|
|
6571
|
+
/**
|
|
6572
|
+
* Send a payment request response (decline/expiration notification).
|
|
6573
|
+
* @param targetPubkeyHex Original requester's public key
|
|
6574
|
+
* @param response Response details
|
|
6575
|
+
* @returns Promise that resolves with the event ID
|
|
6576
|
+
*/
|
|
6577
|
+
async sendPaymentRequestResponse(targetPubkeyHex, response) {
|
|
6578
|
+
const PaymentRequestProtocol$1 = await Promise.resolve().then(function () { return PaymentRequestProtocol; });
|
|
6579
|
+
const event = await PaymentRequestProtocol$1.createPaymentRequestResponseEvent(this.keyManager, targetPubkeyHex, {
|
|
6580
|
+
requestId: response.requestId,
|
|
6581
|
+
originalEventId: response.originalEventId,
|
|
6582
|
+
status: response.status === 'DECLINED'
|
|
6583
|
+
? PaymentRequestProtocol$1.ResponseStatus.DECLINED
|
|
6584
|
+
: PaymentRequestProtocol$1.ResponseStatus.EXPIRED,
|
|
6585
|
+
reason: response.reason,
|
|
6586
|
+
});
|
|
6587
|
+
return this.publishEvent(event);
|
|
6588
|
+
}
|
|
6589
|
+
/**
|
|
6590
|
+
* Send a payment request decline response.
|
|
6591
|
+
* Convenience method for declining a payment request.
|
|
6592
|
+
* @param originalRequestSenderPubkey Pubkey of who sent the original payment request
|
|
6593
|
+
* @param originalEventId Event ID of the original payment request
|
|
6594
|
+
* @param requestId Request ID from the original payment request
|
|
6595
|
+
* @param reason Optional reason for declining
|
|
6596
|
+
* @returns Promise that resolves with the event ID
|
|
6597
|
+
*/
|
|
6598
|
+
async sendPaymentRequestDecline(originalRequestSenderPubkey, originalEventId, requestId, reason) {
|
|
6599
|
+
return this.sendPaymentRequestResponse(originalRequestSenderPubkey, {
|
|
6600
|
+
requestId,
|
|
6601
|
+
originalEventId,
|
|
6602
|
+
status: 'DECLINED',
|
|
6603
|
+
reason,
|
|
6604
|
+
});
|
|
6605
|
+
}
|
|
6564
6606
|
/**
|
|
6565
6607
|
* Publish a nametag binding.
|
|
6566
6608
|
* @param nametagId Nametag identifier
|
|
@@ -10196,6 +10238,20 @@ var TokenTransferProtocol = /*#__PURE__*/Object.freeze({
|
|
|
10196
10238
|
*/
|
|
10197
10239
|
/** Prefix for payment request messages */
|
|
10198
10240
|
const MESSAGE_PREFIX = 'payment_request:';
|
|
10241
|
+
/** Prefix for payment request response messages */
|
|
10242
|
+
const RESPONSE_PREFIX = 'payment_request_response:';
|
|
10243
|
+
/** Default deadline duration: 5 minutes in milliseconds */
|
|
10244
|
+
const DEFAULT_DEADLINE_MS = 5 * 60 * 1000;
|
|
10245
|
+
/**
|
|
10246
|
+
* Payment request response status.
|
|
10247
|
+
*/
|
|
10248
|
+
var ResponseStatus;
|
|
10249
|
+
(function (ResponseStatus) {
|
|
10250
|
+
/** Payment request was declined by the recipient */
|
|
10251
|
+
ResponseStatus["DECLINED"] = "DECLINED";
|
|
10252
|
+
/** Payment request expired (deadline passed) */
|
|
10253
|
+
ResponseStatus["EXPIRED"] = "EXPIRED";
|
|
10254
|
+
})(ResponseStatus || (ResponseStatus = {}));
|
|
10199
10255
|
/**
|
|
10200
10256
|
* Generate a short unique request ID.
|
|
10201
10257
|
*/
|
|
@@ -10236,6 +10292,13 @@ function generateRequestId() {
|
|
|
10236
10292
|
async function createPaymentRequestEvent(keyManager, targetPubkeyHex, request) {
|
|
10237
10293
|
// Generate request ID if not provided
|
|
10238
10294
|
const requestId = request.requestId || generateRequestId();
|
|
10295
|
+
// Calculate deadline: use provided value, or default to 5 minutes from now
|
|
10296
|
+
// If explicitly set to null, no deadline
|
|
10297
|
+
const deadline = request.deadline === null
|
|
10298
|
+
? null
|
|
10299
|
+
: request.deadline !== undefined
|
|
10300
|
+
? request.deadline
|
|
10301
|
+
: Date.now() + DEFAULT_DEADLINE_MS;
|
|
10239
10302
|
// Serialize request to JSON
|
|
10240
10303
|
const requestJson = JSON.stringify({
|
|
10241
10304
|
amount: String(request.amount), // Convert to string for JSON compatibility with bigint
|
|
@@ -10243,6 +10306,7 @@ async function createPaymentRequestEvent(keyManager, targetPubkeyHex, request) {
|
|
|
10243
10306
|
message: request.message,
|
|
10244
10307
|
recipientNametag: request.recipientNametag,
|
|
10245
10308
|
requestId: requestId,
|
|
10309
|
+
deadline: deadline,
|
|
10246
10310
|
});
|
|
10247
10311
|
// Add prefix and encrypt
|
|
10248
10312
|
const message = MESSAGE_PREFIX + requestJson;
|
|
@@ -10314,6 +10378,7 @@ async function parsePaymentRequest(event, keyManager) {
|
|
|
10314
10378
|
senderPubkey: event.pubkey,
|
|
10315
10379
|
timestamp: event.created_at * 1000, // Convert to milliseconds
|
|
10316
10380
|
eventId: event.id,
|
|
10381
|
+
deadline: parsed.deadline !== undefined ? parsed.deadline : null,
|
|
10317
10382
|
};
|
|
10318
10383
|
}
|
|
10319
10384
|
/**
|
|
@@ -10411,18 +10476,166 @@ function parseAmount(amountStr, decimals = 8) {
|
|
|
10411
10476
|
const fractionalPart = BigInt(fractionalStr);
|
|
10412
10477
|
return wholePart * multiplier + fractionalPart;
|
|
10413
10478
|
}
|
|
10479
|
+
// ============================================================================
|
|
10480
|
+
// Payment Request Response Functions
|
|
10481
|
+
// ============================================================================
|
|
10482
|
+
/**
|
|
10483
|
+
* Check if a parsed payment request has expired.
|
|
10484
|
+
* @param request Parsed payment request
|
|
10485
|
+
* @returns true if the request has a deadline and it has passed
|
|
10486
|
+
*/
|
|
10487
|
+
function isExpired(request) {
|
|
10488
|
+
return request.deadline !== null && Date.now() > request.deadline;
|
|
10489
|
+
}
|
|
10490
|
+
/**
|
|
10491
|
+
* Get remaining time until deadline in milliseconds.
|
|
10492
|
+
* @param request Parsed payment request
|
|
10493
|
+
* @returns Remaining time in ms, 0 if expired, null if no deadline
|
|
10494
|
+
*/
|
|
10495
|
+
function getRemainingTimeMs(request) {
|
|
10496
|
+
if (request.deadline === null)
|
|
10497
|
+
return null;
|
|
10498
|
+
const remaining = request.deadline - Date.now();
|
|
10499
|
+
return remaining > 0 ? remaining : 0;
|
|
10500
|
+
}
|
|
10501
|
+
/**
|
|
10502
|
+
* Create a payment request response event (for decline/expiration).
|
|
10503
|
+
*
|
|
10504
|
+
* Event structure:
|
|
10505
|
+
* - Kind: 31116 (PAYMENT_REQUEST_RESPONSE)
|
|
10506
|
+
* - Tags:
|
|
10507
|
+
* - ["p", "<target_pubkey_hex>"] - Original requester
|
|
10508
|
+
* - ["type", "payment_request_response"]
|
|
10509
|
+
* - ["status", "DECLINED" | "EXPIRED"]
|
|
10510
|
+
* - ["e", "<original_event_id>", "", "reply"] - Reference to original request
|
|
10511
|
+
* - Content: NIP-04 encrypted response JSON
|
|
10512
|
+
*
|
|
10513
|
+
* @param keyManager Key manager with signing keys
|
|
10514
|
+
* @param targetPubkeyHex Original requester's public key
|
|
10515
|
+
* @param response Response details
|
|
10516
|
+
* @returns Signed event
|
|
10517
|
+
*/
|
|
10518
|
+
async function createPaymentRequestResponseEvent(keyManager, targetPubkeyHex, response) {
|
|
10519
|
+
// Serialize response to JSON
|
|
10520
|
+
const responseJson = JSON.stringify({
|
|
10521
|
+
requestId: response.requestId,
|
|
10522
|
+
originalEventId: response.originalEventId,
|
|
10523
|
+
status: response.status,
|
|
10524
|
+
reason: response.reason,
|
|
10525
|
+
});
|
|
10526
|
+
// Add prefix and encrypt
|
|
10527
|
+
const message = RESPONSE_PREFIX + responseJson;
|
|
10528
|
+
const encryptedContent = await keyManager.encryptHex(message, targetPubkeyHex);
|
|
10529
|
+
// Build tags
|
|
10530
|
+
const tags = [
|
|
10531
|
+
['p', targetPubkeyHex],
|
|
10532
|
+
['type', 'payment_request_response'],
|
|
10533
|
+
['status', response.status],
|
|
10534
|
+
];
|
|
10535
|
+
// Add reference to original event
|
|
10536
|
+
if (response.originalEventId) {
|
|
10537
|
+
tags.push(['e', response.originalEventId, '', 'reply']);
|
|
10538
|
+
}
|
|
10539
|
+
const event = Event.create(keyManager, {
|
|
10540
|
+
kind: PAYMENT_REQUEST_RESPONSE,
|
|
10541
|
+
tags,
|
|
10542
|
+
content: encryptedContent,
|
|
10543
|
+
});
|
|
10544
|
+
return event;
|
|
10545
|
+
}
|
|
10546
|
+
/**
|
|
10547
|
+
* Parse a payment request response event.
|
|
10548
|
+
* Decrypts and parses the response data.
|
|
10549
|
+
*
|
|
10550
|
+
* @param event Payment request response event
|
|
10551
|
+
* @param keyManager Key manager for decryption
|
|
10552
|
+
* @returns Parsed payment request response
|
|
10553
|
+
* @throws Error if the event is not a valid payment request response
|
|
10554
|
+
*/
|
|
10555
|
+
async function parsePaymentRequestResponse(event, keyManager) {
|
|
10556
|
+
// Verify event kind
|
|
10557
|
+
if (event.kind !== PAYMENT_REQUEST_RESPONSE) {
|
|
10558
|
+
throw new Error('Event is not a payment request response');
|
|
10559
|
+
}
|
|
10560
|
+
// Determine the peer's public key for decryption
|
|
10561
|
+
let peerPubkeyHex;
|
|
10562
|
+
if (keyManager.isMyPublicKey(event.pubkey)) {
|
|
10563
|
+
// We sent this response, decrypt with target's key
|
|
10564
|
+
const targetPubkey = event.getTagValue('p');
|
|
10565
|
+
if (!targetPubkey) {
|
|
10566
|
+
throw new Error('No target found in event');
|
|
10567
|
+
}
|
|
10568
|
+
peerPubkeyHex = targetPubkey;
|
|
10569
|
+
}
|
|
10570
|
+
else {
|
|
10571
|
+
// We received this response, decrypt with sender's key
|
|
10572
|
+
peerPubkeyHex = event.pubkey;
|
|
10573
|
+
}
|
|
10574
|
+
// Decrypt the content
|
|
10575
|
+
const decrypted = await keyManager.decryptHex(event.content, peerPubkeyHex);
|
|
10576
|
+
// Validate prefix
|
|
10577
|
+
if (!decrypted.startsWith(RESPONSE_PREFIX)) {
|
|
10578
|
+
throw new Error('Invalid payment request response format: missing prefix');
|
|
10579
|
+
}
|
|
10580
|
+
// Parse JSON
|
|
10581
|
+
const responseJson = decrypted.slice(RESPONSE_PREFIX.length);
|
|
10582
|
+
const parsed = JSON.parse(responseJson);
|
|
10583
|
+
return {
|
|
10584
|
+
requestId: parsed.requestId,
|
|
10585
|
+
originalEventId: parsed.originalEventId,
|
|
10586
|
+
status: parsed.status,
|
|
10587
|
+
reason: parsed.reason,
|
|
10588
|
+
senderPubkey: event.pubkey,
|
|
10589
|
+
eventId: event.id,
|
|
10590
|
+
timestamp: event.created_at * 1000,
|
|
10591
|
+
};
|
|
10592
|
+
}
|
|
10593
|
+
/**
|
|
10594
|
+
* Check if an event is a payment request response.
|
|
10595
|
+
* @param event Event to check
|
|
10596
|
+
* @returns true if the event is a payment request response
|
|
10597
|
+
*/
|
|
10598
|
+
function isPaymentRequestResponse(event) {
|
|
10599
|
+
return (event.kind === PAYMENT_REQUEST_RESPONSE &&
|
|
10600
|
+
event.getTagValue('type') === 'payment_request_response');
|
|
10601
|
+
}
|
|
10602
|
+
/**
|
|
10603
|
+
* Get the response status from a payment request response event (from unencrypted tag).
|
|
10604
|
+
* @param event Payment request response event
|
|
10605
|
+
* @returns Status string, or undefined if not found
|
|
10606
|
+
*/
|
|
10607
|
+
function getResponseStatus(event) {
|
|
10608
|
+
return event.getTagValue('status');
|
|
10609
|
+
}
|
|
10610
|
+
/**
|
|
10611
|
+
* Get the referenced original event ID from the response event.
|
|
10612
|
+
* @param event Payment request response event
|
|
10613
|
+
* @returns Original event ID, or undefined if not found
|
|
10614
|
+
*/
|
|
10615
|
+
function getOriginalEventId(event) {
|
|
10616
|
+
return event.getTagValue('e');
|
|
10617
|
+
}
|
|
10414
10618
|
|
|
10415
10619
|
var PaymentRequestProtocol = /*#__PURE__*/Object.freeze({
|
|
10416
10620
|
__proto__: null,
|
|
10621
|
+
DEFAULT_DEADLINE_MS: DEFAULT_DEADLINE_MS,
|
|
10622
|
+
get ResponseStatus () { return ResponseStatus; },
|
|
10417
10623
|
createPaymentRequestEvent: createPaymentRequestEvent,
|
|
10624
|
+
createPaymentRequestResponseEvent: createPaymentRequestResponseEvent,
|
|
10418
10625
|
formatAmount: formatAmount,
|
|
10419
10626
|
getAmount: getAmount,
|
|
10627
|
+
getOriginalEventId: getOriginalEventId,
|
|
10420
10628
|
getRecipientNametag: getRecipientNametag,
|
|
10629
|
+
getRemainingTimeMs: getRemainingTimeMs,
|
|
10630
|
+
getResponseStatus: getResponseStatus,
|
|
10421
10631
|
getSender: getSender,
|
|
10422
10632
|
getTarget: getTarget,
|
|
10633
|
+
isExpired: isExpired,
|
|
10423
10634
|
isPaymentRequest: isPaymentRequest,
|
|
10635
|
+
isPaymentRequestResponse: isPaymentRequestResponse,
|
|
10424
10636
|
parseAmount: parseAmount,
|
|
10425
|
-
parsePaymentRequest: parsePaymentRequest
|
|
10637
|
+
parsePaymentRequest: parsePaymentRequest,
|
|
10638
|
+
parsePaymentRequestResponse: parsePaymentRequestResponse
|
|
10426
10639
|
});
|
|
10427
10640
|
|
|
10428
10641
|
/**
|
|
@@ -10441,5 +10654,5 @@ function isReadReceipt(message) {
|
|
|
10441
10654
|
return message.kind === 15;
|
|
10442
10655
|
}
|
|
10443
10656
|
|
|
10444
|
-
export { AGENT_LOCATION, AGENT_PROFILE, APP_DATA, bech32 as Bech32, CHAT_MESSAGE, CLOSED, CLOSING, CONNECTING, CONTACTS, CallbackEventListener, DELETION, ENCRYPTED_DM, Event, EventKinds, FILE_METADATA, Filter, FilterBuilder, GIFT_WRAP, nip04 as NIP04, nip17 as NIP17, nip44 as NIP44, NametagBinding, NametagUtils, NostrClient, NostrKeyManager, OPEN, PAYMENT_REQUEST, PROFILE, PaymentRequestProtocol, REACTION, READ_RECEIPT, RECOMMEND_RELAY, RELAY_LIST, SEAL, schnorr as SchnorrSigner, TEXT_NOTE, TOKEN_TRANSFER, TokenTransferProtocol, areSameNametag, createBindingEvent, createGiftWrap, createNametagToPubkeyFilter, createPubkeyToNametagFilter, createReadReceipt, createWebSocket, decode, decodeNpub, decodeNsec, encode, encodeNpub, encodeNsec, extractMessageData, formatForDisplay, getName, getPublicKey, getPublicKeyHex, hashNametag, isChatMessage, isEphemeral, isParameterizedReplaceable, isPhoneNumber, isReadReceipt, isReplaceable, isValidBindingEvent, normalizeNametag, parseAddressFromEvent, parseNametagHashFromEvent, sign, signHex, unwrap, verify, verifyHex };
|
|
10657
|
+
export { AGENT_LOCATION, AGENT_PROFILE, APP_DATA, bech32 as Bech32, CHAT_MESSAGE, CLOSED, CLOSING, CONNECTING, CONTACTS, CallbackEventListener, DELETION, ENCRYPTED_DM, Event, EventKinds, FILE_METADATA, Filter, FilterBuilder, GIFT_WRAP, nip04 as NIP04, nip17 as NIP17, nip44 as NIP44, NametagBinding, NametagUtils, NostrClient, NostrKeyManager, OPEN, PAYMENT_REQUEST, PAYMENT_REQUEST_RESPONSE, PROFILE, PaymentRequestProtocol, REACTION, READ_RECEIPT, RECOMMEND_RELAY, RELAY_LIST, SEAL, schnorr as SchnorrSigner, TEXT_NOTE, TOKEN_TRANSFER, TokenTransferProtocol, areSameNametag, createBindingEvent, createGiftWrap, createNametagToPubkeyFilter, createPubkeyToNametagFilter, createReadReceipt, createWebSocket, decode, decodeNpub, decodeNsec, encode, encodeNpub, encodeNsec, extractMessageData, formatForDisplay, getName, getPublicKey, getPublicKeyHex, hashNametag, isChatMessage, isEphemeral, isParameterizedReplaceable, isPhoneNumber, isReadReceipt, isReplaceable, isValidBindingEvent, normalizeNametag, parseAddressFromEvent, parseNametagHashFromEvent, sign, signHex, unwrap, verify, verifyHex };
|
|
10445
10658
|
//# sourceMappingURL=index.js.map
|