Skip to main content

PaymentLinksService Documentation

The PaymentLinksService facilitates the creation and management of payment links within the Stratosphere Network wallet module. It allows users to request payments from others and to create links for sending funds to specific individuals or to anyone who can claim them. The service also provides redirect URL registration capabilities for custom payment flows.

Core Concepts

The service revolves around several key functionalities:

  1. Payment Requests: These are links generated by a user who wishes to receive a specific amount of a particular token on a designated blockchain. Another user can then use this link (or its associated nonce) to fulfill the payment request.
  2. Send Links: These are links created by a user who intends to send funds. They can be:
    • Specific Send Links: Targeted at a particular recipient (identified by username, phone number, or email). Only this recipient can claim the funds.
    • Open Send Links: Not tied to a specific recipient. Anyone with the link can claim the funds on a first-come, first-served basis.
  3. Widget-Based Cross-Platform Support: Payment links work across web, mobile, and Telegram through a widget system that handles platform detection and redirection.
  4. Redirect URL Registration: Allows applications to register platform-specific redirect URLs that work with the widget system.

Payment Flow Overview with Widget System

The complete payment flow involves both a widget for initial link handling and platform-specific redirects for final destination routing.

Complete Payment Flow:

  1. Project Setup:

  2. Create Payment Links:

    • Create payment request or send link using the SDK
    • Register platform-specific redirect URLs (web, mobile, Telegram)
  3. Link Sharing & Consumption:

    • Share the payment link with recipients
    • Recipient clicks link → Redirected to your deployed widget
    • Widget detects/asks for platform → User selects preferred platform (web/mobile/Telegram)
    • Widget redirects to platform → Using your registered platform-specific URLs
    • User completes transaction → On the target platform
    • Platform redirects back → To your registered success/cancel URLs

Visual Flow:

Payment Link → Your Widget → Platform Selection → Target App → Transaction → Back to Your App

Why a Widget System is Required

Since there's no universal link format that works across all platforms (web browsers, mobile apps, Telegram bots), Sphere uses a widget-based approach:

  • Universal Entry Point: One payment link works for all platforms
  • Platform Detection: Widget can detect user's platform or ask for preference
  • Dynamic Routing: Routes users to the appropriate platform-specific app
  • Consistent Branding: Your widget maintains your app's look and feel

Widget Deployment Requirements

⚠️ Critical: You must deploy a payment widget before using Payment Links in production. Without a widget, payment links won't work properly across platforms.

Setting Up Widget and Redirect URLs via Dashboard

The redirect URL system works with your deployed widget to provide cross-platform payment link support. All configuration should be done through the Sphere Dashboard interface for easier management.

Complete Setup Process

Step 1: Deploy Your Widget

First, deploy your widget from the template:

# Clone and deploy the widget template
git clone https://github.com/stratosphere-network/sphere-widget.git
cd sphere-widget

# Configure with your project API key
echo "SPHERE_PROJECT_API_KEY=your-project-api-key" > .env

# Deploy to your hosting service
npm run build
# Deploy to Vercel/Netlify/etc. → https://widget.yourapp.com

Step 2: Configure Widget and Redirect URLs in Dashboard

Instead of programmatic registration, configure everything through the Sphere Dashboard interface:

  1. Go to Dashboard: Navigate to https://dashboard.sphere-id.com
  2. Select Your Project: Choose the project you want to configure
  3. Navigate to Additional Features: Click on the "Additional Features" tab

