Skip to main content

Token Purchase Flow

This guide walks through the complete integration for purchasing NOCY claim allocations using the Get NOCY API.

Claim Allocations

The Get NOCY API allows users to purchase claim allocations, not immediate token transfers. Once payment is confirmed (order status = PAID), the allocation is secured. Tokens will be claimable via the Claim Portal at a later date.

Overview

The purchase flow consists of these steps:

  1. Get Price - Fetch current NOCY price
  2. Create Order - Submit allocation order
  3. Get Payment Address - Retrieve ADA payment address
  4. Send Payment - User sends ADA
  5. Monitor Status - Poll until PAID

Step 1: Get Current Price

First, fetch the current NOCY token price:

async function getNocyPrice() {
const response = await fetch('https://api.get.nocy.io/api/price');
const { priceInAda } = await response.json();
return priceInAda;
}

// Calculate cost for desired amount
const price = await getNocyPrice();
const nocyAmount = 10000;
const totalCost = nocyAmount * price;

console.log(`${nocyAmount} NOCY = ${totalCost} ADA`);

Step 2: Create Purchase Order

Create an order with the user's wallet address and desired NOCY amount:

async function createOrder(payerAddress, amountNocy) {
const response = await fetch('https://api.get.nocy.io/api/order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
payerAddress,
amountNocy
})
});

if (!response.ok) {
throw new Error('Failed to create order');
}

return response.json();
}

const order = await createOrder(
'addr1qy2z3w4x5y6z7a8b9c0d1e2f3g4h5i6j7k8l9m0n1o2p3q4r5s6t7u8v9w0x1y2z3a4b5c6d7e8f9g0h1i2j3k4l5m6n7',
10000
);

console.log('Order ID:', order.id);
console.log('Amount to pay:', order.amountAda, 'ADA');
console.log('Status:', order.status); // PENDING

Step 3: Get Payment Address

Retrieve the ADA address where the user should send payment:

async function getPaymentAddress() {
const response = await fetch('https://api.get.nocy.io/api/payment-address');
const { address } = await response.json();
return address;
}

const paymentAddress = await getPaymentAddress();
console.log('Send ADA to:', paymentAddress);

Step 4: Display Payment Instructions

Show the user clear payment instructions:

function displayPaymentInstructions(order, paymentAddress) {
return {
title: 'Complete Your Purchase',
steps: [
'Open your Cardano wallet',
`Send exactly ${order.amountAda} ADA`,
`To address: ${paymentAddress}`,
'Wait for confirmation (usually 1-2 minutes)'
],
orderId: order.id,
amount: order.amountAda,
address: paymentAddress,
expiresIn: '30 minutes'
};
}

Step 5: Monitor Order Status

Poll the order status until completion:

async function waitForCompletion(orderId, options = {}) {
const {
maxAttempts = 60,
intervalMs = 10000,
onStatusChange = () => {}
} = options;

let lastStatus = null;

for (let attempt = 0; attempt < maxAttempts; attempt++) {
const response = await fetch(
`https://api.get.nocy.io/api/order/${orderId}`
);
const order = await response.json();

if (order.status !== lastStatus) {
lastStatus = order.status;
onStatusChange(order.status, order);
}

switch (order.status) {
case 'PAID':
// Allocation secured! Tokens claimable later via Claim Portal
return { success: true, order };
case 'EXPIRED':
return { success: false, reason: 'Order expired', order };
case 'PENDING':
// Still waiting for payment
break;
}

await new Promise(r => setTimeout(r, intervalMs));
}

return { success: false, reason: 'Timeout' };
}

// Usage
const result = await waitForPayment(order.id, {
onStatusChange: (status, order) => {
console.log(`Status changed to: ${status}`);
}
});

if (result.success) {
console.log('Allocation secured!');
console.log('NOCY allocated:', result.order.amountNocy);
console.log('Claim your tokens later at https://claim.nocy.io');
} else {
console.log('Purchase failed:', result.reason);
}

Complete Integration Example

Here's a complete class that handles the entire purchase flow:

class NocyPurchase {
constructor() {
this.baseUrl = 'https://api.get.nocy.io';
}

async getPrice() {
const res = await fetch(`${this.baseUrl}/api/price`);
return (await res.json()).priceInAda;
}

async getPaymentAddress() {
const res = await fetch(`${this.baseUrl}/api/payment-address`);
return (await res.json()).address;
}

async createOrder(payerAddress, amountNocy) {
const res = await fetch(`${this.baseUrl}/api/order`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ payerAddress, amountNocy })
});
return res.json();
}

async getOrder(orderId) {
const res = await fetch(`${this.baseUrl}/api/order/${orderId}`);
return res.json();
}

