# Guia de implementação

Este guia cobre a integração do NASPIP via **API REST Fluxis** (recomendado) ou a biblioteca **naspip-go** para implementações personalizadas.

## Pré-requisitos

- Credenciais da API Fluxis (Bearer token + API key) para integração REST
- Para uso de biblioteca: Go 1.21+ e `github.com/fluxisus/naspip-go/v3`
- Compreensão do formato de token NASPIP (`naspip;{kis};{kid};v4.public....`) — consulte [Protocol Specification](/pt/docs/key-concepts/naspip/protocol-specification)

## API REST Fluxis (Recomendado)

### Ler um token NASPIP

```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...." }'
```

A resposta `data` é um `PasetoCompleteResult` (`version`, `purpose`, `footer`, `payload`). Para payloads URL sem `payment_options`, a Fluxis busca instruções de `payload.data.url` e retorna dados de pagamento resolvidos.

### Criar um token NASPIP

```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
    }
  }'
```

Payloads URL também são suportados: passe `{ "url": "https://...", "payment_options": ["..."] }`.

::::note Aviso Swagger
OpenAPI pode documentar uma resposta de leitura plana com forma de checkout; a forma em runtime é `PasetoCompleteResult`. Consulte `NASPIP_ENHANCEMENTS.md`.
:::::

---

## Implementações por linguagem

::::warning Exemplos legados abaixo
As seções JavaScript, Python e Go abaixo implementam um protocolo **não padrão** `naspip://` + base64. **Não** interoperarão com Fluxis ou naspip-go. Prefira as seções REST API ou naspip-go acima até que estes exemplos sejam reescritos.
:::::

### Implementação JavaScript/TypeScript

#### 1. Instalar dependências

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

#### 2. Classe NASPIP principal

```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. Criar uma instrução de pagamento (TypeScript)

A Fluxis assina tokens NASPIP com suas chaves emissoras. **Não** construa instruções de pagamento localmente a menos que opere seu próprio emissor NASPIP. Em produção, crie uma [solicitação de pagamento](/pt/docs/developers/tutorials/payment-request) e use o `token` da resposta.

```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);
}
```

O payload assinado dentro do token corresponde ao [esquema de payload de instrução](/pt/docs/key-concepts/naspip/protocol-specification#instruction-payload-direct-payment): vault `address`, `unique_asset_id`, `amount`, `is_open`, `expires_at`, e metadados `order` opcionais. A Fluxis atribui o endereço vault quando a solicitação de pagamento é criada.

Para minting de tokens de baixo nível quando você já controla endereço e expiração (apenas ferramentas avançadas/internas), use `POST /v1/naspip/create` com um objeto `payment` — consulte o exemplo curl na seção [API REST Fluxis](#criar-um-token-naspip) acima.

### Implementação Python

#### 1. Instalar dependências

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

#### 2. Implementação principal

```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")
```

### Implementação Go

#### 1. Dependências

```go
go mod init naspip-example
go get github.com/fluxisus/naspip-go/v3
go get github.com/skip2/go-qrcode
```

Use `protocol.PaymentInstructionsBuilder` com `paseto.PasetoV4Handler{}` para operações create/read. Consulte [exemplos naspip-go](https://github.com/fluxisus/naspip-go/tree/v3.1.0).

#### 2. Implementação principal

```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)
}
```

## Testar sua implementação

### Testes unitários

```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");
  });
});
```

## Boas práticas

### Segurança

1. **Key Management**: Armazene chaves privadas em módulos de hardware seguros (HSM) quando possível
2. **Key Rotation**: Implemente procedimentos regulares de rotação de chaves
3. **Input Validation**: Sempre valide todos os parâmetros de entrada
4. **Error Handling**: Não exponha informações sensíveis em mensagens de erro

### Performance

1. **Caching**: Armazene em cache chaves públicas para destinatários frequentes
2. **Batch Processing**: Processe múltiplos tokens em lotes quando possível
3. **Async Operations**: Use operações assíncronas para tarefas intensivas em I/O

### Integração

1. **Webhooks**: Implemente webhooks para atualizações de status de pagamento
2. **Rate Limiting**: Implemente rate limiting para geração de tokens
3. **Monitoring**: Adicione logging e monitoramento abrangente
4. **Fallbacks**: Implemente mecanismos de fallback para falhas de rede

## Próximos passos

Depois de implementar a funcionalidade básica:

1. **Add Network Support**: Implemente suporte para redes adicionais
2. **Multi-Currency**: Adicione suporte para múltiplas moedas em um único token
3. **Advanced Features**: Implemente atomic swaps, pagamentos com bloqueio temporal
4. **Mobile Integration**: Crie SDKs móveis para iOS e Android
5. **Testing**: Adicione testes de integração abrangentes

## Recursos

- [Protocol Specification](/pt/docs/key-concepts/naspip/protocol-specification)
- [Modelo de segurança](/pt/docs/key-concepts/naspip/security-model)
- [Solicitação de pagamento](/pt/docs/key-concepts/payment-request)
- [Repositório GitHub](https://github.com/fluxisus/naspip)
