Send a payment

This page is for developers of new applications that send RippleNet payments. It is recommended that new integrations use the orchestration API operations that make it easy to initiate and fulfill a payment, and to obtain notifications about payment status.

The goals of this topic are:

  • Describe the high-level payment flow for senders.
  • Provide a tutorial to send one test payment.
  • Outline the options for getting notifications about payments.

Overview

Note

Before you read this tutorial, make sure you're familiar with the concepts presented in the Payment flow article.

Payment flow for senders — automatic quote acceptance

The following describes the flow of tasks a sender must complete to successfully send a payment:

  1. Get access token — All RippleNet API operations (except the Authentication operation itself) must include a valid access token in the Authorization header of each request. This is described in Authentication .
  2. Create payment — A single call to the Create orchestration payment operation lets you specify all the fields needed to initiate and fulfill a new payment, including quote retrieval and approval, and payment instructions, including the identifying information of the originator and beneficiary of the payment.
  3. Check payment status — You can check the status of your payment, by polling , or using use a webhook . These are described below.
  4. Additional actions (if required) — In case of an exception in payment processing, your client application can indicate the next course of action using the Post action to orchestration payment API operation.

Payment flow for senders — manual quote acceptance

Ordinarily, Create orchestration payment takes care of the steps of fetching and approving a quote for you. All you need to do as the sender is execute the Create orchestration payment operation, and this sets the status of the new payment to ACCEPTED, ready for your receiver counterparty to lock the payment, which initiates the settlement process automatically.

However, if you prefer, you can choose to control the process of fetching and approving the quote.

The process is exactly the same as described above, except instead of calling Create orchestration payment, you first call Create quote collection to fetch the quote, and then the Accept orchestration payment operation. The rest of the payment flow is the same.

  1. Get Access Token — All RippleNet API operations (except the Authentication operation itself) must include a valid access token in the Authorization header of each request. This is described in Authentication .
  2. Fetch quote — The sender supplies payment parameters with a Create quote collection request for all available quotes from RippleNet. Typically you will chose the first of these.
  3. Accept payment — A call to Accept orchestration payment lets you specify all the fields needed to first accept the quote, and then initiate and fulfill a new payment, including the identifying information of the originator and beneficiary of the payment.
  4. Check payment status — You can check the status of your payment, using either webhooks (recommended) or polling . These are described below.
  5. Additional actions (if required) — In case of an exception in payment processing, your client application can indicate the next course of action via the Post action to orchestration payment API operation.

The orchestration API operations

To complete a payment, both senders and receivers require a very small set of API operations that use orchestration to simplify the workflow. These orchestration operations not only simplify the process of completing a payment, but they also support webhooks for delivering notifications about payment states.

Note about 'orchestration' and 'templates'

The term orchestration refers to a pre-defined sequence of actions that occur during a payment flow. The payment flow is governed by an orchestration template (sometimes referred to as a workflow template) that defines the action that the RippleNet service takes automatically at each stage of the payment process.

Orchestration templates are pre-defined by Ripple, and different payment flows are represented by different templates.

There are three categories of orchestration operations:

  • Orchestration payments — These operations are primarily of use by senders for initiating and and working with payments.
  • Orchestration notifications — These operations are used both by senders and receivers to get notifications about payments and payment states.
  • Orchestration actions — This category consists of a single API operation, Post action to orchestration payment , that lets you perform various webhook-enabled actions on payments. For example, receivers use this API operation both to lock and to complete a payment.

Check payment status

To check the status of your payment, you first need to receive notification of a status change.

The recommended way to do this is set up webhooks notifications, to have RippleNet post to a previously registered callback URL. The body of the post contains a message ID (uuid) that you can use with a call to Get orchestration notification to retrieve the full notification details about the status of your payment. For more information, see Webhooks under Best Practices.

Alternatively, if webhook notifications are not an option for you, you can use polling — repeatedly calling a Get orchestration notifications operation, until your payment appears in the response. For more information, see Polling.

Send a payment — instructions

Prerequisites

