Skip to content

Latest commit

 

History

History
400 lines (300 loc) · 21.5 KB

sep-0031.md

File metadata and controls

400 lines (300 loc) · 21.5 KB

Preamble

SEP: 0031
Title: Direct Payments
Author: SDF
Status: Active
Created: 2020-04-07
Updated: 2020-05-06
Version 1.0.0

Simple Summary

This SEP defines a protocol for enabling direct fiat to fiat payments between two financial accounts that exist outside of the Stellar network. The payments are facilitated by two anchors.

Abstract

This proposal facilitates the ability for anchors to build a rail between two regions, allowing end users to send fiat from one bank account directly into another end users bank account. In this flow, neither user needs to deal with the stellar network, the two anchors take care of everything for them.

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).

NigeriaPay will utilize its European rail, enabled with EuroPay Anchor service, to move the money to EuroPay in order to deposit it into Bob’s bank account.

Prerequisites

  • An anchor must define the location of their DIRECT_PAYMENT_SERVER in their stellar.toml. Anchors will find each others servers by pulling this TOML file from their home domains.
  • Anchors will create bi-lateral agreements to interoperate with each other. This differs from other protocols in that there is no concept of 'discoverability'. Anchors should keep a mapping of their partnerships in different regions to their home domains.
  • Each anchor registers a stellar key with each counterparty anchor they interact with in order to identify themselves via SEP-10 Web Authentication.

Authentication

Anchors should support SEP-10 web authentication to ensure the counterparty they're interoperating with is actually who they say they are. Clients must submit the JWT obtained via the SEP-10 authentication flow to all API endpoints except /info.

The JWT should be included as a request header:

Authorization: Bearer <JWT>

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

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.

Implementation Notes

Entities Involved

  • Sending Client: The end user who is initiating a payment via the sending anchor
  • Sending Anchor: The business offering outbound payment services. Takes fiat in from the sending client, and has a business relationship with the receiving anchor.
  • Receiving Anchor: The business offering inbound payment processing. Deposits fiat in the receiving clients bank account, and has a business relationship with the sending anchor.
  • Receiving Client: The owner of the destination bank account.

Setting up rails

  1. To create a rail find a counterparty who implements this SEP in the region you wish to provide access to, and agrees to do business with you.
  2. Trade public keys with each other in order to identify and securely interoperate with each other
  3. Keep a mapping of region to home_domain, using home_domain as your initial entry point to interoperating.

User Onboarding

  1. User onboarding happens with the sending anchor, and is out of the scope of this spec.
  2. The sending anchor should, at minimum, collect the KYC information of the sending and receiving users specified by the receiving anchor's SEP-12 implementation.

Payment Flow

  1. Payment initiation happens in the sending anchors interface, and is also out of the scope of this spec.
  2. The sending client chooses a destination region.
  3. The sending anchor fetches the TOML file to find the DIRECT_PAYMENT_SERVER API root and /info endpoint fields for the receiving anchor for this region.
  4. For each _sep12_type value specified in the relevant asset's /info object, the sending anchor makes a SEP-12 GET /customer request.
  5. The sending anchor collects all information specified in the GET /customer response(s).
  6. The sending anchor PUTs all that information to the /customer endpoint of the receiving anchor.
  7. The sending anchor POSTs the transaction information to the /transactions endpoint of the receiving anchor.
  8. If some of the information POSTed was missing or incorrect, or the amount is value is large enough to require additional information, the receiving anchor returns a 400 error response using either the transaction_info_needed or customer_info_needed status strings. More information about these responses can be found in the /transactions section.
  9. Once the sending anchor has provided all the required information and the receiving anchor is ready to complete this transaction, the POST /transactions call will return a 201 status along with information needed to complete the transaction over the stellar network.
  10. The sending anchor should collect the fiat from the sending client, and perform the specified stellar transaction to send the money to the receiving anchor. This is usually a path payment, but can be done with a regular payment as well, so long as the receiving anchor gets the token they expect.
  11. Once the stellar transaction is completed, the receiving anchor should deposit the money in the receivers bank account.
  12. If the sender finds out during a bank deposit that some of the receivers information is incorrect, the transaction should be placed in either the pending_customer_info_update or pending_transaction_info_update status so the sender can correct it. More information on these status values can be found in the /transactions/:id section.
  13. The sending anchor can query the status of this transaction via the /transactions/:id endpoint, and should communicate updates to the sending client as it changes.
  14. Once the /transactions/:id endpoint returns a completed status the transaction has been completed.

API Endpoints

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":{
      "USD":{
         "enabled":true,
         "fee_fixed":5,
         "fee_percent":1,
         "min_amount":0.1,
         "max_amount":1000,
         "sender_sep12_type": "sep31-sender",
         "receiver_sep12_type": "sep31-receiver",
         "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 asset that the anchor supports for receiving and completing a direct payment.

For each asset available for receiving, response contains:

  • min_amount: Optional minimum amount. No limit if not specified.
  • max_amount: Optional maximum amount. No limit if not specified.
  • fee_fixed: Optional fixed (flat) fee for deposit. In units of the received asset. Leave blank if there is no fee or the fee schedule is complex.
  • fee_percent: Optional percentage fee for deposit. In percentage points. Leave blank if there is no fee or the fee schedule is complex.
  • sender_sep12_type: Optional 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.
  • receiver_sep12_type: Optional 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.
  • fields: as explained below.

The fields object allows an anchor to describe fields that must be passed into POST /transactions. Only fields related to the transaction should be described in the fields object. In the example above, the receiving anchor requires the account and routing number of the receiving client's bank account.

Each fields sub-object contains a key for each field name and an object with the following fields as the value:

  • description: (required) description of field to show to user.
  • choices: (optional) list of possible values for the field.
  • optional: (optional) false if not specified.

If sender_sep12_type or receiver_sep12_type are present in the response, the client must register the sender and receiver via SEP-12 using the type argument provided. The receiving anchor uses this parameter to know what information needs to be collected on the customer.

Transactions

Request

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"
    }
  }
}

