Skip to content

Latest commit

 

History

History
716 lines (558 loc) · 42.3 KB

sep-0031.md

File metadata and controls

716 lines (558 loc) · 42.3 KB

Preamble

SEP: 0031
Title: Cross-Border Payments API
Author: SDF
Status: Active
Created: 2020-04-07
Updated: 2022-05-13
Version 1.6.0

Simple Summary

This SEP defines a protocol for enabling payments between two financial accounts that exist outside the Stellar network.

Abstract

The entities involved in a transaction are:

  • A Sending Client: The owner of the origin financial account.
  • A Sending Anchor: The business receiving funds from the Sending Client and delivering them to the Receiving Anchor for the Receiving Client. Must have a business relationship with the Receiving Anchor.
  • A Receiving Anchor: The business receiving funds from the Sending Anchor and delivering them to the Receiving Client.
  • A Receiving Client: The owner of the destination financial account.

At a high level, the following steps are performed to complete a transaction:

  1. The Sending Client sends funds from their financial account to the Sending Anchor's financial account (off/on-Stellar)
  2. The Sending Anchor sends the funds to the Receiving Anchor's Stellar account (on-Stellar)
  3. The Receiving Anchor sends funds to the Receiving Client's financial account (off/on-Stellar)

Typically, the Sending and Receiving Clients reside in different regulatory jurisdictions and therefore a payment between their financial accounts must be facilitated by two business entities, the Sending and Receiving Anchors, who have the necessary licenses in their respective jurisdictions.

Example

Alice in Nigeria wants to send money to Bob in Europe. Alice signs up with NigeriaPay to make this payment to send money directly into Bob’s bank account. Bob doesn’t need to do anything, or know anything about this payment, besides letting Alice know what his bank account information is. Alice only needs to deal with her anchor (NigeriaPay). Alice passes this information and her money to NigeriaPay, and NigeriaPay sends those funds to the EuroPay Anchor service, and Europay deposits those funds into Bob’s bank account.

Diagrams

The diagrams provided offer a detailed view of various flows that are possible using this protocol. Note that these diagrams are opinionated. Variations of the these flows may also be sufficient.

Completing a Transaction with a Firm Quote

This diagram demonstrates the interactions between the entities involved in a successful transaction. Specifically it uses the Receiving Anchor Asset Conversion strategy defined described later in the document.

sequenceDiagram
    participant Sending Client
    participant Sending Anchor
    participant Stellar
    participant Receiving Anchor
    participant Receiving Client
    Sending Client->>Sending Anchor: initiates send to <br> Receiving Client's country
    Sending Anchor-->>Sending Anchor: maps country to Receiving Anchor

    Sending Anchor->>+Receiving Anchor: GET /.well-known/stellar.toml
    Receiving Anchor-->>-Sending Anchor: SEP-10, 12, 31, & 38 URLs
    Sending Anchor->>+Receiving Anchor: GET [SEP-31]/info
    Receiving Anchor-->>-Sending Anchor: Stellar assets, customer types, fees
    Sending Anchor->>+Receiving Anchor: GET [SEP-38]/prices
    Receiving Anchor-->>-Sending Anchor: pairs and est. rates for Stellar asset

    Sending Anchor-->>Sending Anchor: calculates fiat-to-fiat est. rates
    Sending Anchor->>+Sending Client: provides pair <br> est. rates & fees
    Sending Client-->>-Sending Anchor: selects asset pair

    Sending Anchor->>+Receiving Anchor: GET [SEP-10]
    Receiving Anchor-->>-Sending Anchor: challenge transaction
    Sending Anchor-->>Sending Anchor: signs challenge
    Sending Anchor->>+Receiving Anchor: POST [SEP-10]
    Receiving Anchor-->>Receiving Anchor: verifies challenge
    Receiving Anchor-->>-Sending Anchor: authentication token

    loop once for Sending Client, once of Receiving Client
        note right of Sending Anchor: If Sending Client or Receiving Client <br >has already been <br> accepted, this section <br> is unnecessary.
        Sending Anchor->>+Receiving Anchor: GET [SEP-12]/customer?type=
        Receiving Anchor-->>-Sending Anchor: fields required
        Sending Anchor->>+Sending Client: requests fields
        Sending Client-->>-Sending Anchor: provides field values
        Sending Anchor->>+Receiving Anchor: PUT [SEP-12]/customer?type=
        Receiving Anchor-->>Receiving Anchor: validates KYC values
        Receiving Anchor-->>-Sending Anchor: id, ACCEPTED
    end

    Sending Anchor->>+Receiving Anchor: GET [SEP-38]/price
    Receiving Anchor-->>-Sending Anchor: exchange rate
    Sending Anchor->>+Sending Client: provides estimated rate
    Sending Client-->>-Sending Anchor: continues
    Sending Anchor->>+Receiving Anchor: POST [SEP-38]/quote
    Receiving Anchor-->>Sending Anchor: quote id, rate, expiration
    
    Sending Anchor->>+Receiving Anchor: POST [SEP-31]/transactions
    Receiving Anchor-->>Receiving Anchor: checks customer statuses, <br> links quote, <br> creates transaction record
    Receiving Anchor-->>-Sending Anchor: transaction id, receiving account & memo

    Sending Anchor->>+Receiving Anchor: GET [SEP-31]/transactions/:id
    Receiving Anchor-->>-Sending Anchor: transaction object containing amount_in, amount_in_asset, stellar_account_id, etc.
    note right of Sending Anchor: Use the info returned from <br>GET [SEP-31]/transactions/:id to <br>make the stellar payment.

    Sending Anchor->>+Stellar: submit Stellar payment
    Stellar->>+Receiving Anchor: receives payment, matches w/ transaction
    Receiving Anchor-->>Receiving Anchor: updates transaction status
    Stellar-->>-Sending Anchor: success response

    Receiving Anchor->>Receiving Client: Sends off-chain payment to recipient
    Receiving Anchor-->>Receiving Anchor: updates transaction to complete
    Sending Anchor->>+Receiving Anchor: GET /transactions?id=
    Receiving Anchor-->>-Sending Anchor: transaction complete
    Sending Anchor->>Sending Client: notifies sender
