I am using monero-nodejs liberary for rpc calls. I generated an integrated address and made a payment to this address.

By splitting the integrated address, I could see the paymentId is a33582a5a77c125b.

But when I check payments of this address, I get nothing (empty object)


Do I need any decoding on this paymentId? I can see the tx mined.

  • 1
    Integrated payment IDs are encrypted. I don't know how to decrypt it however, but some advanced users might help. Aug 22, 2018 at 6:49
  • I think I should change the title :) Aug 22, 2018 at 6:52
  • @knaccc If you can help? Aug 22, 2018 at 7:46
  • The payment id is stored in the tx_extra part of the transaction. To decode it you need to extract it, and then XOR it with (8a*R||0x8d) where a is your private view key, R is the transaction public key (which is also in tx_extra), || means concatenation, and * means elliptic curve scalar multiplication. I'm not familiar with this library, it looks like the maintainer will need to add this functionality.
    – knaccc
    Aug 22, 2018 at 11:14
  • So everyone using this integrated address is using this complex maths to check if they received paymets correspoing to an integarted address? Aug 22, 2018 at 12:42

1 Answer 1


MyMonero provides a useful JavaScript library (mymonero-core-js) for dealing with this sort of thing.

Here is a code snippet that shows how to encrypt/decrypt a payment id using this bit of code from mymonero-core-js.

const mm = require('mymonero-core-js').monero_utils

const viewKey = mm.random_keypair()
const txKey = mm.random_keypair()
const paymentId = 'badc0ffeefabcafe'


// If you have the private tx key and public view key
const pidKey = mm.cn_fast_hash(
  mm.generate_key_derivation(viewKey.pub, txKey.sec) +
).slice(0, INTEGRATED_ID_SIZE * 2)

// If you have the private view key and public tx key
const pidKey2 = mm.cn_fast_hash(
  mm.generate_key_derivation(txKey.pub, viewKey.sec) +
).slice(0, INTEGRATED_ID_SIZE * 2)

// Assert that the pidKeys are equal
console.log((pidKey === pidKey2)
  ? 'pidKeys are equal (expected)'
  : 'pidKeys are not equal (unexpected)')

// Encrypt Payment ID
const encryptedPaymentId = mm.hex_xor(paymentId, pidKey)
console.log('Encrypted: ' + encryptedPaymentId)

// Decrypt Payment ID
const decryptedPaymentId = mm.hex_xor(encryptedPaymentId, pidKey)
console.log('Decrypted: ' + decryptedPaymentId)

// Assert that the original and decrypted payment ids are equal
console.log((decryptedPaymentId === paymentId)
  ? 'Successful Decryption!'
  : 'Unsuccessful Decryption')

Should provide an output similar to:

pidKeys are equal (expected)
Encrypted: cfc3f2120b7db7c0
Decrypted: badc0ffeefabcafe
Successful Decryption!

Hope this helps!

  • Thanks. But I can see I get the same payment Id that I provided. I have encrypted payment Id , and I wish to decrypt it. Also, both encrypted and decrypted keys are of same length. But encrypted payment id is 16 chars where normal Payment ids is 106 chars. Aug 23, 2018 at 6:38
  • No, payment IDs are either 32 bytes (64 hexadecimal characters) or 8 bytes (in the case of integrated addresses). As you asked about integrated addresses, the payment ID would be 8 bytes (or 16 chars as hex). There is no case where "Payment ids is 106 chars", whether encrypted, integrated, whatever.
    – jtgrassie
    Aug 23, 2018 at 13:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.