Skip to main content

Integration

One of the main points of interaction with the Anchor Platform is notifying the Anchor Platform about events related to the transaction.

In general, you'll want to provide updates for the following events:

  • Your business is processing the KYC information provided by the user
  • Your business is ready to receive funds from the user
  • Your business has received funds from the user
  • Your business has sent funds to the user
  • Your business has processed a refund for the user's transaction
  • Your business experienced an unexpected error

This is done by making a request to the Platform API's PATCH /transactions endpoint. This endpoint allows you to update the data returned from the Platform API's GET /transaction/:id endpoint as well as the data provided to wallet applications through the applicable SEP endpoints.

The Anchor Platform PATCH /transactions endpoint is designed to notify the platform about changes of the transaction state. Given that, it will be called every time a user or you (the anchor) take some action that progresses the transaction state in the flow.

You can find more about transaction flow and statuses in the SEP-24 protocol document

info

In the next major version release of the Anchor Platform, we'll be replacing the PATCH /transactions Platform API endpoint with a JSON RPC endpoint that will simplify the request schema, provide better validation of data, and make the API easier to understand in general.

Securing Platform API

caution

By default, the Platform API's endpoints such as PATCH /transactions and GET /transaction/:id are not protected, and are accessible by anyone who has access to the server, including wallet applications.

info

It's recommended to keep Platform server accessible only from the private network. However, you may want to add additional layer of protection via securing the API.

Using API Key

To enable API key authentication, modify your dev.env file:

# dev.env
PLATFORM_SERVER_AUTH_TYPE=api_key
# Will be used as API key
SECRET_PLATFORM_API_AUTH_SECRET="your API key that business server will use"

After it's enabled, all requests must contain a valid X-Api-Key header, set to the configured API key.

Using JWT

To enable JWT authentication, modify your dev.env file:

# dev.env
PLATFORM_SERVER_AUTH_TYPE=jwt
SECRET_PLATFORM_API_AUTH_SECRET="your encryption key shared with your business server"

After it's enabled, all requests must contain a valid Authorization header. The JWT provided must have the jti and exp fields representing a valid transaction and token expiration time, respectively.

Making Patch Requests

Before diving into making PATCH requests, let's create a template for making a request to the Anchor Platform.

# call-patch.sh
#!/usr/bin/env bash

transaction=`cat $1`

curl localhost:8085/transactions \
-X PATCH \
-H 'Content-Type: application/json' \
-d '{"records":['"$transaction"']}'

This small script will make a PATCH /transactions request to the Anchor Platform hosted on the default port (8085). JSON transaction data stored in provided file will be used as body (wrapped into records singleton array).

tip

It's possible to provide multiple updates in a single PATCH /transactions call (by placing multiple transaction objects in the records array). When an update is done in such manner, all updates will be done sequentially.

Importantly, this operation is NOT atomic. If one update fails, all previous updates would be applied, all subsequent updates would be discarded, and an error would be raised.

We will also be making a reference to the $transaction_id variable. It's an identification of transaction, that's being returned from the Anchor Platform on an interactive withdrawal or deposit start request. You can obtain the transaction ID by connecting the test wallet to your local Anchor Platform instance. Set this variable in your terminal to be able to execute scripts below.

Updating Deposit Transaction Via PATCH

When the user has provided you the information necessary to facilitate the transaction, you need to update the status of the transaction and provide some of the information you collected to the Anchor Platform.

The first step of the deposit flow after starting the deposit itself is collecting KYC. It's usually done in the web-app, but can also be optionally provided by the wallet application, using SEP-9. After necessary KYC is collected, a PATCH /transactions request should be made.

tip

When the KYC process is long (for example, ID verification), it's advised to first set the transaction status to pending_anchor, indicating to the user that KYC is being processed.

Processing KYC Information

tip

This step is optional. Most businesses don't use it: you can skip it and go to the next step.

Using this status is encouraged when KYC verification may need to be performed asynchronously.

The update for this action is simply changing the transaction status. It's a good idea to modify the message too, reflecting details of the process:

// kyc-in-process.json
{
"transaction": {
"id": "<transaction_id>",
"status": "pending_anchor",
"message": "KYC is being verified"
}
}

To execute this update, simply run

./call-path.sh kyc-in-process.json

Ready to Receive Funds

After KYC is collected, you should be ready to receive the off-chain deposit. While you're waiting for the user to deliver funds, you'll want to provide an updated transaction status and basic transaction information.

{
"transaction": {
"id": "<transaction_id>",
"status": "pending_user_transfer_start",
"message": "waiting on the user to transfer funds",
"amount_in": {
"asset": "iso4217:USD",
"amount": "10"
},
"amount_out": {
"asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"amount": "9.0"
},
"amount_fee": {
"asset": "iso4217:USD",
"amount": "1.0"
}
}
}

The amount should be collected in the interactive flow. However, it can also be specified by the wallet as part of the request (see data.amount of the JWT token).

Please note that you must specify amount fields, as wallets do not provide this information.

  • amount_in is the amount the user requested. If JWT token's data.amount is not null, amount_in should be equal to that value. In that case, the amount should not be collected in the interactive flow.
  • amount_out is the amount the user will receive.
  • amount_fee is the total fees collected by the application.
  • asset is part of the amount_x field and is in a SEP-38 format. In this example, it's set to USD, assuming the user made a bank transfer to the system using USD.
tip

The Asset field is optional. When not set, it defaults to the asset the user requested.

tip

The asset requested by the user is stored in the transaction.amount_expected.amount field (where the transaction is obtained via the GET /transactions platform request)

Waiting For User Funds

tip

This step is optional. Most businesses don't use it: you can skip it and go to the next step

You should only use this status if the off-chain payment method being used notifies you of a pending inbound payment.

After the update in the previous chapter has been made, the user will be notified that your service is ready to receive funds. In the real world, the transfer confirmation process may take time. In such cases, transactions should be set to a new status indicating that the confirmation of the transfer has been received but the funds themselves have not yet been received.

{
"transaction": {
"id": "<transaction_id>",
"status": "pending_external",
"external_transaction_id": "<id_of_external_transaction>",
"message": "Transfer is being verified"
}
}

Sending Stellar Transaction

The next step is to send a Stellar transaction. First, let's change the status to reflect the latest transaction state:

{
"transaction": {
"id": "<transaction_id>",
"status": "pending_anchor",
"message": "Transaction is ready to be fulfilled by the anchor"
}
}

Next, send a transaction on the Stellar network to fulfill a user request. Currently, the Anchor Platform doesn't offer such functionality. You may use a custodial service, or manage the distribution account yourself. In the latter case, you may want to use the Stellar SDK on your server side to create, sign, and submit the transaction.

info

In the future, the Anchor Platform will also support making payments associated with deposit transactions to users on behalf of the business. This is another reason why you may want to use the Anchor Platform's event system.

caution

A user account may not be ready to receive funds. You should check that the account has established a trustline. Otherwise, set the transaction to pending_trust status to indicate that the anchor is waiting for the user to establish the trustline.

tip

Optional You may set the transaction status to pending_stellar, when it has been submitted to the network, but not yet included in the ledger. Due to the nature of the Stellar network, transaction confirmation is almost instant. Some SDKs, like Java, have a blocking transaction submission method. After receiving a successful result from the submission method, the transaction will already be completed.

Completing Transaction

Finally, after the transaction has been sent and successfully submitted to the network, it's time to complete the transaction:

{
"transaction": {
"id": "<transaction_id>",
"status": "completed",
"message": "completed",
"stellar_transactions": [
{
"id": "<stellar_transaction_id>",
"payments": [
{
"id": "<payment_operation_id>",
"amount": {
"amount": "10",
"asset": "USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"
}
}
]
}
]
}
}
caution

It's possible to send multiple transactions to fulfill a user request, but due to current protocol restrictions, only the latest one will be visible for a user via the stellar_transaction_id field.

Handling Errors

If you encounter an unrecoverable error when processing the transaction, it's required to set the transaction status to error. The message field can be used to describe error details.

