This guide covers integrating NASPIP via the Fluxis REST API (recommended) or the naspip-go library for custom implementations.
Prerequisites
Fluxis API credentials (Bearer token + API key) for REST integration
For library use: Go 1.21+ and github.com/fluxisus/naspip-go/v3
Understanding of NASPIP token format (naspip;{kis};{kid};v4.public....) — see Protocol Specification
Fluxis REST API (Recommended)
Read a NASPIP token
curl -X POST https://api.fluxis.us/v1/naspip/read \
-H "Authorization: Bearer <token>" \
-H "x-fluxis-api-key: <api_key_id>" \
-H "Content-Type: application/json" \
-d '{ "token": "naspip;fluxis.us;fluxis.qr.dyn.1;v4.public...." }'
Response data is a PasetoCompleteResult (version, purpose, footer, payload). For URL payloads without payment_options, Fluxis fetches instructions from payload.data.url and returns resolved payment data.
Create a NASPIP token
curl -X POST https://api.fluxis.us/v1/naspip/create \
-H "Authorization: Bearer <token>" \
-H "x-fluxis-api-key: <api_key_id>" \
-H "Content-Type: application/json" \
-d '{
"payment": {
"address": "0x1234...",
"unique_asset_id": "npolygon_t0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
"amount": 49.99,
"is_open": false,
"expires_at": 1759397652
}
}'
URL payloads are also supported: pass { "url": "https://...", "payment_options": ["..."] }.
::::note Swagger caveat
OpenAPI may document a flat checkout-shaped read response; the runtime shape is PasetoCompleteResult. See NASPIP_ENHANCEMENTS.md.
:::::
Language-Specific Implementations
::::warning Legacy examples below
The JavaScript, Python, and Go sections below implement a non-standard naspip:// + base64 protocol. They will not interoperate with Fluxis or naspip-go. Prefer the REST API or naspip-go sections above until these examples are rewritten.
:::::
JavaScript/TypeScript Implementation
1. Install Dependencies
npm install crypto-js qrcode ed25519-supercop
2. Core NASPIP Class
import * as crypto from "crypto" ;
import * as QRCode from "qrcode" ;
interface PaymentInstruction {
version : string ;
id : string ;
amount : {
value : string ;
currency : string ;
type : "open" | "closed" ;
};
network : {
name : string ;
chainId : string ;
};
recipient : {
address : string ;
type : string ;
};
metadata ?: {
description ?: string ;
reference ?: string ;
expiresAt ?: string ;
};
options ?: {
allowPartial ?: boolean ;
maxAmount ?: string ;
minAmount ?: string ;
};
}
class NASPIP {
private privateKey : string ;
private publicKey : string ;
constructor ( privateKey ?: string ) {
if (privateKey) {
this .privateKey = privateKey;
this .publicKey = this . derivePublicKey (privateKey);
} else {
const keyPair = this . generateKeyPair ();
this .privateKey = keyPair.privateKey;
this .publicKey = keyPair.publicKey;
}
}
private generateKeyPair () {
const keyPair = crypto. generateKeyPairSync ( "ed25519" );
return {
privateKey: keyPair.privateKey. export ({ type: "pkcs8" , format: "pem" }),
publicKey: keyPair.publicKey. export ({ type: "spki" , format: "pem" }),
};
}
private derivePublicKey ( privateKey : string ) {
// Implementation depends on your crypto library
// This is a simplified example
return crypto
. createPublicKey (privateKey)
. export ({ type: "spki" , format: "pem" });
}
async createPaymentInstruction ( params : {
amount : string ;
currency : string ;
network : string ;
recipient : string ;
description ?: string ;
expiresAt ?: string ;
}) : Promise < string > {
const instruction : PaymentInstruction = {
version: "1.0" ,
id: this . generateUniqueId (),
amount: {
value: params.amount,
currency: params.currency,
type: "closed" ,
},
network: {
name: params.network,
chainId: "mainnet" ,
},
recipient: {
address: params.recipient,
type: "address" ,
},
metadata: {
description: params.description,
expiresAt: params.expiresAt || this . getDefaultExpiration (),
},
};
return this . signAndEncode (instruction);
}
private generateUniqueId () : string {
return crypto. randomBytes ( 16 ). toString ( "hex" );
}
private getDefaultExpiration () : string {
const expiration = new Date ();
expiration. setHours (expiration. getHours () + 24 ); // 24 hours from now
return expiration. toISOString ();
}
private async signAndEncode (
instruction : PaymentInstruction ,
) : Promise < string > {
// Canonical JSON serialization
const serialized = JSON . stringify (
instruction,
Object. keys (instruction). sort (),
);
// Create hash
const hash = crypto. createHash ( "sha256" ). update (serialized). digest ();
// Sign the hash
const signature = crypto. sign ( "ed25519" , hash, this .privateKey);
// Encode as NASPIP token
const tokenData = {
version: "1.0" ,
data: Buffer. from (serialized). toString ( "base64" ),
signature: signature. toString ( "base64" ),
};
return `naspip://${ Buffer . from ( JSON . stringify ( tokenData )). toString (
"base64" ,
) }` ;
}
async generateQRCode ( token : string ) : Promise < string > {
return await QRCode. toDataURL (token, {
errorCorrectionLevel: "M" ,
type: "image/png" ,
width: 256 ,
});
}
async verifyToken (
token : string ,
publicKey : string ,
) : Promise < PaymentInstruction > {
// Decode token
const tokenData = JSON . parse (
Buffer. from (token. replace ( "naspip://" , "" ), "base64" ). toString (),
);
// Decode payment instruction
const instruction : PaymentInstruction = JSON . parse (
Buffer. from (tokenData.data, "base64" ). toString (),
);
// Verify signature
const serialized = JSON . stringify (
instruction,
Object. keys (instruction). sort (),
);
const hash = crypto. createHash ( "sha256" ). update (serialized). digest ();
const signature = Buffer. from (tokenData.signature, "base64" );
const isValid = crypto. verify ( "ed25519" , hash, publicKey, signature);
if ( ! isValid) {
throw new Error ( "Invalid signature" );
}
return instruction;
}
}
3. Create a payment instruction (TypeScript)
Fluxis signs NASPIP tokens with its issuer keys. Do not build payment instructions locally unless you operate your own NASPIP issuer. In production, create a payment request and use the token from the response.
import QRCode from "qrcode" ;
const API_BASE = "https://api.fluxis.us" ;
const POS_ID = process.env. FLUXIS_POS_ID ! ;
const BEARER = process.env. FLUXIS_ACCESS_TOKEN ! ;
const API_KEY_ID = process.env. FLUXIS_API_KEY_ID ! ;
type CreatePaymentRequestResponse = {
status : string ;
data : {
id : string ;
reference_id ?: string ;
status : string ;
token : string ; // NASPIP token: naspip;fluxis.us;fluxis.qr.dyn.1;v4.public....
expiration ?: number ;
};
};
async function createPaymentInstruction () : Promise < CreatePaymentRequestResponse [ "data" ]> {
const res = await fetch ( `${ API_BASE }/v1/pos/${ POS_ID }/payment-request` , {
method: "POST" ,
headers: {
Authorization: `Bearer ${ BEARER }` ,
"x-fluxis-api-key" : API_KEY_ID ,
"Content-Type" : "application/json" ,
},
body: JSON . stringify ({
amount: "49.99" ,
unique_asset_id: "npolygon_t0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359" ,
reference_id: "order-12345" ,
order: {
total: "49.99" ,
coin_code: "USD" ,
description: "Payment for services" ,
merchant: { name: "My Store" },
},
}),
});
if ( ! res.ok) {
throw new Error ( `Payment request failed: ${ res . status } ${ await res . text () }` );
}
const body = ( await res. json ()) as CreatePaymentRequestResponse ;
return body.data;
}
async function example () {
const { id , token , expiration } = await createPaymentInstruction ();
// Present the raw NASPIP string as a QR code (not a naspip:// URI)
const qrCode = await QRCode. toDataURL (token, { width: 256 , errorCorrectionLevel: "M" });
console. log ( "Payment request ID:" , id);
console. log ( "Expires at (unix):" , expiration);
console. log ( "NASPIP token:" , token);
console. log ( "QR data URL:" , qrCode);
// Optional: decode and verify via Fluxis
const readRes = await fetch ( `${ API_BASE }/v1/naspip/read` , {
method: "POST" ,
headers: {
Authorization: `Bearer ${ BEARER }` ,
"x-fluxis-api-key" : API_KEY_ID ,
"Content-Type" : "application/json" ,
},
body: JSON . stringify ({ token }),
});
const decoded = await readRes. json ();
console. log ( "Decoded payment:" , decoded.data.payload.data.payment);
}
The signed payload inside the token matches the instruction payload schema : vault address, unique_asset_id, amount, is_open, expires_at, and optional order metadata. Fluxis assigns the vault address when the payment request is created.
For low-level token minting when you already control address and expiry (advanced / internal tools only), use POST /v1/naspip/create with a payment object — see the curl example in the Fluxis REST API section above.
Python Implementation
1. Install Dependencies
pip install cryptography qrcode pillow
2. Core Implementation
import json
import base64
import hashlib
from datetime import datetime, timedelta
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ed25519
import qrcode
from typing import Dict, Any, Optional
class NASPIP :
def __init__ (self, private_key: Optional[ bytes ] = None ):
if private_key:
self .private_key = ed25519.Ed25519PrivateKey.from_private_bytes(private_key)
else :
self .private_key = ed25519.Ed25519PrivateKey.generate()
self .public_key = self .private_key.public_key()
def create_payment_instruction (self,
amount: str ,
currency: str ,
network: str ,
recipient: str ,
description: Optional[ str ] = None ,
expires_at: Optional[ str ] = None ) -> str :
instruction = {
"version" : "1.0" ,
"id" : self ._generate_unique_id(),
"amount" : {
"value" : amount,
"currency" : currency,
"type" : "closed"
},
"network" : {
"name" : network,
"chainId" : "mainnet"
},
"recipient" : {
"address" : recipient,
"type" : "address"
},
"metadata" : {
"description" : description,
"expiresAt" : expires_at or self ._get_default_expiration()
}
}
return self ._sign_and_encode(instruction)
def _generate_unique_id (self) -> str :
import secrets
return secrets.token_hex( 16 )
def _get_default_expiration (self) -> str :
expiration = datetime.now() + timedelta( hours = 24 )
return expiration.isoformat()
def _sign_and_encode (self, instruction: Dict[ str , Any]) -> str :
# Canonical JSON serialization
serialized = json.dumps(instruction, sort_keys = True , separators = ( ',' , ':' ))
# Create hash
hash_obj = hashlib.sha256(serialized.encode( 'utf-8' ))
hash_bytes = hash_obj.digest()
# Sign the hash
signature = self .private_key.sign(hash_bytes)
# Encode as NASPIP token
token_data = {
"version" : "1.0" ,
"data" : base64.b64encode(serialized.encode( 'utf-8' )).decode( 'utf-8' ),
"signature" : base64.b64encode(signature).decode( 'utf-8' )
}
token_json = json.dumps(token_data, separators = ( ',' , ':' ))
return f "naspip:// { base64.b64encode(token_json.encode( 'utf-8' )).decode( 'utf-8' ) } "
def generate_qr_code (self, token: str ) -> bytes :
qr = qrcode.QRCode(
version = 1 ,
error_correction = qrcode.constants. ERROR_CORRECT_M ,
box_size = 10 ,
border = 4 ,
)
qr.add_data(token)
qr.make( fit = True )
img = qr.make_image( fill_color = "black" , back_color = "white" )
# Convert to bytes
import io
img_bytes = io.BytesIO()
img.save(img_bytes, format = 'PNG' )
return img_bytes.getvalue()
def verify_token (self, token: str , public_key_bytes: bytes ) -> Dict[ str , Any]:
# Decode token
token_data = json.loads(
base64.b64decode(token.replace( 'naspip://' , '' )).decode( 'utf-8' )
)
# Decode payment instruction
instruction = json.loads(
base64.b64decode(token_data[ 'data' ]).decode( 'utf-8' )
)
# Verify signature
serialized = json.dumps(instruction, sort_keys = True , separators = ( ',' , ':' ))
hash_obj = hashlib.sha256(serialized.encode( 'utf-8' ))
hash_bytes = hash_obj.digest()
signature = base64.b64decode(token_data[ 'signature' ])
public_key = ed25519.Ed25519PublicKey.from_public_bytes(public_key_bytes)
try :
public_key.verify(signature, hash_bytes)
return instruction
except Exception :
raise ValueError ( "Invalid signature" )
# Usage example
def example ():
naspip = NASPIP()
# Create payment instruction
token = naspip.create_payment_instruction(
amount = "100.00" ,
currency = "USD" ,
network = "polygon" ,
recipient = "0x1234567890abcdef1234567890abcdef12345678" ,
description = "Payment for services"
)
# Generate QR code
qr_code_bytes = naspip.generate_qr_code(token)
# Save QR code to file
with open ( 'payment_qr.png' , 'wb' ) as f:
f.write(qr_code_bytes)
print ( f "Generated token: { token } " )
print ( "QR code saved as payment_qr.png" )
Go Implementation
1. Dependencies
go mod init naspip - example
go get github.com / fluxisus / naspip -go/ v3
go get github.com / skip2 /go- qrcode
Use protocol.PaymentInstructionsBuilder with paseto.PasetoV4Handler{} for create/read operations. See naspip-go examples .
2. Core Implementation
package main
import (
" crypto/ed25519 "
" crypto/rand "
" crypto/sha256 "
" encoding/base64 "
" encoding/json "
" fmt "
" time "
" github.com/skip2/go-qrcode "
)
type PaymentInstruction struct {
Version string `json:"version"`
ID string `json:"id"`
Amount struct {
Value string `json:"value"`
Currency string `json:"currency"`
Type string `json:"type"`
} `json:"amount"`
Network struct {
Name string `json:"name"`
ChainID string `json:"chainId"`
} `json:"network"`
Recipient struct {
Address string `json:"address"`
Type string `json:"type"`
} `json:"recipient"`
Metadata struct {
Description string `json:"description,omitempty"`
ExpiresAt string `json:"expiresAt,omitempty"`
} `json:"metadata,omitempty"`
}
type TokenData struct {
Version string `json:"version"`
Data string `json:"data"`
Signature string `json:"signature"`
}
type NASPIP struct {
privateKey ed25519 . PrivateKey
publicKey ed25519 . PublicKey
}
func NewNASPIP () ( * NASPIP , error ) {
publicKey, privateKey, err := ed25519. GenerateKey (rand.Reader)
if err != nil {
return nil , err
}
return & NASPIP {
privateKey: privateKey,
publicKey: publicKey,
}, nil
}
func ( n * NASPIP ) CreatePaymentInstruction ( amount , currency , network , recipient , description string ) ( string , error ) {
instruction := PaymentInstruction {
Version: "1.0" ,
ID: generateUniqueID (),
}
instruction.Amount.Value = amount
instruction.Amount.Currency = currency
instruction.Amount.Type = "closed"
instruction.Network.Name = network
instruction.Network.ChainID = "mainnet"
instruction.Recipient.Address = recipient
instruction.Recipient.Type = "address"
instruction.Metadata.Description = description
instruction.Metadata.ExpiresAt = getDefaultExpiration ()
return n. signAndEncode (instruction)
}
func ( n * NASPIP ) signAndEncode ( instruction PaymentInstruction ) ( string , error ) {
// Canonical JSON serialization
data, err := json. Marshal (instruction)
if err != nil {
return "" , err
}
// Create hash
hash := sha256. Sum256 (data)
// Sign the hash
signature := ed25519. Sign (n.privateKey, hash[:])
// Encode as NASPIP token
tokenData := TokenData {
Version: "1.0" ,
Data: base64.StdEncoding. EncodeToString (data),
Signature: base64.StdEncoding. EncodeToString (signature),
}
tokenJSON, err := json. Marshal (tokenData)
if err != nil {
return "" , err
}
return "naspip://" + base64.StdEncoding. EncodeToString (tokenJSON), nil
}
func ( n * NASPIP ) GenerateQRCode ( token string ) error {
return qrcode. WriteFile (token, qrcode.Medium, 256 , "payment_qr.png" )
}
func ( n * NASPIP ) VerifyToken ( token string ) ( * PaymentInstruction , error ) {
// Decode token
tokenDataBytes, err := base64.StdEncoding. DecodeString (token[ 8 :]) // Remove "naspip://"
if err != nil {
return nil , err
}
var tokenData TokenData
if err := json. Unmarshal (tokenDataBytes, & tokenData); err != nil {
return nil , err
}
// Decode payment instruction
instructionBytes, err := base64.StdEncoding. DecodeString (tokenData.Data)
if err != nil {
return nil , err
}
var instruction PaymentInstruction
if err := json. Unmarshal (instructionBytes, & instruction); err != nil {
return nil , err
}
// Verify signature
hash := sha256. Sum256 (instructionBytes)
signature, err := base64.StdEncoding. DecodeString (tokenData.Signature)
if err != nil {
return nil , err
}
if ! ed25519. Verify (n.publicKey, hash[:], signature) {
return nil , fmt. Errorf ( "invalid signature" )
}
return & instruction, nil
}
func generateUniqueID () string {
b := make ([] byte , 16 )
rand. Read (b)
return fmt. Sprintf ( " %x " , b)
}
func getDefaultExpiration () string {
return time. Now (). Add ( 24 * time.Hour). Format (time.RFC3339)
}
func main () {
naspip, err := NewNASPIP ()
if err != nil {
panic (err)
}
// Create payment instruction
token, err := naspip. CreatePaymentInstruction (
"100.00" ,
"USD" ,
"polygon" ,
"0x1234567890abcdef1234567890abcdef12345678" ,
"Payment for services" ,
)
if err != nil {
panic (err)
}
fmt. Printf ( "Generated token: %s\n " , token)
// Generate QR code
if err := naspip. GenerateQRCode (token); err != nil {
panic (err)
}
fmt. Println ( "QR code saved as payment_qr.png" )
// Verify token
instruction, err := naspip. VerifyToken (token)
if err != nil {
panic (err)
}
fmt. Printf ( "Verified instruction: %+v\n " , instruction)
}
Testing Your Implementation
Unit Tests
// JavaScript/TypeScript example
describe ( "NASPIP" , () => {
let naspip : NASPIP ;
beforeEach (() => {
naspip = new NASPIP ();
});
test ( "should create valid payment instruction" , async () => {
const token = await naspip. createPaymentInstruction ({
amount: "100.00" ,
currency: "USD" ,
network: "polygon" ,
recipient: "0x1234567890abcdef1234567890abcdef12345678" ,
description: "Test payment" ,
});
expect (token). toMatch ( / ^ naspip: \/\/ / );
});
test ( "should verify valid token" , async () => {
const token = await naspip. createPaymentInstruction ({
amount: "100.00" ,
currency: "USD" ,
network: "polygon" ,
recipient: "0x1234567890abcdef1234567890abcdef12345678" ,
});
const instruction = await naspip. verifyToken (token, naspip.publicKey);
expect (instruction.amount.value). toBe ( "100.00" );
});
test ( "should reject invalid signature" , async () => {
const fakeToken = "naspip://invalid-token" ;
await expect (
naspip. verifyToken (fakeToken, naspip.publicKey),
).rejects. toThrow ( "Invalid signature" );
});
});
Best Practices
Security
Key Management : Store private keys in secure hardware modules (HSM) when possible
Key Rotation : Implement regular key rotation procedures
Input Validation : Always validate all input parameters
Error Handling : Don't expose sensitive information in error messages
Performance
Caching : Cache public keys for frequently used recipients
Batch Processing : Process multiple tokens in batches when possible
Async Operations : Use asynchronous operations for I/O intensive tasks
Integration
Webhooks : Implement webhooks for payment status updates
Rate Limiting : Implement rate limiting for token generation
Monitoring : Add comprehensive logging and monitoring
Fallbacks : Implement fallback mechanisms for network failures
Next Steps
After implementing the basic functionality:
Add Network Support : Implement support for additional networks
Multi-Currency : Add support for multiple currencies in a single token
Advanced Features : Implement atomic swaps, time-locked payments
Mobile Integration : Create mobile SDKs for iOS and Android
Testing : Add comprehensive integration tests
Resources
Last modified on October 12, 2026