• Tutorials
  • >
  • Institutional Host Implementation
  • >
  • Regulation Server

Regulation Server

Regulation Server

The task of an Institutional Host is handling regulatory regulation, like Anti-Money Laundering (AML). To accomplish that, you should use the HC Net regulation protocol, a standard way to exchange regulation information and pre-approve a transaction with another financial institution.

You can write your own server that matches the regulation protocol, but HCNet.org also provides a regulation server that takes care of most of the work for you.

Your communication server contacts your regulation server in order to authorize a transaction before sending it. Your regulation server uses the regulation protocol to clear the transaction with the recipient’s regulation server, then lets the communication server know the transaction is ok to send. When another regulation server contacts yours to clear a transaction, a series of callbacks are used to check the information with you. Later, when your communication server receives a transaction, it contacts your regulation server to verify that it was cleared.

Create a Database

The regulation server requires a MySQL or PostgreSQL database in order to save transaction and regulation information. Create a new database named HCNet_regulation and a user to manage it. You don’t need to add any tables; the server includes a command to configure and update your database.

Download and Configure Regulation Server

Start by downloading the latest regulation server for your platform and install the executable anywhere you like. In the same directory, create a file named config_regulation.toml. This will store the configuration for the regulation server. It should look something like:

Config_regulation.toml TOML

  • external_port = 8003
  • internal_port = 8004
  • # Set this to `true` if you need to check the information of a person receiving
  • # a payment you are sending (if false, only the sender will be checked). For
  • # more information, see the callbacks section below.
  • needs_auth = false
  • network_passphrase = "Test HNDF Network ; NOV 2017"
  • [database]
  • type = "mysql" # Or "postgres" if you created a PostgreSQL
  • database
  • url = "dbuser:dbpassword@/HCNet_regulation"
  • [keys]
  • # This should be the secret seed for your base account (or another account that
  • # can authorize transactions from your base account).
  • signing_seed = "S6EXXWNZ7J7EXXWNZ2P5VIMT7KAPJNMZN27MTLUSW7KZW7KCN2P5VIM"
  • encryption_key = "S6EXXWNZ7J7EXXWNZ2P5VIMT7KAPJNMZN27MTLUSW7KZW7KCN2P5VIM"
  • [callbacks]
  • sanctions = "http://localhost:8005/regulation/sanctions"
  • ask_user = "http://localhost:8005/regulation/ask_user"
  • fetch_info = "http://localhost:8005/regulation/fetch_info"
  • # The regulation server must be available via HTTPS. Specify your SSL
  • # certificate and key here. If the server is behind a proxy or load balancer
  • # that implements HTTPS, you can omit this section.
  • [tls]
  • certificate_file = "server.crt"
  • private_key_file = "server.key"

The configuration file lists both an external_port and an internal_port. The external port must be publicly accessible. This is the port that other organizations will contact in order to determine whether you will accept a payment.

The internal port should not be publicly accessible. It is the port through which you initiate compliance operations and transmit private information. It’s up to you to keep this port secure through a firewall, a proxy, or some other means.

You’ll also need to tell your communication server that you now have a regulation server it can use. Update config_communication.toml with the address of your regulation server’s internal port:

Config_communication.toml TOML

  • port = 8001
  • aurora = "https://network.paybito.com/"
  • network_passphrase = "Test HNDF Network ; NOV 2017"
  • compliance = "https://your_org.com:8004"
  • # ...the rest of your configuration...

Implement Regulation Callbacks