Loading

Prerequisites

  • The Receiving Anchor must define DIRECT_PAYMENT_SERVER in their stellar.toml.
  • The Sending and Receiving Anchors must create bi-lateral agreements to interoperate with each other.
  • If the Receiving Anchor requires KYC information for the Sending or Receiving Clients, the Receiving Anchor must implement SEP-12 and define a KYC_SERVER in their stellar.toml.
  • If the Receiving Anchor supports the Receiving Anchor Asset Conversion flow using SEP-38, ANCHOR_QUOTE_SERVER must be defined in their stellar.toml.

Authentication

Sending Anchors must authenticate with Receiving Anchors via SEP-10 Web Authentication. Sending Anchors must provide the Stellar account they will authenticate with to their Receiving Anchors, and Receiving Anchors must ensure that the authenticated Stellar account belongs to a Sending Anchor for which a bi-lateral agreement has been made.

The SEP-10 JWT be included as a header in requests to all endpoints:

Authorization: Bearer <JWT>

Any API request that fails to meet proper authentication should return a 403 Forbidden response.

Note that the source account of payments made by Sending Anchors can differ from the account used to authenticate with Receiving Anchors. Only the payment transaction's memo should be used to match incoming payments with the transaction record in the Receiving Anchor's database.

HTTPS Only

This protocol involves the transfer of value, and so HTTPS is required for all endpoints for security. Anchors should refuse to interact with any insecure HTTP endpoints.

Content Type

All endpoints accept in requests the following Content-Types:

  • application/json

All endpoints respond with content type:

  • application/json

Asset Conversions and Liquidity Strategies

This protocol is designed for the common case where the Sending Client provides the Sending Anchor with a particular asset (the source asset) and the Receiving Client receives a different asset (the destination asset). It is possible to use this protocol when the Sending and Receiving clients wish to send and receive the same asset, but that is not the common use case it has been designed.

When the source asset and destination assets are different, the source asset must be converted into the receiving asset at some point prior to delivering funds to the Receiving Client. This conversion must be done by either the Sending Anchor, the Stellar Network, or the Receiving Anchor. The subsections below outline the strategies used for each method.

Sending Anchor Asset Conversion

The Sending Anchor can collect the source asset from the Sending Client and send the destination asset to the Receiving Anchor.

This approach requires the Sending Anchor to provide an exchange rate, or quote, to the Sending Client. It also requires the Sending Anchor to hold a balance of the destination asset on Stellar. The Sending Anchor would use Payment operations to send the quoted amount of the destination asset to the Receiving Anchor.

Stellar Network Asset Conversion