In the Payment Link Widget section:

  • Click "Add Payment Widget"
  • Enter your deployed widget URL (e.g., https://widget.yourapp.com)
  • Save the configuration

Redirect URL Management

In the Redirect URL Management section, you'll find two main areas:

Request Links (for payment requests):

  • Configure where users are redirected after requesting a payment link
  • Set up URLs for different platforms (Web, Mobile, Telegram)
  • Click "Edit" to configure Request Link Redirect URLs

Send Links (for send link operations):

  • Configure where users are redirected after sending a payment link
  • Set up URLs for different platforms (Web, Mobile, Telegram)
  • Click "Edit" to configure Send Link Redirect URLs

Platform-Specific URL Examples

When configuring redirect URLs in the dashboard, use URLs like these:

Web Platform URLs:

Payment Success: https://yourapp.com/payment-success
Payment Cancel: https://yourapp.com/payment-cancel
Send Success: https://yourapp.com/send-success
Send Cancel: https://yourapp.com/send-cancel

Mobile Platform URLs (Deep Links):

Payment Success: yourapp://payment/success
Payment Cancel: yourapp://payment/cancel
Send Success: yourapp://send/success
Send Cancel: yourapp://send/cancel

Telegram Platform URLs:

Payment Success: https://t.me/your_bot?start=payment_success
Payment Cancel: https://t.me/your_bot?start=payment_cancel
Send Success: https://t.me/your_bot?start=send_success
Send Cancel: https://t.me/your_bot?start=send_cancel

How the Complete Flow Works

Once you've configured your widget and redirect URLs through the dashboard:

  1. User clicks payment link → Redirected to your registered widget
  2. Widget fetches registered URLs → Using your project API key to get platform-specific redirects from dashboard
  3. Widget shows platform options → Displays available platforms (Web, Mobile, Telegram)
  4. User selects platform → Widget redirects to appropriate dashboard-configured URL
  5. User completes transaction → On the selected platform
  6. Transaction complete → User is redirected back to your platform-specific success/cancel URLs

What happens without proper widget setup:

  • Payment links lead to broken or generic pages
  • Users can't access transactions from their preferred platform
  • No cross-platform compatibility
  • Manual navigation required, leading to user drop-off

What happens with proper widget + redirect URL setup:

  • Universal payment links work across all platforms (web, mobile, Telegram)
  • Users are guided to their preferred platform automatically
  • Seamless platform-specific user experience
  • Your app receives transaction results and can update UI accordingly
  • Better conversion rates and user satisfaction across all platforms

Widget + Redirect URL Benefits:

  • Cross-platform compatibility: One link works everywhere
  • Branded experience: Widget maintains your app's branding
  • Platform detection: Automatic or user-selected platform routing
  • Deep linking: Direct integration with mobile apps and Telegram bots
  • Analytics: Track user platform preferences and conversion rates

When creating Send Links (createSpecificSendLink and createOpenSendLink), the time parameter specifies the link's expiration. The format must be a number followed by a time unit:

  • s: seconds (e.g., "30s")
  • m: minutes (e.g., "15m")
  • h: hours (e.g., "2h")
  • d: days (e.g., "7d")

The numeric value must be greater than 0. The service validates this format before making an API call.

Prerequisites

Before using the PaymentLinks service, ensure:

  1. Project Setup: Create a project on Sphere Dashboard and get your API key
  2. Widget Deployment: Deploy the payment widget from sphere-widget template and register it in your dashboard
  3. User Authentication: The user must be authenticated through the Sphere SDK
  4. SDK Authenticated: Your Sphere SDK instance must be authenticated with the user's accessToken
  5. Platform-Specific Redirect URLs: Register redirect URLs for each platform (web, mobile, Telegram) your app supports through the dashboard

⚠️ Critical: Without a deployed widget and registered redirect URLs, payment links won't work properly. Users will get stuck on default Sphere pages instead of being routed to your app.

import Sphere, {
Environment,
ChainName,
TokenSymbol,
Platform,
CreatePaymentRequestInput,
PaymentRequestResult,
SendSpecificPaymentRequest,
SendOpenPaymentRequest,
ClaimPaymentRequest,
ClaimPaymentResponse,
PayPaymentRequestResponse,
RegisterRedirectUrlRequest,
RegisterRedirectUrlResponse,
GetRedirectLinksRequest,
GetRedirectLinksResponse,
RequestLinkRedirect,
SendLinkRedirect,
GetPaymentRequestsInput,
GetSendLinksInput,
PaymentRequestItem,
SendLinkItem,
} from "@stratosphere-network/wallet";

const sphere = new Sphere({
apiKey: "YOUR_API_KEY", // Your project API key
environment: Environment.DEVELOPMENT,
});

// Example: After user login, you get an accessToken
// sphere.setBearerToken("USER_ACCESS_TOKEN");

Before creating payment links or send links, you can fetch all users from your project to help with recipient selection. This is especially useful for building user-friendly interfaces where users can select recipients by their email, phone number, or external ID:

// Fetch all users grouped by identifier type
const users = await sphere.paymentLinks.getAllUsers();

console.log("👥 Available users:", {
byExternalId: users.externalId, // Array of external IDs
byPhoneNumber: users.phoneNumber, // Array of phone numbers
byEmail: users.email, // Array of email addresses
});

// Use for recipient selection in your UI
// - Show dropdown/search of available emails for send links
// - Auto-complete phone numbers when creating specific send links
// - Validate external IDs before creating payment requests
// - Build contact lists for easy recipient selection

// Example: Create send link to a user from the fetched list
if (users.email.length > 0) {
const recipientEmail = users.email[0]; // First available email
const sendLink = await sphere.paymentLinks.createSpecificSendLink({
time: "1h",
email: recipientEmail, // Use fetched email
value: "50",
token: "USDC",
chain: "ARBITRUM",
});
console.log("💸 Send link created for:", recipientEmail);
}

Get Current Redirect URLs

Retrieve the currently registered redirect URLs for your project:

// Get current redirect URLs for your project
const redirectLinks = await sphere.paymentLinks.getRedirectLinks({
project_api_key: "your-project-api-key",
});

console.log("🔗 Current redirect URLs:", {
requestRedirectLink: redirectLinks.requestRedirectLink, // For payment requests
sendLinkRedirect: redirectLinks.sendLinkRedirect, // For send links
});

// Each redirect link contains:
// {
// url: string,
// project_api_key: string,
// createdAt: string,
// updatedAt: string,
// platform: Platform
// }

// Use this to check if redirect URLs are configured
if (redirectLinks.requestRedirectLink) {
console.log("✅ Payment request redirect configured:", {
url: redirectLinks.requestRedirectLink.url,
platform: redirectLinks.requestRedirectLink.platform,
});
} else {
console.log("⚠️ No payment request redirect URL configured");
}

if (redirectLinks.sendLinkRedirect) {
console.log("✅ Send link redirect configured:", {
url: redirectLinks.sendLinkRedirect.url,
platform: redirectLinks.sendLinkRedirect.platform,
});
} else {
console.log("⚠️ No send link redirect URL configured");
}

Payment Requests (Request money from others)

Service Methods

The PaymentLinksService class provides the following methods:

💡 Pro Tip: Before creating payment requests or send links, ensure you have configured your widget and redirect URLs through the Sphere Dashboard to provide users with a seamless experience after completing transactions.


requestPayment(request: CreatePaymentRequestInput): Promise<PaymentRequestResult>

Creates a new payment request. This generates a link (represented by a nonce in the response) that can be shared with someone to request a payment.

⚠️ Important: To provide a complete user experience, configure redirect URLs for your project in the Sphere Dashboard before creating payment requests. This ensures users are redirected back to your application after completing payments.

  • Parameters:
    • request: CreatePaymentRequestInput - An object containing the amount, chain, and token for the payment request.
  • Returns: Promise<PaymentRequestResult> - A promise that resolves with the data containing the nonce for the created payment request.

SDK Usage

async function createPaymentRequest(
amount: number,
chain: ChainName,
token: TokenSymbol
): Promise<string | null> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return null;
}

