• Tutorials
  • >
  • Institutional Host Implementation
  • >
  • Communication Server

Communication Server

Communication Server

HCNet.org maintains a communication server, which makes it easier to use the mapping and regulation servers to send and receive payments. When using the communication server, the only code you need to write is a private service to receive payment notifications and respond to regulatory checks from the communication and regulation servers.

When using the communication server, you send payments by making an HTTP POST request to it instead of a Aurora server. It doesn’t change a whole lot for simple transactions, but it will make the next steps of map and regulation much simpler.

Create a Database

The communication server requires a MySQL or PostgreSQL database in order to track and coordinate transaction and regulation information. Create an empty database named HCNet_communication and a user to manage it. You don’t need to add any tables; the communication server has a special command to do that for you.

Download and Configure Communication Server

Next, download the latest communication server for your platform. Install the executable anywhere you like. In the same directory, create a file named communication.cfg. This will store the configuration for the communication server. It should look something like:

Download and Configure Communication Server

Next, download the latest communication server for your platform. Install the executable anywhere you like. In the same directory, create a file named communication.cfg. This will store the configuration for the communication server. It should look something like:

  • Communication.cfg TOML
  • port = 8006
  • aurora = "https://network.paybito.com/"
  • network_passphrase = "Test HNDF Network ; NOV 2017"
  • # We'll fill this in once we set up a compliance server regulation= ""
  • # This describes the assets that can be sent and received.
  • # Repeat this section to add support for more asset types.
  • [[assets]]
  • code="USD"
  • issuer="GAPJN32J7O6EXXWNZ2P5VIMT7KFETQMZN27IILUSW7KZDZKCNRLQFON"
  • [database]
  • type = "mysql" # or "postgres" if you created a postgres
  • database
  • url = "dbuser:dbpassword@/HCNet_communication"
  • [accounts]
  • # The secret seed for your base account, from which payments are made base_seed = "S6EXXWNZ7J7EXXWNZ2P5VIMT7KAPJNMZN27MTLUSW7KZW7KCN2P5VIM"
  • # The account ID that receives payments on behalf of your customers. In this
  • # case, it is the account ID that matches `base_seed` above. receiving_account_id = "GAIGZHHWK3REZQPLQX5DNUN4A32CSEONTU6CMDBO7GDWLPSXZDSYA4BU"
  • # A secret seed that can authorize trustlines for assets you issue. For more,
  • # see
  • # authorizing_seed = "GAKO2Y56HOH44EWUP5C4BEQKLLION2TPJX4JQONZX7XOVJNMY5LGUWWO"
  • # The ID of the account that issues your assets issuing_account_id = "GAPJN32J7O6EXXWNZ2P5VIMT7KFETQMZN27IILUSW7KZDZKCNRLQFON"
  • [callbacks]
  • # The server will send POST requests to this URL to notify you of payments receive = "http://localhost:8005/receive"

Start the Server

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

./communication --migrate-db

Each time you update the communication 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 communication server by running:

./communication

Send a Payment

The communication server takes commands in the form of HTTP requests, so we can test submitting a payment by sending a POST request to /payments. Try sending 1 USD to the account

GRTBKZPYOQYPDEFJKLKY9FNNRQMGFLVHKBVCGNSLRRGSMPGF78GFDCVK5.

(Remember that the receiving account will need to trust the asset first. See issuing assets for more details.)

Send a Payment

curl -X POST -d \

"id=unique_payment_id&\

amount=1&\

asset_code=USD&\

asset_issuer=GAPJN32J7O6EXXWNZ2P5VIMT7KFETQMZN27IILUSW7KZDZKCNRLQFON&\

destination=GAIGZHHWK3REZQPLQX5DNUN4A32CSEONTU6CMDBO7GDWLPSXZDSYA4BU&\

source=S6EXXWNZ7J7EXXWNZ2P5VIMT7KAPJNMZN27MTLUSW7KZW7KCN2P5VIM" \

http://localhost:8006/payment

var request = require('request');

request.post({

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

form: {

id: 'unique_payment_id',

amount: '1',

asset_code: 'USD',

asset_issuer:

'GAPJN32J7O6EXXWNZ2P5VIMT7KFETQMZN27IILUSW7KZDZKCNRLQFON',

destination:

'GAIGZHHWK3REZQPLQX5DNUN4A32CSEONTU6CMDBO7GDWLPSXZDSYA4BU',

source:

'S6EXXWNZ7J7EXXWNZ2P5VIMT7KAPJNMZN27MTLUSW7KZW7KCN2P5VIM'

}

}, 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:8006/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", "GAIGZHHWK3REZQPLQX5DNUN4A32CSEONTU6CMDBO7GDWLPSXZDSYA4BU"));

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

HttpResponse response = httpClient.execute(paymentRequest);

HttpEntity entity = response.getEntity();

if (entity != null) {

String body = EntityUtils.toString(entity);

System.out.println(body);

}

}

}

Create a Server to Receive Payments

In the communication server configuration file, you might have noticed a callback URL named receive. Whenever a payment is received, the communication server will send an HTTP POST request to the URL you specified. The main responsibility of the receive endpoint is to update your customer’s balance in response to receiving a payment (since the payment went to your account on HC Net).

Implementing the Receive Callback

/**

* A small Express.js web server for handling payments from the communication server.

*/

var express = require('express');

var bodyParser = require('body-parser');

var app = express();

app.use(bodyParser.urlencoded({ extended: false }));

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

