Send or Receive Funds

  • Deposit: Monitor the funds account for USDC payments from MoneyGram, matching the memo provided during initiation.
  • Withdrawal: Send USDC to MoneyGram’s specified Stellar account with the provided memo.

Send / Deposit

from stellar_sdk import (
    Server, TransactionBuilder, Network, Asset, IdMemo
)

submit_payment(
   destination=response_body["transaction"]["withdraw_anchor_account"],
   memo=response_body["transaction"]["withdraw_memo"],
   amount=response_body["transaction"]["amount_in"]
)

def submit_payment(destination: str, memo: str, amount: str):
   server = Server()
   account = server.load_account(FUNDS_STELLAR_KEYPAIR.public_key)
   transaction = TransactionBuilder(
       source_account=account,
       network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE,
       base_fee=10000  # this is 0.001 XLM
   ).append_payment_op(
       destination=destination,
       asset=Asset(ASSET_CODE, ASSET_ISSUER),
       amount=amount,
   ).add_memo(
       IdMemo(int(memo))
   ).build()
   transaction.sign(FUNDS_STELLAR_KEYPAIR)
   response = server.submit_transaction(transaction)
   print(f"Stellar-generated transaction ID: {response['id']}")
import { Wallet, IssuedAssetId } from "@stellar/typescript-wallet-sdk";
import { Horizon } from "@stellar/stellar-sdk";

const wallet = Wallet.TestNet();

// This creates a Stellar instance to manage the connection with Horizon.
const stellar = wallet.stellar();

// Let's make sure Anchor supports the token we want to withdraw.
const assetCode = "USDC";
const info = await anchor.getInfo();
const currency = info.currencies.find(({ code }) => code === assetCode);
if (!currency?.code || !currency?.issuer) {
  throw new Error(
    `Anchor does not support ${assetCode} asset or is not correctly configured on TOML file`,
  );
}

// This creates the Stellar asset object which we'll need while creating the
// transfer withdrawal transaction below.
const asset = new IssuedAssetId(currency.code, currency.issuer);

// This creates a transaction builder which we'll be using to assemble
// our transfer withdrawal transaction as shown below.
const txBuilder = await stellar.transaction({
  sourceAddress: FUNDS_STELLAR_KEYPAIR,
  baseFee: 10000, // this is 0.001 XLM
  timebounds: 180, // in seconds
});