Instead of holding a balance of the destination asset on Stellar, the Sending Anchor can use Path Payments to convert the source asset to the destination asset on the Stellar Network.

This approach requires a sufficiently liquid market to exist between the source and destination assets on the Stellar Decentralized Exchange (SDEX).

Receiving Anchor Asset Conversion

Finally, the Receiving Anchor can receive payments of the source asset on Stellar and deliver payments of the destination asset to the Receiving Client.

This requires the Receiving Anchor to implement the SEP-38 Anchor RFQ API, which enables the Receiving Anchor to provide quotes to the Sending Anchor. These quotes can be indicative or firm. If a firm quote is used, the Stellar transaction must be submitted to the Stellar network prior to the quote's expiration. More specifically, the created_at timestamp returned in the Horizon GET /transactions/:id request must be earlier than the quote's expiration.

API Endpoints

Detailed Sending Anchor Flow

  1. The Sending Client initiates a payment to the Receiving Client.
  2. The Sending Anchor identifies the Receiving Anchor it will use for the payment based on the receipient's location.
  3. The Sending Anchor makes a request to the Receiving Anchor's GET /info endpoint to collect asset information and the transaction.fields describing the transaction-related information required by the Receiving Anchor.
  4. The Sending Anchor fetches the relevant KYC type values from the sep12 object for the Sending and Receiving Client. If no type values are defined for either client, KYC information is not required for that client.
  5. The Sending Anchor makes SEP-12 GET /customer?type= requests for the Sending and Receiving Clients. The response includes the SEP-9 KYC attributes required for registering a client of the associated type.
  6. The Sending Anchor collects all required information from the Sending Client. This includes the custom fields listed in the Receiving Anchor's GET /info response as well as the KYC fields described in the GET /customer responses for both the Sending and Recieving Clients. How the Sending Anchor collects this information from the Sending Client is out of the scope of this document, but the Receving Client should not be required to take any action in order for the Sending Client to make the payment to the Receiving Anchor.
  7. The Sending Anchor makes SEP-12 PUT /customer requests containing the collected information for each client.
  8. If the Receiving Anchor supports the SEP-38 Anchor RFQ API, the Sending Anchor can request a quote from the Receiving Anchor. See the Receiving Anchor Asset Conversion section for more information.
  9. The Sending Anchor makes a POST /transactions request to create a transaction record with the Receiving Anchor. This request contains the ids returned by the PUT /customer requests, the transaction.fields values collected from the Sending Client, as well as the transaction information. The response will include a id that will be used to check the transaction data and status.
  10. The Sending Anchor makes GET /transactions/:id requests until the transaction's status is pending_sender.
  11. The Sending Anchor submits the payment transaction to Stellar using the information provided in the GET /transactions/:id response.
  12. The Sending Anchor makes GET /transactions/:id requests until the transaction's status is completed, error, pending_customer_info_update, or pending_transaction_info_update.
  13. If completed, the Sending Anchor should notify the Sending Client that funds have been delivered to the Receiving Client.
  14. If error, the Receiving Anchor should be contacted to resolve the situation.
  15. If pending_transaction_info_update, the transaction.fields values collected from the Sending Client were invalid and must be corrected by the Sending Client. See the Pending Transaction Info Update section for more information.
  16. If pending_customer_info_update, the SEP-9 KYC values collected were invalid and must be corrected by the Sending Client. See the Pending Customer Info Update section for more information.
  17. After providing the Receiving Anchor with updated values, the status should ultimately change to completed.