var payment = request.body;

// `receive` may be called multiple times for the same payment, so check that

// you haven't already seen this payment ID.

if (getPaymentByIdFromDb(payment.id)) {

return response.status(200).end();

}

// Because we have one HC Net account representing many customers, the

// customer the payment is intended for should be in the transaction memo.

var customer = getAccountFromDb(payment.memo);

// You need to check the asset code and issuer to make sure it's an asset

// that you can accept payment to this account for. In this example, we just

// convert the amount to USD and adding the equivalent amount to the customer

// balance. You need to implement `convertToUsd()` yourself.

var dollarAmount = convertToUsd(

payment.amount, payment.asset_code, payment.asset_issuer);

addToBankAccountBalance(customer, dollarAmount);

response.status(200).end();

console.log('Added ' + dollarAmount + ' USD to account: ' + customer);

});

app.listen(8005, function () {

console.log('Communication server callbacks running on port 8005!');

});

import javax.ws.rs.POST;

import javax.ws.rs.Path;

import javax.ws.rs.Consumes;

import javax.ws.rs.FormParam;

import javax.ws.rs.core.MediaType;

import javax.ws.rs.core.Response;

/**

* A small Jersey web server for handling callbacks from HC Net services

*/

@Path("/")

public class HashcashCallbacks {

@POST

@Path("receive")

@Consumes(MediaType.APPLICATION_FORM_URLENCODED)

public Response receive(

@FormParam("id") String id,

@FormParam("amount") String amount,

@FormParam("asset_code") String assetCode,

@FormParam("asset_issuer") String assetIssuer,

@FormParam("memo") String memo) {

// `receive` may be called multiple times for the same payment, so check

// that you haven't already seen this payment ID. (getPaymentByIdFromDb is

// a method you’ll need to implement.)

if (getPaymentByIdFromDb(id)) {

return Response.ok().build();

}

// Because we have one HC Net account representing many customers, the

// customer the payment is intended for should be in the transaction memo.

// (getAccountFromDb is a method you’ll need to implement.) Customer customer = getAccountFromDb(memo);

// You need to check the asset code and issuer to make sure it's an asset

// that you can accept payment to this account for. In this example, we just

// convert the amount to USD and adding the equivalent amount to the

// customer balance. You need to implement `convertToUsd()` yourself.

Double dollarAmount = convertToUsd(amount, assetCode, assetIssuer);

addToBankAccountBalance(customer, dollarAmount);

return Response.ok().build();

System.out.println(String.format(

"Add %s, USD to account: %s",

dollarAmount,

customer));

}

}

To test that your receive callback works, let’s try sending 1 USD to a customer with the account name Amy at your bank. (For a review of sending payments using the API, check step 3 of “get started”.)

Test Receive Callback

var HCNetSdk = require('HCNet-sdk');

HCNetSdk.Network.useTestNetwork()

var server = new HCNetSdk.Server('https://network.paybito.com/');

var sourceKeys = HCNetSdk.Keypair.fromSecret( 'S6EXXWNZ7J7EXXWNZ2P5VIMT7KAPJNMZN27MTLUSW7KZW7KCN2P5VIM');

var destinationId = 'GAIGZHHWK3REZQPLQX5DNUN4A32CSEONTU6CMDBO7GDWLPSXZDSYA4BU';

server.loadAccount(sourceKeys.publicKey())

.then(function(sourceAccount) {

var transaction = new HashcashSdk.TransactionBuilder(sourceAccount)

.addOperation(HCNetSdk.Operation.payment({

destination: destinationId,

asset: new HCNetSdk.Asset(

'USD', 'GAPJN32J7O6EXXWNZ2P5VIMT7KFETQMZN27IILUSW7KZDZKCNRLQFON'),

amount: '1'

}))

// Use the memo to indicate the customer this payment is intended for.

.addMemo(HCNetSdk.Memo.text('Amy'))

.build();

transaction.sign(sourceKeys);

return server.submitTransaction(transaction);

})

.then(function(result) {

console.log('Success! Results:', result);

})

.catch(function(error) {

console.error('Something went wrong!', error);

});

Server server = new Server("https://network.paybito.com/");

KeyPair source = KeyPair.fromSecretSeed( "S6EXXWNZ7J7EXXWNZ2P5VIMT7KAPJNMZN27MTLUSW7KZW7KCN2P5VIM");

KeyPair destination = KeyPair.fromAccountId(

"GAIGZHHWK3REZQPLQX5DNUN4A32CSEONTU6CMDBO7GDWLPSXZDSYA4BU");

Asset dollar = Asset.createNonNativeAsset("USD",

KeyPair.fromAccountId(

"GAPJN32J7O6EXXWNZ2P5VIMT7KFETQMZN27IILUSW7KZDZKCNRLQFON")

);

AccountResponse sourceAccount = server.accounts().account(source);

Transaction transaction = new Transaction.Builder(sourceAccount)

.addOperation(new PaymentOperation.Builder(destination, dollar, "1").build())

// Use the memo to indicate the customer this payment is intended for.

.addMemo(Memo.text("Amy"))

.build();

transaction.sign(source);

try {

SubmitTransactionResponse response = server.submitTransaction(transaction);

System.out.println("Success!");

System.out.println(response);

} catch (Exception e) {

System.out.println("Something went wrong!");

System.out.println(e.getMessage());

}

After running the above code, your callback server should have logged information about the payment.