# API Documentationeer

## Overview

This document will guide you step by step to implement our API service. Here is the API flow with its example.

You may need to refer to these environment URLs:

Sandbox: For testing and integration. No real money is involved.

* Payment: <https://dev-payments.gaian-dev.network>
* User: <https://dev-user.gaian-dev.network>

Production: For live transactions with real customers.

* Payment:<https://payments.gaian-dev.network>
* User:<https://user.gaian-dev.network>

You can have a look at the general sequence diagram.

{% @mermaid/diagram content="sequenceDiagram
participant Client as Client App
participant UserAPI as User API
participant KYC as KYC Service
participant PaymentAPI as Payment API
participant Blockchain as Solana Chain
participant Bank as Bank System

```
Note over Client, Bank: User Registration Flow
Note right of Client: Store API key for subsequent requests
Note right of Client: Add to Request Header with this format:"X-api-key": "api_key"

Client->>UserAPI: POST /api/v1/user/register
Note right of Client: {email, walletAddress}
UserAPI-->>Client: User registered successfully
Note left of UserAPI: {status, message, user}

Client->>UserAPI: POST /api/v1/kyc/link
Note right of Client: {walletAddress}
UserAPI->>KYC: Generate KYC link
KYC-->>UserAPI: KYC URL generated
UserAPI-->>Client: KYC external link
Note left of UserAPI: {success, websdkUrl}

Note over Client, Bank: Standard Payment Flow

Client->>PaymentAPI: POST /api/v1/placeOrder
Note right of Client: {qrString, amount, fiatCurrency,<br/>cryptoCurrency, chain, fromAddress}
PaymentAPI-->>Client: Order placed
Note left of PaymentAPI: {orderId, status: "awaiting_crypto_transfer",<br/>cryptoTransferInfo, qrInfo,...}

Client->>Client: Build & Sign Transaction
Note right of Client: Create Solana transaction<br/>using cryptoTransferInfo

Client->>Blockchain: Submit signed transaction
Blockchain-->>Client: Transaction hash

Client->>PaymentAPI: POST /api/v1/verifyOrder
Note right of Client: {orderId, transactionProof}
PaymentAPI->>Blockchain: Verify transaction on chain
Blockchain-->>PaymentAPI: Transaction confirmed
PaymentAPI-->>Client: Order verified
Note left of PaymentAPI: {status: "verified",<br/>bankTransferStatus: "queued"}

loop Polling for status updates
    Client->>PaymentAPI: GET /api/v1/status
    Note right of Client: {orderId}
    PaymentAPI-->>Client: Current status
    Note left of PaymentAPI: {status: "processing" | "completed"}
end

PaymentAPI->>Bank: Process bank transfer
Bank-->>PaymentAPI: Transfer completed
PaymentAPI-->>Client: Final status: "completed"

Note over Client, Bank: Prefunded Payment Flow

Client->>PaymentAPI: POST /api/v1/placeOrder/prefund
Note right of Client: {qrString, amount, fiatCurrency,<br/>cryptoCurrency, fromAddress}
PaymentAPI->>PaymentAPI: Deduct from user's<br/>prefunded balance
	PaymentAPI->>PaymentAPI: Proceed order automatically
loop Polling for status updates
    Client->>PaymentAPI: GET /api/v1/status
    Note right of Client: {orderId}
    PaymentAPI-->>Client: Current status
    Note left of PaymentAPI: {status: "processing" | "completed"}
end

PaymentAPI->>Bank: Process bank transfer
Bank-->>PaymentAPI: Transfer completed
PaymentAPI-->>Client: Final status: "completed"

Note over Client, Bank: Additional APIs

opt Parse QR Code
    Client->>PaymentAPI: POST /api/v1/parseQr
    Note right of Client: {qrString, country}
    PaymentAPI-->>Client: QR info parsed
    Note left of PaymentAPI: {success, qrInfo}
end

opt Calculate Exchange Rate
    Client->>PaymentAPI: POST /api/v1/calculateExchange
    Note right of Client: {amount, country, chain, token}
    PaymentAPI-->>Client: Exchange calculation
    Note left of PaymentAPI: {exchangeInfo}
end

opt Get Transaction History
    Client->>UserAPI: GET /api/v1/users/{email}/orders<br/>GET /api/v1/users/wallet/{wallet}/orders
    Note right of Client: {page, limit, status}
    UserAPI-->>Client: Transaction history
    Note left of UserAPI: {user, orders, pagination}
end" %}
```

## API Key

### Step 1: Add API Key to Request Header

You will need to add API Key field to Header of every Requests as following:

```bash
"x-api-key": "api_key"
```

## User Registeration flow

With this flow follow `User` path

### Step 1: User Register

#### Step 1.1: User Register (Register and KYC on Gaian side)

You will need to make a POST request to `/api/v1/user/register`

```bash
POST /api/v1/user/register

Resquest
{
	email: "abcd1456@gmail.com",(Optional)
	walletAddress: "abcd...feg234"
}

Response
{
  status: "success",
  message: "User registered successfully",
  user: { id: 18, email: "abcd1456@gmail.com" }
}
```

#### Step 1.2: KYC Sharing (KYC on Partner side)

If you want to do KYC on your side and want to share it, make user flow more smoothly, you can use this `KYC Sharing` API

{% hint style="info" %}
Users must be registered via the `/user/register` API; automatic creation is no longer supported.
{% endhint %}

You need to make a POST request to `api/v1/submit-kyc-information`

```bash
POST api/v1/submit-kyc-information

Request
{
	userWalletAddress: "abcd....132ds"
	userEmail: "nguyenvana@gmail.com",
	firstName: "A",
	lastName: "Nguyen Van",
	dateOfBirth: "1996-10-10", #YYYY-MM-DD
	gender: "male", # ["male","female"]
	nationality: "VN",
	type: "ID_CARD", # ["ID_CARD","PASSPORT"]
	nationalId: "12130163",
	issueDate: "2020-06-06", #YYYY-MM-DD
	expiryDate: "2030-06-06", #YYYY-MM-DD
	addressLine1: "Ho Chi Minh",
	addressLine2: "Go Vap",
	city: "Ho Chi Minh",
	state: "Ho Chi Minh",
	zipCode: "70000",
	frontIdImage: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/.........", #Base 64 string
	backIdImage: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/.........", #Base 64 string
	holdIdImage: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/.........", #Base 64 string
	phoneNumber: "0968861116",
	phoneCountryCode: "84",
}

Response
{
	success: true,
	message: "KYC submitted successfully",
	data: {
		user: "nguyenvana@gmail.com",
		walletAddress: "abcd....132ds",
		kycStatus: "status" #Status gonna have:["not started", "under review", "approved", "rejected"]
	}
}
```

### Step 2: User KYC

User need to be KYC-ed to be able to make a payment. You will need to make a POST request to `/api/v1/kyc/link`

```bash
POST /api/v1/kyc/link

Request
{
  email: "abcd1456@gmail.com", (Optional)
  walletAddress: "wallet_address_string"
}

Response
{
	success: true ,
	message: "KYC external link generated successfully",
	websdkUrl: "<https://linktokyc/websdk/p/example.com>",
}
```

## Payment Flow

With this flow follow `Payment` path

### Step 1: Place Order

#### Step 1.1: Place Order (Without Prefunded)

You will need to make a POST request to `/api/v1/placeOrder`

⚠️Please be caution: These QR string was meant to be use in SANDBOX. DO NOT USE IT IN PRODUCTION

To test with `BRA` please use this QR String:

```bash
qrString: "00020126490014br.gov.bcb.pix0127geovannamendes245@gmail.com5204000053039865802BR5924GEOVANNA MENDES SIQUEIRA6009Sao Paulo62290525REC691B4B40C700E4859304836304FB76"
```

To test with `VND` please use this QR String:

```bash
qrString: "00020101021126400010vn.zalopay0115uvsgayNI4Xsrqwz020300238620010A00000072701320006970454011899ZP24250M421803650208QRIBFTTA5204739953037045802VN63041428"
```

To test with `PHP` please use this QR String:

```bash
qrString: "00020101021127590012com.p2pqrpay0111UBPHPHMMXXX02089996440304121096459500755204601653036085802PH5925Sophia Marie Chavez Dever6009SAN PEDRO63043708"
```

```bash
POST /api/v1/placeOrder

Request
{
  qrString: "qrString",
  amount: 10, #Please test with the amount higher than 50,000 VND or 10 PHP
  fiatCurrency: "PHP", #Currently supported ["VND", "PHP", "BRA"]
  cryptoCurrency: "USDC",  #Currently only USDC supported
  chain: "Solana", #Currently supported ["Solana", "Ethereum", "Polygon", "Arbitrum", "Base"]
  fromAddress: "fromWalletAddress",
  transactionReference: "Custom label - defaults to Crypto payout {orderId}" (Optional)
}

Response
{
  "orderId": "order_id",
  "status": "awaiting_crypto_transfer",
  "fiatAmount": 10,
  "fiatCurrency": "PHP",
  "cryptoAmount": 0.3329508,
  "cryptoCurrency": "USDC",
  "exchangeRate": 58.50346529177664,
  "qrInfo": {
      "encodedString": "00020101021127590012com.p2pqrpay0111UBPHPHMMXXX02089996440304121096459500755204601653036085802PH5925Sophia Marie Chavez Dever6009SAN PEDRO63043708",
      "providerInfo": {
          "name": "com.p2pqrpay",
          "service": "UNION BANK OF THE PHILIPPINES"
      },
      "bankInfo": {
          "bankBin": "UBPHPHMM",
          "bankNumber": "109645950075"
      },
      "additionalData": "{\\"merchantMobileNumber\\":\\"\\",\\"merchantCity\\":\\"SAN PEDRO\\",
										      \\"crcCode\\":\\"3708\\",\\"purpose\\":\\"\\",\\"mobileNumber\\":\\"\\",
										      \\"tfrName\\":\\"Sophia Marie Chavez Dever\\",\\"tfrBnkCode\\":\\"UBPHPHMMXXX\\",
										      \\"userId\\":\\"\\",\\"paymentType\\":\\"99964403\\",\\"customerLabel\\":\\"\\",
										      \\"merchantCategoryCode\\":\\"6016\\",\\"tfrAcctNo\\":\\"109645950075\\",\\"postCode\\":\\"\\",
										      \\"uniqueId\\":\\"com.p2pqrpay\\",\\"loyaltyNumber\\":\\"\\"}",
      "beneficiaryName": "Sophia Marie Chavez Dever",
      "countryCode": "PH"
  },
  "cryptoTransferInfo": {
      "chain": "Solana",
      "fromAddress": "CKhbBVLBCYaRH6GFP7TXs3PBrKkbLys9WvDs5Mm7UuLH",
      "toAddress": "FK2ABh32TKMHgZRQKrxAQqopcs8Gm4igPB4s2roUCKJb",
      "token": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
      "amount": 332951,
      "encodedTransaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQABBKg6575mmykdPEHBGvqzj3YdwYf/J7jO9ng5O+x57uTG/xZ9DjASbM7EGxZeyxmfQhR/gQWXvfemaKfwEv3Kz2tm09cpQsp1a9JslSMyq8KzlUACfcX9FMqjwWKIwPl8/wbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpfYXAvMSOOX463KV9P2vHV03BrbYR/die/tndzaBb9VsBAwMBAgAJA5cUBQAAAAAAAA=="
  },
  "timestamp": "2025-12-02T08:05:29.588Z",
  "transactionReference":"Crypto payout {orderId}"
}
```

#### Step 1.2: Place Order (With Prefunded)

To use this flow please reach out to us.

You will need to make a POST request to `/api/v1/placeOrder/prefund`

After place an order, it will be proceeded automatically.

⚠️Please be caution: These QR string was meant to be use in SANDBOX. DO NOT USE IT IN PRODUCTION

To test with `VND` please use this QR String:

```bash
qrString: "00020101021126400010vn.zalopay0115uvsgayNI4Xsrqwz020300238620010A00000072701320006970454011899ZP24250M421803650208QRIBFTTA5204739953037045802VN63041428"
```

To test with `PHP` please use this QR String:

```bash
qrString: "00020101021127590012com.p2pqrpay0111UBPHPHMMXXX02089996440304121096459500755204601653036085802PH5925Sophia Marie Chavez Dever6009SAN PEDRO63043708"
```

```bash
POST /api/v1/placeOrder/prefund

Request
{
  qrString: "qrString",
  amount: 10, #Please test with the amount higher than 50,000 VND or 10 PHP
  fiatCurrency: "PHP", #Currently supported ["VND", "PHP", "BRA"]
  cryptoCurrency: "USDC", #Currently supported ["USDC", "USDT"]
  fromAddress: "fromWalletAddress", #User wallet for reference
  transactionReference: "Custom label - defaults to Crypto payout {orderId}" (Optional)
}

Response
{
	"orderId":"order_id",
	"status":"awaiting_crypto_transfer",
	"fiatAmount":10,
	"fiatCurrency":"PHP",
	"cryptoAmount":0.1709,
	"cryptoCurrency":"USDC",
	"exchangeRate":58.52906449777173,
	"qrInfo":
		{
			"encodedString":"00020101021127590012com.p2pqrpay0111UBPHPHMMXXX02089996440304121096459500755204601653036085802PH5925Sophia Marie Chavez Dever6009SAN PEDRO63043708",
			"providerInfo":{},
			"bankInfo":{},
			"beneficiaryName":"Sophia Marie Chavez Dever",
			"additionalData":"{\\"merchantMobileNumber\\":\\"\\",\\"merchantCity\\":\\"SAN PEDRO\\",
												\\"crcCode\\":\\"3708\\",\\"purpose\\":\\"\\",\\"mobileNumber\\":\\"\\",
												\\"tfrName\\":\\"Sophia Marie Chavez Dever\\",\\"tfrBnkCode\\":\\"UBPHPHMMXXX\\",
												\\"userId\\":\\"\\",\\"paymentType\\":\\"99964403\\",\\"customerLabel\\":\\"\\",
												\\"merchantCategoryCode\\":\\"6016\\",\\"tfrAcctNo\\":\\"109645950075\\",
												\\"postCode\\":\\"\\",\\"uniqueId\\":\\"com.p2pqrpay\\",\\"loyaltyNumber\\":\\"\\"}"
		},
	"cryptoTransferInfo":
			{
				"chain":"solana",
				"fromAddress":"CKhbBVLBCYaRH6GFP7TXs3PBrKkbLys9WvDs5Mm7UuLH",
				"toAddress":"FK2ABh32TKMHgZRQKrxAQqopcs8Gm4igPB4s2roUCKJb",
				"token":"4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
				"amount":0.1709
			},
	"timestamp":"2025-12-02T08:01:26.914Z",
	"isPrefunded":true,
	"transactionReference":"Crypto payout {orderId}"
	}
```

### Step 2: Build and Sign the Transaction

Please notice⚠️: Skip this step if you’re using `Prefunded` flow.

This will need to be perform on your side. You will need a build instruction function, example in TypeScript:

```tsx
  decodeTransaction = async (
    data: PlaceOrderResponse
  ): Promise<Transaction> => {
    if (!this.solanaConnection)
      throw new Error(Solana connection not defined);

    const { blockhash } = await this.solanaConnection.getLatestBlockhash(
      confirmed
    );

    const transferInstruction = await buildSolanaSendTransactionInstruction(
      new PublicKey(data.cryptoTransferInfo.fromAddress),
      new PublicKey(data.cryptoTransferInfo.toAddress),
      new PublicKey(data.cryptoTransferInfo.token),
      data.cryptoTransferInfo.amount
    );

    // Create Legacy Transaction instead of VersionedTransaction
    const transaction = new Transaction();
    transaction.add(transferInstruction);
    transaction.recentBlockhash = blockhash;
    transaction.feePayer = new PublicKey(data.cryptoTransferInfo.fromAddress);

    return transaction;
  };
```

Then sign and submit the transaction to chain.

### Step 3: Verify Order

Please notice⚠️: Skip this step if you’re using `Prefunded` flow.

After submitting the transaction to chain, you will need to make a POST request with the transaction hash (tx\_Hash) hash and order Id ( `order_id_string`), to `/api/v1/verifyOrder`

```bash
POST /api/v1/verifyOrder

Request
{
	orderId: "order_id_string",
	transactionProof: "tx_Hash"
}

Response
{
	orderId: "order_id_string",
	status: "verified" , #List of status: awaiting_crypto_transfer, verified, processing, failed or completed
	transactionHash: "tx_Hash",
	message: "Transaction verified and bank transfer queued", 
	bankTransferStatus: "queued"
}
```

### Step 4: Polling the Status

After the order is `verified` , it will be auto-processing. Then you will need to make a polling function to make a GET request to `/api/v1/status` after an amount of time

```bash
GET /api/v1/status

Request
{
	orderId: "order_id_string"
}

Response (Processing)
{
  id: 138,
  orderId: "order_id_string",
  status: "processing",
  fiatAmount: 20000,
  fiatCurrency: "VND",
  cryptoAmount: 0.8,
  cryptoCurrency: USDC,
  exchangeRate: 26000,
  qrInfo: {
    bankInfo: {
      bankBin: "123456",
      bankNumber: "567891"
    },
    providerInfo: {
      guid: "A000000727",
      name: "VIETQR",
      service: "QRIBFTTA"
    },
    encodedString: "qrString"
  },
  paymentMethod: "qr_code",
  expiresAt: "timeStamp",
  bankTransactionReference: {},
  createdAt: "timeStamp",
  updatedAt: "timeStamp",
  userId: null,
  transactionHash: "tx_Hash",
  pollCount: 24,
  lastChecked: "time"
}

Response (Completed)
{
  id: 138,
  orderId: "order_id_string",
  status: "completed",
  fiatAmount: 20000,
  fiatCurrency: "VND",
  cryptoAmount: 0.8,
  cryptoCurrency: USDC,
  exchangeRate: 26000,
  qrInfo: {
    bankInfo: {
      bankBin: "123456",
      bankNumber: "567891"
    },
    providerInfo: {
      guid: "A000000727",
      name: "VIETQR",
      service: "QRIBFTTA"
    },
    encodedString: "qrString"
  },
  paymentMethod: "qr_code",
  expiresAt: "timeStamp",
  bankTransactionReference: {
	  requestId: "require_id",
	  requestDate "timeStamp"
  },
  createdAt: "timeStamp",
  updatedAt: "timeStamp",
  userId: null,
  transactionHash: "tx_Hash",
  pollCount: 24,
  lastChecked: "time"
}
```

## Additional API

### Parse QR API

With this flow follow `Payment` path

You will need to make a POST request to `/api/v1/parseQr`

```bash
POST /api/v1/parseQr

Request
{
	qrString: "Qr_string",
	country: "VN"
}

Response
{
	success: true ,
	qrInfo: {
		isValid: true ,
		encodedString: "Qr_string",
		country: "VN",
		qrProvider: "VIETQR",
		bankBin:  "123456",
		accountNumber: "567891",
		currency: "704",
		nation: "VN",
		beneficiaryName: "NGUYEN VAN A",
		detailedQrInfo: {
			provider: {
			 fieldId:  "38",
			 guid:  "A000000727",
			 name:  "VIETQR",
			 data:  "data_string",
			 service:  "QRIBFTTA"
			},
			consumer: {
				bankBin:  "123456",
				bankNumber: "567891",
			},
			merchant: {
			
			},
			additionalData: {
			
			},
			version:  "01",
			initMethod:  "11",
			currency:  "704",
			nation:  "VN",
			crc:  "60C4"
		}
	},
}
```

### Calculate Exchange

With this flow follow `Payment` path

Calculate cryptocurrency equivalent for fiat amount with current exchange rates

You will need to make a POST request to `/api/v1/calculateExchange`

```bash
POST /api/v1/calculateExchange

Request
{
  amount: 26000,
  country: "VN",
  chain: "Solana",
  token: "USDC"
}

Response
{
  success: true,
  exchangeInfo: {
    fiatAmount: 26000,
    fiatCurrency: "VND",
    cryptoAmount: "0.9846",
    cryptoCurrency: "USDC",
    exchangeRate: "26407.63793324218",
    chain: "Solana",
    token: "USDC",
    timestamp: "timeStamp",
    feeAmount: "fee_Amount"
  }
}
```

### Get Transaction History

With this flow follow `User` path

#### By User Email

You will need to make a GET request to `/api/v1/users/{User_Email}/orders`

```bash
GET /api/v1/users/{User_Email}/orders

Request
{
	page: 1, (Optional)
	limit: 5, (Optional)
	status: "completed" (Optional)
}

Response
{
  status: "success",
  data: {
    user: {
      id: 12,
      email: "User_Email",
      walletAddress: "Wallet_Address",
      createdAt: "2025-09-15T04:28:20.451Z",
      updatedAt: "2025-09-15T05:33:57.873Z"
    },
    orders: {
      items: [], #Empty because there are no complete order
      pagination: {
        page: 1,
        limit: 5,
        total: 0,
        totalPages: 0,
        hasNext: false,
        hasPrev: false
      }
    }
  }
}
```

#### By User Wallet

You will need to make a GET request to `/api/v1/users/wallet/{User_Wallet}/orders`

```bash
GET /api/v1/users/wallet/{User_Wallet}/orders

Request
{
	page: 1, (Optional)
	limit: 5, (Optional)
	status: "completed" (Optional)
}

Response
{
  status: "success",
  data: {
    user: {
      id: 12,
      email: "User_Email",
      walletAddress: "Wallet_Address",
      createdAt: "2025-09-15T04:28:20.451Z",
      updatedAt: "2025-09-15T05:33:57.873Z"
    },
    orders: {
      items: [], #Empty because there are no complete order
      pagination: {
        page: 1,
        limit: 5,
        total: 0,
        totalPages: 0,
        hasNext: false,
        hasPrev: false
      }
    }
  }
}
```

### Get User Infomation

With this flow follow `User` path

#### By User Email or User Wallet Address

You will need to make a GET request to `/api/v1/users/?email="?"`

Or, you will need to make a GET request to `/api/v1/users/?walletAddress="?"`

```bash
GET /api/v1/users/?email="abcdg@gmail.com"
Request
{
	"email" :"abcd@gmail.com"
}

Response
{
    "status": "success",
    "user": {
        "id": 1,
        "email": "abcd@gmail.com",
        "walletAddress": "abdcefghijklmnopqrstuvwxyz",
        "kyc": {
            "status": "approved", //Status gonna have:[not started, under review, approved, rejected]
            "firstName": "John",
            "lastName": "Mock-Doe",
            "country": "VNM",
            "applicantPlatform": "Web",
            "phone": null
        }
    }
}

GET /api/v1/users/?walletAddress="abdcefghijklmnopqrstuvwxyz"

Request
{
	walletAddress:"abdcefghijklmnopqrstuvwxyz"
}

Response
{
    "status": "success",
    "user": {
        "id": 1,
        "email": "abcd@gmail.com",
        "walletAddress": "abdcefghijklmnopqrstuvwxyz",
        "kyc": {
            "status": "approved",
            "firstName": "John",
            "lastName": "Mock-Doe",
            "country": "VNM",
            "applicantPlatform": "Web",
            "phone": null
        }
    }
}
```

## API Reference

[Register User](https://developer.gaian.network/api-refeneces/register-user)

[Get User Infomation](https://developer.gaian.network/api-refeneces/get-user-information)

[KYC User](https://developer.gaian.network/api-refeneces/get-order-status)

[Place Order](https://developer.gaian.network/api-refeneces/place-order)

[Verify Order](https://developer.gaian.network/api-refeneces/verify-order)

[Get Order Status](https://developer.gaian.network/api-refeneces/get-order-status)

[Parse QR String](https://developer.gaian.network/api-refeneces/parse-qr-string)

[Calculate Exchange](https://developer.gaian.network/api-refeneces/calculate-exchange)

[Get User Transaction History](https://www.notion.so/Get-User-Transaction-History-2782c41f99d780e8912acc79848e0872?pvs=21)

[KYC Sharing](https://developer.gaian.network/api-refeneces/kyc-sharing)

## KYC Requirement

[KYC Requirement](https://developer.gaian.network/kyc-requirement/kyc-requirement)
