100%
  • Tutorials
  • >
  • Get Started
  • >
  • Send and Receive Money

Send and Receive Money

Send and Receive Money

Now that you have an account, you can send and receive funds through the HC Net. If you haven’t created an account yet, read step 2 of the Get Started guide.

Most of the time, you’ll be sending money to someone else who has their own account. For this interactive guide, however, you should make a second account to transact with using the same method you used to make your first account.

Send Payments

Actions that change things in HC Net, like sending payments, changing your account, or making offers to trade various kinds of currencies, are called operations.[1] In order to actually perform an operation, you create a transaction, which is just a group of operations accompanied by some extra information, like what account is making the transaction and a cryptographic signature to verify that the transaction is authentic.[2]

If any operation in the transaction fails, they all fail. For example, let’s say you have 100 HCX and you make two payment operations of 60 HCX each. If you make two transactions (each with one operation), the first will succeed and the second will fail because you don’t have enough HCX. You’ll be left with 40 HCX. However, if you group the two payments into a single transaction, they will both fail and you’ll be left with the full 100 HCX still in your account.

Finally, every transaction costs a small fee. Like the minimum balance on accounts, this fee helps stop people from overloading the system with lots of transactions. Known as the base fee, it is very small—100 Jots per operation (that’s 0.00001 HCX; Jots are easier to talk about than such tiny fractions of a HCX). A transaction with two operations would cost 200 Jots.[3]

Building a Transaction

HC Net stores and communicates transaction data in a binary format called XDR.[4] Luckily, the HC Net SDKs provide tools that take care of all that. Here’s how you might send 10 HCX to another account:

Submitting a Transaction JavaScript

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

HCNetSdk.Network.useTestNetwork();

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

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

var destinationId = 'GRSX5RFPE6GCKMY3US5U8B6UZLKIGSPIUKSLRB6Q723BM2OARMDUYEIO';

// Transaction will hold a built transaction we can resubmit if the result is unknown.

var transaction;

// First, check to make sure that the destination account exists.

// You could skip this, but if the account does not exist, you will be charged

// the transaction fee when the transaction fails.

server.loadAccount(destinationId)

// If the account is not found, surface a nicer error message for logging.

.catch(HCNetSdk.NotFoundError, function (error) { throw new Error('The destination account does not exist!'); })

// If there was no error, load up-to-date information on your account.

.then(function() {
return server.loadAccount(sourceKeys.publicKey());
})

.then(function(sourceAccount) {

// Start building the transaction.

transaction =new HCNetSdk.TransactionBuilder(sourceAccount)

.addOperation(HCNetSdk.Operation.payment({

destination: destinationId,

// Because HC Net allows transaction in many currencies, you must

// specify the asset type. The special "native" asset represents HCXs.

asset: HCNetSdk.Asset.native(),

amount: "10"
}))

// A memo allows you to add your own metadata to a transaction. It's

// optional and does not affect how HC Net treats the transaction.

.addMemo(HCNetSdk.Memo.text('Test Transaction'))

.build();

// Sign the transaction to prove you are actually the person sending it.

transaction.sign(sourceKeys);

// And finally, send it off to HC Net!

return server.submitTransaction(transaction);
})

.then(function(result) {

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

.catch(function(error) {

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

// If the result is unknown (no response body, timeout etc.) we simply resubmit

// already built transaction:

// server.submitTransaction(transaction);

});

Network.useTestNetwork();

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

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

KeyPair destination = KeyPair.fromAccountId("GRSX5RFPE6GCKMY3US5U8B6UZLKIGSPIUKSLRB6Q723BM2OARMDUYEIO");

// First, check to make sure that the destination account exists.

// You could skip this, but if the account does not exist, you will be charged

// the transaction fee when the transaction fails.

// It will throw HttpResponseException if account does not exist or there was another error.

server.accounts().account(destination);

// If there was no error, load up-to-date information on your account.

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

// Start building the transaction.

Transaction transaction = new Transaction.Builder(sourceAccount)

.addOperation(new PaymentOperation.Builder(destination,

new AssetTypeNative(), "10").build())

// A memo allows you to add your own metadata to a transaction. It's

// optional and does not affect how HC Net treats the transaction.

.addMemo(Memo.text("Test Transaction"))

.build();

// Sign the transaction to prove you are actually the person sending it.

transaction.sign(source);

// And finally, send it off to HC Net!

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());

// If the result is unknown (no response body, timeout etc.) we simply resubmit

// already built transaction:

// SubmitTransactionResponse response = server.submitTransaction(transaction);

}