In the server configuration file, there are three callback URLs, much like those for the communication server. They are HTTP POST URLs that will be sent form-encoded data:

  • fetch_info is sent a mapping address (like tunde_adebayo*your_org.com) and should return all the information necessary for another organization to perform regulation checks. It can be any data you deem reasonable and must be formatted as JSON.

    fetch_info is sent a mapping address (like tunde_adebayo*your_org.com) and should return all the information necessary for another organization to perform regulation checks. It can be any data you deem reasonable and must be formatted as JSON.

    Implementing the fetch_info callback

    app.post('/regulation/fetch_info', function (request, response) {

    var addressParts = response.body.address.split('*');

    var friendlyId = addressParts[0];

    // You need to create `accountDatabase.findByFriendlyId()`. It should look

    // up a customer by their Hashcash account and return account information.

    accountDatabase.findByFriendlyId(friendlyId)

    .then(function(account) {

    // This can be any data you determine is useful and is not limited to

    // these three fields.

    response.json({

    name: account.fullName,

    address: account.address,

    date_of_birth: account.dateOfBirth

    });

    response.end();

    })

    .catch(function(error) {

    console.error('Fetch Info Error:', error);

    response.status(500).end(error.message);

    });

    });

    @POST

    @Path("regulation/fetch_info")

    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)

    @Produces(MediaType.APPLICATION_JSON)

    public Response fetchInfo(

    @FormParam("address") String address) {

    String friendlyId = address.split("\\*", 2)[0];

    // You need to create `accountDatabase.findByFriendlyId()`. It should

    // find customers by their HC Net account and return account information.

    try {

    Account account = accountDatabase.findByFriendlyId(friendlyId);

    return Response.ok(

    // This can be any data you determine is useful and is not limited to

    // these three fields.

    Json.createObjectBuilder()

    HttpClient httpClient = HttpClients.createDefault();

    .add("name", account.fullName)

    .add("address", account.address)

    .add("date_of_birth", account.dateOfBirth)

    .build())

    .build();

    )

    } catch (Exception error) {

    System.out.println(

    String.format("Could not find account: %s", address));

    return

    Response.status(500).entity(error.getMessage()).build();

    }

    }

  • sanctions is given information about the person who is sending a payment to you or one of your customers. This is the same data the sending server would have received from its own fetch_info callback. The HTTP response code it produces indicates whether the payment will be accepted (status 200), denied (status 403), or if you need additional time for processing (status 202).

    Implementing the sanctions callback

    app.post('/regulation/sanctions', function (request, response) {

    var sender = JSON.parse(request.body.sender);

    // You need to create a function to check whether there are any sanctions

    // against someone.

    sanctionsDatabase.isAllowed(sender)

    .then(function() {

    response.status(200).end();

    })

    .catch(function(error) {

    // In this example, we're assuming `isAllowed` returns an error with a

    // `type` property that indicates the kind of error. Your systems may

    // work differently; just return the same HTTP status codes.

    if (error.type === 'DENIED') {

    response.status(403).end();

    }

    else if (error.type === 'UNKNOWN') {

    // If you need to wait and perform manual checks, you'll have to

    // create a way to do that as well

    notifyHumanForManualSanctionsCheck(sender);

    // The value for `pending` is a time to check back again in seconds

    response.status(202).json({pending: 3600}).end();

    }

    else {

    response.status(500).end(error.message);

    }

    });

    });

    import java.io.*;

    import javax.json.Json;

    import javax.json.JsonObject;

    import javax.json.JsonReader;

    @POST

    @Path("regulation/sanctions")

    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)

    @Produces(MediaType.APPLICATION_JSON)

    public Response sanctions(@FormParam("sender") String sender) {

    JsonReader jsonReader = Json.createReader(new StringReader(sender));

    JsonObject senderData = jsonReader.readObject();

    jsonReader.close();(

    // You need to create a function to check whether there are any sanctions

    // against someone.

    Permission permission = sanctionsDatabase.isAllowed(

    senderData.getString("name"),

    senderData.getString("address"),

    senderData.getString("date_of_birth"));

    // In this example, we're assuming `isAllowed` returns a Permissions enum

    // that indicates whether someone is Allowed, Denied, or Unknown. Your

    // systems may work differently; just return the same HTTP status codes.

    if (permission.equals(Permission.Allowed)) {

    return Response.ok().build();

    }

    else if (permission.equals(Permission.Denied)) {

    return Response.status(403).build();

    }

    else {

    // If you need to wait and perform manual checks, you'll have to implement

    // a way to do that as well.

    notifyHumanForManualSanctionsCheck(senderData);

    // The value for `pending` is a time to check back again in seconds.

    return Response.accepted(

    Json.createObjectBuilder()

    .add("pending", 3600)

    .build())

    .build();

    }

    }

  • ask_user is called when receiving a payment if the sender has requested information about the receiver. Its return code indicates whether you will send that information (fetch_info is then called to actually get the info). It is sent information on both the payment and the sender.

    Implementing the ask_user callback

    app.post('/reulation/ask_user', function (request, response) {

    var sender = JSON.parse(request.body.sender);

    // You can do any checks that make sense here. For example, you may not

    // want to share information with someone who has sanctions as above:

    sanctionsDatabase.isAllowed(sender)

    .then(function() {

    response.status(200).end();

    })

    .catch(function(error) {

    if (error.type === 'UNKNOWN') {

    // If you need to wait and perform manual checks, you'll have to

    // create a way to do that as well.

    notifyHumanForManualInformationSharing(sender);

    // The value for `pending` is a time to check back again in seconds

    response.status(202).json({pending: 3600}).end();

    }

    else {

    // create a way to do that as well

    response.status(403).end();

    }

    });

    });

    @POST

    @Path("regulation/ask_user")

    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)

    @Produces(MediaType.APPLICATION_JSON)

    public Response askUser(@FormParam("sender") String sender)

    {

    JsonReader jsonReader = Json.createReader(new StringReader(sender));

    JsonObject senderData = jsonReader.readObject();

    jsonReader.close();

    // You can do any checks that make sense here. For example, you may not

    // want to share information with someone who has sanctions as above:

    Permission permission = sanctionsDatabase.isAllowed(

    senderData.getString("name"),

    senderData.getString("address"),

    senderData.getString("date_of_birth"));

    if (permission.equals(Permission.Allowed)) {

    return Response.ok().build();

    }

    else if (permission.equals(Permission.Denied)) {

    return Response.status(403).build();

    }

    else {

    // If you need to wait and perform manual checks, you'll have to create

    // a way to do that as well.

    notifyHumanForManualInformationSharing(senderData);

    // The value for `pending` is a time to check back again in seconds.

    return Response.accepted(

    Json.createObjectBuilder()

    .add("pending", 3600)

    .build())

    .build();

    }

    }