This post requests attempts to initiate a payment through this anchor. It should provide the amount and all the required fields (specified in the /info endpoint). The values for sender_id and receiver_id are from the receiving anchor's SEP-12 PUT /customer responses.

If the request describes a valid transaction that this anchor can fulfill, we return a success response with details on what to send. If the request is not valid, or we need more info, we can return with an error response and expect the sending anchor to try again with updated values.

Request Parameters
Name Type Description
amount number Amount of payment in destination currency
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 /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.
sender_id string (optional) The ID included in the SEP-12 PUT /customer response for the sending client. If sender_sep12_type was included in the /info response, this field is required.
receiver_id string (optional) The ID included in the SEP-12 PUT /customer response for the receiving client. If receiver_sep12_type was included in the /info response, this field is required.
fields object A key-pair object containing the values requested by the receiving anchor in their /info endpoint containing a single "transaction" object.
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 Persistent identifier to check the status of this payment
stellar_account_id string Stellar account to send payment to
stellar_memo_type string Type of memo to attach to the stellar payment `(text
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 GET /customer, or where the receiver requires additional KYC information after learning of the transaction amount, the request should fail with a 400 status code and the following body in JSON format. The sender should then retry both the GET /customer request to collect the additional fields and the 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 GET /customer request
Transaction Info Needed (400 Bad Request)

In the case where the sending anchor didn't provide all the information requested in /info, or if the transaction requires extra information, the request should fail with a 400 status code and the following body in JSON format. The sender 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 /info.
Error (400 Bad Request)

In the case where the transaction just cannot be completed, return an error response with a JSON object 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"
}

Transaction

The transaction endpoint enables senders to query/validate a specific transaction at a receiving anchor.

GET DIRECT_PAYMENT_SERVER/transactions/:id

Request parameters:

Name Type Description
id string The id of the transaction.

On success the endpoint should return 200 OK HTTP status code and a JSON object with the following fields:

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

The transaction object should be of the following schema.

Name Type Description
id string Unique, anchor-generated id for the deposit/withdrawal.
status string Processing status of deposit/withdrawal.
status_eta number (optional) Estimated number of seconds until a status change is expected
amount_in string (optional) Amount received by anchor at start of transaction as a string with up to 7 decimals. Excludes any fees charged before the anchor received the funds.
amount_out string (optional) Amount sent by anchor to user at end of transaction as a string with up to 7 decimals.
amount_fee string (optional) Amount of fee charged by anchor.
stellar_account_id string Stellar account to send payment to
stellar_memo_type string Type of memo to attach to the stellar payment `(text
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) transaction_id on Stellar network of the transfer that either initiated the payment.
external_transaction_id string (optional) ID of transaction on external network that either completes the payment into the receivers account.
refunded boolean (optional) Should be true if the transaction was refunded. Not including this field means the transaction was not refunded.
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 from the sender, in the same format as described in /info. This field is only relevant when status is pending_transaction_info_update.

status should be one of:

  • pending_sender -- awaiting payment to be initiated 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 pending customer info update section
  • pending_transaction_info_update -- certain pieces of information need to be updated by the sending anchor. See pending transaction info update section
  • 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 -- deposit/withdrawal fully completed.
  • error -- catch-all for any error not enumerated above.

Example response:

{
  "transaction": {
      "id": "82fhs729f63dh0v4",
      "status": "pending_external",
      "status_eta": 3600,
      "external_transaction_id": "ABCDEFG1234567890",
      "amount_in": "18.34",
      "amount_out": "18.24",
      "amount_fee": "0.1",
      "started_at": "2017-03-20T17:05:32Z"
    }
}
{
  "transaction": {
      "id": "82fhs729f63dh0v4",
      "status": "pending_info_update",
      "status_eta": 3600,
      "external_transaction_id": "ABCDEFG1234567890",
      "amount_in": "18.34",
      "amount_out": "18.24",
      "amount_fee": "0.1",
      "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"
            }
         }
      }
    }
}

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

Pending customer info update

In certain cases the receiver might need to request updated information. For example, if the bank tells the anchor that the provided receiver last 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 sender makes another PUT /customer request to update. The sending anchor can check which fields need to be updated by making a 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 bank tells the receiving anchor that the provided account or routing number is incorrect. Since this information was sent via a POST /transactions request, the transaction should go into the pending_transaction_info_update state until the sender makes a request to the endpoint outlined below.

Update

This endpoint should only be used when the receiver requests more info via the pending_transaction_info_update status. The required_info_updates transaction field should contain the fields required for the update. If the sender tries to update at a time when no info is requested the receiver 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 /transactions.

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, respond with a 200 status code, and return the transaction json in the body. 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"
}