try {
const request: CreatePaymentRequestInput = {
amount: amount,
chain: chain,
token: token,
};

console.log(
`Creating payment request for ${amount} ${token} on ${chain}...`
);
const response = await sphere.paymentLinks.requestPayment(request);

if (response.data) {
console.log("Payment request created successfully!");
console.log("Payment nonce:", response.data);
if (response.message) {
console.log("Message:", response.message);
}
return response.data;
} else {
console.error("Failed to create payment request:", response.error);
return null;
}
} catch (error) {
console.error("Error creating payment request:", error);
throw error;
}
}

// Example Usage:
// createPaymentRequest(10, ChainName.POLYGON, TokenSymbol.USDC)
// .then(nonce => {
// if (nonce) {
// console.log(`Share this payment request: ${nonce}`);
// console.log("�� Make sure you have configured redirect URLs in the Sphere Dashboard for complete user experience!");
// console.log(" Visit https://dashboard.sphere-id.com to configure your project.");
// }
// });

// Complete workflow example:
// 1. First configure redirect URLs in dashboard (one time setup)
// 2. Deploy and register your widget in dashboard
// 3. Then create payment requests using the SDK
// await createPaymentRequest(10, ChainName.POLYGON, TokenSymbol.USDC);

payPaymentRequest(nonce: string): Promise<PayPaymentRequestResponse>

Executes (pays) an existing payment request using its nonce.

  • Parameters:
    • nonce: string - The nonce of the payment request to be paid.
  • Returns: Promise<PayPaymentRequestResponse> - A promise that resolves with the payment response, including transaction data on success or error details on failure.

SDK Usage

async function payExistingPaymentRequest(nonce: string): Promise<boolean> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return false;
}

try {
console.log(`Executing payment request with nonce: ${nonce}...`);
const response = await sphere.paymentLinks.payPaymentRequest(nonce);

if (response.message && !response.error) {
console.log("Payment executed successfully!");
console.log("Status:", response.message);
if (response.data) {
console.log("Transaction data:", response.data);
}
return true;
} else {
console.error("Payment failed:", response.error || response.message);
return false;
}
} catch (error) {
console.error("Error executing payment request:", error);
throw error;
}
}

// Example Usage:
// payExistingPaymentRequest("payment_nonce_123")
// .then(success => {
// if (success) {
// console.log("Payment completed successfully!");
// } else {
// console.log("Payment failed. Please try again.");
// }
// });

createSpecificSendLink(request: SendSpecificPaymentRequest): Promise<PaymentRequestResult>

Creates a send link that is designated for a specific recipient. The recipient can be identified by username, phone number, or email. Only the specified recipient can claim the funds from this link. The time parameter (e.g., "1h", "30d") dictates the link's expiration.

⚠️ Important: To provide a complete user experience, configure redirect URLs for your project in the Sphere Dashboard before creating send links. This ensures users are redirected back to your application after claiming funds.

  • Parameters:
    • request: SendSpecificPaymentRequest - An object detailing the send link, including expiration time, recipient identifier (username, phoneNumber, or email), value, token, and chain.
  • Returns: Promise<PaymentRequestResult> - A promise that resolves with data related to the created send link (often a URL identifier or nonce).
  • Validation: The request.time field is validated for the correct format (e.g., "1h", "30m", "7d") before calling the API.

SDK Usage

// Example 1: Create send link with username
async function createSendLinkForUsername(
username: string,
value: string,
token: TokenSymbol,
chain: ChainName,
expirationTime: string
): Promise<string | null> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return null;
}

try {
const request: SendSpecificPaymentRequest = {
time: expirationTime,
value: value,
token: token,
chain: chain,
username: username,
};

console.log(`Creating send link for username: ${username}...`);
const response = await sphere.paymentLinks.createSpecificSendLink(request);

if (response.data) {
console.log("Specific send link created successfully!");
console.log("Link ID:", response.data);
console.log(`Recipient: @${username}`);
console.log(`Amount: ${value} ${token} on ${chain}`);
console.log(`Expires: ${expirationTime}`);
return response.data;
} else {
console.error("Failed to create send link:", response.error);
return null;
}
} catch (error) {
console.error("Error creating specific send link:", error);
throw error;
}
}

// Example 2: Create send link with phone number
async function createSendLinkForPhone(
phoneNumber: string,
value: string,
token: TokenSymbol,
chain: ChainName,
expirationTime: string
): Promise<string | null> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return null;
}

try {
const request: SendSpecificPaymentRequest = {
time: expirationTime,
value: value,
token: token,
chain: chain,
phoneNumber: phoneNumber,
};

console.log(`Creating send link for phone: ${phoneNumber}...`);
const response = await sphere.paymentLinks.createSpecificSendLink(request);

if (response.data) {
console.log("Specific send link created successfully!");
console.log("Link ID:", response.data);
return response.data;
} else {
console.error("Failed to create send link:", response.error);
return null;
}
} catch (error) {
console.error("Error creating specific send link:", error);
throw error;
}
}

// Example 3: Create send link with email
async function createSendLinkForEmail(
email: string,
value: string,
token: TokenSymbol,
chain: ChainName,
expirationTime: string
): Promise<string | null> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return null;
}