To keep things simple, we’ll add all three callbacks to the same server we are using for the communication server callbacks. However, you can implement them on any service that makes sense in your infrastructure. Just make sure they’re reachable at the URLs in your config file.

Update HCNet.toml

When other organizations need to contact your regulation server to authorize a payment to one of your customers, they consult your domain’s HCNet.toml file for the address, just as when finding your mapping server.

For regulation operations, you’ll need to list two new properties in your

HCNet.toml:

HCNet.toml TOML

  • MAPPING_SERVER = "https://www.your_org.com:8002/mapping"
  • AUTH_SERVER = "https://www.your_org.com:8003"
  • SIGNING_KEY = "GAPJN32J7O6EXXWNZ2P5VIMT7KFETQMZN27IILUSW7KZDZKCNRLQFON"

AUTH_SERVER is the address for the external port of your regulation server. Like your mapping server, this can be any URL you like, but it must support HTTPS and use a valid SSL certificate.[1]

SIGNING_KEY is the public key that matches the secret seed specified for signing_seed in your compliance server’s configuration. Other organizations will use it to verify that messages were actually sent by you.

Start the Server

Before starting the server the first time, the tables in your database need to be created. Running regulation server with the --migrate-db argument will make sure everything is set to go:

./regulation --migrate-db

Each time you update the regulation server to a new version, you should run this command again. It will upgrade your database in case anything needs to be changed.

Now that your database is fully set up, you can start the regulation server by running:

./regulation

Try It Out