{
"transaction": {
"id": "<transaction_d>",
"status": "error",
"message": "Example error"
}
}
tip

This status should not be used after the user has made a transfer (if there are some external factors that prevent completing the transaction). Instead, a refund should be initiated. It can be done either by the user's request (in your web view), or automatically.

Updating Withdrawal Transaction Via PATCH

Once the deposit flow is finished, implementing withdrawal becomes quite easy. Some parts of the flow are similar and can be reused.

The starting point for withdrawal and deposit is the same, i.e., collecting and verifying KYC.

Ready to Receive Funds

Similar to a deposit, the next step would be to notify the user that the anchor is ready to receive funds. However, as your service will be receiving transactions over the Stellar network, the update would look a bit different.

{
"transaction": {
"id": "<transaction_id>",
"status": "pending_user_transfer_start",
"message": "waiting on the user to transfer funds",
"amount_in": {
"asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"amount": "10"
},
"amount_out": {
"asset": "iso4217:USD",
"amount": "9.0"
},
"amount_fee": {
"asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"amount": "1.0"
},
"memo": "466857256912784999",
"memo_type": "id"
}
}
tip

Setting memo and memo_type is optional if you don't use a custodial service.

If your business uses a third-party custodian, you would need to generate a memo with your custodian that can be used to receive the user's payment. These memos should be generated per transaction. You should provide memo and memo_type to the request as shown above. Note that the memo must be unique, this is what helps to associate Stellar transactions with SEP transactions.

If your business manages the assets, the Anchor Platform can generate memos for you. When the status is changed to pending_user_transfer_start, the Anchor Platform sets the memo and memo_type automatically (only if it's not included in the request).

note

The Stellar account that will be used to receive funds should be configured in assets.

Tracking Stellar Transactions

Using the Payment Observer allows you to delegate this step to the Anchor Platform. To enable the Payment Observer use the --stellar-observer flag in the command section of the compose file.

The Payment Observer will track all transactions sent to the distribution account. When the transaction with the correct memo is detected in the network, the status will automatically change to pending_anchor and event will be the emitted (if Kafka is used).

In order to update transaction statuses, the observer makes PATCH /transactions requests to the platform, so let's configure the URL it should use.

# dev.env
PLATFORM_API_BASE_URL=http://platform-server:8085
caution

The Payment Observer won't validate amounts, it's your responsibility to verify that the amount sent by the user is correct.

info

If you already have a system that monitors payments, make sure that the logic of the system matches the description below:

First, wait for the transaction to be included in the ledger (using an SDK). This transaction must have the correct memo and destination address (distribution account). Once this transaction has been detected and verified, notify the user that the funds have been received:

{
"id": "<transaction_id>",
"status": "pending_anchor",
"message": "Stellar transaction has been received",
"stellar_transactions": [
{
"id": "<stellar_transaction_id>",
"payments": [
{
"id": "<payment_operation_id>",
"amount": {
"amount": "10",
"asset": "USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"
}
}
]
}
]
}

Sending External Transfer

The next step is to send an external transfer, such as a bank transfer, to the user. When done, change the status to notify the user that the transfer has been sent, but has not been confirmed yet.

info

If your off-chain rails don't provide enough information on whether the payment is pending instead of delivered, you should skip this step and mark payments as completed after initiating the payment.

{
"transaction": {
"id": "<transaction_id>",
"status": "pending_external",
"externaltransactionid": "<id_of_external_transaction>",
"message": "Transfer is being verified"
}
}

Completing the Transaction

Finally, after the transaction has been successfully sent and submitted to the network, it's time to complete the transaction:

{
"transaction": {
"id": "<transaction_id>",
"status": "completed",
"message": "completed"
}
}

Extra Transaction Updates

Expiring Transactions

Your business may want to expire transactions that have been abandoned by the user after some time. It's a good practice to clean up inactive transactions in the incomplete status. To do so, simply change the transaction status to expired

{
"transaction": {
"id": "<transaction_id>",
"status": "expired",
"message": "expired"
}
}