Detailed Receiving Anchor Flow

  1. The Sending Anchor makes a request to the Receiving Anchor's GET /info endpoint.
  2. The Sending Anchor makes a SEP-12 GET /customer request for the Sending and Receiving Clients if required.
  3. The Sending Anchor makes a SEP-12 PUT /customer request for the Sending and Receiving Clients if required.
  4. The Receiving Anchor must validate the KYC data provided and reject the request with useful error messages if invalid.
  5. The Sending Anchor may request a quote using the SEP-38 GET /price or SEP-38 POST /quote endpoints.
  6. The Sending Anchor makes a POST /transactions request.
  7. The Receiving Anchor must ensure the asset_code, amount, transaction fields, and customers IDs are valid. The Sending Anchor may provide a destination_asset attribute if the Receiving Anchor supports the exchange of asset_code and destination_asset via SEP-38, and if a firm quote was provided previously the Sending Anchor may provide the quote_id as well.
  8. The Recieving Anchor must create a transaction record in their database and expose it via GET /transactions/:id.
  9. Transactions should initially be pending_sender. If any preprocessing is required before receiving a payment, mark the transaction as pending_receiver until ready to receive funds.
  10. The Receiving Anchor then waits to receive the payment identified by the stellar_memo included in the POST /transactions response.
  11. Once the Stellar payment has been received and matched with the internal transaction record, the Receiving Anchor must attempt to transfer an equivalent amount of the asset (minus fees) off-chain to the Receiving Client using the KYC and rails data collected by the Sending Anchor.
  12. If the off-chain payment succeeds, the transaction's status should be updated to completed.
  13. If the off-chain payment cannot be received by the Recieving Client almost immediately, the transaction's status should be updated to pending_external until received.
  14. If the off-chain payment fails, the Recieving Anchor must determine why, which is outside the scope of this document. Once determined, the Receiving Anchor must either correct it themselves (internal error) or receive updated values from the Sending Anchor for the fields that were discovered to be invalid.
  15. If the invalid values were described in GET /info's transaction.fields object, the transaction's status should be updated to pending_transaction_info_update and required_info_updates should contain an object describing the errors.
  16. If the invalid values were described in SEP-12 GET /customer responses, the transaction's status should be updated to pending_customer_info_update and the invalid field names should be returned in the next GET /customer?id= request for each Client.
  17. The Sending Anchor will detect the transaction's status and invalid fields, collect the info from the Sending Client, and make requests to the Receiving Anchor containing the updated information.
  18. Once the provided information is validated, the Receiving Anchor should update the transaction's status to pending_receiver and retry the off-chain transfer. This loop of attempting the transfer and waiting for updated information should continue until the transfer is successful.

GET Info

Request

GET DIRECT_PAYMENT_SERVER/info

Allows an anchor to communicate basic info about what currencies their DIRECT_PAYMENT_SERVER supports receiving from partner anchors.

Request parameters:

Name Type Description
lang string (optional) Defaults to en. Language code specified using ISO 639-1. description fields in the response should be in this language.

Response

The response should be a JSON object like:

{
  "receive": {
    "USDC": {
      "quotes_supported": true,
      "quotes_required": false,
      "fee_fixed": 5,
      "fee_percent": 1,
      "min_amount": 0.1,
      "max_amount": 1000,
      "sep12": {
        "sender": {
          "types": {
            "sep31-sender": {
              "description": "U.S. citizens limited to sending payments of less than $10,000 in value"
            },
            "sep31-large-sender": {
              "description": "U.S. citizens that do not have sending limits"
            },
            "sep31-foreign-sender": {
              "description": "non-U.S. citizens sending payments of less than $10,000 in value"
            }
          }
        },
        "receiver": {
          "types": {
            "sep31-receiver": {
              "description": "U.S. citizens receiving USD"
            },
            "sep31-foreign-receiver": {
              "description": "non-U.S. citizens receiving USD"
            }
          }
        }
      },
      "fields":{
        "transaction":{
          "receiver_routing_number":{
            "description": "routing number of the destination bank account"
          },
          "receiver_account_number":{
            "description": "bank account number of the destination"
          },
          "type":{
            "description": "type of deposit to make",
            "choices": [
              "SEPA",
              "SWIFT"
            ]
          }
        }
      }
    }
  }
}

The JSON object contains an entry for each Stellar asset that the Receiving Anchor supports receiving from the Sending Anchor.

If the Receiving Anchor supports SEP-38, the Sending Client can check the SEP-38 GET /prices endpoint to determine which off-chain assets the Receiving Anchor can deliver to recipients in exchange for an asset listed in the SEP-31 GET /info response. If the Receiving Anchor does not provide exchange rates for an asset, it is assumed that the Stellar asset can be exchanged 1-for-1 with the corresponding off-chain asset.

Asset Object Schema

