UNPKG

@gaian-sdk/payments

Version:

A powerful SDK for processing cryptocurrency payments via QR codes on the Solana blockchain.

580 lines (423 loc) 15.8 kB
# @gaian-sdk/payments Documentation A powerful SDK for processing cryptocurrency payments via QR codes on the Solana blockchain. ## Installation ```bash npm install @gaian-sdk/payments # or yarn add @gaian-sdk/payments ``` ## Required Dependencies ```bash npm install @solana/web3.js @solana/spl-token bs58 dotenv ``` ## Environment Setup Create a `.env` file in your project root: ```env SOLANA_PRIVATE_KEY=your_base58_encoded_private_key_here SOLANA_RPC=https://api.devnet.solana.com # or your preferred RPC endpoint NODE_ENV=development ``` ### Getting Your Private Key 1. **Generate a new keypair** (if you don't have one): ```bash solana-keygen new --outfile ~/.config/solana/id.json ``` 2. **Export as base58** (required format): ```javascript 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); ``` 3. **Fund your wallet** with SOL and USDC: - SOL: `solana airdrop 2 YOUR_WALLET_ADDRESS --url devnet` - USDC: Visit [SPL Token Faucet](https://spl-token-faucet.com/) ## Basic Usage ### 1. Initialize the SDK ```typescript 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 ```typescript 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 ```typescript const userResult = await scanToPay.registerUser({ email: "user@example.com", walletAddress: "AYwJ2JCteMY2FxSJXjFpkCSxTvff1wzHgFrT1iDJpi8W", // Solana wallet address }); console.log("User registered:", userResult); // Returns: { user: { id: number, email: string, walletAddress: string } } ``` ### Generate KYC Link ```typescript 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 verification ``` ### Complete User Registration Example ```typescript 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: "test@example.com", 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 ```typescript 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 ```typescript // 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 ```typescript 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 ```typescript // 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 ```typescript 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: ```typescript const orderHistory = await scanToPay.getOrderHistoryByUserName({ identifier: "user@example.com", // 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: ```typescript 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 ```typescript 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: "test@example.com", 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 ```typescript 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 ```typescript 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 address - `walletAddress`: 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 address - `phone`: 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 process - `amount`: Payment amount in minor currency units - `fiatCurrency`: Target fiat currency (e.g., "VND") - `cryptoCurrency`: Cryptocurrency to use (e.g., "USDC") - `fromAddress`: Your Solana wallet address - `chain`: Blockchain name ("Solana") **Returns:** Order object with `orderId` and `status` ### decodeTransaction(orderResult) Decodes the payment transaction from an order. **Parameters:** - `orderResult`: Result from `placeOrder()` **Returns:** Solana `Transaction` object (Legacy format) ### verifyOrder(params) Verifies a completed transaction. **Parameters:** - `orderId`: Order ID from `placeOrder()` - `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 ID - `page`: 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 address - `page`: 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 1. **"SOLANA_PRIVATE_KEY required"** - Ensure your `.env` file has the private key in base58 format 2. **"insufficient funds"** - Fund your wallet with SOL for transaction fees - Fund your wallet with USDC for payments 3. **"blockhash not found"** - The transaction took too long - retry with a fresh transaction 4. **Network timeouts** - Check your RPC endpoint is working - Try increasing the timeout value 5. **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/](https://faucet.circle.com/) ### Checking Balances ```typescript // 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