// We can use the transaction object received on the onMessage callback from
// the watcher, or, we can also fetch the transaction object using either
// getTransactionBy or getTransactionsForAsset as illustrated in previous step.
onMessage: (transaction) => {
  if (transaction.status === "pending_user_transfer_start") {
    // Use the builder to assemble the transfer transaction. Behind the scenes
    // it extracts the Stellar account (withdraw_anchor_account), memo (withdraw_memo)
    // and amount (amount_in) to use in the Stellar payment transaction that will
    // be submitted to the Stellar network.
    const transferTransaction = txBuilder
      .transferWithdrawalTransaction(transaction, asset)
      .build();

    // Signs it with the source (funds) account key pair
    transferTransaction.sign(FUNDS_STELLAR_KEYPAIR);

    // Finally submits it to the stellar network. This stellar.submitTransaction()
    // function handles '504' status codes (timeout) by keep retrying it until
    // submission succeeds or we get a different error.
    try {
      const response = await stellar.submitTransaction(transferTransaction);
      console.log("Stellar-generated transaction ID: ", response.id);
    } catch (error) {
      /*
        In case it's not a 504 (timeout) error, the application could try some
        resolution strategy based on the error kind.

        On Stellar docs you can find a page dedicated to error handling:
        https://developers.stellar.org/docs/learn/encyclopedia/errors-and-debugging/error-handling

        And status/result codes:
        https://developers.stellar.org/docs/data/apis/horizon/api-reference/errors
      */

      // Let's illustrate here how we could handle an 'invalid sequence number' error.

      // We can access all possible result codes through Horizon's API.
      const sdkResultCodes = Horizon.HorizonApi.TransactionFailedResultCodes;

      // We can access error's response data to check for useful error details.
      const errorData = error.response?.data;
      /*
        Sample of errorData object returned by the Wallet SDK:
        {
          type: 'https://stellar.org/horizon-errors/transaction_failed',
          title: 'Transaction Failed',
          status: 400,
          detail: 'The transaction failed when submitted to the stellar network.
            The `extras.result_codes` field on this response contains further details.
            Descriptions of each code can be found at:
            https://developers.stellar.org/docs/data/apis/horizon/api-reference/errors/http-status-codes/horizon-specific/transaction-failed',
          extras: {
            envelope_xdr: 'AAAAAgAAAADBjF7n9gfByOwlnyaJH...k4BRagf/////////8AAAAAAAAAAA==',
            result_codes: { transaction: 'tx_bad_seq' },
            result_xdr: 'AAAAAAAAAGT////6AAAAAA=='
          }
        }
      */

      /*
        Example scenario: invalid sequence numbers.

        These errors typically occur when you have an outdated view of an account.
        This could be because multiple devices are using this account, you have
        concurrent submissions happening, or other reasons. The solution is relatively
        simple: retrieve the account details and try again with an updated sequence number.
      */
      if (
        errorData?.status == 400 &&
        errorData?.extras?.result_codes?.transaction ===
          sdkResultCodes.TX_BAD_SEQ
      ) {
        // Creating a new transaction builder means retrieving an updated sequence number.
        const txBuilder2 = await stellar.transaction({
          sourceAddress: FUNDS_STELLAR_KEYPAIR,
          baseFee: 10000,
          timebounds: 180,
        });

        // ...

        // Repeat all the steps until submitting the transaction again.

        // ...

        const response2 = await stellar.submitTransaction(transferTransaction);
        console.log(
          "Stellar-generated transaction ID on retry: ",
          response2.id,
        );

        // The application should take care to not resubmit the same transaction
        // blindly with an updated sequence number as it could result in more than
        // one payment being made when only one was intended.
      }
    }
  }
};

Receive / Withdrawal

from stellar_sdk import Server
from .queries import get_transaction_by_memo

def stream_payments(account: str, cursor: str):
    s = Server()
    payments = s.payments().for_account(account).join("transactions")
    for payment in payments.cursor(cursor).stream():
        if (
            payment["type"] != "payment"
            or payment["from"] == account
            or payment["asset_type"] == "native"
            or payment["asset_code"] != ASSET_CODE
            or payment["asset_issuer"] != ASSET_ISSUER
        ):
            continue
        transaction = get_transaction_by_memo(
            payment["transaction"]["memo"],
            payment["transaction"]["memo_type"]
        )  # DB query
        if not transaction:
            continue
        print(
            f"Payment for deposit transaction {transaction.id} "
            "matched with Stellar transaction "
            f"{payment['transaction']['id']}"
        )
// The Wallet SDK does not support payments streaming yet so let's build
// it using the underlying Horizon SDK
import { Horizon } from "@stellar/stellar-sdk";
import { getTransactionByMemo } from "./queries";

const streamPayments = (account: string, cursor: string) => {
  const server = new Horizon.Server("https://horizon-testnet.stellar.org");
  server
    .payments()
    .forAccount(account)
    .join("transactions")
    .cursor(cursor)
    .stream({
      onmessage: (payment) => {
        if (
          payment["type"] !== "payment" ||
          payment["from"] === account ||
          payment["asset_type"] === "native" ||
          payment["asset_code"] !== ASSET_CODE ||
          payment["asset_issuer"] !== ASSET_ISSUER
        ) {
          return;
        }

        const transaction = getTransactionByMemo(
          payment["transaction_attr"]["memo"],
          payment["transaction_attr"]["memo_type"],
        ); // this is your own DB query function

        if (!transaction) {
          return;
        }

        console.log(
          `Payment for deposit transaction ${transaction.id}`,
          `matched with Stellar transaction `,
          `${payment["transaction_attr"]["id"]}`,
        );
      },
      onerror: (error) => {
        // handle error
      },
    });
};