Name Type Description
sep12 object An object containing sender and receiver keys.
min_amount number (optional) Minimum amount. No limit if not specified.
max_amount number (optional) Maximum amount. No limit if not specified.
fee_fixed number (optional) A fixed fee in units of the Stellar asset. Leave blank if there is no fee or fee calculation cannot be modeled using a fixed and percentage fee.
fee_percent number (optional) A percentage fee in percentage points. Leave blank if there is no fee or fee calculation cannot be modeled using a fixed and percentage fee.
sender_sep12_type string (deprecated, optional) The value of the type parameter the Sending Anchor should use for a SEP-12 GET /customer request. This field can be omitted if no KYC is necessary. Use a value from sep12.sender.types instead if any are present.
receiver_sep12_type string (deprecated, optional) The value of the type parameter the Sending Anchor should use for a SEP-12 GET /customer request. This field can be omitted if no KYC is necessary. Use a values from sep12.receiver.types instead if any are present.
fields object An object containing the per-transaction parameters required in POST /transactions requests.
quotes_supported boolean (optional) If true, the Receiving Anchor can deliver the off-chain assets listed in the SEP-38 GET /prices response in exchange for receiving the Stellar asset.
quotes_required boolean (optional) If true, the Receiving Anchor can only deliver an off-chain asset listed in the SEP-38 GET /prices response in exchange for receiving the Stellar asset.

sep12 Object Schema

Name Type Description
sender object An object containing a types key if KYC information is required for the Sending Client, empty otherwise.
receiver object An object containing a types key if KYC information is required for the Receiving Client, empty otherwise.

types Object Schema

Name Type Description
types object (optional) An object containing the accepted values for the type parameter in SEP-12 requests. Each key should map to an object with a human-readable description.

If KYC is required for a Sending or Receiving client in some cases but not others, it is recommended to provide values in the respective types object for all cases and return an empty fields object from SEP-12 GET /customer for the cases where no KYC is necessary.

fields Object Schema

Name Type Description
fields object An object containing single transaction key.

transaction Object Schema

Name Type Description
description string A description of field to show to user.
choices array (optional) A list of possible values for the field.
optional boolean (optional) false if not specified.

POST Transactions

Request

This request initiates a payment. The Sending and Receiving Client must be registered via SEP-12 if required by the Receiving Anchor.

The following is an example request body if Sending Anchor Asset Conversions or Stellar Network Asset Conversions are used.

POST DIRECT_PAYMENT_SERVER/transactions
Content-Type: application/json

{
  "amount": 100,
  "asset_code": "USD",
  "asset_issuer": "GDRHDSTZ4PK6VI3WL224XBJFEB6CUXQESTQPXYIB3KGITRLL7XVE4NWV",
  "sender_id": "d2bd1412-e2f6-4047-ad70-a1a2f133b25c",
  "receiver_id": "137938d4-43a7-4252-a452-842adcee474c",
  "fields": {
    "transaction": {
      "receiver_routing_number": "442928834",
      "receiver_account_number": "0029483242",
      "type": "SEPA"
    }
  }
}

The following is an example request body if the Sending Anchor requested an indicative quote from SEP-38 GET /price and found the estimated rate acceptable.

Note that the amount delivered to the Receiving Client in this scenario varies depending on the exchange rate used by the Receiving Anchor, which may differ from the rate provided in the GET /price response.

POST DIRECT_PAYMENT_SERVER/transactions
Content-Type: application/json

{
  "amount": 100,
  "asset_code": "USD",
  "asset_issuer": "GDRHDSTZ4PK6VI3WL224XBJFEB6CUXQESTQPXYIB3KGITRLL7XVE4NWV",
  "destination_asset": "iso4217:BRL",
  "sender_id": "d2bd1412-e2f6-4047-ad70-a1a2f133b25c",
  "receiver_id": "137938d4-43a7-4252-a452-842adcee474c",
  "fields": {
    "transaction": {
      "receiver_routing_number": "442928834",
      "receiver_account_number": "0029483242",
      "type": "SEPA"
    }
  }
}

The following is an example request body if the Sending Anchor requested a firm quote from SEP-38 POST /quote and found the rate acceptable. In this case, the Receiving Anchor must ensure that the information passed in the POST /transactions request matches the assets and amounts defined in the POST /quote request and response.

POST DIRECT_PAYMENT_SERVER/transactions
Content-Type: application/json