package main

import (

"fmt"

)

func main () {

source := "SKLANGBA5YHTNYVVV4C3U2522EB6P6F5T3U6MM63WBSBZATA3IEQJKE5"

destination := "GRSX5RFPE6GCKMY3US5U8B6UZLKIGSPIUKSLRB6Q723BM2OARMDUYEIO"

// Make sure destination account exists

if _, err := aurora.DefaultTestNetClient.LoadAccount(destination); err != nil {

panic(err)

}

passphrase := network.TestNetworkPassphrase

tx, err := build.Transaction(

build.TestNetwork,

build.SourceAccount{source},

build.AutoSequence{aurora.DefaultTestNetClient},

build.Payment(

build.Destination{destination},

build.NativeAmount{"10"},

),

)

if err != nil {

panic(err)

}

// Sign the transaction to prove you are actually the person sending it.

txe, err := tx.Sign(source)

if err != nil {

panic(err)

}

txeB64, err := txe.Base64()

if err != nil {

panic(err)

}

// And finally, send it off to HC Net!

resp, err :=

aurora.DefaultTestNetClient.SubmitTransaction(txeB64)

if err != nil {

panic(err)

}

fmt.Println("Successful Transaction:")

fmt.Println("Ledger:", resp.Ledger)

fmt.Println("Hash:", resp.Hash)

}

What exactly happened there? Let’s break it down.

1. Confirm that the account ID you are sending to actually exists by loading the associated account data from the HC Net. Everything will actually be OK if you skip this step, but doing it gives you an opportunity to avoid making a transaction you know will fail. You can also use this call to perform any other verification you might want to do on a destination account. If you are writing banking software, for example, this is a good place to insert regulatory compliance checks and KYC verification.

Load an Account

server.loadAccount(destinationId)

.then(function(account) { /* validate the account */ })

server.accounts().account(destination);

if _, err := aurora.DefaultTestNetClient.LoadAccount(destination); err != nil {

panic (err)

}

2. Load data for the account you are sending from. An account can only perform one transaction at a time[5] and has something called a sequence number, which helps HC Net verify the order of transactions. A transaction’s sequence number needs to match the account’s sequence number, so you need to get the account’s current sequence number from the network.

Load Source Account

.then(function() {

return server.loadAccount(sourceKeys.publicKey());

})

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

The SDK will automatically increment the account’s sequence number when you build a transaction, so you won’t need to retrieve this information again if you want to perform a second transaction.

3. Start building a transaction. This requires an account object, not just an account ID, because it will increment the account’s sequence number.

Build a Transaction

var transaction = new HCNetSdk.TransactionBuilder(sourceAccount)

Transaction transaction = new Transaction.Builder(sourceAccount)

tx, err := build.Transaction(

// ...

)

4. Add the payment operation to the account. Note that you need to specify the type of asset you are sending—HC Net’s “native” currency is the HCX, but you can send any type of asset or currency you like, from dollars to bitcoin to any sort of asset you trust the issuer to redeem (more details below). For now, though, we’ll stick to HCX, which are called “native” assets in the SDK:

Add an Operation

.addOperation(HCNetSdk.Operation.payment({

destination: destinationId,

asset: HCNetSdk.Asset.native(),

amount: "10"

}))

.addOperation(new PaymentOperation.Builder(destination, new AssetTypeNative(), "10").build())

tx, err := build.Transaction(

build.Network{passphrase},

build.SourceAccount{from},

build.AutoSequence{aurora.DefaultTestNetClient},

build.MemoText{"Test Transaction"},

build.Payment(

build.Destination{to},

build.NativeAmount{"10"},

),

)

You should also note that the amount is a string rather than a number. When working with extremely small fractions or large values, floating point math can introduce small inaccuracies. Since not all systems have a native way to accurately represent extremely small or large decimals, HC Net uses strings as a reliable way to represent the exact amount across any system.

5. Optionally, you can add your own metadata, called a memo, to a transaction. HC Net doesn’t do anything with this data, but you can use it for any purpose you’d like. If you are a bank that is receiving or sending payments on behalf of other people, for example, you might include the actual person the payment is meant for here.

Add a Memo

.addMemo(hashcashSdk.Memo.text('Test Transaction'))

.addMemo(Memo.text("Test Transaction"));

build.MemoText{"Test Transaction"},

