@unicitylabs/nostr-js-sdk 0.2.5 → 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.
@@ -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,
@@ -6563,6 +6568,41 @@ class NostrClient {
6563
6568
  const event = await PaymentRequestProtocol$1.createPaymentRequestEvent(this.keyManager, targetPubkeyHex, request);
6564
6569
  return this.publishEvent(event);
6565
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
+ }
6566
6606
  /**
6567
6607
  * Publish a nametag binding.
6568
6608
  * @param nametagId Nametag identifier
@@ -10198,6 +10238,20 @@ var TokenTransferProtocol = /*#__PURE__*/Object.freeze({
10198
10238
  */
10199
10239
  /** Prefix for payment request messages */
10200
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 = {}));
10201
10255
  /**
10202
10256
  * Generate a short unique request ID.
10203
10257
  */
@@ -10238,6 +10292,13 @@ function generateRequestId() {
10238
10292
  async function createPaymentRequestEvent(keyManager, targetPubkeyHex, request) {
10239
10293
  // Generate request ID if not provided
10240
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;
10241
10302
  // Serialize request to JSON
10242
10303
  const requestJson = JSON.stringify({
10243
10304
  amount: String(request.amount), // Convert to string for JSON compatibility with bigint
@@ -10245,6 +10306,7 @@ async function createPaymentRequestEvent(keyManager, targetPubkeyHex, request) {
10245
10306
  message: request.message,
10246
10307
  recipientNametag: request.recipientNametag,
10247
10308
  requestId: requestId,
10309
+ deadline: deadline,
10248
10310
  });
10249
10311
  // Add prefix and encrypt
10250
10312
  const message = MESSAGE_PREFIX + requestJson;
@@ -10316,6 +10378,7 @@ async function parsePaymentRequest(event, keyManager) {
10316
10378
  senderPubkey: event.pubkey,
10317
10379
  timestamp: event.created_at * 1000, // Convert to milliseconds
10318
10380
  eventId: event.id,
10381
+ deadline: parsed.deadline !== undefined ? parsed.deadline : null,
10319
10382
  };
10320
10383
  }
10321
10384
  /**
@@ -10413,18 +10476,166 @@ function parseAmount(amountStr, decimals = 8) {
10413
10476
  const fractionalPart = BigInt(fractionalStr);
10414
10477
  return wholePart * multiplier + fractionalPart;
10415
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
+ }
10416
10618
 
10417
10619
  var PaymentRequestProtocol = /*#__PURE__*/Object.freeze({
10418
10620
  __proto__: null,
10621
+ DEFAULT_DEADLINE_MS: DEFAULT_DEADLINE_MS,
10622
+ get ResponseStatus () { return ResponseStatus; },
10419
10623
  createPaymentRequestEvent: createPaymentRequestEvent,
10624
+ createPaymentRequestResponseEvent: createPaymentRequestResponseEvent,
10420
10625
  formatAmount: formatAmount,
10421
10626
  getAmount: getAmount,
10627
+ getOriginalEventId: getOriginalEventId,
10422
10628
  getRecipientNametag: getRecipientNametag,
10629
+ getRemainingTimeMs: getRemainingTimeMs,
10630
+ getResponseStatus: getResponseStatus,
10423
10631
  getSender: getSender,
10424
10632
  getTarget: getTarget,
10633
+ isExpired: isExpired,
10425
10634
  isPaymentRequest: isPaymentRequest,
10635
+ isPaymentRequestResponse: isPaymentRequestResponse,
10426
10636
  parseAmount: parseAmount,
10427
- parsePaymentRequest: parsePaymentRequest
10637
+ parsePaymentRequest: parsePaymentRequest,
10638
+ parsePaymentRequestResponse: parsePaymentRequestResponse
10428
10639
  });
10429
10640
 
10430
10641
  /**
@@ -10443,5 +10654,5 @@ function isReadReceipt(message) {
10443
10654
  return message.kind === 15;
10444
10655
  }
10445
10656
 
10446
- 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 };
10447
10658
  //# sourceMappingURL=index.js.map