User info section
The user_info
section is an array in the payment object that provides a placeholder to exchange information between the payment participants. It is used by the participants to describe the payment beneficiary and originator information as sent by a sending institution to a receiving institution. The user_info
section is an important constituent of the full payment object by virtue of it containing time stamps and actions taken by each participant in a payment, effectively tracing the life-cycle of the payment from quote to completion.
Personal identifiable information (PII)
The user_info
section stores personally identifiable information (PII) required for payment processing. This data is retained for 90 days to support essential reporting features and compliance functions.
Each element in the user_info
array corresponds to a participant in the payment chain. Each of these elements has further information relating to the actions performed and the timestamps indicating when they were performed.
For example:
"user_info" :
{
"node_address":"rn.uk.sender",
"accepted": [
{
"json": [
{
"Cdtr": {
"Nm": "Widget Ltd"
},
"CdtrAcct": {
"Id": {
"IBAN": "DE89370400440532013000"
}
},
"CdtrAgt": {
"FinInstnId": {
"BICFI": "BANKGB22"
}
},
"Dbtr": {
"Nm": "Widgets Worldwide Ltd",
"PstlAdr": {
"AdrLine": [
"Main St"
],
"BldgNb": "10204",
"Ctry": "OM"
},
"Id": {
"PrvtId": {
"Othr": {
"Id": "10",
"Issr": "10"
}
}
},
"CtryOfRes": "US"
},
"DbtrAcct": {
"Id": {
"IBAN": "GB29NWBK60161331926819"
}
},
"RmtInf": {
"Ustrd": "LEGAL INVOICE NO. PAY/X/2021/344129345/101"
}
{...}
}
],
"created_at": "2023-09-04T23:28:50.572Z"
}
],
"locked": [],
"lock_declined": [],
"retry_accept": [],
"retry_settlement": [],
"settlement": [],
"failed": [],
"executed": [],
"completed": [],
"forwarded": [],
"returned": []
}
{
"node_address":"rn.us.receiver",
"accepted": [],
"locked": [
"created_at": "2023-09-05T23:28:50.572Z"
],
"lock_declined": [],
{...}
}
In the above example, each participant node contains a static list of “state” nodes for each possible state of a payment (ACCEPTED
, LOCKED
, FAILED
, etc.). When a participant acts upon the payment object, the payment transitions to a new state, and the state node in the acting participant’s node records this change.
For example, in the second node of the user_info
object above, it is the second node (rn.us.receiver) which locked the payment, as shown by the entry inside the locked
element below:
"locked": [
"created_at": "2023-09-05T23:28:50.572Z"
]
This action logging allows you to analyze the life cycle of the payment by looking at the user_info
object, identifying which participant performed what action, and when.
User info state node
Each state node contains the details of the actions performed by the participant that caused the payment state to change. These details are used by participants to perform all required compliance checks and validations on a payment.
The user_info.<payment_state>.json
node contains all the mandatory fields required by the Standard RippleNet Payment Object specification. It may contain additional fields if the receiver requires them to satisfy local regulations. Per the RippleNet Rulebook, the receiver is responsible for informing the sender of the fields, allowed data formats, and supported characters they need to process a payment. The superset of all allowed fields is stipulated by the RippleNet Rulebook, which governs the activity of all RippleNet participants.
In multi-hop scenarios where more than two entities are involved in the payment chain, the intermediary and the receiving participants process the payment object containing the same user_info[].<sending_participant_address>.accepted[].json[]
section as sent by the sending institution. All participants perform their own compliance and validation checks as needed on the above element.
The fields required by Receiving participants vary depending on both the countries and currencies that they operate in and the payment rails that they use to forward payments to beneficiaries who are not customers of the Receiving participant.
The RippleNet Payment Object Schema closely aligns to the ISO20022 PACS.008 message specifications. Each property of the JSON schema shares the same naming convention as the equivalent ISO 20022 definition.
Some fields based on the ISO nomenclature only allow ISO values to be passed in, specifically the fields that end in a .Cd
in the payment object. A good example for this is the account type field: CdtrAcct.Tp.Cd
. This field should only contain values from the ISO20022 ExternalCashAccountType1Code
List.
We recommend RippleNet participants to adhere to ISO standards, as the usage of proprietary fields would mean additional development work for your partner.
Roles
SENDING
For all the payments sent by an institution, the connector_role
field has the value SENDING
. This is helpful when the institution wants to get a list of all payments it has initiated for payment processing.
For example, when the sending institution wants to identify all the LOCKED
or COMPLETED
payments for the next step of payment processing, they can call the Get Payments API with the connector_role
parameter specified as SENDING
. This ensures that only the relevant payments are returned.
RECEIVING
For all the payments received by an institution, where it is the terminating institution, the connector_role
field has the value RECEIVING
. This is helpful when the institution wants to get a list of all payments it has received for payment processing.
For example, when the receiving institution wants to identify all the ACCEPTED
or SETTLED
payments for the next step of payment processing, they can call the Get Payments API with the connector_role
parameter specified as RECEIVING
. This ensures that only the relevant payments are returned.
INTERMEDIARY
For all the payments where an institution is playing the role of an intermediary in a multi-hop payment chain, the connector_role
field has the value INTERMEDIARY
. This is helpful when the institution wants to get a list of all payments where it served as an intermediary for payment processing.
For example, when the intermediary institution wants to identify all the ACCEPTED
or SETTLED
payments for the next step of payment processing, they can call the Get Payments API with the connector_role
parameter specified as INTERMEDIARY
. This ensures that only the relevant payments are returned.
Coding against the RippleNet Payment Object
All implementations require a standard set of fields for processing a payment. Not all of these fields are available in the user_info
. Many are available only as part of the contract
object or other parts of the RippleNet Payment Object (RPO).
For example:
Originating or sending amount and currency
Originating amount →
contract.quote.quote_elements[0].sending_amount
Originating currency →
if (contract.quote.quote_elements[0].quote_element_type == TRANSFER)
orig_crncy = contract.quote.quote_elements[0].transfer_currency_code
else if (contract.quote.quote_elements[0].quote_element_type == EXCHANGE)
orig_crncy = contract.quote.quote_elements[0].sending_currency_code
Note
The logic is different if a terminal or intermediary node in a multi-hop scenario wants to know the details of the institution preceding. The institution must iterate through the quote_elements
array and identify all the quote elements belonging to the node immediately prior to itself by looking at the sender_address
or receiver_address
fields (note that the value after the @
gives the RippleNet address of the transacting node). Once these quote elements are identified, the amount and currency derivation logic remains the same as mentioned above.
FX rate used
Depending on the party applying the FX, the FX Rate could be applied on the sender’s side or the receiver’s side.
The following examples refer to a single-hop scenario only.
If the sender is applying the rate:
if (contract.quote.quote_elements[0].quote_element_type == TRANSFER)
fx_rate = 1 //(no rate applied)
else if (contract.quote.quote_elements[0].quote_element_type == EXCHANGE)
fx_rate = contract.quote.quote_elements[0].fx_rate
If the receiver is applying the rate:
if (contract.quote.quote_elements[<last>].quote_element_type == TRANSFER)
fx_rate = 1 //(no rate applied)
else if (contract.quote.quote_elements[<last>].quote_element_type == EXCHANGE)
fx_rate = contract.quote.quote_elements[<last>].fx_rate
Note
There could also be cases where multiple rates are used within a liquidity path. This needs special handling to identify the effective rate used.
Originating or sending institution charges and currency
Originating institution charges →
contract.quote.quote_elements[0].sending_fee
Originating institution charge currency →
if (contract.quote.quote_elements[0].quote_element_type == TRANSFER)
contract.quote.quote_elements[0].transfer_currency_code
else if (contract.quote.quote_elements[0].quote_element_type == EXCHANGE)
contract.quote.quote_elements[0].sending_currency_code
Note
The logic is different if a terminal or intermediary node in a multi-hop scenario wants to know the details of the institution preceding. The institution must iterate through the quote_elements
array and identify all the quote elements belonging to the node immediately prior to itself by looking at the sender_address
or receiver_address
fields (note that the value after the @
gives the RippleNet address of the transacting node). Once these quote elements are identified, the amount and currency derivation logic remains the same as above.
Identifying the originating or sending institution
To derive the internal Vostro account of a partner, clients usually need to identify the Originating or Sending Institution. The recommended way is to read the validator
field, which contains the address of the Originator or Sender of the payment.
If the terminal or intermediary node in a multi-hop scenario wants to know the address of the institution preceding it, the logic is different. In this scenario, you must iterate through the user_info[].node_address
field values and identify the node_address
immediately preceding the current node’s address.
Settled or received amount and currency
Received amount →
contract.quote.quote_elements[<last>].receiving_amount
Received currency →
if (contract.quote.quote_elements[<last>].quote_element_type == TRANSFER)
received_crncy = contract.quote.quote_elements[<last>].transfer_currency_code
else if (contract.quote.quote_elements[<last>].quote_element_type == EXCHANGE)
received_crncy = contract.quote.quote_elements[<last>].receiving_currency_code
Note
The logic is different if a terminal or intermediary node in a multi-hop scenario wants to know the settled or received amount. The institution must iterate through the quote_elements
array and identify all the quote_elements
belonging to the node immediately prior to itself by looking at the sender_address
or receiver_address
fields (note that the value after the @
gives the RippleNet address of the transacting node). Once these quote_elements
are identified, the amount and currency derivation logic remains the same as above.
Charges to be collected
Receiving institution charges →
contract.quote.quote_elements[<last>].receiving_fee
Receiving institution charge currency →
if (contract.quote.quote_elements[<last>].quote_element_type == TRANSFER)
contract.quote.quote_elements[<last>].transfer_currency_code
else if (contract.quote.quote_elements[<last>].quote_element_type == EXCHANGE)
contract.quote.quote_elements[<last>].sending_currency_code
Note
The above logic does not hold for an intermediary in a multi-hop scenario.
Payment created date
contract.created_at
Payment expiry date
contract.expires_at
How to map fields with user info
In addition to the transaction amount and currency fields mentioned in the previous section, the receiving participant may require the sending participant to include other fields like Account, Address, Identification details of the originator and beneficiary. These are available as part of the user_info
object.
If you are a receiver:
- Identify the list of fields as per your internal payment processing requirements.
- Clearly classify these fields as Mandatory, Optional, or Conditional along with the necessary description for each of these fields.
- Find the corresponding fields in the RPO from the Ripple documentation page.
- Contact your Ripple liaison if you need assistance or clarification.
- Refer to the Best Practices section.
If you are a sender:
- Request for the RPO specification from your Receiving partner. This should include a clear indication of all Mandatory, Optional, and Conditional fields along with their descriptions.
- Map the fields from your internal payment originating system to the above RPO specification.
- Contact your Ripple liaison if you need assistance or clarification.
Best practices when mapping fields
Map all required fields to the latest payment object standard
When an institution requires supporting information in addition to the information already provided using the mandatory fields of the SRPO, RippleNet participants should use the existing non-mandatory fields in the SRPO to provide this information. If you are having trouble mapping fields, contact the Ripple support team for clarification and direction.
Reduce mandatory fields
If you are a receiving participant, try to minimize the number of mandatory fields that the sending participant must provide in addition to the fields required by the SRPO. This makes the onboarding easier and quicker.
Identifying and specifying corporates and individuals
It is often necessary for both RippleNet participants to identify whether the Debtor or Creditor is an Organization or an Individual. There are sub-fields available in the RPO where this information can be provided.
For organizations and corporate entities
-
Use
Cdtr.Id.OrgId.Othr.SchmeNm.Cd
orDbtr.Id.OrgId.Othr.SchmeNm.Cd
to hold the ID type of the organization like CICN (for Certificate of Company Incorporation Number) or TXID (for the Tax Identification Number). -
Use
Cdtr.Id.OrgId.Othr.Id
orDbtr.Id.OrgId.Othr.Id
to hold the ID value of the organization like the value of CICN or TXID. -
Use
Cdtr.Nm
orDbtr.Nm
to specify the name of the organization. -
Your middleware should be able to identify that it is a corporate payment by the presence of
OrgId
parameters.
For individuals
-
Use
Cdtr.Id.PrvtId.Othr.SchmeNm.Cd
orDbtr.Id.PrvtId.Othr.SchmeNm.Cd
to hold the individual's ID type, like CCPT (for Passport Number) or NIDN (for National Identification Number). -
Use
Cdtr.Id.PrvtId.Othr.Id
orDbtr.Id.PrvtId.Othr.Id
to hold the individual's ID value, like the Passport Number or the NIDN. -
To specify the individual's name, specify the
StrdNm
field in the RPO. This provides flexibility in mapping internal APIs, catering to both of the following scenarios:-
If the participant’s internal API expects a single string in the “name” field, concatenate the
FirstNm
,MidNm
,LastNm
fields in the middleware code to get the string. - If the participant’s internal API expects a breakup of the First, Middle, and Last names, direct one-to-one mapping is available in the RPO.
-
If the participant’s internal API expects a single string in the “name” field, concatenate the
-
Your middleware should be able to identify that it is an individual payment by the presence of
PrvtId
parameters.
Note
In some cases, the sender or receiver on RippleNet do not need the ID and Type values of the party. It may not be possible for them to capture these identification details at all. But, they still need to identify whether the party is an individual or an organization. In this case, it is suggested that the sender populate the <Cdtr/Dbtr>.Id.<PrvtId/OrgId>.Othr.Id
with a fixed string. This indicates to both the sender and receiver whether the party is an individual or an organization. Correspondingly the <Cdtr/Dbtr>.Id.<OrgId/PrvtId>.Othr.SchmeNm.Cd
field must be populated with a fixed value. The recommended values are CICN for organizations and NIDN for individuals.
Use ISO codes
Wherever possible, RippleNet participants must adhere to the Cd
(for example, CtgyPurp.Cd
) fields and make use of the published list of ISO code values for these fields to process payments.
If the allowed list of ISO Code values does not suffice for the mapped RPO field, please contact your Ripple liaison for alternatives and solutions. In some cases, the Ripple team will be able to help you to extend the list of ISO codes to cater to unique requirements.
For example:
The FinInstnId.ClrSysMmbId.ClrSysId.Cd
field is used to populate the local bank identification schemes and is specified in the ISO20022 ExternalClearingSystemIdentification1Code
list. This list has been extended in the Ripple standards to include additional values. For example, Ripple has added SLCFT for Sri Lankan CEFT identification scheme and BEFTN for Bangladesh EFT identification scheme.
Using proprietary values only when ISO values do not suffice
Use the Proprietary (Prtry
) fields available on the RPO if and only if the ISO Code values of the corresponding Cd
field do not satisfy the requirements of the participant. The most common example is the Purpose Code (CtgryPurp.Prtry
) which is usually a list of predefined values dictated by the central bank of the receiving country.
If proprietary values are used, the allowed list of values and their description should be captured in the Ripple project workbook.
Frequently encountered issues
Improper or mismatched JSON parsing logic
If a field is deemed optional, it may appear in the RPO as an empty value or may be missing altogether. In both cases, the receiving participant should be able to process the RPO. Similarly, the receiving participant should be able to process the RPO if the sending participant specifies fields that are not mandatory. Sending participants are connected to multiple receiving participants on the network, and they capture as much information as possible to pass on to the receiving participants. Receiving participants' systems should only parse and work with the fields they need.
For example, if a receiving participant marks the Cdtr.PstlAdr.PstCd
field as "Optional" or "Not Required", then they should be able to accept and process any of the following usages:
"Cdtr": {
"PstlAdr": {
"PstCd": "",
"Ctry": "US"
}
}
or,
"Cdtr": {
"PstlAdr": {
"PstCd": "123456",
"Ctry": "US"
}
}
or,
"Cdtr": {
"PstlAdr": {
"Ctry": "US"
}
}
Party address usage
It is important to note that as per the RPO Specifications, the AdrLine
field is an array of strings, each string having a maximum length of 70 characters. Some RippleNet participants have implemented this field as a string instead of an array, causing mismatches and errors during payment processing. We recommend providing a clear example of this JSON object to new participants so that they develop in accordance with the specs.
For example:
"Dbtr": {
"PstlAdr": {
"AdrLine": [
"123 Main St",
"City Name",
"PIN - 000000"
]
}
}
Unmappable fields
Though the RPO has a significant number of fields, occasionally it falls short of providing an appropriate field to map. Examples of such fields:
- Employer name of the party
- Employer identification details
- Native Nname (full name in native language)
- Geolocation code or lat-long specs (indicating where the payment was initiated)
- Attach supporting documents to the RPO
-
Sender’s Nostro with receiver (in rare cases the receiver needs this to be sent in the RPO, but it should ideally be derived from the information within the
contract
object, either from the connector account or sending party’s RNS address) - Province and regency code (for example, in Indonesia)
When this happens, the RippleNet participant should first check whether the field is mandatory and can preferably be skipped from mapping. If not, the RippleNet participant can contact the Ripple support team for guidance. Ripple will be able to help you identify a closely related field or a usable alternative. We also recommend raising a ticket with the Ripple Team to include these fields as part of future RPO versions.
Examples:
-
Native name (full name in native language) → use
CtctDtlsOthr
as an alternative. - Attach supporting documents to the RPO → we can capture the key details of a document in the RPO fields (for example, we have fields like invoice, tax, and reference available)
-
Sender’s Nostro with receiver → use
DbtrAgt.FinInstnId.Othr.Id
as a placeholder to send the Ripple Segregated Account (RSA or the sender’s Nostro with the receiver). Please note that this is usually not required as the receiver is expected to derive the RSA from the information contained in thecontract
object. This information is either the connector account value or the RNS address of the sender. -
Province and regency code in Indonesia → As a workaround, both Province and Regency Code are mapped to a single concatenated field (
Dbtr.PstlAdr.CtrySubDvsn
orCdtr.PstlAdr.CtrySubDvsn
). This field will contain a total of 6 digits, out of which the first 2 digits are the Province Code and the next 4 digits are the Regency Code.
Non-standard or mismatched mapping
Non-standard or mismatched mapping happens when two existing RippleNet participants decide to form a new corridor, but during the initial analysis find that the same field was mapped to different fields on the RPO.
Some examples:
-
Sending participant maps
Party Name
to
Nm
but the receiving participant extracts fromStrd.Nm
. -
Sending participant maps
Payment Purpose
to
PmtTpInf.CtgyPurp.Prtry
but the receiving participant extracts it fromPurp.Cd
. -
Sending participant maps
Payment Channel
to
PmtTpInf.ClrChnl
but Receiving participant extracts it fromPmtTpInf.CtgyPurp.Prtry
.
These mismatched mappings have resulted in unplanned work and delays in implementations.