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.

user_info container

For example:

Copy
Copied!
"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:

Copy
Copied!
"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 →

Copy
Copied!
    contract.quote.quote_elements[0].sending_amount

Originating currency →

Copy
Copied!
    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:

Copy
Copied!
    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:

Copy
Copied!
    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 →

Copy
Copied!
    contract.quote.quote_elements[0].sending_fee

Originating institution charge currency →

Copy
Copied!
    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 →

Copy
Copied!
    contract.quote.quote_elements[<last>].receiving_amount

Received currency →

Copy
Copied!
    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 →

Copy
Copied!
    contract.quote.quote_elements[<last>].receiving_fee

Receiving institution charge currency →

Copy
Copied!
    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

Copy
Copied!
    contract.created_at

Payment expiry date

Copy
Copied!
    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 or Dbtr.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 or Dbtr.Id.OrgId.Othr.Id to hold the ID value of the organization like the value of CICN or TXID.
  • Use Cdtr.Nm or Dbtr.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 or Dbtr.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 or Dbtr.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.
  • 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:

Copy
Copied!
        "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:

Copy
Copied!
        "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 the contract 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 or Cdtr.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 from Strd.Nm .
  • Sending participant maps Payment Purpose to PmtTpInf.CtgyPurp.Prtry but the receiving participant extracts it from Purp.Cd .
  • Sending participant maps Payment Channel to PmtTpInf.ClrChnl but Receiving participant extracts it from PmtTpInf.CtgyPurp.Prtry .

These mismatched mappings have resulted in unplanned work and delays in implementations.