{
  "amount": 100,
  "asset_code": "USD",
  "asset_issuer": "GDRHDSTZ4PK6VI3WL224XBJFEB6CUXQESTQPXYIB3KGITRLL7XVE4NWV",
  "destination_asset": "iso4217:BRL",
  "quote_id": "2bc5b322-5117-413f-869f-e7ca494cb1a4",
  "sender_id": "d2bd1412-e2f6-4047-ad70-a1a2f133b25c",
  "receiver_id": "137938d4-43a7-4252-a452-842adcee474c",
  "fields": {
    "transaction": {
      "receiver_routing_number": "442928834",
      "receiver_account_number": "0029483242",
      "type": "SEPA"
    }
  }
}
Request Parameters
Name Type Description
amount number Amount of the Stellar asset sent to the Receiving Anchor.
asset_code string Code of the asset the Sending Anchor intends to send. This must match one of the entries listed in the receiving anchor's GET /info endpoint.
asset_issuer string (optional) The issuer of the Stellar asset the Sending Anchor intends to send. If not specified, the asset sent must be issued by the Receiving Anchor.
destination_asset string (optional) The off-chain asset the Receiving Anchor will deliver to the Receiving Client. The value must match one of the asset values included in a SEP-38 GET /prices?sell_asset=stellar:<asset_code>:<asset_issuer> response using SEP-38 Asset Identification Format. If neither this field nor quote_id are set, it's assumed that Sending Anchor Asset Conversions was used.
quote_id string (optional) The id returned from a SEP-38 POST /quote response. If this attribute is specified, the values for the fields defined above must match the values associated with the quote.
sender_id string (optional) The ID included in the SEP-12 PUT /customer response for the Sending Client. Required if the Receiving Anchor requires SEP-12 KYC on the Sending Client.
receiver_id string (optional) The ID included in the SEP-12 PUT /customer response for the Receiving Client. Required if the Receiving Anchor requires SEP-12 KYC on the Receiving Client.
fields object An object containing the values requested by the Receiving Anchor in the GET /info endpoint.
lang string (optional) Defaults to en. Language code specified using ISO 639-1. Any human-readable error codes or field descriptions will be returned in this language.

Responses

Success (201 Created)

This is the successful case where a Receiving Anchor confirms that they can fulfill this payment as described. The response body should be a JSON object with the following values

Name Type Description
id string The persistent identifier to check the status of this payment transaction.
stellar_account_id string The Stellar account to send payment to.
stellar_memo_type string The type of memo to attach to the Stellar payment (text, hash, or id).
stellar_memo string The memo to attach to the Stellar payment.
Customer Info Needed (400 Bad Request)

In the case where the Sending Anchor didn't provide all the KYC information requested in SEP-12 GET /customer, or where the Receiving Anchor requires additional KYC information after amount, the response should include a 400 status code and the following body. The sender should then retry both the SEP-12 GET /customer request to collect the additional fields and the SEP-12 PUT /customer request including all fields described in the GET /customer response.

Name Type Description
error string customer_info_needed
type string (optional) A string for the type URL argument the Sending Anchor should use when making the SEP-12 GET /customer request. The value should be included in the sender.types or receiver.types object from GET /info.
Transaction Info Needed (400 Bad Request)

In the case where the Sending Anchor didn't provide all the information requested in GET /info, the response should include a 400 status code the following body. The Sending Anchor should then retry the entire request including all the previously sent fields plus the fields described in the response.

Name Type Description
error string transaction_info_needed
fields object A key-value pair of missing fields in the same format as fields described in GET /info.
Error (400 Bad Request)

In the case where the transaction cannot be completed, return an error response body containing an error key describing the error in human-readable format in the language indicated in the request.

{
  'error': "The amount was above the maximum limit"
}

{
  'error': "That bank account is restricted via AML laws"
}

GET Transaction

The transaction endpoint enables Sending Clients to fetch information on a specific transaction with the Receiving Anchor.

GET DIRECT_PAYMENT_SERVER/transactions/:id

Request parameters:

Name Type Description
id string The id of the transaction.

On success the response should include a 200 OK HTTP status code and the following body:

Name Type Description
transaction object The transaction that was requested by the client.

transaction Object Schema

