Send Payment/Token From A Wallet
POST /v1/trovo-api/users/payment
Protocol To Send Payment/Token From A Wallet
Sending only happens from within wallets that are within the enterprise's scope. Attempts to send a token from a different wallet will fail. Payment and transaction bound interactions typically happen in two phases. Initial call with base parameters which then pre-fills the other parameter. SPECIFY THE SENDER/SOURCE WALLET using the Headers.
NOTE: When using a sub-wallet with shared access, ENSURE TO GRANT THE owner an INITIATOR PRIVILEGE on the subwallet.
The returned object contains the submitted information as well as all needed information that will help complete signing of transactions. When the transaction is signed, then append it to the right key in the returned object and then submit the entire object back to the API. Once the signatures are verified and transaction integrity is determined, then it is submitted to the blockchain for execution or saved for approvals in case of shared access transactions. The JSON response object is the same as the JSON body object. But for convenience and clarity, we will specify only the basic JSON body required parameters for the initial request. This means that other fields can be left empty.
Destination: is the public key or trovo app username of the wallet that will receive the sent token. Required.
Memo: this is max of 28bytes (character) which can be used to denote the transaction. This is Optional.
AssetIssuer: this is the 56 character public key of the issuer of the asset/token. Required.
AssetCode: Max of 12 alphanumeric characters that represents the code of the token to be sent.
Amount: this is the amount of token units to be sent to the destination wallet.
Transaction: this is the composed base64Txn XDR transaction string for the operation that is returned as a response after the initial call with the initial JSON BODY. This transaction would be signed with the proper stellar SDK. The signature obtained from the singing progress is then appended to the TransactionSignature key. Mandatory return with the second call to the API. The value must be used exactly as it was returned.
TransactionSignature: should hold the resulting string from signing of the Transaction that was retrieved from the initial call to the api. SAMPLE CODE
TransactionID: is returned after the second call to the API, if it is successful and blockchain execution occurred, then it will return a TRANSACTION HASH for the execution on the blockchain. If it is a shared access operation, then it will return PENDING_AUTH as a string, which indicates the signers should approve the transaction on their wallets.
NetworkPassPhrase: is returned after the initial call. This is the network passphrase that would be used during signing of the transaction returned on the initial call to API. The string should be used exactly as it is.
DestinationFirstName/DestinationLastName/DestinationThumbnail: depicts the corresponding data of the destination specified.
SignatureRequired: value of 1 indicates that the transaction signature is required and the client must sign the attached transaction and append the transaction signature in the right key. Value 0 means the client does not need to sign the transaction.
Commit: This is for all shared access operations that are being submitted by an initiator. When an initiator submits a request request, it should initially be sent with Commit = 0, and when you have confirmed the information returned, you can alter the commit = 1 and send back the objects. This will commit the request and notify all stakeholders that the operation concerns. If you are doing operations from a wallet that is not a shared access wallet, this should be ignored.
Messages: This is a list of messages and warnings from the api service that concerns your transaction you submitted. Usually it is an array of messages to be shown to the user or ignored.
Specific Headers For Sending Payment: Must Be Available
-
X-TW-PUBLIC-KEY must be the public key of the wallet that you are sending the token from.
-
X-TW-SIGNER must be the signer of the sender wallet. Usually it is the same as the primary wallet public key. Primary wallet is the wallet that is default on the profile.
Json Body/Response:
{
Destination string `json:"destination"`
Memo string `json:"memo"`
AssetIssuer string `json:"assetIssuer"`
AssetCode string `json:"assetCode"`
Amount string `json:"amount"`
Transaction string `json:"transaction"`
TransactionSignature string `json:"transactionSignature"`
TransactionID string `json:"transactionId"`
NetworkPassPhrase string `json:"networkPassPhrase"`
DestinationFirstName string `json:"destinationFirstName"`
DestinationLastName string `json:"destinationLastName"`
DestinationThumbnail string `json:"destinationThumbnail"`
DestinationVerified int `json:"destinationVerified"`
SignatureRequired int `json:"signatureRequired"`
Commit int `json:"commit"`
Fee string `json:"fee"`
FeeAmount string `json:"feeAmount"`
AmountToPay string `json:"amountToPay"`
Messages [ ]string `json:"messages"`
}
INITIAL CALL:
Destination, AssetIssuer, AssetCode, Amount
SECOND CALL must include: TransactionSignature
Sample Send Token Code
Sign Transaction Function
- JavaScript1
- Python
- Go
import { Keypair, Transaction, Utils } from "stellar-sdk";
/**
* Sign a Stellar Transaction XDR (base64) with 1 secret key
* and return ONLY the base64-encoded signatures (not the signed envelope)
*
* @param {string} primarySecretKey
* @param {string} base64Txn
* @param {string} networkPassPhrase
* @returns {Promise<{primarySignature: string}>}
*/
export async function SignSendTokenBase64Txn(
primarySecretKey,
base64Txn,
networkPassPhrase
) {
try {
// ---- equivalent of keypair.ParseFull ----
const primaryKP = Keypair.fromSecret(primarySecretKey);
// ---- parse transaction from base64 XDR ----
const tx = new Transaction(base64Txn, networkPassPhrase);
// ---- calculate tx hash ----
const txHash = tx.hash(); // returns Buffer of the transaction hash
const primarySignature = primaryKP.sign(txHash).toString("base64");
return {
primarySignature,
};
} catch (err) {
throw err;
}
}
import base64
from stellar_sdk import Keypair, TransactionEnvelope
def sign_send_token_base64_txn(
primary_secret_key: str,
base64_txn: str,
network_passphrase: str
) -> dict:
"""
Sign a Stellar Transaction XDR (base64) with 1 secret key
and return ONLY the base64-encoded signatures (not the signed envelope)
Args:
primary_secret_key: The secret key to sign with
base64_txn: The base64-encoded transaction XDR
network_passphrase: The network passphrase
Returns:
Dictionary containing the primary signature
"""
try:
# Parse the keypair from secret key
primary_kp = Keypair.from_secret(primary_secret_key)
# Parse transaction from base64 XDR
tx_envelope = TransactionEnvelope.from_xdr(base64_txn, network_passphrase)
# Calculate transaction hash
tx_hash = tx_envelope.hash_meta()
# Sign the transaction hash
primary_signature = base64.b64encode(
primary_kp.sign(tx_hash)
).decode('utf-8')
return {
"primarySignature": primary_signature
}
except Exception as err:
raise err
package main
import (
"encoding/base64"
"fmt"
"github.com/stellar/go/keypair"
"github.com/stellar/go/txnbuild"
)
type SignatureResult struct {
PrimarySignature string `json:"primarySignature"`
}
// SignSendTokenBase64Txn signs a Stellar Transaction XDR (base64) with 1 secret key
// and returns ONLY the base64-encoded signatures (not the signed envelope)
func SignSendTokenBase64Txn(
primarySecretKey string,
base64Txn string,
networkPassPhrase string,
) (*SignatureResult, error) {
// Parse the keypair from secret key
primaryKP, err := keypair.ParseFull(primarySecretKey)
if err != nil {
return nil, fmt.Errorf("failed to parse keypair: %w", err)
}
// Parse transaction from base64 XDR
tx, err := txnbuild.TransactionFromXDR(base64Txn)
if err != nil {
return nil, fmt.Errorf("failed to parse transaction: %w", err)
}
// Get the generic transaction
genericTx, ok := tx.Transaction()
if !ok {
return nil, fmt.Errorf("failed to get transaction")
}
// Calculate transaction hash
txHash, err := genericTx.Hash(networkPassPhrase)
if err != nil {
return nil, fmt.Errorf("failed to hash transaction: %w", err)
}
// Sign the transaction hash
signature, err := primaryKP.Sign(txHash[:])
if err != nil {
return nil, fmt.Errorf("failed to sign transaction: %w", err)
}
// Encode signature to base64
primarySignature := base64.StdEncoding.EncodeToString(signature)
return &SignatureResult{
PrimarySignature: primarySignature,
}, nil
}
Send Token Function
- JavaScript
- Python
- Go
import axios from "axios";
import { Keypair, Networks, Transaction } from "stellar-sdk";
async function sendToken() {
try {
// === 1️⃣ CONFIGURATION ===
const API_URL = BASE_URL + "/v1/trovo-api/users/payment";
// Issuer (the account that created the asset)
const assetIssuer = "PUBLIC KEY OF ISSUER";
// Destination (the account that will receive the token)
const destination = "PUBLIC KEY OR WALLET ALIAS TO RECEIVE THE TOKEN";
// Asset details
const assetCode = "RDH";
const amount = "200000000";
const memo = "send RDH";
// === 2️⃣ INITIAL REQUEST (Create unsigned transaction) ===
const sendRequestBody = {
assetCode,
assetIssuer,
destination,
amount,
memo,
};
console.log("🔹 Sending initial payment request...");
const { data: unsignedTxResponse } = await axios.post(
API_URL,
sendRequestBody
);
// The response should contain: Transaction (XDR) and NetworkPassPhrase
const { transaction: base64Txn, networkPassPhrase } = unsignedTxResponse;
if (!base64Txn || !networkPassPhrase) {
throw new Error(
"Invalid response — missing transaction or networkPassPhrase"
);
}
console.log("✅ Received unsigned transaction from server.");
// === 3️⃣ SIGN THE TRANSACTION ===
const primaryKeypair = Keypair.fromSecret(USER_PRIMARY_SECRET);
const result = await SignSendTokenBase64Txn(
primaryKeypair.secret(),
base64Txn,
networkPassPhrase
);
// === 4️⃣ SECOND CALL — Send back signed transaction ===
const signedBody = {
...unsignedTxResponse,
transactionSignature: result.primarySignature,
commit: 0,
};
console.log("🔹 Sending signed transaction back for payment commit...");
const { data: finalResponse } = await axios.post(API_URL, signedBody);
// === 5️⃣ SUCCESS RESPONSE ===
console.log("✅ Token send completed successfully!");
console.log("Transaction ID:", finalResponse.transactionId);
console.log("Full response:", finalResponse);
} catch (error) {
console.error(
"❌ Error sending token:",
error.response?.data || error.message
);
}
}
// Run the function
sendToken();
import requests
from stellar_sdk import Keypair
def send_token():
try:
# === 1️⃣ CONFIGURATION ===
API_URL = f"{BASE_URL}/v1/trovo-api/users/payment"
# Issuer (the account that created the asset)
asset_issuer = "PUBLIC KEY OF ISSUER"
# Destination (the account that will receive the token)
destination = "PUBLIC KEY OR WALLET ALIAS TO RECEIVE THE TOKEN"
# Asset details
asset_code = "RDH"
amount = "200000000"
memo = "send RDH"
# === 2️⃣ INITIAL REQUEST (Create unsigned transaction) ===
send_request_body = {
"assetCode": asset_code,
"assetIssuer": asset_issuer,
"destination": destination,
"amount": amount,
"memo": memo,
}
print("🔹 Sending initial payment request...")
response = requests.post(API_URL, json=send_request_body)
unsigned_tx_response = response.json()
# The response should contain: Transaction (XDR) and NetworkPassPhrase
base64_txn = unsigned_tx_response.get("transaction")
network_passphrase = unsigned_tx_response.get("networkPassPhrase")
if not base64_txn or not network_passphrase:
raise Exception("Invalid response — missing transaction or networkPassPhrase")
print("✅ Received unsigned transaction from server.")
# === 3️⃣ SIGN THE TRANSACTION ===
primary_keypair = Keypair.from_secret(USER_PRIMARY_SECRET)
result = sign_send_token_base64_txn(
primary_keypair.secret,
base64_txn,
network_passphrase
)
# === 4️⃣ SECOND CALL — Send back signed transaction ===
signed_body = {
**unsigned_tx_response,
"transactionSignature": result["primarySignature"],
"commit": 0
}
print("🔹 Sending signed transaction back for payment commit...")
final_response = requests.post(API_URL, json=signed_body).json()
# === 5️⃣ SUCCESS RESPONSE ===
print("✅ Token send completed successfully!")
print(f"Transaction ID: {final_response.get('transactionId')}")
print(f"Full response: {final_response}")
except Exception as error:
print(f"❌ Error sending token: {error}")
if __name__ == "__main__":
send_token()
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/stellar/go/keypair"
)
type SendRequest struct {
AssetCode string `json:"assetCode"`
AssetIssuer string `json:"assetIssuer"`
Destination string `json:"destination"`
Amount string `json:"amount"`
Memo string `json:"memo"`
}
type TransactionResponse struct {
Transaction string `json:"transaction"`
NetworkPassPhrase string `json:"networkPassPhrase"`
TransactionID string `json:"transactionId"`
}
func sendToken() error {
// === 1️⃣ CONFIGURATION ===
apiURL := BASE_URL + "/v1/trovo-api/users/payment"
// Issuer (the account that created the asset)
assetIssuer := "PUBLIC KEY OF ISSUER"
// Destination (the account that will receive the token)
destination := "PUBLIC KEY OR WALLET ALIAS TO RECEIVE THE TOKEN"
// Asset details
assetCode := "RDH"
amount := "200000000"
memo := "send RDH"
// === 2️⃣ INITIAL REQUEST (Create unsigned transaction) ===
sendRequestBody := SendRequest{
AssetCode: assetCode,
AssetIssuer: assetIssuer,
Destination: destination,
Amount: amount,
Memo: memo,
}
fmt.Println("🔹 Sending initial payment request...")
jsonData, err := json.Marshal(sendRequestBody)
if err != nil {
return fmt.Errorf("failed to marshal request: %w", err)
}
resp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response: %w", err)
}
var unsignedTxResponse map[string]interface{}
if err := json.Unmarshal(body, &unsignedTxResponse); err != nil {
return fmt.Errorf("failed to unmarshal response: %w", err)
}
base64Txn, ok := unsignedTxResponse["transaction"].(string)
if !ok {
return fmt.Errorf("missing transaction in response")
}
networkPassPhrase, ok := unsignedTxResponse["networkPassPhrase"].(string)
if !ok {
return fmt.Errorf("missing networkPassPhrase in response")
}
fmt.Println("✅ Received unsigned transaction from server.")
// === 3️⃣ SIGN THE TRANSACTION ===
primaryKeypair, err := keypair.ParseFull(USER_PRIMARY_SECRET)
if err != nil {
return fmt.Errorf("failed to parse keypair: %w", err)
}
result, err := SignSendTokenBase64Txn(
primaryKeypair.Seed(),
base64Txn,
networkPassPhrase,
)
if err != nil {
return fmt.Errorf("failed to sign transaction: %w", err)
}
// === 4️⃣ SECOND CALL — Send back signed transaction ===
unsignedTxResponse["transactionSignature"] = result.PrimarySignature
unsignedTxResponse["commit"] = 0
fmt.Println("🔹 Sending signed transaction back for payment commit...")
signedData, err := json.Marshal(unsignedTxResponse)
if err != nil {
return fmt.Errorf("failed to marshal signed request: %w", err)
}
finalResp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(signedData))
if err != nil {
return fmt.Errorf("failed to send signed request: %w", err)
}
defer finalResp.Body.Close()
finalBody, err := io.ReadAll(finalResp.Body)
if err != nil {
return fmt.Errorf("failed to read final response: %w", err)
}
var finalResponse map[string]interface{}
if err := json.Unmarshal(finalBody, &finalResponse); err != nil {
return fmt.Errorf("failed to unmarshal final response: %w", err)
}
// === 5️⃣ SUCCESS RESPONSE ===
fmt.Println("✅ Token send completed successfully!")
fmt.Printf("Transaction ID: %v\n", finalResponse["transactionId"])
fmt.Printf("Full response: %+v\n", finalResponse)
return nil
}
func main() {
if err := sendToken(); err != nil {
fmt.Printf("❌ Error sending token: %v\n", err)
}
}
NOTE: If sender wallet is a shared access wallet with approvers, please ensure to set commit = 1 so as to commit the transaction for approval.