Before you begin, you must have the following:

  • If you plan to use webhook notifications, your callback URL needs to be registered with RippleNet. Contact your CPE for details.
  • Access to your RippleNet test instance.
  • Access to RippleNet Home.
  • Access to a counterparty instance; either through an ODL Test Mode environment that Ripple provides, or a counterparty's UAT instance.
  • Credentials to authenticate API authorization to your RippleNet test instance.
  • A way to make API requests and view the responses. For example, you can use a REST client like Postman, or cURL from the command line.

Get access token

To access all RippleNet API operations (except the Authentication operation itself), you need a valid access token.

See Authentication.

Create the payment

To create a payment using orchestration, you have two options:

  • You can create the payment using automatic quote acceptance; or
  • You can get a quote collection and examine the quote before accepting it manually.

These two options are described next.

Create the payment using automatic quote acceptance

If you want to auto-accept the quote, just call the Create orchestration payment API operation as follows:

Copy
Copied!
POST /v4/orchestration/payment HTTP/1.1
Host: aclient.i.ripple.com
Content-Type: application/json
Authorization: Bearer <token>

In the body of the POST call, you specify all the parameters needed to fetch and accept the quote, and to settle the payment when the receiver locks the payment.

Click to view the Create orchestration payment body ...
Copy
Copied!
{
	"sender_end_to_end_id": "19999999999-012",
    "sending_address": "dev.bfox01.sgd",
    "receiving_address": "dev.bfox02.sgd",
    "currency": "SGD",
    "amount": "10",
    "paymentMethod": "CASH",
    "quote_type": "SENDER_AMOUNT",
    "user_info" : {
    	"ChrgBr" : "SHAR",
    	"Cdtr" : {
    	"Id" : "8675309",
    	"Nm" : "John3"
    	},
    	"Dbtr" : {
    	"Id" : "8675309",
    	"Nm" : "Jenny"
    	}
	}
}

Each of these required parameters is described below.

If RippleNet successfully receives your Create orchestration payment request, its response will simply confirm the unique sender_end_to_end_id that you provided in the API call.

Copy
Copied!
{
  "sender_end_to_end_id": "19999999999-012"
}

Create the payment and manually accept the quote

If you prefer to fetch and accept the quote manually, first call the Create orchestration payment operation, followed by the Accept orchestration payment operation, as follows:

Copy
Copied!
POST /v4/quote_collections HTTP/1.1
Host: aclient.i.ripple.com
Content-Type: application/json
Authorization: Bearer <token>

In the body of the POST call, you specify all the parameters need to fetch the quote:

Click to view the Create quote collection body ...
Copy
Copied!
{
    "sending_address": "dev.bfox01.sgd",
    "receiving_address": "dev.bfox02.sgd",
    "currency": "SGD",
    "amount": "10",
    "paymentMethod": "CASH",
    "quote_type": "SENDER_AMOUNT",
}

The response from the Create quote collection operation contains all the information about the quote, including the quote_id you need to accept the quote.

Click to view the response from Create quote collection call ...
Copy
Copied!
{
    "quote_collection_id": "23bfaac6-1dc6-4f41-8d8a-58859301bebf",
    "quotes": [
        {
            "quote_id": "02d6cfe6-6ba8-4d9e-a52b-452d934af952",
            "created_at": "2022-09-13T00:12:58.178Z",
            "expires_at": "2022-09-13T01:12:58.178Z",
            "type": "SENDER_AMOUNT",
            "price_guarantee": "FIRM",
            "sender_address": "trans_usd_alpha@rn.dev.apac.alpha",
            "receiver_address": "trans_usd_beta@rn.dev.apac.beta",
            "amount": "25",
            "currency_code": "USD",
            "currency_code_filter": null,
            "service_type": null,
            "quote_elements": [
                {
                    "quote_element_id": "0787550e-b547-4d58-ab48-19e426398fc7",
                    "quote_element_type": "TRANSFER",
                    "quote_element_order": "1",
                    "sender_address": "trans_usd_alpha@rn.dev.apac.alpha",
                    "receiver_address": "alias_usd_alpha@rn.dev.apac.alpha",
                    "sending_amount": "25.00",
                    "receiving_amount": "25.00",
                    "sending_fee": "0.00",
                    "receiving_fee": "0.00",
                    "sending_currency_code": null,
                    "receiving_currency_code": null,
                    "fx_rate": null,
                    "transfer_currency_code": "USD"
                },
                {
                    "quote_element_id": "4eb4a084-0f64-48e8-8b1e-3a7d2c73d18b",
                    "quote_element_type": "TRANSFER",
                    "quote_element_order": "2",
                    "sender_address": "alias_usd_beta@rn.dev.apac.beta",
                    "receiver_address": "trans_usd_beta@rn.dev.apac.beta",
                    "sending_amount": "25.00",
                    "receiving_amount": "25.00",
                    "sending_fee": "0.00",
                    "receiving_fee": "0.00",
                    "sending_currency_code": null,
                    "receiving_currency_code": null,
                    "fx_rate": null,
                    "transfer_currency_code": "USD"
                }
            ],
            "liquidity_warning": null,
            "payment_method": null
        }
    ],
    "quote_errors": []
}