6. Now that the transaction has all the data it needs, you have to cryptographically sign it using your secret seed. This proves that the data actually came from you and not someone impersonating you.

Sign the Transaction

transaction.sign(sourceKeys);

transaction.sign(source);

txe, err := tx.Sign(from)

7. And finally, send it to the HC Net!

Submit the Transaction

server.submitTransaction(transaction);

transaction.server.submitTransaction(transaction);

resp, err := aurora.DefaultTestNetClient.SubmitTransaction(txeB64)

IMPORTANT It’s possible that you will not receive a response from Aurora server due to a bug, network conditions, etc. In such situation it’s impossible to determine the status of your transaction. That’s why you should always save a built transaction (or transaction encoded in XDR format) in a variable or a database and resubmit it if you don’t know it’s status. If the transaction has already been successfully applied to the ledger, Aurora will simply return the saved result and not attempt to submit the transaction again. Only in cases where a transaction’s status is unknown (and thus will have a chance of being included into a ledger) will a resubmission to the network occur.

Receive Payments

You don’t actually need to do anything to receive payments into a HC Net account—if a payer makes a successful transaction to send assets to you, those assets will automatically be added to your account.

However, you’ll want to know that someone has actually paid you. If you are a bank accepting payments on behalf of others, you need to find out what was sent to you so you can disburse funds to the intended recipient. If you are operating a retail business, you need to know that your customer actually paid you before you hand them their merchandise. And if you are an automated rental car with a HC Net account, you’ll probably want to verify that the customer in your front seat actually paid before that person can turn on your engine.

A simple program that watches the network for payments and prints each one might look like:

Receive Payments

var hashcashSdk = require('hashcash-sdk');

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

var accountId = 'GRTBKZPYOQYPDEFJKLKY9FNNRQMGFLVHKBVCGNSLRRGSMPGF78GFDCVK5';

// Create an API call to query payments involving the account.

var payments = server.payments().forAccount(accountId);

// If some payments have already been handled, start the results from the

// last seen payment. (See below in `handlePayment` where it gets saved.)

var lastToken = loadLastPagingToken();

if (lastToken) {

payments.cursor(lastToken);

}

// `stream` will send each recorded payment, one by one, then keep the

// connection open and continue to send you new payments as they occur.

payments.stream({

onmessage:function(payment) {

// Record the paging token so we can start from here next time.

savePagingToken(payment.paging_token);

// The payments stream includes both sent and received payments. We only

// want to process received payments here.

if (payment.to !== accountId) {

return;

}

// In HC Net’s API, HCXs are referred to as the “native” type. Other

// asset types have more detailed information.

var asset;

if (payment.asset_type === 'native') {

asset = 'HCX';

}

else {

asset = payment.asset_code + ':' + payment.asset_issuer;

}

console.log(payment.amount + ' ' + asset + ' from ' + payment.from);

},

onerror: function(error) {

console.error('Error in payment stream');

}

});

function savePagingToken(token) {

// In most cases, you should save this to a local database or file so that

// you can load it next time you stream new payments.

}

function loadLastPagingToken() {

// Get the last paging token from a local database or file

}

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

KeyPair account = KeyPair.fromAccountId("GRTBKZPYOQYPDEFJKLKY9FNNRQMGFLVHKBVCGNSLRRGSMPGF78GFDCVK5");

// Create an API call to query payments involving the account.

PaymentsRequestBuilder paymentsRequest = server.payments().forAccount(account);

// If some payments have already been handled, start the results from the

// last seen payment. (See below in `handlePayment` where it gets saved.)

String lastToken = loadLastPagingToken();

if (lastToken != null) {

paymentsRequest.cursor(lastToken);

}

// `stream` will send each recorded payment, one by one, then keep the

// connection open and continue to send you new payments as they occur.

paymentsRequest.stream(new EventListener() {

@Override

public void onEvent(OperationResponse payment) {

// Record the paging token so we can start from here next time.

savePagingToken(payment.getPagingToken());

// The payments stream includes both sent and received payments. We only

// want to process received payments here.

if (payment instanceof PaymentOperationResponse) {

if (((PaymentOperationResponse) payment).getTo().equals(account)) {

return;

}

String amount = ((PaymentOperationResponse) payment).getAmount();

Asset asset = ((PaymentOperationResponse) payment).getAsset();

String assetName;

if (asset.equals(new AssetTypeNative())) {

assetName = "HCX";

} else {

StringBuilder assetNameBuilder = new StringBuilder();

assetNameBuilder.append(((AssetTypeCreditAlphaNum) asset).getCode());

assetNameBuilder.append(":");

assetNameBuilder.append(((AssetTypeCreditAlphaNum) asset).getIssuer().getAccountId());

assetName = assetNameBuilder.toString();

}

StringBuilder output = new StringBuilder();

output.append(amount);

output.append(" ");

output.append(assetName);

output.append(" from ");

output.append(((PaymentOperationResponse) payment).getFrom().getAccountId());

System.out.println(output.toString());

}

}

});

