From 79965e442776cf474024466e8714718fe428ab21 Mon Sep 17 00:00:00 2001 From: Zhen Lu Date: Tue, 16 Jul 2024 15:59:17 -0700 Subject: [PATCH] Add invoice uuid field to PayRequest, and add it to the bolt11 invoice metadata. (#41) --- uma/protocol/pay_request.go | 4 ++ uma/uma.go | 95 +++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/uma/protocol/pay_request.go b/uma/protocol/pay_request.go index b4cb900..3b48ef7 100644 --- a/uma/protocol/pay_request.go +++ b/uma/protocol/pay_request.go @@ -46,6 +46,9 @@ type PayRequest struct { // if the receiver included the `commentAllowed` field in the lnurlp response. The length of // the comment must be less than or equal to the value of `commentAllowed`. Comment *string `json:"comment,omitempty"` + // InvoiceUUID is the invoice UUID that the sender is paying. + // This only exists in the v1 pay request since the v0 SDK won't support invoices. + InvoiceUUID *string `json:"invoiceUUID,omitempty"` // UmaMajorVersion is the major version of the UMA protocol that the VASP supports for this currency. This is used // for serialization, but is not serialized itself. UmaMajorVersion int `json:"-"` @@ -65,6 +68,7 @@ type v1PayRequest struct { PayerData *PayerData `json:"payerData,omitempty"` RequestedPayeeData *CounterPartyDataOptions `json:"payeeData,omitempty"` Comment *string `json:"comment,omitempty"` + InvoiceUUID *string `json:"invoiceUUID,omitempty"` } // IsUmaRequest returns true if the request is a valid UMA request, otherwise, if any fields are missing, it returns false. diff --git a/uma/uma.go b/uma/uma.go index f84a002..052230b 100644 --- a/uma/uma.go +++ b/uma/uma.go @@ -539,6 +539,78 @@ func GetUmaPayRequest( utxoCallback string, requestedPayeeData *protocol.CounterPartyDataOptions, comment *string, +) (*protocol.PayRequest, error) { + return GetUmaPayRequestWithInvoice( + amount, + receiverEncryptionPubKey, + sendingVaspPrivateKey, + receivingCurrencyCode, + isAmountInReceivingCurrency, + payerIdentifier, + umaMajorVersion, + payerName, + payerEmail, + trInfo, + trInfoFormat, + payerKycStatus, + payerUtxos, + payerNodePubKey, + utxoCallback, + requestedPayeeData, + comment, + nil, + ) +} + +// GetUmaPayRequestWithInvoice Creates a signed UMA pay request to pay an UMA invoice. For non-UMA LNURL requests, just construct a protocol.PayRequest directly. +// +// Args: +// +// amount: the amount of the payment in the smallest unit of the specified currency (i.e. cents for USD). +// receiverEncryptionPubKey: the public key of the receiver that will be used to encrypt the travel rule information. +// sendingVaspPrivateKey: the private key of the VASP that is sending the payment. This will be used to sign the request. +// receivingCurrencyCode: the code of the currency that the receiver will receive for this payment. +// isAmountInReceivingCurrency: whether the amount field is specified in the smallest unit of the receiving +// currency or in msats (if false). +// payerIdentifier: the identifier of the sender. For example, $alice@vasp1.com +// umaMajorVersion: the major version of UMA used for this request. If non-UMA, this version is still relevant +// for which LUD-21 spec to follow. For the older LUD-21 spec, this should be 0. For the newer LUD-21 spec, +// this should be 1. +// payerName: the name of the sender (optional). +// payerEmail: the email of the sender (optional). +// trInfo: the travel rule information. This will be encrypted before sending to the receiver. +// trInfoFormat: the standardized format of the travel rule information (e.g. IVMS). Null indicates raw json or a +// custom format, or no travel rule information. +// payerKycStatus: whether the sender is a KYC'd customer of the sending VASP. +// payerUtxos: the list of UTXOs of the sender's channels that might be used to fund the payment. +// payerNodePubKey: If known, the public key of the sender's node. If supported by the receiving VASP's compliance provider, +// this will be used to pre-screen the sender's UTXOs for compliance purposes. +// utxoCallback: the URL that the receiver will call to send UTXOs of the channel that the receiver used to receive +// the payment once it completes. +// requestedPayeeData: the payer data options that the sender is requesting about the receiver. +// comment: a comment that the sender would like to include with the payment. This can only be included +// if the receiver included the `commentAllowed` field in the lnurlp response. The length of +// the comment must be less than or equal to the value of `commentAllowed`. +// invoiceUUID: the UUID of the invoice that the sender is paying. +func GetUmaPayRequestWithInvoice( + amount int64, + receiverEncryptionPubKey []byte, + sendingVaspPrivateKey []byte, + receivingCurrencyCode string, + isAmountInReceivingCurrency bool, + payerIdentifier string, + umaMajorVersion int, + payerName *string, + payerEmail *string, + trInfo *string, + trInfoFormat *protocol.TravelRuleFormat, + payerKycStatus protocol.KycStatus, + payerUtxos *[]string, + payerNodePubKey *string, + utxoCallback string, + requestedPayeeData *protocol.CounterPartyDataOptions, + comment *string, + invoiceUUID *string, ) (*protocol.PayRequest, error) { complianceData, err := getSignedCompliancePayerData( receiverEncryptionPubKey, @@ -584,6 +656,7 @@ func GetUmaPayRequest( RequestedPayeeData: requestedPayeeData, Comment: comment, UmaMajorVersion: umaMajorVersion, + InvoiceUUID: invoiceUUID, }, nil } @@ -657,6 +730,21 @@ type InvoiceCreator interface { CreateInvoice(amountMsats int64, metadata string) (*string, error) } +func addInvoiceUUIDToMetadata(metadata string, invoiceUUID string) (string, error) { + var data [][]interface{} + err := json.Unmarshal([]byte(metadata), &data) + if err != nil { + return "", err + } + invoice := []interface{}{"text/plain", invoiceUUID} + data = append(data, invoice) + updatedJSON, err := json.Marshal(data) + if err != nil { + return "", err + } + return string(updatedJSON), nil +} + // GetPayReqResponse Creates an uma pay request response with an encoded invoice. // // Args: @@ -732,6 +820,13 @@ func GetPayReqResponse( } payerDataStr = string(encodedPayerData) } + if request.InvoiceUUID != nil { + var err error + metadata, err = addInvoiceUUIDToMetadata(metadata, *request.InvoiceUUID) + if err != nil { + return nil, err + } + } encodedInvoice, err := invoiceCreator.CreateInvoice(msatsAmount, metadata+payerDataStr) if err != nil { return nil, err