Name Type Description
id string The ID returned from the POST /transactions request that created this transaction record.
status string The status of the transaction. Values are outlined below.
status_eta number (optional) The estimated number of seconds until a status change is expected.
amount_in string (optional) The amount of the Stellar asset received or to be received by the Receiving Anchor. Excludes any fees charged after Receiving Anchor receives the funds.
amount_in_asset string (optional) The asset received or to be received by the Receiving Anchor. Must be present if quote_id or destination_asset was included in the POST /transactions request. The value must be in SEP-38 Asset Identification Format.
amount_out string (optional) The amount sent by the Receiving Anchor to Receiving Client.
amount_out_asset string (optional) The asset delivered to the Receiving Client. Must be present if quote_id or destination_asset was included in the POST /transactions request. The value must be in SEP-38 Asset Identification Format.
amount_fee string (optional) The amount of fee charged by the Receiving Anchor.
amount_fee_asset string (optional) The asset in which fees are calculated in. Must be present if quote_id or destination_asset was included in the POST /transactions request. The value must be in SEP-38 Asset Identification Format.
stellar_account_id string The Receiving Anchor's Stellar account that the Sending Anchor will be making the payment to.
stellar_memo_type string The type of memo to attach to the Stellar payment: text, hash, or id.
stellar_memo string The memo to attach to the Stellar payment.
started_at UTC ISO 8601 string (optional) Start date and time of transaction.
completed_at UTC ISO 8601 string (optional) Completion date and time of transaction.
stellar_transaction_id string (optional) The transaction_id on Stellar network of the transfer that initiated the payment.
external_transaction_id string (optional) The ID of transaction on external network that completes the payment into the receivers account.
refunded boolean (deprecated, optional) This field is deprecated in favor of the refunds object. True if the transaction was refunded in full. False if the transaction was partially refunded or not refunded. For more details about any refunds, see the refunds object.
refunds object (optional) An object describing any on-chain refund associated with this transaction. The schema for this object is defined in the Refunds Object Schema section below.
required_info_message string (optional) A human-readable message indicating any errors that require updated information from the sender.
required_info_updates object (optional) A set of fields that require update values from the Sending Anchor, in the same format as described in GET /info. This field is only relevant when status is pending_transaction_info_update.

status should be one of:

  • pending_sender -- awaiting payment to be sent by Sending Anchor.
  • pending_stellar -- transaction has been submitted to Stellar network, but is not yet confirmed.
  • pending_customer_info_update -- certain pieces of information need to be updated by the Sending Anchor. See the Pending Customer Info Update section for more information.
  • pending_transaction_info_update -- certain pieces of information need to be updated by the Sending Anchor. See the Pending Transaction Info Update section for more information.
  • pending_receiver -- payment is being processed by the Receiving Anchor.
  • pending_external -- payment has been submitted to external network, but is not yet confirmed.
  • completed -- funds have been delivered to the Receiving Client.
  • error -- catch-all for any error not enumerated above.

Status Diagram

Refunds Object Schema

Name Type Description
amount_refunded string The total amount refunded to the Sending Anchor, in units of amount_in_asset. If a full refund was issued, this amount should match amount_in.
amount_fee string The total amount charged in fees for processing all refund payments, in units of amount_in_asset. The sum of all fee values in the payments object list should equal this value.
payments array A list of objects containing information on the individual payments made back to the Sending Anchor as refunds. The schema for these objects is defined in the section below.
Refund Payment Object Schema
Name Type Description
id string The Stellar transaction hash of the transaction that included the refund payment. This id is not guaranteed to be unique.
amount string The amount sent back to the Sending Anchor for the payment identified by id, in units of amount_in_asset.
fee string The amount charged as a fee for processing the refund, in units of amount_in_asset.

Amount Formulas

The following should hold true for all transaction records, assuming amount_in_asset and amount_out_asset are the same. If they are different, the following should still hold true after converting all amounts to units of one of the assets.

amount_out = amount_in - amount_fee - refunds.amount_refunded - refunds.amount_fee
refunds.amount_refunded = sum(refunds.payments[].amount)
refunds.amount_fee = sum(refunds.payments[].fee)

Example response:

{
  "transaction": {
      "id": "82fhs729f63dh0v4",
      "status": "pending_external",
      "status_eta": 3600,
      "stellar_transaction_id": "b9d0b2292c4e09e8eb22d036171491e87b8d2086bf8b265874c8d182cb9c9020",
      "external_transaction_id": "ABCDEFG1234567890",
      "stellar_account_id": "GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H",
      "stellar_memo": "123456789",
      "stellar_memo_type": "id",
      "amount_in": "18.34",
      "amount_out": "18.24",
      "amount_fee": "0.1",
      "started_at": "2017-03-20T17:05:32Z"
    }
}
{
  "transaction": {
      "id": "82fhs729f63dh0v4",
      "status": "pending_transaction_info_update",
      "status_eta": 3600,
      "stellar_transaction_id": "b9d0b2292c4e09e8eb22d036171491e87b8d2086bf8b265874c8d182cb9c9020",
      "external_transaction_id": "ABCDEFG1234567890",
      "amount_in": "18.34",
      "amount_out": "18.24",
      "amount_fee": "0.1",
      "stellar_account_id": "GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H",
      "stellar_memo": "123456789",
      "stellar_memo_type": "id",
      "started_at": "2017-03-20T17:05:32Z",
      "required_info_message": "The bank reported an incorrect account number for the receiver, please ensure the account matches legal documents",
      "required_info_updates": {
         "transaction": {
            "receiver_account_number": {
               "description": "The receiver's bank account number"
            }
         }
      }
    }
}
{
  "transaction": {
      "id": "82fhs729f63dh0v4",
      "status": "completed",
      "amount_in": "110",
      "amount_out": "90",
      "amount_fee": "5",
      "started_at": "2017-03-20T17:05:32Z",
      "stellar_transaction_id": "b9d0b2292c4e09e8eb22d036171491e87b8d2086bf8b265874c8d182cb9c9020",
      "stellar_account_id": "GBRPYHIL2CI3FNQ4BXLFMNDLFJUNPU2HY3ZMFSHONUCEOASW7QC7OX2H",
      "stellar_memo": "123456789",
      "stellar_memo_type": "id",
      "refunds": {
        "amount_refunded": "10",
        "amount_fee": "5",
        "payments": [
          {
            "id": "54321ab047a193c6fda1c47f5962cbcca8708d79b87089ababd57532c21c5402",
            "amount": "10",
            "fee": "5"
          }
        ]
      }
    }
}

If the transaction cannot be found, the endpoint should return a 404 NOT FOUND result.

Pending Customer Info Update

In certain cases the Receiving Anchor might need to request updated information from the Sending Anchor. For example, if the bank tells the Receiving Anchor that the provided Receiving Client's name is incorrect or missing a middle initial. Since this information was sent via SEP-12, the transaction should go into the pending_customer_info_update state until the Sending Anchor makes another SEP-12 PUT /customer request to update. The Sending Anchor can check which fields need to be updated by making a SEP-12 GET /customer request including the id or account & memo parameters. The Receiving Anchor should respond with a NEEDS_INFO status and last_name included in the fields described.

Pending Transaction Info Update

Another possibility is that the per-transaction information provided in the POST /transactions fields object was later discovered to be invalid. In this case, the transaction should go into the pending_transaction_info_update state until the Sending Anchor makes a request to the endpoint outlined below.

PATCH Transaction

This endpoint should only be used when the Receiving Anchor needs more info via the pending_transaction_info_update status from the Sending Anchor. The required_info_updates transaction field should contain the fields required for the update. If the Sending Anchor tries to update at a time when no info is requested, the Receiving Anchor should fail with an error response.

PATCH DIRECT_PAYMENT_SERVER/transactions/:id

Request parameters:

Name Type Description
id string The id of the transaction.
fields object A key-pair object containing the values requested to be updated by the receiving anchor in the same format as fields in the POST /transactions request.

Example

PATCH DIRECT_PAYMENT_SERVER/transactions/82fhs729f63dh0v4

{
   "fields": {
      "transaction": {
         "receiver_bank_account": "12345678901234",
         "receiver_routing_number": "021000021"
      }
   }
}

Success 200 OK

If the information was successfully updated and all fields required for the transaction are patched, respond with a 200 status code, and return a response body matching GET /transactions/:id. The transaction should return to pending_receiver, though it is possible that the information could still need to be updated again.

Not Found 404

If the transaction specified by "id" does not exist, return a 404 response.

Error 400

If the information was malformed, or if the sender tried to update data that isn't updatable, return a 400 with an object containing an error message.

{
   "error": "Supplied fields do not allow updates, please only try to updates the fields requested"
}

Changelog

  • v1.6.0: Updated the "Sending Anchor Flow" so the Sending Anchor needs to reach the GET /transactions/:id endpoint to get the payment information before sending the payment to the Receiving Anchor. (#????)
  • v1.5.5: Updated the description of PATCH /transactions. (#1166)
  • v1.5.4: Add the state diagram of a SEP-31 transaction. (#1164)
  • v1.5.3: Clarify when sender_id and receiver_id attributes are required for POST /transactions requests. (#1158)
  • v1.5.2: Add a use case when the receiving client may receive on-stellar asset. (#1149)
  • v1.5.1: Add a sequence diagram for successful transactions using firm quotes. (#1145)
  • v1.5.0: Deprecate refunded boolean. Add refund object to transaction records. (#1128)