Build a SEP-31 Anchor on Testnet
Before setting up a server that connects to live banking rails and real money, you should build out a test rig connected to the Stellar test network. The testnet works just like the main Stellar network, but it uses test data and allows you to fund test accounts for free using a tool called Friendbot.
Note: the testnet is reset every three months, so when building on it, make sure you have a plan to recreate necessary accounts and other data. For more info, check out the best practices for using the testnet.
At the end of this section, you should have a sandboxed system capable of interfacing with the Stellar testnet that you can easily convert into a production-ready deployment on the main Stellar network. You should always keep a testnet deployment up and running — even once you have a production version — so that wallets can test your implementation without the risk of losing real user funds.
You can also find an outline of the basic steps anchors need to complete in the intro to SEP-31 section of the cross-border payment standard.
Prerequisites
Before you start building infrastructure to connect a Stellar-network token to banking rails, you need to:
- Issue an asset on the network – which you can find out how to do in the Issue Assets section — or find another trustworthy issuer and coordinate with them to broker their asset.
- Add meta-information about the asset (if you issue one) and define the location of your
DIRECT_PAYMENT_SERVER
in yourstellar.toml
so that wallets know where to find the server and relevant endpoints. You can consult this guide for help completing yourstellar.toml
.
Once you've taken those steps, you're ready to implement an /info
endpoint.
Implementing the /info
Endpoint
The /info
endpoint allows receiving anchors to communicate basic information to sending anchors, exchange interfaces, and other Stellar apps. It responds to client queries with a JSON object detailing which currencies the anchor supports and specifically what pieces of information are required for the sender to collect and provide to the receiver in future requests.
For example, the SDF's reference server has the following /info
response:
- JSON
{
"receive": {
"SRT": {
"enabled": true,
"min_amount": 10.0,
"max_amount": 10000.0,
"sender_sep12_type": "sep31-sender",
"receiver_sep12_type": "sep31-receiver",
"fee_fixed": 1.0,
"fee_percent": 0.01,
"fields": {
"transaction": {
"routing_number": {
"description": "routing number of the destination bank account"
},
"account_number": {
"description": "bank account number of the destination"
}
}
}
}
}
}
It has a single SRT
asset enabled with minimum and maximum amounts and both fixed rate and percentage fees taken from the amount received. If the receiving anchor has a complex fee structure that can't be expressed with the fee attributes from /info
, sending anchors can support a /fee
endpoint. Unfortunately, this endpoint is a pending change to the SEP that has not yet been approved. An update will be made when the /fee
endpoint specification is finalized.
The sender_sep12_type
and receiver_sep12_type
keys signal to the to the SA application that SEP-12 customer registration is required for both the SU and RU. The values of these attributes are the values the SA should include in the GET /customer?type=<SEP-12 type value>
request for each user. In this context, users are synonymous with customers.
The fields
object contains the single transaction
key-value pair, which contains the custom fields the RA requires from the SA. Typically, these attributes are off-chain rails-related attributes like routing and banking numbers. SAs should be aware of their partner RA's fields.transaction
required attributes and should ideally validate the collected information from the SU prior to making the POST /transaction
API call on the RA's server.
Note: Generally, RAs should require the KYC fields of the SU and RU to be sent via SEP-12 and per-transaction information via SEP-31's POST /transaction
endpoint in the forms.transactions
object. Be aware that SUs must be able to understand what fields to collect for your service and ideally how to validate. Vague descriptions or poorly named attributes will cause confusion.
Authentication
Stellar Web Authentication (SEP-10) is a protocol for verifying that a user controls a Stellar private key for a given account, and for creating a persistent session for that user. It relies on a variation of mutual challenge-response, and uses Stellar transactions to encode challenges and responses: the auth server provides a 'challenge' transaction; the client signs it on behalf of the user and returns it to the anchor; the anchor checks that the signature is valid, and if it is, issues a JWT.
In the context of SEP-31, the client and key holder is the sending anchor (SA). Receiving anchors (RAs) should have a list of approved sending anchors and their public keys used in SEP-10 authentication. If an authentication request is made with an unknown Stellar public key address, the receiving anchor should reject the request.
Once received, the JWT then acts as a reusable key for the SA to perform an action on behalf of a SU. It can contain an arbitrary amount of information, and is signed by the provider — in this case the asset anchor — to ensure validity.
Requesting a Challenge
To start the authentication flow, the client requests a challenge, which is a Stellar transaction with the sequence number set to 0. A transaction with a sequence number of 0 is, by definition, invalid, so it can't actually be submitted to the network. The issuer can, however, verify that the transaction is signed correctly (which is what happens in the next step).
The only information needed to request a challenge is the client's public key, passed as the account
parameter. Here's an example: GET <AUTH_ENDPOINT>?account=GXXXXXX
Exchanging the Signed Challenge
Once the client signs the challenge transaction on behalf of the user using standard Stellar SDK tools, it sends the signed transaction back to the provider. Using those same Stellar SDKs, the provider then checks to see if the transaction is properly signed, and if it is, offers the client a JWT.
This JWT should be created with the claims that are appropriate given the account that signed the challenge. It can be created with any existing JWT library.
Here are the fields included in the JWT:
- The
sub
key contains the account of the authenticated user - The
exp
key contains the expiration of the JWT. Some JWTs should be short-lived if the claims inside of it are expected to change, or if the JWT is used in less secure environments - Other keys can contain any type of claim or data the Anchor wishes.
Since the sub
field contains the address of the authenticated user, it's kind of like a username. The JWT authenticates said user.
Since some tokens are used in less secure environments, such as as query parameters in URLs, you may want to create a short-lived or one-time use token to prevent a JWT from falling into the wrong hands.
Customer Registration
Once you have implemented an /info
endpoint and set up SEP-10 authentication, your next step is to build out SEP-12. This SEP is a standard for communicating user KYC information between clients and servers running Stellar services.
In the context of SEP-31, the SA collects the information the RA specifies as required in the response to GET /customer?type=<sender/receiver SEP-12 type from /info>
and makes a PUT /customer
request containing that information back to the RA.
SEP-12 uses the standardized fields defined in SEP-9, so SAs should be familiar with the descriptions and formats of the fields used by the RA.
On testnet, KYC validation is discouraged in order to lower the barrier for other to test your service. However, if information passed to an RA turns out to be invalid after attempting to validate it on mainnet, specific customers (SUs or RUs) can be placed in a pending_customer_info_update
status.
This signals to the SA that the fields should be updated by the SU and resent. The problematic fields can be identified using the GET /customer?id=<customer ID returned from the initial PUT call>
API call. These fields should be included in the future PUT /customer
request.
RAs should also respect the DELETE /customer
requests made by the SA.
Note: Ideally, RAs validate KYC data passed by SAs during the request/response cycle instead of reporting KYC issues via the transaction's status later in the process.
SAs should poll the GET /transactions/:id
endpoint(s) provided by their partner RA(s) and ensure the transactions initiated are always carried out to completion by fetching updated info from the SU when necessary.
The two diagram below outline a potential flow between a SA and RA including all possible SEP-12 states:
Now that both the SU and RU are registered with the RA, the SA must initiate the transaction request with the RA. This process is described in the section below. Assuming the SA made a successful POST /transaction
request after collecting the KYC and transaction info, a potential update-flow could look something this:
Note: It is perfectly acceptable for RAs to only require KYC information on the RU. To do this, simply omit the sender_sep12_type
key from the GET /info
response. This signals to the SA to make one GET
and PUT
for receiver fields and registration, respectively.
Transaction Processing
After registering the receiving and potentially sending user, but prior to any update flow, the SA must initiate the transaction creation process by making a POST /transactions
request including the receiver_id
(and sender_id
) as well as the fields.transaction
object containing the per-transaction level fields defined by the RA in /info
.
On a successful response, the SA then makes the path payment on Stellar between the deposited asset from the SU to the asset anchored by RA. The transaction must contain a memo of type stellar_memo_type
with a value of stellar_memo
. These attributes are returned in the RA's POST /transactions
response.
Note: the payment transaction can be a normal strict-receive payment transaction instead of a path payment. Typically, cross-border payments will involve a cross-currency transaction.
The RA should be polling or streaming incoming payment transactions on their Stellar distribution account. Once detected and matched with the transaction record created by the SA's POST /transactions
request, the RA will attempt to transfer the value received off-chain to the associated RU.
The following diagram outlines the process for a successful transaction initiation request and an update flow that differs from the SEP-12 pending_customer_info_update
process. Most of the time, SAs will not have to collect and provide updated transaction information. This is only in the case of data entry error by the SU.
Note: If the SU and RU are already registered, only the per-transaction information defined in the GET /info
forms.transaction
object needs to be collected. If SEP-12 registration has not happened or an update is required, it is recommended to collect both KYC and per-transaction field values from the SU at the same time.
Due to the separate documentation sections outlining the KYC and transaction API calls, we'll ask the SU for information a second time for per-transaction attributes.
Transactions Endpoint
As demonstrated in the diagram from the previous section, the /transactions
endpoint provides a way for SAs to initiate (POST
), poll (GET :id
), and update (PATCH :id
) transactions with an RA.
Check out the SEP-31 standard for the API request and response specifications for the /transactions
endpoint.
Streaming Incoming Path Payments
Receiving anchors (RAs) must be able to detect and match incoming Stellar path payment transactions with receiving users. This can be done one of two ways:
- Polling: this option consists of periodically fetching the transactions submitted after an already-seen transaction's
paging_id
that should be updated after each iteration. - Streaming: this option consists of maintaining a persistent process that streams incoming transactions from the anchored asset's distribution account, which allows detection and transaction matching in close to real time.
For more information on path payment transactions, checkout the operation definition.