async purchase(payerAddress, amountNocy, callbacks = {}) {
const { onStep, onStatusChange, onComplete, onError } = callbacks;

try {
// Step 1: Get price
onStep?.('Getting current price...');
const price = await this.getPrice();
const totalCost = amountNocy * price;

// Step 2: Create order
onStep?.('Creating order...');
const order = await this.createOrder(payerAddress, amountNocy);

// Step 3: Get payment address
onStep?.('Getting payment address...');
const paymentAddress = await this.getPaymentAddress();

// Step 4: Return payment instructions
onStep?.('Waiting for payment...');
const instructions = {
orderId: order.id,
paymentAddress,
amountAda: totalCost,
amountNocy,
pricePerToken: price
};

// Step 5: Wait for payment confirmation
const result = await this.waitForPayment(order.id, {
onStatusChange
});

if (result.success) {
onComplete?.(result.order);
return { success: true, order: result.order, instructions };
} else {
onError?.(result.reason);
return { success: false, reason: result.reason, instructions };
}

} catch (error) {
onError?.(error.message);
return { success: false, reason: error.message };
}
}

async waitForPayment(orderId, options = {}) {
const { maxAttempts = 60, intervalMs = 10000, onStatusChange } = options;

for (let i = 0; i < maxAttempts; i++) {
const order = await this.getOrder(orderId);
onStatusChange?.(order.status, order);

if (order.status === 'PAID') {
// Allocation secured! Tokens claimable later
return { success: true, order };
}
if (order.status === 'EXPIRED') {
return { success: false, reason: 'Order expired', order };
}

await new Promise(r => setTimeout(r, intervalMs));
}

return { success: false, reason: 'Timeout' };
}
}

// Usage
const purchase = new NocyPurchase();

const result = await purchase.purchase(
'addr1qy...', // User's wallet address
10000, // NOCY amount
{
onStep: (step) => console.log('Step:', step),
onStatusChange: (status) => console.log('Status:', status),
onComplete: (order) => console.log('Complete!', order),
onError: (error) => console.error('Error:', error)
}
);

React Integration Example

import { useState, useCallback } from 'react';

function usePurchaseNocy() {
const [state, setState] = useState({
status: 'idle',
order: null,
paymentAddress: null,
error: null
});

const purchase = useCallback(async (walletAddress, amount) => {
setState({ status: 'creating', order: null, paymentAddress: null, error: null });

try {
// Create order
const orderRes = await fetch('https://api.get.nocy.io/api/order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ payerAddress: walletAddress, amountNocy: amount })
});
const order = await orderRes.json();

// Get payment address
const addrRes = await fetch('https://api.get.nocy.io/api/payment-address');
const { address } = await addrRes.json();

setState({ status: 'awaiting_payment', order, paymentAddress: address, error: null });

// Start polling
pollOrderStatus(order.id);

} catch (error) {
setState(s => ({ ...s, status: 'error', error: error.message }));
}
}, []);

const pollOrderStatus = useCallback(async (orderId) => {
const checkStatus = async () => {
const res = await fetch(`https://api.get.nocy.io/api/order/${orderId}`);
const order = await res.json();

if (order.status === 'PAID') {
// Allocation secured!
setState(s => ({ ...s, status: 'paid', order }));
return;
}

if (order.status === 'EXPIRED') {
setState(s => ({ ...s, status: 'expired', order }));
return;
}

// Continue polling
setTimeout(checkStatus, 10000);
};

checkStatus();
}, []);

return { ...state, purchase };
}

// Component
function PurchaseForm() {
const { status, order, paymentAddress, error, purchase } = usePurchaseNocy();
const [amount, setAmount] = useState(10000);
const [wallet, setWallet] = useState('');

if (status === 'awaiting_payment') {
return (
<div>
<h2>Send Payment</h2>
<p>Amount: {order.amountAda} ADA</p>
<p>Address: {paymentAddress}</p>
<p>Waiting for payment...</p>
</div>
);
}

if (status === 'paid') {
return (
<div>
<h2>Allocation Secured!</h2>
<p>You have secured {order.amountNocy} NOCY</p>
<p>Claim your tokens at <a href="https://claim.nocy.io">claim.nocy.io</a></p>
</div>
);
}

return (
<form onSubmit={(e) => { e.preventDefault(); purchase(wallet, amount); }}>
<input
type="text"
value={wallet}
onChange={(e) => setWallet(e.target.value)}
placeholder="Your Cardano address"
/>
<input
type="number"
value={amount}
onChange={(e) => setAmount(Number(e.target.value))}
/>
<button type="submit" disabled={status === 'creating'}>
{status === 'creating' ? 'Creating...' : 'Buy NOCY Allocation'}
</button>
{error && <p className="error">{error}</p>}
</form>
);
}

Error Handling

Handle common error scenarios:

async function safePurchase(walletAddress, amount) {
try {
// Validate inputs
if (!walletAddress.startsWith('addr1')) {
throw new Error('Invalid Cardano address');
}
if (amount < 1) {
throw new Error('Minimum purchase is 1 NOCY');
}

// Proceed with purchase
const order = await createOrder(walletAddress, amount);
return { success: true, order };

} catch (error) {
// Handle specific errors
if (error.message.includes('rate limit')) {
return { success: false, error: 'Too many requests. Please wait.' };
}
if (error.message.includes('insufficient')) {
return { success: false, error: 'Insufficient supply available.' };
}

return { success: false, error: error.message };
}
}

Next Steps