npm package: @gaian-sdk/payments
Description: A powerful SDK for processing cryptocurrency payments via QR codes on the Solana blockchain.
Author: Your Name
Latest version: 0.6.4

# @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