try {
const request: SendSpecificPaymentRequest = {
time: expirationTime,
value: value,
token: token,
chain: chain,
email: email,
};

console.log(`Creating send link for email: ${email}...`);
const response = await sphere.paymentLinks.createSpecificSendLink(request);

if (response.data) {
console.log("Specific send link created successfully!");
console.log("Link ID:", response.data);
return response.data;
} else {
console.error("Failed to create send link:", response.error);
return null;
}
} catch (error) {
console.error("Error creating specific send link:", error);
throw error;
}
}

// Example Usage:
// createSendLinkForUsername("alice123", "5.0", TokenSymbol.USDC, ChainName.POLYGON, "24h");
// createSendLinkForPhone("+1234567890", "10.0", TokenSymbol.USDT, ChainName.ETHEREUM, "7d");
// createSendLinkForEmail("alice@example.com", "2.5", TokenSymbol.USDC, ChainName.BASE, "1h");

createOpenSendLink(request: SendOpenPaymentRequest): Promise<PaymentRequestResult>

Creates an open send link that can be claimed by anyone on a first-come, first-served basis. The time parameter (e.g., "1h", "30d") dictates the link's expiration.

⚠️ Important: To provide a complete user experience, configure redirect URLs for your project in the Sphere Dashboard before creating send links. This ensures users are redirected back to your application after claiming funds.

  • Parameters:
    • request: SendOpenPaymentRequest - An object detailing the send link, including expiration time, value, token, and chain.
  • Returns: Promise<PaymentRequestResult> - A promise that resolves with data related to the created send link.
  • Validation: The request.time field is validated for the correct format (e.g., "1h", "30m", "7d") before calling the API.

SDK Usage

async function createOpenSendLink(
value: string,
token: TokenSymbol,
chain: ChainName,
expirationTime: string
): Promise<string | null> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return null;
}

try {
const request: SendOpenPaymentRequest = {
time: expirationTime,
value: value,
token: token,
chain: chain,
};

console.log(`Creating open send link for ${value} ${token} on ${chain}...`);
console.log(`Link will expire in: ${expirationTime}`);

const response = await sphere.paymentLinks.createOpenSendLink(request);

if (response.data) {
console.log("Open send link created successfully!");
console.log("Link ID:", response.data);
console.log("⚠️ Anyone with this link can claim the funds!");
console.log(`Amount: ${value} ${token} on ${chain}`);
console.log(`Expires: ${expirationTime}`);
if (response.message) {
console.log("Message:", response.message);
}
return response.data;
} else {
console.error("Failed to create open send link:", response.error);
return null;
}
} catch (error) {
console.error("Error creating open send link:", error);
throw error;
}
}

// Example Usage:
// createOpenSendLink("1.0", TokenSymbol.USDC, ChainName.POLYGON, "30m")
// .then(linkId => {
// if (linkId) {
// console.log(`Share this open link: ${linkId}`);
// console.log("First person to claim gets the funds!");
// console.log("�� Make sure you have configured redirect URLs in the Sphere Dashboard for complete user experience!");
// console.log(" Visit https://dashboard.sphere-id.com to configure your project.");
// }
// });

// Complete workflow example:
// 1. First configure redirect URLs in dashboard (one time setup)
// 2. Deploy and register your widget in dashboard
// 3. Then create send links using the SDK
// await createOpenSendLink("1.0", TokenSymbol.USDC, ChainName.POLYGON, "30m");

claimSpecificSendLink(request: ClaimPaymentRequest): Promise<ClaimPaymentResponse>

Allows the designated recipient to claim the funds from a specific send link.

  • Parameters:
    • request: ClaimPaymentRequest - An object containing the id of the send link to claim.
  • Returns: Promise<ClaimPaymentResponse> - A promise that resolves with the result of the claim attempt.

SDK Usage

async function claimSpecificSendLink(linkId: string): Promise<boolean> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return false;
}

try {
const request: ClaimPaymentRequest = {
id: linkId,
};

console.log(`Claiming specific send link: ${linkId}...`);
const response = await sphere.paymentLinks.claimSpecificSendLink(request);

if (response.message && !response.error) {
console.log("Specific send link claimed successfully!");
console.log("Status:", response.message);
return true;
} else {
console.error(
"Failed to claim specific send link:",
response.error || response.message
);
return false;
}
} catch (error) {
console.error("Error claiming specific send link:", error);
throw error;
}
}

// Example Usage:
// claimSpecificSendLink("link_id_123")
// .then(success => {
// if (success) {
// console.log("Funds received in your wallet!");
// } else {
// console.log("Unable to claim link. Check if you're the designated recipient.");
// }
// });

claimOpenSendLink(request: ClaimPaymentRequest): Promise<ClaimPaymentResponse>

Allows any user to claim the funds from an open send link.

  • Parameters:
    • request: ClaimPaymentRequest - An object containing the id of the send link to claim.
  • Returns: Promise<ClaimPaymentResponse> - A promise that resolves with the result of the claim attempt.

SDK Usage

async function claimOpenSendLink(linkId: string): Promise<boolean> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return false;
}

try {
const request: ClaimPaymentRequest = {
id: linkId,
};

console.log(`Attempting to claim open send link: ${linkId}...`);
const response = await sphere.paymentLinks.claimOpenSendLink(request);

if (response.message && !response.error) {
console.log("Open send link claimed successfully!");
console.log("Status:", response.message);
console.log("🎉 You were first to claim the funds!");
return true;
} else {
console.error(
"Failed to claim open send link:",
response.error || response.message
);
console.log(
"💔 Someone else may have claimed it first, or the link expired."
);
return false;
}
} catch (error) {
console.error("Error claiming open send link:", error);
throw error;
}
}

