# Implementation Guide

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](/docs/key-concepts/naspip/protocol-specification)

## Fluxis REST API (Recommended)

### Read a NASPIP token

```bash
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

```bash
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

```bash
npm install crypto-js qrcode ed25519-supercop
```

#### 2. Core NASPIP Class

```typescript
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](/docs/developers/tutorials/payment-request) and use the `token` from the response.

```typescript
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](/docs/key-concepts/naspip/protocol-specification#instruction-payload-direct-payment): 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](#create-a-naspip-token) section above.

### Python Implementation

#### 1. Install Dependencies

```bash
pip install cryptography qrcode pillow
```

#### 2. Core Implementation

```python
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
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](https://github.com/fluxisus/naspip-go/tree/v3.1.0).

#### 2. Core Implementation

```go
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

```typescript
// 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

1. **Key Management**: Store private keys in secure hardware modules (HSM) when possible
2. **Key Rotation**: Implement regular key rotation procedures
3. **Input Validation**: Always validate all input parameters
4. **Error Handling**: Don't expose sensitive information in error messages

### Performance

1. **Caching**: Cache public keys for frequently used recipients
2. **Batch Processing**: Process multiple tokens in batches when possible
3. **Async Operations**: Use asynchronous operations for I/O intensive tasks

### Integration

1. **Webhooks**: Implement webhooks for payment status updates
2. **Rate Limiting**: Implement rate limiting for token generation
3. **Monitoring**: Add comprehensive logging and monitoring
4. **Fallbacks**: Implement fallback mechanisms for network failures

## Next Steps

After implementing the basic functionality:

1. **Add Network Support**: Implement support for additional networks
2. **Multi-Currency**: Add support for multiple currencies in a single token
3. **Advanced Features**: Implement atomic swaps, time-locked payments
4. **Mobile Integration**: Create mobile SDKs for iOS and Android
5. **Testing**: Add comprehensive integration tests

## Resources

- [Protocol Specification](/docs/key-concepts/naspip/protocol-specification)
- [Security Model](/docs/key-concepts/naspip/security-model)
- [Payment Request](/docs/key-concepts/payment-request)
- [GitHub Repository](https://github.com/fluxisus/naspip)
