@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
|
@@ -5606,6 +5606,8 @@
|
|
|
5606
5606
|
const FILE_METADATA = 31114;
|
|
5607
5607
|
/** Unicity: Payment request */
|
|
5608
5608
|
const PAYMENT_REQUEST = 31115;
|
|
5609
|
+
/** Unicity: Payment request response (accept/decline) */
|
|
5610
|
+
const PAYMENT_REQUEST_RESPONSE = 31116;
|
|
5609
5611
|
// ============================================================================
|
|
5610
5612
|
// Event Kind Classification Functions
|
|
5611
5613
|
// ============================================================================
|
|
@@ -5681,6 +5683,8 @@
|
|
|
5681
5683
|
return 'File Metadata';
|
|
5682
5684
|
case PAYMENT_REQUEST:
|
|
5683
5685
|
return 'Payment Request';
|
|
5686
|
+
case PAYMENT_REQUEST_RESPONSE:
|
|
5687
|
+
return 'Payment Request Response';
|
|
5684
5688
|
default:
|
|
5685
5689
|
if (isReplaceable(kind)) {
|
|
5686
5690
|
return `Replaceable (${kind})`;
|
|
@@ -5707,6 +5711,7 @@
|
|
|
5707
5711
|
FILE_METADATA: FILE_METADATA,
|
|
5708
5712
|
GIFT_WRAP: GIFT_WRAP,
|
|
5709
5713
|
PAYMENT_REQUEST: PAYMENT_REQUEST,
|
|
5714
|
+
PAYMENT_REQUEST_RESPONSE: PAYMENT_REQUEST_RESPONSE,
|
|
5710
5715
|
PROFILE: PROFILE,
|
|
5711
5716
|
REACTION: REACTION,
|
|
5712
5717
|
READ_RECEIPT: READ_RECEIPT,
|
|
@@ -6260,14 +6265,16 @@
|
|
|
6260
6265
|
return;
|
|
6261
6266
|
}
|
|
6262
6267
|
// Send a subscription request as a ping (relays respond with EOSE)
|
|
6263
|
-
//
|
|
6268
|
+
// Use a single fixed subscription ID per relay to avoid accumulating subscriptions
|
|
6269
|
+
// Note: limit:1 is used because some relays don't respond to limit:0
|
|
6264
6270
|
try {
|
|
6265
|
-
const pingSubId = `ping
|
|
6266
|
-
|
|
6267
|
-
relay.socket.send(pingMessage);
|
|
6268
|
-
// Immediately close the subscription
|
|
6271
|
+
const pingSubId = `ping`;
|
|
6272
|
+
// First close any existing ping subscription to ensure we don't accumulate
|
|
6269
6273
|
const closeMessage = JSON.stringify(['CLOSE', pingSubId]);
|
|
6270
6274
|
relay.socket.send(closeMessage);
|
|
6275
|
+
// Then send the new ping request (limit:1 ensures relay sends EOSE)
|
|
6276
|
+
const pingMessage = JSON.stringify(['REQ', pingSubId, { limit: 1 }]);
|
|
6277
|
+
relay.socket.send(pingMessage);
|
|
6271
6278
|
}
|
|
6272
6279
|
catch {
|
|
6273
6280
|
// Send failed, connection likely dead
|
|
@@ -6567,6 +6574,41 @@
|
|
|
6567
6574
|
const event = await PaymentRequestProtocol$1.createPaymentRequestEvent(this.keyManager, targetPubkeyHex, request);
|
|
6568
6575
|
return this.publishEvent(event);
|
|
6569
6576
|
}
|
|
6577
|
+
/**
|
|
6578
|
+
* Send a payment request response (decline/expiration notification).
|
|
6579
|
+
* @param targetPubkeyHex Original requester's public key
|
|
6580
|
+
* @param response Response details
|
|
6581
|
+
* @returns Promise that resolves with the event ID
|
|
6582
|
+
*/
|
|
6583
|
+
async sendPaymentRequestResponse(targetPubkeyHex, response) {
|
|
6584
|
+
const PaymentRequestProtocol$1 = await Promise.resolve().then(function () { return PaymentRequestProtocol; });
|
|
6585
|
+
const event = await PaymentRequestProtocol$1.createPaymentRequestResponseEvent(this.keyManager, targetPubkeyHex, {
|
|
6586
|
+
requestId: response.requestId,
|
|
6587
|
+
originalEventId: response.originalEventId,
|
|
6588
|
+
status: response.status === 'DECLINED'
|
|
6589
|
+
? PaymentRequestProtocol$1.ResponseStatus.DECLINED
|
|
6590
|
+
: PaymentRequestProtocol$1.ResponseStatus.EXPIRED,
|
|
6591
|
+
reason: response.reason,
|
|
6592
|
+
});
|
|
6593
|
+
return this.publishEvent(event);
|
|
6594
|
+
}
|
|
6595
|
+
/**
|
|
6596
|
+
* Send a payment request decline response.
|
|
6597
|
+
* Convenience method for declining a payment request.
|
|
6598
|
+
* @param originalRequestSenderPubkey Pubkey of who sent the original payment request
|
|
6599
|
+
* @param originalEventId Event ID of the original payment request
|
|
6600
|
+
* @param requestId Request ID from the original payment request
|
|
6601
|
+
* @param reason Optional reason for declining
|
|
6602
|
+
* @returns Promise that resolves with the event ID
|
|
6603
|
+
*/
|
|
6604
|
+
async sendPaymentRequestDecline(originalRequestSenderPubkey, originalEventId, requestId, reason) {
|
|
6605
|
+
return this.sendPaymentRequestResponse(originalRequestSenderPubkey, {
|
|
6606
|
+
requestId,
|
|
6607
|
+
originalEventId,
|
|
6608
|
+
status: 'DECLINED',
|
|
6609
|
+
reason,
|
|
6610
|
+
});
|
|
6611
|
+
}
|
|
6570
6612
|
/**
|
|
6571
6613
|
* Publish a nametag binding.
|
|
6572
6614
|
* @param nametagId Nametag identifier
|
|
@@ -10202,6 +10244,20 @@
|
|
|
10202
10244
|
*/
|
|
10203
10245
|
/** Prefix for payment request messages */
|
|
10204
10246
|
const MESSAGE_PREFIX = 'payment_request:';
|
|
10247
|
+
/** Prefix for payment request response messages */
|
|
10248
|
+
const RESPONSE_PREFIX = 'payment_request_response:';
|
|
10249
|
+
/** Default deadline duration: 5 minutes in milliseconds */
|
|
10250
|
+
const DEFAULT_DEADLINE_MS = 5 * 60 * 1000;
|
|
10251
|
+
/**
|
|
10252
|
+
* Payment request response status.
|
|
10253
|
+
*/
|
|
10254
|
+
var ResponseStatus;
|
|
10255
|
+
(function (ResponseStatus) {
|
|
10256
|
+
/** Payment request was declined by the recipient */
|
|
10257
|
+
ResponseStatus["DECLINED"] = "DECLINED";
|
|
10258
|
+
/** Payment request expired (deadline passed) */
|
|
10259
|
+
ResponseStatus["EXPIRED"] = "EXPIRED";
|
|
10260
|
+
})(ResponseStatus || (ResponseStatus = {}));
|
|
10205
10261
|
/**
|
|
10206
10262
|
* Generate a short unique request ID.
|
|
10207
10263
|
*/
|
|
@@ -10242,6 +10298,13 @@
|
|
|
10242
10298
|
async function createPaymentRequestEvent(keyManager, targetPubkeyHex, request) {
|
|
10243
10299
|
// Generate request ID if not provided
|
|
10244
10300
|
const requestId = request.requestId || generateRequestId();
|
|
10301
|
+
// Calculate deadline: use provided value, or default to 5 minutes from now
|
|
10302
|
+
// If explicitly set to null, no deadline
|
|
10303
|
+
const deadline = request.deadline === null
|
|
10304
|
+
? null
|
|
10305
|
+
: request.deadline !== undefined
|
|
10306
|
+
? request.deadline
|
|
10307
|
+
: Date.now() + DEFAULT_DEADLINE_MS;
|
|
10245
10308
|
// Serialize request to JSON
|
|
10246
10309
|
const requestJson = JSON.stringify({
|
|
10247
10310
|
amount: String(request.amount), // Convert to string for JSON compatibility with bigint
|
|
@@ -10249,6 +10312,7 @@
|
|
|
10249
10312
|
message: request.message,
|
|
10250
10313
|
recipientNametag: request.recipientNametag,
|
|
10251
10314
|
requestId: requestId,
|
|
10315
|
+
deadline: deadline,
|
|
10252
10316
|
});
|
|
10253
10317
|
// Add prefix and encrypt
|
|
10254
10318
|
const message = MESSAGE_PREFIX + requestJson;
|
|
@@ -10320,6 +10384,7 @@
|
|
|
10320
10384
|
senderPubkey: event.pubkey,
|
|
10321
10385
|
timestamp: event.created_at * 1000, // Convert to milliseconds
|
|
10322
10386
|
eventId: event.id,
|
|
10387
|
+
deadline: parsed.deadline !== undefined ? parsed.deadline : null,
|
|
10323
10388
|
};
|
|
10324
10389
|
}
|
|
10325
10390
|
/**
|
|
@@ -10417,18 +10482,166 @@
|
|
|
10417
10482
|
const fractionalPart = BigInt(fractionalStr);
|
|
10418
10483
|
return wholePart * multiplier + fractionalPart;
|
|
10419
10484
|
}
|
|
10485
|
+
// ============================================================================
|
|
10486
|
+
// Payment Request Response Functions
|
|
10487
|
+
// ============================================================================
|
|
10488
|
+
/**
|
|
10489
|
+
* Check if a parsed payment request has expired.
|
|
10490
|
+
* @param request Parsed payment request
|
|
10491
|
+
* @returns true if the request has a deadline and it has passed
|
|
10492
|
+
*/
|
|
10493
|
+
function isExpired(request) {
|
|
10494
|
+
return request.deadline !== null && Date.now() > request.deadline;
|
|
10495
|
+
}
|
|
10496
|
+
/**
|
|
10497
|
+
* Get remaining time until deadline in milliseconds.
|
|
10498
|
+
* @param request Parsed payment request
|
|
10499
|
+
* @returns Remaining time in ms, 0 if expired, null if no deadline
|
|
10500
|
+
*/
|
|
10501
|
+
function getRemainingTimeMs(request) {
|
|
10502
|
+
if (request.deadline === null)
|
|
10503
|
+
return null;
|
|
10504
|
+
const remaining = request.deadline - Date.now();
|
|
10505
|
+
return remaining > 0 ? remaining : 0;
|
|
10506
|
+
}
|
|
10507
|
+
/**
|
|
10508
|
+
* Create a payment request response event (for decline/expiration).
|
|
10509
|
+
*
|
|
10510
|
+
* Event structure:
|
|
10511
|
+
* - Kind: 31116 (PAYMENT_REQUEST_RESPONSE)
|
|
10512
|
+
* - Tags:
|
|
10513
|
+
* - ["p", "<target_pubkey_hex>"] - Original requester
|
|
10514
|
+
* - ["type", "payment_request_response"]
|
|
10515
|
+
* - ["status", "DECLINED" | "EXPIRED"]
|
|
10516
|
+
* - ["e", "<original_event_id>", "", "reply"] - Reference to original request
|
|
10517
|
+
* - Content: NIP-04 encrypted response JSON
|
|
10518
|
+
*
|
|
10519
|
+
* @param keyManager Key manager with signing keys
|
|
10520
|
+
* @param targetPubkeyHex Original requester's public key
|
|
10521
|
+
* @param response Response details
|
|
10522
|
+
* @returns Signed event
|
|
10523
|
+
*/
|
|
10524
|
+
async function createPaymentRequestResponseEvent(keyManager, targetPubkeyHex, response) {
|
|
10525
|
+
// Serialize response to JSON
|
|
10526
|
+
const responseJson = JSON.stringify({
|
|
10527
|
+
requestId: response.requestId,
|
|
10528
|
+
originalEventId: response.originalEventId,
|
|
10529
|
+
status: response.status,
|
|
10530
|
+
reason: response.reason,
|
|
10531
|
+
});
|
|
10532
|
+
// Add prefix and encrypt
|
|
10533
|
+
const message = RESPONSE_PREFIX + responseJson;
|
|
10534
|
+
const encryptedContent = await keyManager.encryptHex(message, targetPubkeyHex);
|
|
10535
|
+
// Build tags
|
|
10536
|
+
const tags = [
|
|
10537
|
+
['p', targetPubkeyHex],
|
|
10538
|
+
['type', 'payment_request_response'],
|
|
10539
|
+
['status', response.status],
|
|
10540
|
+
];
|
|
10541
|
+
// Add reference to original event
|
|
10542
|
+
if (response.originalEventId) {
|
|
10543
|
+
tags.push(['e', response.originalEventId, '', 'reply']);
|
|
10544
|
+
}
|
|
10545
|
+
const event = Event.create(keyManager, {
|
|
10546
|
+
kind: PAYMENT_REQUEST_RESPONSE,
|
|
10547
|
+
tags,
|
|
10548
|
+
content: encryptedContent,
|
|
10549
|
+
});
|
|
10550
|
+
return event;
|
|
10551
|
+
}
|
|
10552
|
+
/**
|
|
10553
|
+
* Parse a payment request response event.
|
|
10554
|
+
* Decrypts and parses the response data.
|
|
10555
|
+
*
|
|
10556
|
+
* @param event Payment request response event
|
|
10557
|
+
* @param keyManager Key manager for decryption
|
|
10558
|
+
* @returns Parsed payment request response
|
|
10559
|
+
* @throws Error if the event is not a valid payment request response
|
|
10560
|
+
*/
|
|
10561
|
+
async function parsePaymentRequestResponse(event, keyManager) {
|
|
10562
|
+
// Verify event kind
|
|
10563
|
+
if (event.kind !== PAYMENT_REQUEST_RESPONSE) {
|
|
10564
|
+
throw new Error('Event is not a payment request response');
|
|
10565
|
+
}
|
|
10566
|
+
// Determine the peer's public key for decryption
|
|
10567
|
+
let peerPubkeyHex;
|
|
10568
|
+
if (keyManager.isMyPublicKey(event.pubkey)) {
|
|
10569
|
+
// We sent this response, decrypt with target's key
|
|
10570
|
+
const targetPubkey = event.getTagValue('p');
|
|
10571
|
+
if (!targetPubkey) {
|
|
10572
|
+
throw new Error('No target found in event');
|
|
10573
|
+
}
|
|
10574
|
+
peerPubkeyHex = targetPubkey;
|
|
10575
|
+
}
|
|
10576
|
+
else {
|
|
10577
|
+
// We received this response, decrypt with sender's key
|
|
10578
|
+
peerPubkeyHex = event.pubkey;
|
|
10579
|
+
}
|
|
10580
|
+
// Decrypt the content
|
|
10581
|
+
const decrypted = await keyManager.decryptHex(event.content, peerPubkeyHex);
|
|
10582
|
+
// Validate prefix
|
|
10583
|
+
if (!decrypted.startsWith(RESPONSE_PREFIX)) {
|
|
10584
|
+
throw new Error('Invalid payment request response format: missing prefix');
|
|
10585
|
+
}
|
|
10586
|
+
// Parse JSON
|
|
10587
|
+
const responseJson = decrypted.slice(RESPONSE_PREFIX.length);
|
|
10588
|
+
const parsed = JSON.parse(responseJson);
|
|
10589
|
+
return {
|
|
10590
|
+
requestId: parsed.requestId,
|
|
10591
|
+
originalEventId: parsed.originalEventId,
|
|
10592
|
+
status: parsed.status,
|
|
10593
|
+
reason: parsed.reason,
|
|
10594
|
+
senderPubkey: event.pubkey,
|
|
10595
|
+
eventId: event.id,
|
|
10596
|
+
timestamp: event.created_at * 1000,
|
|
10597
|
+
};
|
|
10598
|
+
}
|
|
10599
|
+
/**
|
|
10600
|
+
* Check if an event is a payment request response.
|
|
10601
|
+
* @param event Event to check
|
|
10602
|
+
* @returns true if the event is a payment request response
|
|
10603
|
+
*/
|
|
10604
|
+
function isPaymentRequestResponse(event) {
|
|
10605
|
+
return (event.kind === PAYMENT_REQUEST_RESPONSE &&
|
|
10606
|
+
event.getTagValue('type') === 'payment_request_response');
|
|
10607
|
+
}
|
|
10608
|
+
/**
|
|
10609
|
+
* Get the response status from a payment request response event (from unencrypted tag).
|
|
10610
|
+
* @param event Payment request response event
|
|
10611
|
+
* @returns Status string, or undefined if not found
|
|
10612
|
+
*/
|
|
10613
|
+
function getResponseStatus(event) {
|
|
10614
|
+
return event.getTagValue('status');
|
|
10615
|
+
}
|
|
10616
|
+
/**
|
|
10617
|
+
* Get the referenced original event ID from the response event.
|
|
10618
|
+
* @param event Payment request response event
|
|
10619
|
+
* @returns Original event ID, or undefined if not found
|
|
10620
|
+
*/
|
|
10621
|
+
function getOriginalEventId(event) {
|
|
10622
|
+
return event.getTagValue('e');
|
|
10623
|
+
}
|
|
10420
10624
|
|
|
10421
10625
|
var PaymentRequestProtocol = /*#__PURE__*/Object.freeze({
|
|
10422
10626
|
__proto__: null,
|
|
10627
|
+
DEFAULT_DEADLINE_MS: DEFAULT_DEADLINE_MS,
|
|
10628
|
+
get ResponseStatus () { return ResponseStatus; },
|
|
10423
10629
|
createPaymentRequestEvent: createPaymentRequestEvent,
|
|
10630
|
+
createPaymentRequestResponseEvent: createPaymentRequestResponseEvent,
|
|
10424
10631
|
formatAmount: formatAmount,
|
|
10425
10632
|
getAmount: getAmount,
|
|
10633
|
+
getOriginalEventId: getOriginalEventId,
|
|
10426
10634
|
getRecipientNametag: getRecipientNametag,
|
|
10635
|
+
getRemainingTimeMs: getRemainingTimeMs,
|
|
10636
|
+
getResponseStatus: getResponseStatus,
|
|
10427
10637
|
getSender: getSender,
|
|
10428
10638
|
getTarget: getTarget,
|
|
10639
|
+
isExpired: isExpired,
|
|
10429
10640
|
isPaymentRequest: isPaymentRequest,
|
|
10641
|
+
isPaymentRequestResponse: isPaymentRequestResponse,
|
|
10430
10642
|
parseAmount: parseAmount,
|
|
10431
|
-
parsePaymentRequest: parsePaymentRequest
|
|
10643
|
+
parsePaymentRequest: parsePaymentRequest,
|
|
10644
|
+
parsePaymentRequestResponse: parsePaymentRequestResponse
|
|
10432
10645
|
});
|
|
10433
10646
|
|
|
10434
10647
|
/**
|
|
@@ -10474,6 +10687,7 @@
|
|
|
10474
10687
|
exports.NostrKeyManager = NostrKeyManager;
|
|
10475
10688
|
exports.OPEN = OPEN;
|
|
10476
10689
|
exports.PAYMENT_REQUEST = PAYMENT_REQUEST;
|
|
10690
|
+
exports.PAYMENT_REQUEST_RESPONSE = PAYMENT_REQUEST_RESPONSE;
|
|
10477
10691
|
exports.PROFILE = PROFILE;
|
|
10478
10692
|
exports.PaymentRequestProtocol = PaymentRequestProtocol;
|
|
10479
10693
|
exports.REACTION = REACTION;
|