Once you have the quote_id for the quote, call Accept orchestration payment to finish creating the payment:

Copy
Copied!
POST /v4/orchestration/payment/accept HTTP/1.1
Host: aclient.i.ripple.com
Content-Type: application/json
Authorization: Bearer <token>

And in the body of the POST call, you specify the quote_id needed to accept the quote, and the remaining parameters needed to settle the payment when the receiver locks the quote:

Click to view Accept orchestration payment body ...
Copy
Copied!
{
	"sender_end_to_end_id": "19999999999-012",
    "quote_id": "02d6cfe6-6ba8-4d9e-a52b-452d934af952",
    "user_info" : {
    	"ChrgBr" : "SHAR",
    	"Cdtr" : {
    	"Id" : "8675309",
    	"Nm" : "John3"
    	},
    	"Dbtr" : {
    	"Id" : "8675309",
    	"Nm" : "Jenny"
    	}
	}
}

These required parameters are described below:

If RippleNet successfully receives your Accept orchestration payment request, its response will simply confirm the unique sender_end_to_end_id that you provided in the API call.

Copy
Copied!
{
  "sender_end_to_end_id": "19999999999-012"
}

Required information

As the Create orchestration payment operation is essentially a combination of the Create quote collection and Accept orchestration payment operations, the three operations share a number of parameters that must be supplied in the body of the POST request. This table summarizes which parameters are required by each operation.

Parameter Required by
Create orchestration payment
Required by
Create quote collection
Required by
Accept orchestration payment
sender_end_to_end_id tick tick
sending_address tick tick
receiving_address tick tick
amount tick tick
currency tick tick
quote type tick tick
payment method tick tick
user_info tick tick
quote_id tick

Here is a description of each of these parameters:

  • sender_end_to_end_id — Any randomly generated UUID . This is to allow your client application to distinguish this payment from any others that it has created.
  • sending_address — RippleNet account name and address of the sender , in the format {ACCOUNT_NAME}@{RIPPLENET_ADDRESS} . For example, new_york@rn.usa.north_bank.new_york . For more information, see RippleNet Addressing Scheme .
  • receiving_address — RippleNet address of the destination partner. This is not the beneficiary 's account. It is the address of the last RippleNet member institution that processes the final leg of the payment. For example, the institution that provides the payment delivery over local rails. The field should use the following format: {ACCOUNT_NAME}@{RIPPLENET_ADDRESS} . For more information, see RippleNet Addressing Scheme .
  • amount — The amount of the payment. If you specify SENDER_AMOUNT for quote_type , this amount is debited from your account. If you specify RECEIVER_AMOUNT for quote_type , this amount is delivered to the beneficiary .
  • currency — Currency code of the amount to be sent or received, depending on the quote_type value. If quote_type is set to SENDER_AMOUNT or SENDER_INSTITUTION_AMOUNT , this is the currency of the amount to be sent. If quote_type is set to RECEIVER_AMOUNT or RECEIVER_INSTITUTION_AMOUNT , this is the currency of the amount to be received. Use a valid ISO 2117 currency code.
  • quote_type — Specifies how RippleNet calculates the quote. Valid values are SENDER_AMOUNT , RECEIVER_AMOUNT , SENDER_INSTITUTION_AMOUNT , or RECEIVER_INSTITUTION_AMOUNT .
    • To create a quote based on the amount the sender (on behalf of the originator ) wants to send, set to SENDER_AMOUNT .
    • To create a quote based on the amount the sender institution will send, set to SENDER_INSTITUTION_AMOUNT .
    • To create a quote based on the amount the receiver institution will receive, set to RECEIVER_INSTITUTION_AMOUNT .
    • To create a quote based on the amount the receiver (on behalf of the beneficiary ) will receive, set to RECEIVER_AMOUNT .
  • payment_method — When making a payment through an exchange on local rails, you must provide the payment_method in the Create orchestration payment or Create quote collection request. When you accept a quote, the payout outlet_id you specify in the outbound_instructions for payment on local rails must match the payment_method value in the quote. Examples: If you want to send a payment in MXN through spei , add "payment_method": "spei" to your quote request. If you want to send a payment in PHP through the InstaPay payment outlet chinabankx , add "payment_method": "chinabankx" to your quote request. If you do not provide a payment_method value in your quote request, RippleNet interprets the payment_method value as follows:
    • For payment in PHP on Coins.ph , the value defaults to bdo .
    • For payment in MXN on Bitso, the value defaults to spei .
    • For all other exchanges, the value defaults to "none" . When the value is "none" , The quote response does not include a fee quote for payment on local rails, and your payment terminates in the destination exchange.For a list of payment_method values supported by exchanges for payment over local rails, see Outbound Instructions .