// Example Usage:
// claimOpenSendLink("open_link_456")
// .then(success => {
// if (success) {
// console.log("Lucky you! Funds received in your wallet!");
// } else {
// console.log("Better luck next time!");
// }
// });

listPaymentRequests(request?: GetPaymentRequestsInput): Promise<GetPaymentRequestsResult>

Retrieves a list of payment requests created by the authenticated user. Supports pagination and filtering.

  • Parameters:
    • request (optional): GetPaymentRequestsInput - An object for pagination (limit, page) and filtering (expired).
  • Returns: Promise<GetPaymentRequestsResult> - A promise that resolves with a list of payment request items and pagination details.

SDK Usage

async function listMyPaymentRequests(
page: number = 1,
limit: number = 10,
showExpired?: boolean
): Promise<PaymentRequestItem[] | null> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return null;
}

try {
const request: GetPaymentRequestsInput = {
page: page.toString(),
limit: limit.toString(),
};

if (showExpired !== undefined) {
request.expired = showExpired ? "true" : "false";
}

console.log(`Fetching payment requests (page ${page}, limit ${limit})...`);
const response = await sphere.paymentLinks.listPaymentRequests(request);

if (response.data) {
console.log(`Found ${response.data.length} payment requests:`);
response.data.forEach((item, index) => {
console.log(`\n${index + 1}. Payment Request:`);
console.log(` ID: ${item.id}`);
console.log(` Nonce: ${item.nonce}`);
console.log(` Amount: ${item.amount} ${item.token} on ${item.chain}`);
console.log(` Status: ${item.status}`);
console.log(` Created: ${new Date(item.createdAt).toLocaleString()}`);
if (item.paidByAddress) {
console.log(` Paid by: ${item.paidByAddress}`);
console.log(` Paid at: ${new Date(item.paidAt!).toLocaleString()}`);
}
});

console.log(`\nPagination:`);
console.log(` Current page: ${response.pagination.currentPage}`);
console.log(` Total pages: ${response.pagination.totalPages}`);
console.log(` Total count: ${response.pagination.totalCount}`);
console.log(` Has next page: ${response.pagination.hasNextPage}`);

return response.data;
} else {
console.log("No payment requests found.");
return [];
}
} catch (error) {
console.error("Error fetching payment requests:", error);
throw error;
}
}

// Example Usage:
// listMyPaymentRequests(1, 5) // First 5 payment requests
// .then(requests => {
// if (requests && requests.length > 0) {
// console.log("Payment requests loaded successfully!");
// }
// });

listSendLinks(request?: GetSendLinksInput): Promise<GetSendLinksResult>

Retrieves a list of send links created by the authenticated user. Supports pagination and filtering.

  • Parameters:
    • request (optional): GetSendLinksInput - An object for pagination (limit, page) and filtering (fulfilled).
  • Returns: Promise<GetSendLinksResult> - A promise that resolves with a list of send link items and pagination details.

SDK Usage

async function listMySendLinks(
page: number = 1,
limit: number = 10,
showFulfilled?: boolean
): Promise<SendLinkItem[] | null> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return null;
}

try {
const request: GetSendLinksInput = {
page: page.toString(),
limit: limit.toString(),
};

if (showFulfilled !== undefined) {
request.fulfilled = showFulfilled ? "true" : "false";
}

console.log(`Fetching send links (page ${page}, limit ${limit})...`);
const response = await sphere.paymentLinks.listSendLinks(request);

if (response.data) {
console.log(`Found ${response.data.length} send links:`);
response.data.forEach((item, index) => {
console.log(`\n${index + 1}. Send Link:`);
console.log(` ID: ${item.id}`);
console.log(` URL ID: ${item.urlId}`);
console.log(` Value: ${item.value}`);
console.log(` Receiver: ${item.receiver || "Anyone (Open link)"}`);
console.log(` Created: ${new Date(item.createdAt).toLocaleString()}`);
console.log(` Expires: ${new Date(item.expiresAt).toLocaleString()}`);
});

console.log(`\nPagination:`);
console.log(` Current page: ${response.pagination.currentPage}`);
console.log(` Total pages: ${response.pagination.totalPages}`);
console.log(` Total count: ${response.pagination.totalCount}`);
console.log(` Has next page: ${response.pagination.hasNextPage}`);
console.log(` Fulfilled status: ${response.fulfilled}`);

return response.data;
} else {
console.log("No send links found.");
return [];
}
} catch (error) {
console.error("Error fetching send links:", error);
throw error;
}
}

// Example Usage:
// listMySendLinks(1, 5, false) // First 5 unfulfilled send links
// .then(links => {
// if (links && links.length > 0) {
// console.log("Send links loaded successfully!");
// }
// });

cancelPaymentRequest(nonce: string): Promise<CancelPaymentRequestResult>

Cancels a payment request that has not yet been paid.

  • Parameters:
    • nonce: string - The nonce of the payment request to cancel.
  • Returns: Promise<CancelPaymentRequestResult> - A promise that resolves with details of the cancellation.

SDK Usage

async function cancelMyPaymentRequest(nonce: string): Promise<boolean> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return false;
}

try {
console.log(`Cancelling payment request with nonce: ${nonce}...`);
const response = await sphere.paymentLinks.cancelPaymentRequest(nonce);

if (response.message && response.data) {
console.log("Payment request cancelled successfully!");
console.log("Status:", response.message);
console.log("Cancelled details:");
console.log(` Nonce: ${response.data.nonce}`);
console.log(` Amount: ${response.data.amount} ${response.data.token}`);
console.log(` Chain: ${response.data.chain}`);
return true;
} else {
console.error("Failed to cancel payment request");
return false;
}
} catch (error) {
console.error("Error cancelling payment request:", error);
throw error;
}
}