Now that you’ve got your regulation server set up and ready to verify transactions, you’ll want to test it by sending a payment to someone who is running their own regulation and mapping servers.

The easiest way to do this is to simply test a payment from one of your own customers to another. Your regulation,mapping, and communication servers will perform both the sending and receiving sides of the transaction.

Send a payment through your communication server, but this time, use mapped addresses for the sender and receiver and an extra_memo[2] to trigger compliance checks:

Send a Payment

# NOTE: `extra_memo` is required for regulation (use it instead of `memo`)

curl -X POST -d \

"id=unique_payment_id&\

amount=1&\

asset_code=USD&\

asset_issuer=GAPJN32J7O6EXXWNZ2P5VIMT7KFETQMZN27IILUSW7KZDZKCNRLQFON&\

destination=amy*your_org.com&\

source=S6EXXWNZ7J7EXXWNZ2P5VIMT7KAPJNMZN27MTLUSW7KZW7KCN2P5VIM&\

sender=tunde_adebayo*your_org.com&\

extra_memo=Test%20transaction" \

http://localhost:8001/payment

var request = require('request');

request.post({

url: 'http://localhost:8001/payment',

form: {

id: 'unique_payment_id',

amount: '1',

asset_code: 'USD',

asset_issuer: 'GAPJN32J7O6EXXWNZ2P5VIMT7KFETQMZN27IILUSW7KZDZKCNRLQFON',

destination: 'amy*your_org.com',

source: 'S6EXXWNZ7J7EXXWNZ2P5VIMT7KAPJNMZN27MTLUSW7KZW7KCN2P5VIM',

sender: 'tunde_adebayo*your_org.com',

// `extra_memo` is required for regulation (use it instead of `memo`)

extra_memo: 'Test transaction',

}

}, function(error, response, body) {

if (error || response.statusCode !== 200) {

console.error('ERROR!', error || body);

}

else {

console.log('SUCCESS!', body);

}

});

import org.apache.http.HttpEntity;

import org.apache.http.HttpResponse;

import org.apache.http.NameValuePair;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpPost;

import org.apache.http.impl.client.HttpClients;

import org.apache.http.message.BasicNameValuePair;

import org.apache.http.util.EntityUtils;

import java.util.ArrayList;

import java.util.List;

public class PaymentRequest() {

public static void main(String [] args) {

HttpPost paymentRequest = new HttpPost("http://localhost:8001/payment");

List <NameValuePair> params = new ArrayList <NameValuePair>();

params.add(new BasicNameValuePair("id", "unique_payment_id"));

params.add(new BasicNameValuePair("amount", "1"));

params.add(new BasicNameValuePair("asset_code", "USD"));

params.add(new BasicNameValuePair("asset_issuer", "GAPJN32J7O6EXXWNZ2P5VIMT7KFETQMZN27IILUSW7KZDZKCNRLQFON"));

params.add(new BasicNameValuePair("destination", "amy*your_org.com"));

params.add(new BasicNameValuePair("source", "S6EXXWNZ7J7EXXWNZ2P5VIMT7KAPJNMZN27MTLUSW7KZW7KCN2P5VIM"));

params.add(new BasicNameValuePair("sender", "tunde_adebayo*your_org.com"));

// `extra_memo` is required for regulation (use it instead of `memo`)

params.add(new BasicNameValuePair("extra_memo", "Test transaction"));

HttpResponse response = httpClient.execute(paymentRequest);

HttpEntity entity = response.getEntity();

if (entity != null) {

String body = EntityUtils.toString(entity);

System.out.println(body);

}

}

}

For a more realistic test, set up a duplicate copy of your communication, mapping, and regulation servers at a different domain and send a payment to them!

Regulation Protocol

Complying with Anti-Money Laundering (AML) laws requires financial institutions (FIs) to know not only who their customers are sending money to but who their customers are receiving money from. In some jurisdictions banks are able to trust the AML procedures of other licensed banks. In other jurisdictions each bank must do its own sanction checking of both the sender and the receiver. The Regulation Protocol handles all these scenarios.

