The is a common place where the Internet can find information about an organization’s Stellar integration. Regardless of which type of transfer we want to use (SEP-6 or SEP-24), we'll need to start with SEP-1.
For anchors, we’re interested in the CURRENCIES they issue, the TRANSFER_SERVER and/or TRANSFER_SERVER_SEP0024 keywords that indicate if the anchor supports SEP-6, SEP-24, or both, and the WEB_AUTH_ENDPOINT which allows a wallet to set up an authenticated user session.
BasicPay is interoperating with the testing anchor located at testanchor.stellar.org and you can view its toml file .
/src/lib/stellar/sep1.js
import { StellarTomlResolver } from "stellar-sdk";
// Fetches and returns the stellar.toml file hosted by a provided domain.
export async function fetchStellarToml(domain) {
let stellarToml = await StellarTomlResolver.resolve(domain);
return stellarToml;
}
Source:
Strictly speaking, the StellarTomlResolver function from the JavaScript SDK is the only function we need to retrieve and use the information provided by the anchor (heck, we could even just write our own fetch-based function, and bypass the SDK altogether). However, we've created quite a few "helper" functions to make the rest of our queries a bit more verbose and clear as to what we're looking for from the anchor server. Make sure to check out the sep1.js source file linked above!
Using the stellar.toml information for an asset with a home_domain, we can display to the user some options (depending on the available infrastructure). We'll start with SEP-10 authentication.
SEP-10: Stellar Web Authentication
Similar to the SEP-1 information, both SEP-6 and SEP-24 protocols make use of SEP-10 for authentication with the user. The user must prove they own the account before they can withdraw or deposit any assets as part of SEP-10: Stellar Web Authentication.
Since we have the stellar.toml file information already, we can use that to display some interactive elements to the user.
NOTE
The /src/routes/dashboard/transfers/+page.svelte is doing a lot of work throughout these sections, and we are chopping it up in various ways for display as part of this tutorial. For a full picture of this file, please remember to check the source code.
// Some code
<script>
// `export let data` allows us to pull in any parent load data for use here.
/** @type {import('./$types').PageData} */
export let data;
// We import some of our `$lib` functions
import { fetchStellarToml } from "$lib/stellar/sep1";
// An object to easily and consistently class badges based on the status of
// a user's authentication token for a given anchor.
const authStatusClasses = {
unauthenticated: "badge badge-error",
auth_expired: "badge badge-warning",
auth_valid: "badge badge-success",
};
// A simple function that checks whether a user has a SEP-10 authentication token stored for an anchor, and if it is expired or not.
const getAuthStatus = (homeDomain) => {
if ($webAuthStore[homeDomain]) {
let token = $webAuthStore[homeDomain];
if (webAuthStore.isTokenExpired(token)) {
return "auth_expired";
} else {
return "auth_valid";
}
} else {
return "unauthenticated";
}
};
</script>
<!-- HTML has been omitted from this tutorial. Please check the source file -->
/src/routes/dashboard/transfers/+page.svelte
<script>
/* This <script> tag has been abbreviated for simplicity */
import { getChallengeTransaction } from "$lib/stellar/sep10";
// We import any Svelte components we will need
import ConfirmationModal from "$lib/components/ConfirmationModal.svelte";
// The `open` Svelte context is used to open the confirmation modal
import { getContext } from "svelte";
const { open } = getContext("simple-modal");
// Define some component variables that will be used throughout the page
let challengeXDR = "";
let challengeNetwork = "";
let challengeHomeDomain = "";
// Requests a challenge transaction from a SEP-10 server, and presents it to the user for pincode verification
const auth = async (homeDomain) => {
// Request the challenge transaction, expecting back the XDR string
let { transaction, network_passphrase } = await getChallengeTransaction({
publicKey: data.publicKey,
homeDomain: homeDomain,
});
// Set the component variables to hold the transaction details
challengeXDR = transaction;
challengeNetwork = network_passphrase;
challengeHomeDomain = homeDomain;
// Open the confirmation modal for the user to confirm or reject the
// challenge transaction. We provide our customized `onAuthConfirm`
// function to be called as part of the modal's confirming process.
open(ConfirmationModal, {
title: "SEP-10 Challenge Transaction",
body: "Please confirm your ownership of this account by signing this challenge transaction. This transaction has already been checked and verified and everything looks good from what we can tell. Feel free to double-check that everything lines up with the SEP-10 specification yourself, though.",
transactionXDR: challengeXDR,
transactionNetwork: challengeNetwork,
onConfirm: onAuthConfirm,
});
};
/* ... */
</script>
<!-- HTML has been omitted from this tutorial. Please check the source file -->
As part of the auth function, BasicPay makes a GET request with an account param (the public key of the user) to the anchor, which sends back a Stellar transaction signed by the server's signing key (called a challenge transaction) with an invalid sequence number so it couldn't actually do anything if it were accidentally submitted to the network.
/src/lib/stellar/sep10.js
import { Utils } from "stellar-sdk";
import { fetchStellarToml } from "$lib/stellar/sep1";
// Requests, validates, and returns a SEP-10 challenge transaction from an anchor server.
export async function getChallengeTransaction({ publicKey, homeDomain }) {
let { WEB_AUTH_ENDPOINT, TRANSFER_SERVER, SIGNING_KEY } =
await fetchStellarToml(homeDomain);
// In order for the SEP-10 flow to work, we must have at least a server
// signing key, and a web auth endpoint (which can be the transfer server as
// a fallback)
if (!WEB_AUTH_ENDPOINT || !TRANSFER_SERVER || !SIGNING_KEY) {
throw error(500, {
message:
"could not get challenge transaction (server missing toml entry or entries)",
});
}
// Request a challenge transaction for the users's account
let res = await fetch(
`${WEB_AUTH_ENDPOINT || TRANSFER_SERVER}?${new URLSearchParams({
// Possible parameters are `account`, `memo`, `home_domain`, and
// `client_domain`. For our purposes, we only supply `account`.
account: publicKey,
})}`,
);
let json = await res.json();
// Validate the challenge transaction meets all the requirements for SEP-10
validateChallengeTransaction({
transactionXDR: json.transaction,
serverSigningKey: SIGNING_KEY,
network: json.network_passphrase,
clientPublicKey: publicKey,
homeDomain: homeDomain,
});
return json;
}
// Validates the correct structure and information in a SEP-10 challenge transaction.
function validateChallengeTransaction({
transactionXDR,
serverSigningKey,
network,
clientPublicKey,
homeDomain,
clientDomain,
}) {
if (!clientDomain) {
clientDomain = homeDomain;
}
try {
// Use the `readChallengeTx` function from Stellar SDK to read and
// verify most of the challenge transaction information
let results = Utils.readChallengeTx(
transactionXDR,
serverSigningKey,
network,
homeDomain,
clientDomain,
);
// Also make sure the transaction was created for the correct user
if (results.clientAccountID === clientPublicKey) {
return;
} else {
throw error(400, {
message: "clientAccountID does not match challenge transaction",
});
}
} catch (err) {
throw error(400, { message: JSON.stringify(err) });
}
}
/src/routes/dashboard/transfers/+page.svelte
<script>
/* ... */
// We import any stores we will need to read and/or write
import { invalidateAll } from "$app/navigation";
import { walletStore } from "$lib/stores/walletStore";
import { webAuthStore } from "$lib/stores/webAuthStore";
// We import some of our `$lib` functions
import {
getChallengeTransaction,
submitChallengeTransaction,
} from "$lib/stellar/sep10";
// Takes an action after the pincode has been confirmed by the user on a SEP-10 challenge transaction.
const onAuthConfirm = async (pincode) => {
// Sign the transaction with the user's keypair
let signedTransaction = await walletStore.sign({
transactionXDR: challengeXDR,
network: challengeNetwork,
pincode: pincode,
});
// Submit the signed tx to the SEP-10 server, and get the JWT token back
let token = await submitChallengeTransaction({
transactionXDR: signedTransaction.toXDR(),
homeDomain: challengeHomeDomain,
});
// Add the token to our store
webAuthStore.setAuth(challengeHomeDomain, token);
// Reload any relevant `load()` functions (i.e., refresh the page)
invalidateAll();
};
/* ... */
</script>
<!-- HTML has been omitted from this tutorial. Please check the source file -->
The submitChallengeTransaction function is quite simple. We take the transaction (in XDR format) and the domain name, and submit it to the relevant WEB_AUTH_ENDPOINT provided by the home domain's stellar.toml file.
/src/lib/stellar/sep10.js
// Submits a SEP-10 challenge transaction to an authentication server and returns the SEP-10 token.
export async function submitChallengeTransaction({
transactionXDR,
homeDomain,
}) {
let webAuthEndpoint = await getWebAuthEndpoint(homeDomain);
if (!webAuthEndpoint)
throw error(500, {
message: "could not authenticate with server (missing toml entry)",
});
let res = await fetch(webAuthEndpoint, {
method: "POST",
mode: "cors",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ transaction: transactionXDR }),
});
let json = await res.json();
if (!res.ok) {
throw error(400, { message: json.error });
}
return json.token;
}
Like so much of our BasicPay application, the various authentication tokens the user may have accumulated over time are stored in the browser's localStorage. There's not much special about this particular store, but here's how we put it together:
/src/lib/stores/webAuthStore.js
import { get } from "svelte/store";
import { persisted } from "svelte-local-storage-store";
import { Buffer } from "buffer";
function createWebAuthStore() {
const { subscribe, update } = persisted("bpa:webAuthStore", {});
return {
subscribe,
// Stores a JWT authentication token associated with a home domain server.
setAuth: (homeDomain, token) =>
update((store) => {
return {
...store,
[homeDomain]: token,
};
}),
// Determine whether or not a JSON web token has an expiration date in the future or in the past.
isTokenExpired: (homeDomain) => {
let token = get(webAuthStore)[homeDomain];
if (token) {
let payload = JSON.parse(
Buffer.from(token.split(".")[1], "base64").toString(),
);
let timestamp = Math.floor(Date.now() / 1000);
return timestamp > payload.exp;
} else {
return undefined;
}
},
};
}
export const webAuthStore = createWebAuthStore();
Now that we have successfully authenticated our user with an asset anchor, we can display and process the various transfer capabilities of the anchor in question. We'll begin with SEP-6, since that will lay the groundwork for SEP-24 to follow.
SEP-6: Deposit and Withdrawal API
SEP-6 allows wallets and other clients to interact with anchors directly without the user needing to leave the wallet to go to the anchor’s site. In this integration, a user’s KYC information is gathered and handled by the wallet and submitted to the anchor on behalf of the user.
Before we can ask anything about how to make a SEP-6 transfer, we have to figure out where to discover that information. Fortunately, the SEP-1 protocol describes standardized fields to find out what we need.
/src/lib/stellar/sep1.js
// Fetches and returns the endpoint used for SEP-6 transfer interactions.
export async function getTransferServerSep6(domain) {
let { TRANSFER_SERVER } = await fetchStellarToml(domain);
return TRANSFER_SERVER;
}
NOTE
At this time, BasicPay only supports the deposit and withdraw transfer methods. A future version of this tutorial will incorporate the *-exchange transfer methods.
/src/lib/stellar/sep6.js
import { getTransferServerSep6 } from "$lib/stellar/sep1";
// Fetches and returns basic information about what the SEP-6 transfer server suppports.
export async function getSep6Info(domain) {
let transferServer = await getTransferServerSep6(domain);
let res = await fetch(`${transferServer}/info`);
let json = await res.json();
return json;
}
The user can then initiate one of the transfer methods (in BasicPay, only deposits and withdraws are supported) by clicking the “Deposit” or “Withdraw” button underneath a supported asset.
/src/routes/dashboard/transfers/+page.svelte
<script>
/* This <script> tag has been abbreviated for simplicity */
// We import things from external packages that will be needed
import { LogInIcon, LogOutIcon } from "svelte-feather-icons";
// We import some of our `$lib` functions
import { getSep6Info } from "$lib/stellar/sep6";
// The `open` Svelte context is used to open the confirmation modal
import { getContext } from "svelte";
const { open } = getContext("simple-modal");
/* ... */
</script>
<!-- HTML has been omitted from this tutorial. Please check the source file -->
The above buttons will use the launchTransferModalSep6 function to display the modal to the user. Here's how it's defined in that same file.
/src/routes/dashboard/transfers/+page.svelte
<script>
/* This <script> tag has been abbreviated for simplicity */
// We import any Svelte components we will need
import TransferModalSep6 from "./components/TransferModalSep6.svelte";
// Launch the SEP-6 modal to begin the transfer process and gather information from the user.
const launchTransferModalSep6 = ({
homeDomain,
assetCode,
assetIssuer,
endpoint,
sep6Info,
}) => {
// Open the SEP-6 transfer modal, supplying the relevant props for our
// desired type of transfer.
open(TransferModalSep6, {
homeDomain: homeDomain,
assetIssuer: assetIssuer,
transferData: {
endpoint: endpoint,
},
formData: {
account: data.publicKey,
asset_code: assetCode,
},
sep6Info: sep6Info,
// This `submitPayment` function is described later on.
submitPayment: submitPayment,
});
};
</script>
<!-- HTML has been omitted from this tutorial. Please check the source file -->
Once launched, the TransferModalSep6 will walk the user through a "wizard" to gather all the required information and ultimately create the transfer.
BasicPay prompts the user to input additional information such as transfer type, destination, and amount. Some of this is prepopulated based on which button the user clicked. However, the user can change any of the fields if they so choose.
To find out what infrastructure the anchor has made available for us to use, we need to query the anchor's SEP-1 stellar.toml file for the KYC_SERVER field. If this is not defined, BasicPay will fallback to using the TRANSFER_SERVER for these requests.
/src/lib/stellar/sep1.js
// Fetches and returns the endpoint used for SEP-12 KYC interactions.
export async function getKycServer(domain) {
let { KYC_SERVER, TRANSFER_SERVER } = await fetchStellarToml(domain);
// If `KYC_SERVER` is undefined in the domain's TOML file, `TRANSFER_SERVER`
// will be used
return KYC_SERVER ?? TRANSFER_SERVER;
}
Our SEP-6 modal then queries the anchor’s SEP-12 endpoint for the required KYC fields with a GET request, and we present these fields for the user to complete.
import { getKycServer } from "$lib/stellar/sep1";
// Sends a `GET` request to query KYC status for a customer, returns current status of KYC submission
export async function getSep12Fields({ authToken, homeDomain }) {
let kycServer = await getKycServer(homeDomain);
let res = await fetch(`${kycServer}/customer`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`,
},
});
let json = await res.json();
return json;
}
Now that the user has provided the necessary information for the KYC requirements of the anchor, we can submit them to the anchor's KYC server with a PUT request.
/src/lib/stellar/sep12.js
// Sends a `PUT` request to the KYC server, submitting the supplied fields for the customer's record.
export async function putSep12Fields({ authToken, fields, homeDomain }) {
let kycServer = await getKycServer(homeDomain);
let res = await fetch(`${kycServer}/customer`, {
method: "PUT",
mode: "cors",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`,
},
body: JSON.stringify(fields),
});
let json = await res.json();
return json;
}
BasicPay receives back from the anchor a status message for the user to see. Once the status message is ACCEPTED, we can finally submit the actual transfer request!
We submit a GET request to the URL with our authorization token in the headers, and the anchor takes it from there!
/src/lib/stellar/sep6.js
// Initiates a transfer using the SEP-6 protocol.
export async function initiateTransfer6({
authToken,
endpoint,
formData,
domain,
}) {
let transferServer = await getTransferServerSep6(domain);
let searchParams = new URLSearchParams(formData);
let res = await fetch(`${transferServer}/${endpoint}?${searchParams}`, {
method: "GET",
mode: "cors",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`,
},
});
let json = await res.json();
if (!res.ok) {
throw error(res.status, {
message: json.error,
});
} else {
return json;
}
}
We then store the details of the transfer in the transfersStore store and display the transfer server's response to the user. We then wait for them to close the modal.
NOTE
The only reason we're storing anything about the transfer in BasicPay is to help us keep track of which anchors the user has initiated transfers with. Otherwise, we wouldn't be able query for a transfer history (like on the /dashboard page).
In a withdrawal transaction, BasicPay will also build and present to the user a Stellar transaction for them to sign with their pincode. Here we will finally get to move back to our "regular" modal that is so good at so many things!
/src/routes/dashboard/transfers/+page.svelte
<script>
/* This <script> tag has been abbreviated for simplicity */
// Define some component variables that will be used throughout the page
let paymentXDR = "";
let paymentNetwork = "";
// After a withdraw transaction has been presented to the user, and they've confirmed with the correct pincode, sign and submit the transaction to the Stellar network.
const onPaymentConfirm = async (pincode) => {
// Use the walletStore to sign the transaction
let signedTransaction = await walletStore.sign({
transactionXDR: paymentXDR,
network: paymentNetwork,
pincode: pincode,
});
// Submit the transaction to the Stellar network
await submit(signedTransaction);
};
// Builds a Stellar payment to present to the user which will complete a transfer to the Anchor.
let submitPayment = async ({
withdrawDetails,
assetCode,
assetIssuer,
amount,
}) => {
let { transaction, network_passphrase } = await createPaymentTransaction({
source: data.publicKey,
destination: withdrawDetails.account_id,
asset: `${assetCode}:${assetIssuer}`,
amount: amount,
memo: withdrawDetails.memo
? Buffer.from(withdrawDetails.memo, "base64")
: undefined,
});
// Set the component variables to hold the transaction details
paymentXDR = transaction;
paymentNetwork = network_passphrase;
// We close the SEP-6 modal, and open the regular confirmation modal
close();
open(ConfirmationModal, {
transactionXDR: paymentXDR,
transactionNetwork: paymentNetwork,
onConfirm: onPaymentConfirm,
});
};
/* ... */
</script>
<!-- HTML has been omitted from this tutorial. Please check the source file -->
SEP-24: Hosted Deposit and Withdrawal
SEP-24 provides a standard way for wallets and anchors to interact by having the user open a webview hosted by an anchor to collect and handle KYC information. In this integration, a user's KYC information is gathered and handled entirely by the anchor. For the most part, after the anchor's webview has opened, BasicPay will have little knowledge about what's going on.
INFO
Before we can ask anything about how to make a SEP-24 transfer, we have to figure out where to discover that information. Fortunately, the SEP-1 protocol describes standardized fields to find out what we need.
/src/lib/stellar/sep1.js
// Fetches and returns the endpoint used for SEP-24 transfer interactions.
export async function getTransferServerSep24(domain) {
let { TRANSFER_SERVER_SEP0024 } = await fetchStellarToml(domain);
return TRANSFER_SERVER_SEP0024;
}
Our application will request the /info endpoint from the anchor's transfer server to understand the supported transfer methods (deposit, withdraw) and available endpoints, as well as additional features that may be available during transfers.
// Fetches and returns basic information about what the SEP-24 transfer server supports.
export async function getSep24Info(domain) {
let transferServerSep24 = await getTransferServerSep24(domain);
let res = await fetch(`${transferServerSep24}/info`);
let json = await res.json();
if (!res.ok) {
throw error(res.status, {
message: json.error,
});
} else {
return json;
}
}
Now that we have all the SEP-24 information the anchor has made available to us, it's up to the user to actually begin the initiation process. In BasicPay, they do that by simply clicking a button that will then trigger the launchTransferWindowSep24 function.
NOTE
/src/routes/dashboard/transfers/+page.svelte
<script>
/* This <script> tag has been abbreviated for simplicity */
// Launch the interactive SEP-24 popup window for the user to interact directly with the anchor to begin a transfer.
const launchTransferWindowSep24 = async ({
homeDomain,
assetCode,
assetIssuer,
endpoint,
}) => {
// We initiate the transfer from the SEP-24 server, and get the
// interactive URL back from it
let { url } = await initiateTransfer24({
authToken: $webAuthStore[homeDomain],
endpoint: endpoint,
homeDomain: homeDomain,
urlFields: {
asset_code: assetCode,
account: data.publicKey,
},
});
/* ... */
};
</script>
<!-- HTML has been omitted from this tutorial. Please check the source file -->
BasicPay then initiates a transfer method by sending a POST request to either the “SEP-24 Deposit” or “SEP-24 Withdraw” endpoint. The anchor then sends an interactive URL that BasicPay will open as a popup for the user to complete and confirm the transfer.
/src/lib/stellar/sep24.js
// Initiates a transfer using the SEP-24 protocol.
export async function initiateTransfer24({
authToken,
endpoint,
homeDomain,
urlFields = {},
}) {
let transferServerSep24 = await getTransferServerSep24(homeDomain);
let res = await fetch(
`${transferServerSep24}/transactions/${endpoint}/interactive`,
{
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`,
},
body: JSON.stringify(urlFields),
},
);
let json = await res.json();
if (!res.ok) {
throw error(res.status, {
message: json.error,
});
} else {
return json;
}
}
BasicPay doesn't really need (or want) to know everything that's happening between the user and the anchor during a SEP-24 transfer. However, we do want to know when the interaction is over, since we may need to take some action at that point. So, we add a callback to the interactive URL and open the popup window.
CAUTION
/src/routes/dashboard/transfers/+page.svelte
<script>
/* This <script> tag has been abbreviated for simplicity */
// Launch the interactive SEP-24 popup window for the user to interact directly with the anchor to begin a transfer.
const launchTransferWindowSep24 = async ({
homeDomain,
assetCode,
assetIssuer,
endpoint,
}) => {
/* ... */
// We add our callback method to the end of the URL and launch the popup
// window for the user to interact with
let interactiveUrl = `${url}&callback=postMessage`;
let popup = window.open(interactiveUrl, "bpaTransfer24Window", "popup");
// We listen for the callback `message` from the popup window
window.addEventListener("message", async (event) => {
/* ... */
});
};
</script>
<!-- HTML has been omitted from this tutorial. Please check the source file -->
Once the user is finished with the interactive window from the anchor, they'll be brought back to BasicPay. We store the details of the transfer in the transfersStore store (remember, this is just so we can track which anchors to query for transfers later on).
/src/routes/dashboard/transfers/+page.svelte
<script>
/* This <script> tag has been abbreviated for simplicity */
// Launch the interactive SEP-24 popup window for the user to interact directly with the anchor to begin a transfer.
const launchTransferWindowSep24 = async ({
homeDomain,
assetCode,
assetIssuer,
endpoint,
}) => {
/* ... */
// We listen for the callback `message` from the popup window
window.addEventListener("message", async (event) => {
// Close the interactive window if it's not already
popup?.close();
// Store the transfer in the browser's localStorage
transfers.addTransfer({
homeDomain: homeDomain,
protocol: "sep24",
assetCode: assetCode,
transferID: event.data.transaction.id,
});
/* ... */
});
};
</script>
<!-- HTML has been omitted from this tutorial. Please check the source file -->
In a withdrawal transaction, BasicPay will also build and present to the user a Stellar transaction for them to sign with their pincode.
/src/routes/dashboard/transfers/+page.svelte
<script>
/* This <script> tag has been abbreviated for simplicity */
// Launch the interactive SEP-24 popup window for the user to interact directly with the anchor to begin a transfer.
const launchTransferWindowSep24 = async ({
homeDomain,
assetCode,
assetIssuer,
endpoint,
}) => {
/* ... */
// We listen for the callback `message` from the popup window
window.addEventListener("message", async (event) => {
/* ... */
// If the user has requested a withdraw with the anchor, they will
// need to submit a Stellar transaction that sends the asset from
// the user's account to an account controlled by the anchor.
if (event.data.transaction.kind === "withdrawal") {
// Generate a transaction with the necessary details to complete
// the transfer
let { transaction, network_passphrase } =
await createPaymentTransaction({
source: data.publicKey,
destination: event.data.transaction.withdraw_anchor_account,
asset: `${assetCode}:${assetIssuer}`,
amount: event.data.transaction.amount_in,
memo: Buffer.from(event.data.transaction.withdraw_memo, "base64"),
});
// Set the component variables to hold the transaction details
paymentXDR = transaction;
paymentNetwork = network_passphrase;
// Open the confirmation modal for the user to confirm or reject
// the Stellar payment transaction. We provide our customized
// `onPaymentConfirm` function to be called as part of the
// modal's confirming process.
open(ConfirmationModal, {
transactionXDR: paymentXDR,
transactionNetwork: paymentNetwork,
onConfirm: onPaymentConfirm,
});
}
});
};
</script>
<!-- HTML has been omitted from this tutorial. Please check the source file -->
Prompt for authentication
Source:
Requesting a challenge transaction
Now, when the user clicks the "authenticate" button, it triggers the auth function.
Source:
Source:
Sign and submit the challenge transaction
In response, the user signs the transaction. You may have noticed we present this challenge transaction to the user with our regular confirmation modal. Once they've signed the transaction, the application sends it back to the anchor with a POST request. If the signature checks out, the success response will contain a , which BasicPay stores in the webAuthStore store to use for future interactions with the anchor.
Source:
Source:
About the webAuthStore store
Source:
Find the anchor's TRANSFER_SERVER
Source:
Get /info
Now that we know where the transfer server is located, BasicPay needs to fetch the /info endpoint from the anchor's transfer server to understand the supported transfer methods () and available endpoints, as well as additional features that may be available during transfers.
Source:
Display interactive elements
Since many of the SEP-6 (and SEP-24) endpoints require authentication, we wait until our user is before we display what kinds of transfers are available. When they have a valid authentication token, we can display some buttons the user can use to begin a transfer.
sep6
Source:
A special SEP-6 modal component
If you recall back to our , we designed our modal component to be useful for anything we might require. Well, that's almost true. The fact is that SEP-6 interactions are just plain complex. To facilitate that complexity, we've created a purpose-built SEP-6 transfer modal. There is so much to it, that we couldn't possibly cover everything it does here. However, we'll cover the main bits and link to the relevant source files.
The modal component itself has been broken into several smaller Svelte components. Check out this source file to start looking through how we've put it together:
Launching the SEP-6 modal
Source:
Modal step 1: Transfer details
We'll spare the code sample in this section, since it's mostly Svelte things going on. You can view the source here:
Modal step 2: Gather KYC information
Source:
/src/lib/stellar/sep12.js
Source:
Again, the presentation of the fields the user must complete is more on the Svelte side of things, so we won't share those details here. However, the source for this component is available here:
Modal step 3: Put KYC fields and report status
Source:
This component of the SEP-6 modal, like most of them, is almost entirely Svelte-related. So as to keep this tutorial (somewhat) uncluttered, we'll refer you to the source for the component, which you can find here:
Modal step 4: Submit transfer
BasicPay makes this request by taking all the fields that have been collected during this process and wrapping them into a URL that contains query parameters:
Source:
(Sometimes) Modal step 5: Send a Stellar payment
Source:
Remember, SEP-24 depends on . Everything below assumes the user has successfully authenticated with the anchor server, and BasicPay has access to an unexpired authentication token to send with its requests.
Find the anchor's TRANSFER_SERVER_SEP0024
Source:
Get /info
/src/lib/stellar/sep24.js
Source:
The user clicks "deposit" or "withdraw"
This file was pretty heavily covered in the . We'll be presenting here the additions we make to this file, though we won't repeat things we've already covered. Remember to check the source files for the full picture.
Source:
Retrieve the interactive URL
Source:
Launch the popup window and listen for a callback
Since BasicPay is an entirely client-side application, we can't provide a callback as a URL. So, we are using a postMessage callback. For more information on the details of these callback options, check out .