// Example Usage:
// cancelMyPaymentRequest("payment_nonce_123")
// .then(success => {
// if (success) {
// console.log("Payment request has been cancelled and is no longer active.");
// } else {
// console.log("Unable to cancel payment request. It may have already been paid.");
// }
// });

cancelSendLink(urlId: string): Promise<CancelSendLinkResult>

Cancels a send link that has not yet been claimed or has not expired.

  • Parameters:
    • urlId: string - The URL identifier of the send link to cancel.
  • Returns: Promise<CancelSendLinkResult> - A promise that resolves with details of the cancellation.

SDK Usage

async function cancelMySendLink(urlId: string): Promise<boolean> {
if (!sphere.isAuthenticated()) {
console.error("SDK is not authenticated. Please log in the user first.");
return false;
}

try {
console.log(`Cancelling send link with URL ID: ${urlId}...`);
const response = await sphere.paymentLinks.cancelSendLink(urlId);

if (response.message && response.data) {
console.log("Send link cancelled successfully!");
console.log("Status:", response.message);
console.log("Cancelled details:");
console.log(` URL ID: ${response.data.urlId}`);
console.log(` Value: ${response.data.value}`);
console.log(
` Receiver: ${response.data.receiver || "Anyone (Open link)"}`
);
console.log(
` Was set to expire: ${new Date(
response.data.expiresAt
).toLocaleString()}`
);
return true;
} else {
console.error("Failed to cancel send link");
return false;
}
} catch (error) {
console.error("Error cancelling send link:", error);
throw error;
}
}

// Example Usage:
// cancelMySendLink("send_link_456")
// .then(success => {
// if (success) {
// console.log("Send link has been cancelled and funds returned to your wallet.");
// } else {
// console.log("Unable to cancel send link. It may have already been claimed or expired.");
// }
// });

Redirect URL Configuration

📝 Note: The Sphere SDK includes registerRequestLinkRedirectUrl() and registerSendLinkRedirectUrl() methods for programmatic redirect URL registration. However, we strongly recommend using the Sphere Dashboard for all redirect URL configuration instead.

Why use the Dashboard?

  • Easier Management: Visual interface for configuring all platforms at once
  • Better Organization: Clear separation between Request Links and Send Links
  • Platform Toggles: Easy enable/disable for Web, Mobile, and Telegram platforms
  • Validation: Built-in URL validation and testing
  • Team Access: Multiple team members can manage configurations
  • Audit Trail: Track configuration changes over time

How to configure redirect URLs:

  1. Visit https://dashboard.sphere-id.com
  2. Navigate to your project → Additional Features tab
  3. Configure Payment Link Widget with your deployed widget URL
  4. Set up Redirect URL Management for Request Links and Send Links
  5. Configure platform-specific URLs (Web, Mobile, Telegram) using the Edit buttons

Core Data Types

These are the primary data structures used by the PaymentLinksService.

Platform

An enum representing supported platform types for redirect URL registration.

export enum Platform {
SERVER = "SERVER",
MOBILE = "MOBILE",
WEB = "WEB",
TELEGRAM = "TELEGRAM",
}

ChainName

A type alias for supported blockchain names.

export type ChainName =
| "ARBITRUM"
| "BASE"
| "OPTIMISM"
| "ETHEREUM"
| "LISK"
| "BNB"
| "BERACHAIN"
| "POLYGON";

The token field in payment link inputs now uses the TokenSymbol type, which provides comprehensive support for various tokens across different chains. Refer to the SDK's TokenSymbol enum for the complete list of supported tokens.


CreatePaymentRequestInput

Input for creating a payment request.

export interface CreatePaymentRequestInput {
amount: number; // The amount to be requested
chain: ChainName; // The blockchain for the payment
token: TokenSymbol; // The token for the payment
}

PaymentRequestResult

Result after creating a payment request or send link.

export interface PaymentRequestResult {
data: string; // Typically the nonce or identifier for the created link
message?: string; // Optional success message
error?: string; // Optional error message
}

PayPaymentRequestInput

Input for paying/executing a payment request. (Note: payPaymentRequest method directly takes nonce: string)

export interface PayPaymentRequestInput {
nonce: string; // The unique identifier (nonce) of the payment request
}

PayPaymentRequestResponse

Response after executing a payment request.

export interface PayPaymentRequestResponse {
message: string; // Status message about the payment execution
data?: any; // Transaction result data when successful
error?: any; // Error details when transaction fails
}

SendSpecificPaymentRequest

Input for creating a specific send link targeted at a particular recipient.

export type SendSpecificPaymentRequest =
| {
time: string; // Expiration time (format: number + s/m/h/d)
value: string; // The amount of tokens to send
token: TokenSymbol; // The token to send
chain: ChainName; // The blockchain for sending
username: string; // Recipient's username
phoneNumber?: never;
email?: never;
}
| {
time: string;
value: string;
token: TokenSymbol;
chain: ChainName;
phoneNumber: string; // Recipient's phone number
username?: never;
email?: never;
}
| {
time: string;
value: string;
token: TokenSymbol;
chain: ChainName;
email: string; // Recipient's email address
username?: never;
phoneNumber?: never;
};

SendOpenPaymentRequest

Input for creating an open send link that can be claimed by anyone.