The customer information that is exchanged between FIs is flexible but the typical fields are:

  • Full Name
  • Date of birth
  • Physical address

The Regulation Protocol is an additional step after mapping. In this step the sending FI contacts the receiving FI to get permission to send the transaction. To do this the receiving FI creates an AUTH_SERVER and adds its location to the HCNet.toml of the FI.

You can create your own endpoint that implements the regulation protocol or we have also created this simple regulation service that you can use.

AUTH_SERVER

The AUTH_SERVER provides one endpoint that is called by a sending FI to get approval to send a payment to one of the receiving FI’s customers. The AUTH_SERVER url should be placed in organization’s HCNet.toml file.

Request

To send transaction data to the receiving organization, send HTTP POST to AUTH_SERVER with Content-Type equal

application/x-www-form-urlencoded and the following body:

data=<data value>&sig= <sig value> </sig>

data is a block of JSON that contains the following fields:
Name Data Type Description
sender string The payment address of the customer that is initiating the send. Ex. bob*bank.com
need_info boolean If the caller needs the recipient’s AML info in order to send the payment.
tx string: base64 encoded xdr.Transaction The transaction that the sender would like to send in XDR format. This transaction is unsigned and it’s sequence number should be equal 0.
attachment string The full text of the attachment. The hash of this attachment is included as a memo in the transaction. The attachment field follows the HC Net Attachment Convention and should contain at least enough information of the sender to allow the receiving FI to do their sanction check.

sig is the signature of the data block made by the sending FI. The receiving institution should check that this signature is valid against the public signature key that is posted in the sending FI’s HCNet.toml (SIGNING_KEY field).

Example request body (please note it contains both parameters data and sig):

  • data=%7B%22sender%22%3A%22aldi%2AbankA.com%22%2C%22need_info%22%3Atrue%2C%22tx%22%3A%22AAAAAEhAArfpmUJYq%2FQ9SFAH3YDzNLJEBI9i9TXmJ7s608xbAAAAZAAMon0AAAAJAAAAAAAAAAPUg1%2FwDrMDozn8yfiCA8LLC0wF10q5n5lo0GiFQXpPsAAAAAEAAAAAAAAAAQAAAADdvkoXq6TXDV9IpguvNHyAXaUH4AcCLqhToJpaG6cCyQAAAAAAAAAAAJiWgAAAAAA%3D%22%2C%22attachment%22%3A%22%7B%5C%22nonce%5C%22%3A%5C%221488805458327055805%5C%22%2C%5C%22transaction%5C%22%3A%7B%5C%22sender_info%5C%22%3A%7B%5C%22address%5C%22%3A%5C%22678+Mission+St%5C%22%2C%5C%22city%5C%22%3A%5C%22San+Francisco%5C%22%2C%5C%22country%5C%22%3A%5C%22US%5C%22%2C%5C%22first_name%5C%22%3A%5C%22Aldi%5C%22%2C%5C%22last_name%5C%22%3A%5C%22Dobbs%5C%22%7D%2C%5C%22route%5C%22%3A%5C%221%5C%22%2C%5C%22note%5C%22%3A%5C%22%5C%22%2C%5C%22extra%5C%22%3A%5C%22%5C%22%7D%2C%5C%22operations%5C%22%3Anull%7D%22%7D&sig=KgvyQTZsZQoaMy8jdwCUfLayfgfFMUdZJ%2B0BIvEwiH5aJhBXvhV%2BipRok1asjSCUS%2FUaGeGKDoizS1%2BtFiiyAA%3D%3D