Required user info

Payment senders typically provide required compliance information about the originator and beneficiary in this step. You can add this supporting information in the user_info field of JSON request body for the Create orchestration payment or Accept orchestration payment operation.

For more information about the format of supporting information, see the Standard RippleNet Payment Object, or consult with Ripple's Solution Architects.

Optional information — ODL

If you are using On-Demand Liquidity (ODL), you can also supply the following optional fields when making a Create quote collection request:

  • digital_asset_origination - This is a boolean flag that tells RippleNet to use On-Demand-Liquidity (ODL) to originate your payment in XRP from the source digital asset exchange/wallet.

    Example: "digital_asset_origination": true

    Add this flag to request a RippleNet quote for a payment using the currency code provided, with the intention to originate in XRP at the source exchange/wallet using ODL. Any quote you receive contains 0 as a value for FX trading fees for the source exchange trade. When you settle the payment, On-Demand Liquidity skips the FX trade in the source exchange, and executes the payment using XRP from your ODL wallet/exchange account.

    Note

    You must be running on RippleNet 4.6.0 or higher to use this feature.

  • enable_quote_per_payout_method - A boolean flag that tells RippleNet to return a quote collection that includes fees for every supported payment_method for your payment. Example: "enable_quote_per_payout_method": true RippleNet ignores this flag if you supply a payment_method with a value in your request body.
    Tip

    Using this flag with On-Demand Liquidity for payout through an exchange may result in a long response time and a large JSON response body, as some exchanges have many payment_method options available. For a list of payment_method values supported by exchanges for payment over local rails, see Outbound Instructions. This call is especially useful on the ODL Test Mode environment for discovering supported payment methods when building out your business integration logic.

Note

Quotes for payments using On-Demand Liquidity (ODL) expire after 60 minutes (3600 seconds), which is the default expiry time. Allow enough time for payment settlement when accepting a quote.

Other optional information

For other optional information that you can provide in the JSON body when creating payment, see the respective reference documentation for the Create orchestration payment and Create quote collection operations.

Check payment status

To check on the status of your payment request, you first have to check that there is a status notification waiting, and then get the full notification details, as well as further details on the payment itself.

The two ways to check that there is a status notification waiting, are using webhooks (recommended), and using polling.

Checking status using polling

To poll the RippleNet service to check for new notifications, call the Get orchestration notifications by status operation, setting the status parameter to NEW to get a list of any new notifications.

Copy
Copied!
GET /v4/orchestration/payment/notification?status=NEW&size=10 HTTP/1.1
Host: aclient.i.ripple.com
Content-Type: application/json
Authorization: Bearer <token>

The response to the call to Get orchestration notifications by status contains a paginated array of notifications matching the status you specified — all NEW notifications.