package main

import (

" context "

" fmt "

)

func main() {

const address = "GRTBKZPYOQYPDEFJKLKY9FNNRQMGFLVHKBVCGNSLRRGSMPGF78GFDCVK5"

ctx := context.Background()

cursor := aurora.Cursor("now")

fmt.Println("Waiting for a payment...")

err :=aurora.DefaultTestNetClient.StreamPayments(ctx, address, &cursor, func(payment aurora.Payment) {

fmt.Println("Payment type", payment.Type)

fmt.Println("Payment Paging Token", payment.PagingToken)

fmt.Println("Payment From", payment.From)

fmt.Println("Payment To", payment.To)

fmt.Println("Payment Asset Type", payment.AssetType)

fmt.Println("Payment Asset Code", payment.AssetCode)

fmt.Println("Payment Asset Issuer", payment.AssetIssuer)

fmt.Println("Payment Amount", payment.Amount)

fmt.Println("Payment Memo Type", payment.Memo.Type)

fmt.Println("Payment Memo", payment.Memo.Value)

})

if err != nil {

panic(err)

}

}

There are two main parts to this program. First, you create a query for payments involving a given account. Like most queries in HC Net, this could return a huge number of items, so the API returns paging tokens, which you can use later to start your query from the same point where you previously left off. In the example above, the functions to save and load paging tokens are left blank, but in a real application, you’d want to save the paging tokens to a file or database so you can pick up where you left off in case the program crashes or the user closes it.

Create a Payments Query

var payments = server.payments().forAccount(accountId);

var lastToken = loadLastPagingToken();

if (lastToken) {

payments.cursor(lastToken);

}

PaymentsRequestBuilder paymentsRequest = server.payments().forAccount(account)

String lastToken = loadLastPagingToken();

if (lastToken != null) {

paymentsRequest.cursor(lastToken);

}

Second, the results of the query are streamed. This is the easiest way to watch for payments or other transactions. Each existing payment is sent through the stream, one by one. Once all existing payments have been sent, the stream stays open and new payments are sent as they are made.

Try it out: Run this program, and then, in another window, create and submit a payment. You should see this program log the payment.

Stream Payments

payments.stream({

onmessage: function(payment) {

// handle a payment

}

});

paymentsRequest.stream(new EventListener() {

@Override

public void onEvent(OperationResponse payment) {

// Handle a payment

}

});

You can also request payments in groups, or pages. Once you’ve processed each page of payments, you’ll need to request the next one until there are none left.

Paged Payments

payments.call().then(function handlePage(paymentsPage) {

paymentsPage.records.forEach(function(payment) {

// handle a payment

});

return paymentsPage.next().then(handlePage);

});

Page page = payments.execute();

@Override

for (OperationResponse operation : page.getRecords()) {

// Handle a payment

}

page = page.getNextPage();

What Next?

Now that you can send and receive payments using HC Net’s API, you’re on your way to writing all kinds of amazing financial software. Experiment with other parts of the API, then read up on more detailed topics:

Back to Step 2: Create an Account

  • 1. A list of all the possible operations can be found on the operations page. ↩︎
  • 2. The full details on transactions can be found on the transactions page. ↩︎
  • 3. The 100 Jots is called HC Net’s base fee. The base fee can be changed, but a change in HC Net’s fees isn’t likely to happen more than once every several years. You can look up the current fees by checking the details of the latest ledger. ↩︎
  • 4. Even though most responses from the Aurora REST API use JSON, most of the data in HC Net is actually stored in a format called XDR, or External Data Representation. XDR is both more compact than JSON and stores data in a predictable way, which makes signing and verifying an XDR-encoded message easier. You can get more details on our XDR page. ↩︎
  • 5. In situations where you need to perform a high number of transactions in a short period of time (for example, a bank might perform transactions on behalf of many customers using one Hashcash account), you can create several Hashcash accounts that work simultaneously. Read more about this in the guide to channels. ↩︎

+
Chat Now
Welcome to HashCash Support