After decoding the data parameter it has a following form:

  • {
  • "sender": "aldi*bankA.com",
  • "need_info": true,
  • "tx": "AAAAAEhAArfpmUJYq/Q9SFAH3YDzNLJEBI9i9TXmJ7s608xbAAAAZAAMon0AAAAJAAAAAAAAAAPUg1/wDrMDozn8yfiCA8LLC0wF10q5n5lo0GiFQXpPsAAAAAEAAAAAAAAAAQAAAADdvkoXq6TXDV9IpguvNHyAXaUH4AcCLqhToJpaG6cCyQAAAAAAAAAAAJiWgAAAAAA=",
  • "attachment": "{\"nonce\":\"1488805458327055805\",\"transaction\":{\"sender_info\":{\"address\":\"678 Mission St\",\"city\":\"San Francisco\",\"country\":\"US\",\"first_name\":\"Aldi\",\"last_name\":\"Dobbs\"},\"route\":\"1\",\"note\":\"\",\"extra\":\"\"},\"operations\":null}"
  • }

Please note that the memo value of tx is a sha256 hash of the attachment.

Response

AUTH_SERVER will return a JSON object with the following fields:
Name Data Type Description
info_status ok, denied, pending If this FI is willing to share AML information or not.
tx_status ok, denied, pending If this FI is willing to accept this transaction.
dest_info string (only present if info_status is ok) Marshalled JSON of the recipient’s AML information.
pending integer (only present if info_status or tx_status is pending) Estimated number of seconds till the sender can check back for a change in status. The sender should just resubmit this request after the given number of seconds./td>

Response Example

  • {
  • "info_status": "ok",
  • "tx_status": "pending",
  • "dest_info": "{\"name\": \"John Doe\"}",
  • "pending":3600
  • }

Example of Flow

In this example, Aldi aldi*bankA.com wants to send to Bogart bogart*bankB.com:

1) BankA gets the info needed to interact with BankB

This is done by looking up BankB’s HCNet.toml file.

BankA -> fetches https://bankB.com/.well-known/HCNet.toml

from this .toml file it pulls out the following info for BankB:

  • MAPPING_SERVER
  • AUTH_SERVER

2) BankA gets the routing info for Bogart so it can build the transaction

This is done by asking BankB’s mapping server to resolve

bogart*bankB.com.

BankA -> MAPPING_SERVER?type=name&q=bogart*bankB.com

See Mapping for a complete description. The returned fields of interest here are:

  • HC Net AccountID of Bogart’s FI
  • Bogart’s routing info

Example mapping response:

  • {
  • "HCNet_address": "bogart*bankB.com",
  • "account_id": "GAKO2Y56HOH44EWUP5C4BEQKLLION2TPJX4JQONZX7XOVJNMY5LG7UWWO",
  • "memo_type": "id",
  • "memo": 1
  • }
3) BankA makes the Auth Request to BankB

This request will ask BankB for Bogart’s AML info and for permission to send to Bogart.

BankA -> AUTH_SERVER

Example request body (please note it contains both parameters data and sig):

  • data=%7B%22sender%22%3A%22aldi%2AbankA.com%22%2C%22need_info%22%3Atrue%2C%22tx%22%3A%22AAAAAEhAArfpmUJYq%2FQ9SFAH3YDzNLJEBI9i9TXmJ7s608xbAAAAZAAMon0AAAAJAAAAAAAAAAPUg1%2FwDrMDozn8yfiCA8LLC0wF10q5n5lo0GiFQXpPsAAAAAEAAAAAAAAAAQAAAADdvkoXq6TXDV9IpguvNHyAXaUH4AcCLqhToJpaG6cCyQAAAAAAAAAAAJiWgAAAAAA%3D%22%2C%22attachment%22%3A%22%7B%5C%22nonce%5C%22%3A%5C%221488805458327055805%5C%22%2C%5C%22transaction%5C%22%3A%7B%5C%22sender_info%5C%22%3A%7B%5C%22address%5C%22%3A%5C%22678+Mission+St%5C%22%2C%5C%22city%5C%22%3A%5C%22San+Francisco%5C%22%2C%5C%22country%5C%22%3A%5C%22US%5C%22%2C%5C%22first_name%5C%22%3A%5C%22Aldi%5C%22%2C%5C%22last_name%5C%22%3A%5C%22Dobbs%5C%22%7D%2C%5C%22route%5C%22%3A%5C%221%5C%22%2C%5C%22note%5C%22%3A%5C%22%5C%22%2C%5C%22extra%5C%22%3A%5C%22%5C%22%7D%2C%5C%22operations%5C%22%3Anull%7D%22%7D&sig=KgvyQTZsZQoaMy8jdwCUfLayfgfFMUdZJ%2B0BIvEwiH5aJhBXvhV%2BipRok1asjSCUS%2FUaGeGKDoizS1%2BtFiiyAA%3D%3D