Copy
Copied!
[
   {
       "uuid": "720a2aab-88ab-4830-befd-dd10218fcadd",
       "notification_type": "PAYMENT_SUCCESS",
       "notification_version": "1.0",
       "notification_status": "NEW",
       "notification_payload": {
           "sender_end_to_end_id": "19999999999-012",
           "payment_id": "ab2d66c9-e67a-4020-b0c9-c249912a07a0",
           "payment_status": "COMPLETED",
           "previous_payment_status": "PREPARED",
           "connector_role": "SENDING",
           "payment_type": "REGULAR",
           "payout_method": null,
           "original_sender_end_to_end_id": null,
           "output": null
       },
       "created_at": "2021-09-03T12:30:22.081Z",
       "modified_at": "2021-09-03T12:30:22.081Z"
   }
]

If your payment — as identified by the sender_end_to_end_id you specified when you created the payment — is not in the array of results, your application will need to keep calling Get orchestration notifications by status until it appears.

Get payment details

Once you have received notification that the payment fulfillment is complete, you can retrieve the full details of the payment.

Copy
Copied!
GET /v4/orchestration/payment/19999999999-012 HTTP/1.1
Host: aclient.i.ripple.com
Content-Type: application/json
Authorization: Bearer <token>

The response will contain the full payment details.

Click to view the full payment details ...
Copy
Copied!
{
   "uuid": "d148d9b2-2d18-414a-a289-8e7abab23f7f",
   "sender_end_to_end_id": "19999999999-012",
   "payment_type": "REGULAR",
   "connector_role": "SENDING",
   "payment_id": "ab2d66c9-e67a-4020-b0c9-c249912a07a0",
   "payment_status": "ACCEPTED",
   "previous_payment_status": "PENDING",
   "orchestration_template": "defaultSendPaymentTemplateV2",
   "orchestration_status": "PAUSED",
   "current_activity": "waitForRNToLockV1",
   "current_activity_status": "PAUSED",
   "initial_context": {
       "sender_end_to_end_id": "19999999999-012",
       "internal_id": null,
       "payment_type": "REGULAR",
       "connector_role": "SENDING",
       "payment_id": null,
       "payment_status": null,
       "orchestration_template": "defaultSendPaymentTemplateV2",
       "sending_address": "dev.bfox01.sgd",
       "receiving_address": "dev.bfox02.sgd",
       "amount": "10.0",
       "currency": "SGD",
       "quote_type": "SENDER_AMOUNT",
       "sender_or_receiver_currency": null,
       "custom_fee": null,
       "custom_rate": null,
       "quote_route": null,
       "payment_method": null,
       "enable_quote_per_payout_method": null,
       "digital_asset_origination": null,
       "user_info": {
           "ChrgBr": "SHAR",
           "Cdtr": {
               "Id": "8675309",
               "Nm": "John3"
           },
           "Dbtr": {
               "Id": "8675309",
               "Nm": "Jenny"
           }
       },
       "original_sender_end_to_end_id": null,
       "original_payment_id": null,
       "return_amount_type": null,
       "return_reasons": null,
       "receiver_currency": null,
       "quote_id": null,
       "quote_expiry": null,
       "receiving_amount": null,
       "quote_elements": null,
       "reversal_reason": null,
       "force_path_finding_and_liquidity_path_finding": false,
       "payout_method_category": null
   },
   "current_context": {
       "GET_QUOTE": {
           "quote_collection_id": "08b5179e-e643-4a5c-8c5d-de996a99e4e2",
           "quotes": [
               {
                   "quote_id": "f2aff21d-66a2-4131-a980-4d5509d31dbc",
                   "created_at": "2021-09-03T12:10:03.483Z",
                   "expires_at": "2021-09-03T13:10:03.483Z",
                   "type": "SENDER_AMOUNT",
                   "price_guarantee": "FIRM",
                   "sender_address": "trans_sgd_bfox01@dev.bfox01.sgd",
                   "receiver_address": "trans_sgd_bfox02@dev.bfox02.sgd",
                   "amount": "10.0",
                   "currency_code": "SGD",
                   "currency_code_filter": null,
                   "service_type": null,
                   "quote_elements": [
                       {
                           "quote_element_id": "935e9459-30c4-44a4-b71e-45b10147658f",
                           "quote_element_type": "TRANSFER",
                           "quote_element_order": "1",
                           "sender_address": "trans_sgd_bfox01@dev.bfox01.sgd",
                           "receiver_address": "conct_sgd_bfox01@dev.bfox01.sgd",
                           "sending_amount": "10.0",
                           "receiving_amount": "10.0",
                           "sending_fee": "0",
                           "receiving_fee": "0",
                           "sending_currency_code": null,
                           "receiving_currency_code": null,
                           "fx_rate": null,
                           "transfer_currency_code": "SGD"
                       },
                       {
                           "quote_element_id": "877a068e-330b-417c-be35-0c36f3adae70",
                           "quote_element_type": "TRANSFER",
                           "quote_element_order": "2",
                           "sender_address": "conct_sgd_bfox02@dev.bfox02.sgd",
                           "receiver_address": "trans_sgd_bfox02@dev.bfox02.sgd",
                           "sending_amount": "10.0",
                           "receiving_amount": "10.0",
                           "sending_fee": "0",
                           "receiving_fee": "0",
                           "sending_currency_code": null,
                           "receiving_currency_code": null,
                           "fx_rate": null,
                           "transfer_currency_code": "SGD"
                       }
                   ],
                   "liquidity_warning": null,
                   "payment_method": null,
                   "payment_method_fields": null,
                   "payout_method_info": null
               }
           ],
           "quote_errors": []
       },
       "SELECT_QUOTE": {
           "quote_id": "f2aff21d-66a2-4131-a980-4d5509d31dbc",
           "expires_at": "2021-09-03T13:10:03.483Z"
       },
       "ACCEPT_QUOTE": {
           "payment_id": "ab2d66c9-e67a-4020-b0c9-c249912a07a0"
       }
   },
   "error_message": null,
   "activity_list": [
       {
           "name": "getQuoteV1",
           "status": "SUCCESS",
           "startTime": "2021-09-03T12:10:02.466Z",
           "endTime": "2021-09-03T12:10:03.546Z",
           "message": null
       },
       {
           "name": "selectQuoteV1",
           "status": "SUCCESS",
           "startTime": "2021-09-03T12:10:03.557Z",
           "endTime": "2021-09-03T12:10:03.557Z",
           "message": null
       },
       {
           "name": "validateSchemaV1",
           "status": "SUCCESS",
           "startTime": "2021-09-03T12:10:03.567Z",
           "endTime": "2021-09-03T12:10:03.573Z",
           "message": null
       },
       {
           "name": "acceptQuoteV1",
           "status": "SUCCESS",
           "startTime": "2021-09-03T12:10:03.582Z",
           "endTime": "2021-09-03T12:10:04.164Z",
           "message": null
       },
       {
           "name": "waitForRNToLockV1",
           "status": "PAUSED",
           "startTime": "2021-09-03T12:10:04.175Z",
           "endTime": "2021-09-03T12:10:04.175Z",
           "message": null
       }
   ],
   "created_at": "2021-09-03T12:10:01.992Z"
}