export interface SendOpenPaymentRequest {
time: string; // Expiration time (format: number + s/m/h/d)
value: string; // The amount of tokens to send
token: TokenSymbol; // The token to send
chain: ChainName; // The blockchain for sending
}

ClaimPaymentRequest

Input for claiming a send link.

export interface ClaimPaymentRequest {
id: string; // The identifier of the send link to be claimed
}

ClaimPaymentResponse

Result after attempting to claim a send link.

export interface ClaimPaymentResponse {
message: string; // Message regarding the claim status
error?: any; // Error details if the claim fails
}

RegisterRedirectUrlRequest

Input for registering a custom redirect URL. Uses either email or phone for authentication, with a required platform specification and project API key.

export type RegisterRedirectUrlRequest =
| {
url: string; // The redirect URL to register
email: string; // User's email for authentication
otpCode: string; // OTP code for verification
platform: Platform; // Target platform: WEB, MOBILE, SERVER, or TELEGRAM
project_api_key: string; // Project API key (required)
phoneNumber?: never;
}
| {
url: string; // The redirect URL to register
phoneNumber: string; // User's phone for authentication
otpCode: string; // OTP code for verification
platform: Platform; // Target platform: WEB, MOBILE, SERVER, or TELEGRAM
project_api_key: string; // Project API key (required)
email?: never;
};

RegisterRedirectUrlResponse

Response after registering a redirect URL.

export interface RegisterRedirectUrlResponse {
message: string; // Confirmation message
data: {
url: string; // The registered redirect URL
project_api_key: string; // Project API key associated with the URL
createdAt: Date; // Timestamp when the redirect URL was created
updatedAt: Date; // Timestamp when the redirect URL was last updated
platform: Platform; // The platform type specified during registration
};
}

SendPaymentResponse

Response after creating a send link.

export interface SendPaymentResponse {
data: string; // Send link identifier
error?: string; // Optional error message
}

The service can return various specific error types to help with error handling:

export interface PaymentLinkValidationError {
error?: string;
message?: string;
supportedCombinations?: {
[key in ChainName]?: TokenSymbol[];
};
}

export interface PaymentLinkNotFoundError {
message: "Payment link not found" | "Token not found or expired";
}

export interface PaymentLinkAlreadyPaidError {
message: "Payment link already paid";
}

export interface UnauthorizedPaymentError {
message:
| "Unauthorized"
| "Unauthorized: You are not authorized to spend this!"
| "Forbidden: Invalid spend token.";
}

GetRedirectLinksRequest

Input for retrieving current redirect URLs for a project.

export interface GetRedirectLinksRequest {
project_api_key: string; // Project API key to identify the project
}

RequestLinkRedirect

Structure representing a registered payment request redirect URL.

export interface RequestLinkRedirect {
url: string; // The registered redirect URL
project_api_key: string; // Project API key associated with the URL
createdAt: string; // ISO timestamp when the redirect URL was created
updatedAt: string; // ISO timestamp when the redirect URL was last updated
platform: Platform; // The platform type specified during registration
}

SendLinkRedirect

Structure representing a registered send link redirect URL.

export interface SendLinkRedirect {
url: string; // The registered redirect URL
project_api_key: string; // Project API key associated with the URL
createdAt: string; // ISO timestamp when the redirect URL was created
updatedAt: string; // ISO timestamp when the redirect URL was last updated
platform: Platform; // The platform type specified during registration
}

GetRedirectLinksResponse

Response containing the current redirect URL configuration for a project.

export interface GetRedirectLinksResponse {
sendLinkRedirect: SendLinkRedirect | null; // Send link redirect URL (null if not configured)
requestRedirectLink: RequestLinkRedirect | null; // Payment request redirect URL (null if not configured)
}

GetPaymentRequestsInput

Input for listing payment requests.

export interface GetPaymentRequestsInput {
expired?: "true" | "false"; // Optional: Filter by expiration status
limit?: string; // Optional: Number of items per page
page?: string; // Optional: Page number for pagination
}

PaymentRequestItem

Structure representing a single payment request item in a list.

export interface PaymentRequestItem {
id: string; // Unique identifier of the payment request
nonce: string; // Nonce associated with the payment request
amount: number; // Requested amount
token: string; // Requested token symbol
chain: string; // Requested chain name
creatorAddress: string; // Address of the user who created the request
status: string; // Current status (e.g., "pending", "paid", "cancelled")
createdAt: string; // ISO timestamp of creation
updatedAt: string; // ISO timestamp of last update
paidByAddress?: string | null; // Optional: Address of the user who paid the request
paidByUserId?: string | null; // Optional: User ID of the user who paid
paidAt?: string | null; // Optional: ISO timestamp when the request was paid
}

GetPaymentRequestsResult

Result of listing payment requests, including pagination.

export interface GetPaymentRequestsResult {
data: PaymentRequestItem[]; // Array of payment request items
pagination: {
currentPage: number; // Current page number
totalPages: number; // Total number of pages
totalCount: number; // Total number of items
limit: number; // Items per page
hasNextPage: boolean; // True if there is a next page
hasPreviousPage: boolean; // True if there is a previous page
};
}

GetSendLinksInput

Input for listing send links.

export interface GetSendLinksInput {
fulfilled?: "true" | "false"; // Optional: Filter by fulfillment status (claimed or not)
limit?: string; // Optional: Number of items per page
page?: string; // Optional: Page number for pagination
}

SendLinkItem

Structure representing a single send link item in a list.