After decoding data parameter it has a following form:

  • {
  • "sender": "aldi*bankA.com",
  • "need_info": true ,
  • "AAAAAEhAArfpmUJYq/Q9SFAH3YDzNLJEBI9i9TXmJ7s608xbAAAAZAAMon0AAAAJAAAAAAAAAAPUg1/wDrMDozn8yfiCA8LLC0wF10q5n5lo0GiFQXpPsAAAAAEAAAAAAAAAAQAAAADdvkoXq6TXDV9IpguvNHyAXaUH4AcCLqhToJpaG6cCyQAAAAAAAAAAAJiWgAAAAAA=",
  • "attachment": "{\"nonce\":\"1488805458327055805\",\"transaction\":{\"sender_info\":{\"address\":\"678 Mission St\",\"city\":\"San Francisco\",\"country\":\"US\",\"first_name\":\"Aldi\",\"last_name\":\"Dobbs\"},\"route\":\"1\",\"note\":\"\",\"extra\":\"\"},\"operations\":null}"
  • "attachment" : "{\"nonce\":\"1488805458327055805\",\"transaction\":{\"sender_info\":{\"address\":\"678 Mission St\",\"city\":\"San Francisco\",\"country\":\"US\",\"first_name\":\"Aldi\",\"last_name\":\"Dobbs\"},\"route\":\"1\",\"note\":\"\",\"extra\":\"\"},\"operations\":null}"
  • }

Please note that memo value of tx is the sha256 hash of the attachment and payment destination is returned by the mapping server. You can check the transaction above using the XDR Viewer.

4) BankB handles the Auth request
  • BankB gets sender domain by spliting the sender address (aldi*bankA.com) in 2 parts: aldiand bankA.com
  • BankB -> fetches https://bankA.com/.well-known/HCNet.toml From this it gets BankA’s SIGNING_KEY
  • BankB verifies the signature on the Auth Request was signed with BankA’s SIGNING_KEY
  • BankB does its sanction check on Aldi. This determines the value of tx_status.
  • BankB makes the decision to reveal the AML info of Bogart or not based on the following:
    • Bogart has made their info public
    • Bogart has allowed BankA
    • Bogart has allowed Aldi
    • BankB has allowed BankA
  • If none of the above criteria are met, BankB should ask Bogart if he wants to reveal this info to BankA and accept this payment. In this case BankB will return info_status: "pending" in the Auth request reply to give Bogart time to accept the payment or not.
  • If BankB determines it can share the AML info with BankA, it sends this info in dest_info field with the reply.

See Auth Server response for potential return values.

Example Response:

  • {
  • "info_status": "ok",
  • "tx_status": "ok",
  • "dest_info": "{\"name\": \"Bogart Doe\"}",
  • }
5) BankA handles the reply from the Auth request

If the call to the AUTH_SERVER returned pending, BankA must resubmit the request again after the estimated number of seconds.

6) BankA does the sanction checks

Once BankA has been given the dest_info from BankB, BankA does the sanction check using this AML info of Bogart. If the sanction check passes, BankA signs and submits the transaction to the HC Net.

7) BankB handles the incoming payment.
  • It checks the transaction hash against a cache it has or redoes the sanction check on the sender.
  • It credits Bogart’s account with the amount sent or sends the transaction back.