Error handling

Your integration logic should handle the following potential errors. Unsuccessful responses (responses returned with an HTTP status code other than 201) could indicate any of the following issues. (Note: this list is not exhaustive):

  • The request may have timed out.
  • The request may have been malformed.
  • The requesting client does not have the correct OAuth token.
  • The requesting client does not provide an Authorization header or does not have the correct credentials.

If using ODL, some other issues may be involved:

  • If using ODL, one of the digital asset exchanges in the request may not have received the request.
  • If using ODL, you may receive a liquidity_warning in your quote response. If you do receive a warning, it means that one or more accounts on the digital asset exchanges doesn't have enough liquidity to fulfill part of the payment's execution.
  • For a list of ODL errors you may receive during execution of API calls, see On-Demand Liquidity error codes .

At the point you accept a quote — either automatically, or as a separate step — no funds have been transferred or put on hold. Accepting a quote only confirms the originating institution's intent to transfer funds. If network failures or other issues prevent you from accepting a quote before the quote expires, you can restart the process by requesting a new quote.

RippleNet generates the payment_id after it receives the request to accept the quote. This means that you cannot check the status of the payment without a response from RippleNet. If you can't determine the status of the payment, the best workaround is typically to retry the request. If the quote was already accepted, On-Demand Liquidity responds with this error message:

Copy
Copied!
{
 "error_type": "resubmitted_request",
 "message": "Payment with ID [{YOUR_PAYMENT_ID}] has already been accepted"
}