@gaian-sdk/payments
v0.6.4
Published
A powerful SDK for processing cryptocurrency payments via QR codes on the Solana blockchain.
Maintainers
Readme
@gaian-sdk/payments Documentation
A powerful SDK for processing cryptocurrency payments via QR codes on the Solana blockchain.
Installation
npm install @gaian-sdk/payments
# or
yarn add @gaian-sdk/paymentsRequired Dependencies
npm install @solana/web3.js @solana/spl-token bs58 dotenvEnvironment Setup
Create a .env file in your project root:
SOLANA_PRIVATE_KEY=your_base58_encoded_private_key_here
SOLANA_RPC=https://api.devnet.solana.com # or your preferred RPC endpoint
NODE_ENV=developmentGetting Your Private Key
Generate a new keypair (if you don't have one):
solana-keygen new --outfile ~/.config/solana/id.jsonExport as base58 (required format):
import { Keypair } from "@solana/web3.js"; import bs58 from "bs58"; // If you have a JSON keypair file const keypair = Keypair.fromSecretKey( new Uint8Array(JSON.parse(secretKeyArray)) ); const privateKeyBase58 = bs58.encode(keypair.secretKey); console.log("SOLANA_PRIVATE_KEY=" + privateKeyBase58);Fund your wallet with SOL and USDC:
- SOL:
solana airdrop 2 YOUR_WALLET_ADDRESS --url devnet - USDC: Visit SPL Token Faucet
- SOL:
Basic Usage
1. Initialize the SDK
import { InitializeScanToPay } from "@gaian-sdk/payments";
import { Environment } from "@gaian-sdk/payments/types/common";
const scanToPay = new InitializeScanToPay({
environment: Environment.DEV, // or Environment.PROD
paymentsUrl: "http://0.0.0.0:3000", // Your Payments API base URL
userUrl: "http://0.0.0.0:3000", // Your User API base URL
timeout: 30000, // 30 second timeout
solanaRpcUrl: process.env.SOLANA_RPC,
});2. Set Up Solana Connection
import { Connection, Keypair, Transaction } from "@solana/web3.js";
import bs58 from "bs58";
// Load your keypair from environment
function loadKeypairFromEnv() {
const privateKeyBase58 = process.env.SOLANA_PRIVATE_KEY;
if (!privateKeyBase58) throw new Error("SOLANA_PRIVATE_KEY required");
return Keypair.fromSecretKey(bs58.decode(privateKeyBase58));
}
const connection = new Connection(process.env.SOLANA_RPC || "");User Registration and KYC
Before processing payments, users need to be registered and complete KYC verification.
User Registration
const userResult = await scanToPay.registerUser({
email: "[email protected]",
walletAddress: "AYwJ2JCteMY2FxSJXjFpkCSxTvff1wzHgFrT1iDJpi8W", // Solana wallet address
});
console.log("User registered:", userResult);
// Returns: { user: { id: number, email: string, walletAddress: string } }Generate KYC Link
const kycResult = await scanToPay.generateKYCLink({
userId: userResult.user.id.toString(),
email: userResult.user.email,
phone: "+1234567890", // Optional phone number
});
console.log("KYC link:", kycResult);
// Returns KYC link for user verificationComplete User Registration Example
import { InitializeScanToPay, Environment } from "@gaian-sdk/payments";
import "dotenv/config";
async function registerUserAndKYC() {
const scanToPay = new InitializeScanToPay({
environment: Environment.DEV,
paymentsUrl: "http://0.0.0.0:3000",
userUrl: "http://0.0.0.0:3000",
timeout: 30000,
});
try {
// 1. Register User
console.log("📝 Registering user...");
const userResult = await scanToPay.registerUser({
email: "[email protected]",
walletAddress: "AYwJ2JCteMY2FxSJXjFpkCSxTvff1wzHgFrT1iDJpi8W",
});
console.log("✅ User registered:", userResult);
// 2. Generate KYC Link
console.log("🔗 Generating KYC link...");
const kycResult = await scanToPay.generateKYCLink({
userId: userResult.user.id.toString(),
email: userResult.user.email,
phone: "+1234567890",
});
console.log("✅ KYC link generated:", kycResult);
return { success: true, userResult, kycResult };
} catch (error) {
console.error("❌ Registration failed:", error);
return { success: false, error };
}
}
registerUserAndKYC();Complete Payment Flow
Step 1: Place an Order
const orderResult = await scanToPay.placeOrder({
qrString:
"00020101021138520010A000000727012200069704160108355454570208QRIBFTTA53037045802VN6304B345",
amount: 15000, // Amount in minor units (15000 = 15.000 VND)
fiatCurrency: "VND", // Target fiat currency
cryptoCurrency: "USDC", // Crypto currency to use
fromAddress: "YOUR_WALLET_ADDRESS", // Your Solana wallet address
chain: "Solana",
});
console.log("Order ID:", orderResult.orderId);
console.log("Status:", orderResult.status); // Should be "awaiting_crypto_transfer"Step 2: Decode and Prepare Transaction
// The SDK returns a Legacy Transaction (not Versioned)
const transaction = await scanToPay.decodeTransaction(orderResult);
console.log("Fee Payer:", transaction.feePayer?.toString());
console.log("Instructions:", transaction.instructions.length);
console.log("Blockhash:", transaction.recentBlockhash);Step 3: Sign and Submit Transaction
async function signAndSubmitTransaction(
transaction: Transaction,
connection: Connection
) {
const payer = loadKeypairFromEnv();
try {
// Get fresh blockhash for better reliability
const { blockhash } = await connection.getLatestBlockhash("confirmed");
// Sign the transaction
transaction.sign(payer);
console.log("✅ Transaction signed successfully");
// Submit transaction
const txid = await connection.sendRawTransaction(transaction.serialize(), {
skipPreflight: true,
maxRetries: 3,
});
console.log("📤 Transaction submitted:", txid);
return txid;
} catch (error) {
console.error("Transaction failed:", error);
throw error;
}
}
const transactionHash = await signAndSubmitTransaction(transaction, connection);Step 4: Verify the Order
// Wait a moment for transaction confirmation
await new Promise((resolve) => setTimeout(resolve, 10000));
const verifyResult = await scanToPay.verifyOrder({
orderId: orderResult.orderId,
transactionProof: transactionHash,
});
console.log("Verified:", verifyResult.status);Step 5: Check Final Status
const statusResult = await scanToPay.getStatus({
orderId: orderResult.orderId,
});
console.log("Final Status:", statusResult.status);Order History
The SDK provides methods to retrieve order history for users, which can be useful for displaying transaction records and tracking payment activity.
Get Order History by User Email/ID
Retrieve order history using a user's email address or user ID:
const orderHistory = await scanToPay.getOrderHistoryByUserName({
identifier: "[email protected]", // Email address or user ID
page: 1, // Page number (optional, defaults to 1)
limit: 20, // Number of orders per page (optional, defaults to 20)
});
console.log("Order history:", orderHistory);
// Returns: { status, data: { user, orders: { items, pagination } } }Get Order History by Wallet Address
Retrieve order history using a Solana wallet address:
const orderHistory = await scanToPay.getOrderHistoryByWallet({
walletAddress: "AYwJ2JCteMY2FxSJXjFpkCSxTvff1wzHgFrT1iDJpi8W",
page: 1, // Page number (optional, defaults to 1)
limit: 20, // Number of orders per page (optional, defaults to 20)
});
console.log("Order history:", orderHistory);
// Returns: { status, data: { user, orders: { items, pagination } } }Complete Order History Example
import { InitializeScanToPay, Environment } from "@gaian-sdk/payments";
import "dotenv/config";
async function getOrderHistory() {
const scanToPay = new InitializeScanToPay({
environment: Environment.DEV,
paymentsUrl: "https://gaian-payments-backend-3.onrender.com",
userUrl: "http://127.0.0.1:3000",
timeout: 30000,
});
try {
// Get order history by email
console.log("📋 Fetching order history by email...");
const historyByEmail = await scanToPay.getOrderHistoryByUserName({
identifier: "[email protected]",
page: 1,
limit: 10,
});
console.log("✅ Order history by email:", historyByEmail.data.orders.items);
console.log("📄 Pagination:", historyByEmail.data.orders.pagination);
// Get order history by wallet address
console.log("📋 Fetching order history by wallet...");
const historyByWallet = await scanToPay.getOrderHistoryByWallet({
walletAddress: "AYwJ2JCteMY2FxSJXjFpkCSxTvff1wzHgFrT1iDJpi8W",
page: 1,
limit: 10,
});
console.log("✅ Order history by wallet:", historyByWallet.data.orders.items);
console.log("📄 Pagination:", historyByWallet.data.orders.pagination);
} catch (error) {
console.error("❌ Failed to fetch order history:", error);
}
}
getOrderHistory();Error Handling
try {
// Your payment flow code here
} catch (error) {
console.error("Payment failed:", error);
// Handle specific error types
if (error.message.includes("insufficient funds")) {
console.log("💡 Please fund your wallet with SOL and USDC");
} else if (error.message.includes("blockhash")) {
console.log("💡 Try again - blockhash expired");
}
}Complete Example
import { InitializeScanToPay, Environment } from "@gaian-sdk/payments";
import { Connection, Keypair, Transaction } from "@solana/web3.js";
import bs58 from "bs58";
import "dotenv/config";
async function processPayment() {
// Initialize SDK
const scanToPay = new InitializeScanToPay({
environment: Environment.DEV,
paymentsUrl: "http://0.0.0.0:3000",
userUrl: "http://0.0.0.0:3000",
timeout: 30000,
solanaRpcUrl: process.env.SOLANA_RPC,
});
const connection = new Connection(process.env.SOLANA_RPC || "");
const payer = Keypair.fromSecretKey(
bs58.decode(process.env.SOLANA_PRIVATE_KEY!)
);
try {
// 1. Place order
const order = await scanToPay.placeOrder({
qrString: "YOUR_QR_CODE_STRING",
amount: 15000,
fiatCurrency: "VND",
cryptoCurrency: "USDC",
fromAddress: payer.publicKey.toString(),
chain: "Solana",
});
// 2. Decode transaction
const transaction = await scanToPay.decodeTransaction(order);
// 3. Sign and submit
transaction.sign(payer);
const txHash = await connection.sendRawTransaction(
transaction.serialize(),
{
skipPreflight: true,
maxRetries: 3,
}
);
// 4. Wait and verify
await new Promise((resolve) => setTimeout(resolve, 10000));
const verification = await scanToPay.verifyOrder({
orderId: order.orderId,
transactionProof: txHash,
});
// 5. Get final status
const status = await scanToPay.getStatus({ orderId: order.orderId });
console.log("Payment completed:", {
orderId: order.orderId,
transactionHash: txHash,
verified: verification.status,
finalStatus: status.status,
});
} catch (error) {
console.error("Payment failed:", error);
}
}
processPayment();API Reference
InitializeScanToPay Constructor Options
| Property | Type | Description |
| -------------- | ------------------------------------- | ------------------------------- |
| environment | Environment.DEV \| Environment.PROD | SDK environment |
| paymentsUrl | string | Payments API base URL |
| userUrl | string | User API base URL |
| timeout | number | Request timeout in milliseconds |
| solanaRpcUrl | string | Solana RPC endpoint URL |
registerUser(params)
Registers a new user in the system.
Parameters:
email: User's email addresswalletAddress: User's Solana wallet address
Returns: User object with id, email, and walletAddress
generateKYCLink(params)
Generates a KYC verification link for a registered user.
Parameters:
userId: User ID from registration (as string)email: User's email addressphone: User's phone number (optional)
Returns: KYC link object for user verification
placeOrder(params)
Places a new payment order.
Parameters:
qrString: QR code string to processamount: Payment amount in minor currency unitsfiatCurrency: Target fiat currency (e.g., "VND")cryptoCurrency: Cryptocurrency to use (e.g., "USDC")fromAddress: Your Solana wallet addresschain: Blockchain name ("Solana")
Returns: Order object with orderId and status
decodeTransaction(orderResult)
Decodes the payment transaction from an order.
Parameters:
orderResult: Result fromplaceOrder()
Returns: Solana Transaction object (Legacy format)
verifyOrder(params)
Verifies a completed transaction.
Parameters:
orderId: Order ID fromplaceOrder()transactionProof: Transaction hash from blockchain
Returns: Verification result with status
getStatus(params)
Gets the current status of an order.
Parameters:
orderId: Order ID to check
Returns: Status object with current order state
getOrderHistoryByUserName(params)
Gets order history for a user by email or user ID.
Parameters:
identifier: User's email address or user IDpage: Page number (optional, defaults to 1)limit: Number of orders per page (optional, defaults to 20)
Returns: Object containing user information and paginated order history
getOrderHistoryByWallet(params)
Gets order history for a user by wallet address.
Parameters:
walletAddress: User's Solana wallet addresspage: Page number (optional, defaults to 1)limit: Number of orders per page (optional, defaults to 20)
Returns: Object containing user information and paginated order history
Troubleshooting
Common Issues
"SOLANA_PRIVATE_KEY required"
- Ensure your
.envfile has the private key in base58 format
- Ensure your
"insufficient funds"
- Fund your wallet with SOL for transaction fees
- Fund your wallet with USDC for payments
"blockhash not found"
- The transaction took too long - retry with a fresh transaction
Network timeouts
- Check your RPC endpoint is working
- Try increasing the timeout value
User registration errors
- Verify email format is valid
- Ensure wallet address is a valid Solana public key
- Check that userUrl is correctly configured
Getting Test Tokens
For development/testing on devnet:
- SOL:
solana airdrop 2 YOUR_ADDRESS --url devnet - USDC: Visit https://faucet.circle.com/
Checking Balances
// Check SOL balance
const balance = await connection.getBalance(payer.publicKey);
console.log("SOL balance:", balance / 1e9);
// Check USDC balance
import { getAssociatedTokenAddress, getAccount } from "@solana/spl-token";
const USDC_MINT = "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"; // Devnet
const ata = await getAssociatedTokenAddress(
new PublicKey(USDC_MINT),
payer.publicKey
);
const tokenAccount = await getAccount(connection, ata);
console.log("USDC balance:", Number(tokenAccount.amount) / 1e6);Support
For issues and questions:
- Check the troubleshooting section above
- Review your environment setup
- Ensure your wallet has sufficient funds
- Verify your RPC endpoint is responsive