export interface SendLinkItem {
id: string; // Unique identifier of the send link
urlId: string; // URL-friendly identifier for the send link
value: string; // Amount of tokens in the send link
receiver?: string | null; // Optional: Designated receiver identifier (if a specific send link)
expiresAt: string; // ISO timestamp when the link expires
createdAt: string; // ISO timestamp of creation
updatedAt: string; // ISO timestamp of last update
}

GetSendLinksResult

Result of listing send links, including pagination.

export interface GetSendLinksResult {
data: SendLinkItem[]; // Array of send link items
fulfilled: boolean; // This seems to be a top-level status, perhaps indicating overall filter status. Consider if it belongs inside pagination or is a general flag.
pagination: {
currentPage: number; // Current page number
totalPages: number; // Total number of pages
totalCount: number; // Total number of items
limit: number; // Items per page
hasNextPage: boolean; // True if there is a next page
hasPreviousPage: boolean; // True if there is a previous page
};
}

CancelPaymentRequestInput

Input for cancelling a payment request. (Note: cancelPaymentRequest method directly takes nonce: string)

export interface CancelPaymentRequestInput {
nonce: string; // The nonce of the payment request to cancel
}

CancelPaymentRequestResult

Result after cancelling a payment request.

export interface CancelPaymentRequestResult {
message: string; // Confirmation message
data: {
nonce: string; // Nonce of the cancelled request
amount: string; // Amount of the cancelled request
token: string; // Token of the cancelled request
chain: string; // Chain of the cancelled request
};
}

CancelSendLinkInput

Input for cancelling a send link. (Note: cancelSendLink method directly takes urlId: string)

export interface CancelSendLinkInput {
urlId: string; // The URL identifier of the send link to cancel
}

CancelSendLinkResult

Result after cancelling a send link.

export interface CancelSendLinkResult {
message: string; // Confirmation message
data: {
urlId: string; // URL ID of the cancelled send link
value: string; // Value of the cancelled send link
receiver?: string; // Optional: Receiver of the cancelled send link
expiresAt: string; // Expiration time of the cancelled send link
};
}

Complete Integration Checklist

To ensure a seamless payment experience for your users, follow this integration checklist:

Phase 1: Project Setup & Dashboard Configuration

  • Create project on Sphere Dashboard
  • Configure allowed origins for CORS (development, staging, production domains)
  • Get your project API key from the dashboard
  • Test API key with a simple SDK initialization

Phase 2: Widget Deployment

  • Clone widget template: git clone https://github.com/stratosphere-network/sphere-widget.git
  • Configure widget with your project API key
  • Deploy widget to public URL (e.g., Vercel, Netlify)
  • Register widget URL in your Sphere dashboard
  • Test widget by accessing the URL directly

Phase 3: SDK Setup & Authentication

  • Install and initialize Sphere SDK with your API key
  • Implement user authentication flow (signup, OTP verification, login)
  • Ensure SDK is authenticated with user's access token
  • Test authentication with different methods (phone, email, external ID)

Phase 4: Platform-Specific Redirect URL Registration

  • Create platform-specific endpoints in your applications:
    • Web: https://yourapp.com/payment-success, /payment-cancel, /send-success, /send-cancel
    • Mobile: yourapp://payment/success, yourapp://payment/cancel, etc.
    • Telegram: https://t.me/your_bot?start=payment_success, etc.
  • Go to Sphere Dashboard → Additional Features
  • Configure Payment Link Widget with your deployed widget URL
  • Set up Redirect URL Management for Request Links (Web, Mobile, Telegram platforms)
  • Set up Redirect URL Management for Send Links (Web, Mobile, Telegram platforms)
  • Verify all platform configurations are active and URLs are correct
  • Test redirect flows by creating test payment links

Phase 5: Payment Implementation

  • Implement payment request creation using requestPayment()
  • Implement send link creation (specific or open)
  • Add payment execution capability using payPaymentRequest()
  • Add claim functionality for send links
  • Implement listing and management features

Phase 6: Cross-Platform User Experience Testing

  • Test complete widget flow:
    1. User creates payment request/send link in your app
    2. Link is shared and accessed by recipient
    3. Recipient clicks link → Redirected to your widget
    4. Widget shows platform options → User selects platform
    5. Widget redirects to platform → Using registered URLs
    6. Transaction completed → On selected platform
    7. User redirected back → To your platform-specific success/cancel URLs
    8. Your app handles → Post-transaction logic
  • Test from different platforms (desktop browser, mobile browser, mobile app, Telegram)
  • Handle edge cases (expired links, insufficient funds, unregistered platforms)
  • Implement proper error handling and user feedback
  • Test widget branding and customization

Phase 7: Production Readiness

  • Switch to Environment.PRODUCTION for live deployment
  • Update all URLs to production domains (widget, redirect URLs)
  • Update allowed origins in dashboard for production domains
  • Test all flows in production environment
  • Monitor transaction success rates and user completion flows
  • Set up analytics to track platform usage and conversion rates

Phase 8: Advanced Features (Optional)

  • Implement webhook handling for transaction notifications
  • Add deep linking for mobile apps
  • Customize widget styling and branding
  • Implement platform detection and automatic routing
  • Add analytics and conversion tracking

🎯 Success Metrics: A well-integrated PaymentLinks service should have:

  • Cross-platform compatibility: Payment links work on web, mobile, and Telegram
  • Seamless widget experience: Users can easily select their preferred platform
  • Platform-specific routing: Users are directed to the appropriate app/interface
  • Consistent branding: Widget maintains your app's visual identity
  • High conversion rates: Minimal drop-off during cross-platform flows
  • Clear transaction status: Real-time updates across all platforms
  • Proper error handling: Graceful handling of edge cases on all platforms

Need Help?