# Asaas API Integration Standard

> **Scope:** universal
> **Layer:** 2 (on keyword)
> **Keywords:** asaas, payment, brazilian, pix, boleto, billing
> **Load When:** asaas or brazilian payment keywords detected

Brazilian payment gateway integration for PIX, Boleto, and credit card processing.

---

## Overview

Asaas is a Brazilian payment service provider offering:
- PIX instant payments
- Boleto bancário
- Credit card processing
- Subscription billing
- Split payments

**Stack:** .NET Backend

---

## Core Principles

1. **Webhook-First**: All payment state changes via webhooks
2. **Idempotency**: Use idempotency keys for all POST requests
3. **Security**: Validate webhook signatures, never store full card data
4. **Brazil-Specific**: Handle CPF/CNPJ validation, Brazilian tax rules

---

## API Configuration

### Environment Variables

```bash
# .env.local
ASAAS_API_KEY=your_api_key_here
ASAAS_WEBHOOK_SECRET=your_webhook_secret
ASAAS_ENVIRONMENT=sandbox # or production
```

### TypeScript Client Setup

```typescript
// lib/asaas/client.ts
import { createClient } from '@asaas/node-sdk';

export const asaasClient = createClient({
  apiKey: process.env.ASAAS_API_KEY!,
  environment: process.env.ASAAS_ENVIRONMENT as 'sandbox' | 'production'
});
```

---

## Common Operations

### Create PIX Payment

```typescript
// app/api/payments/pix/route.ts
import { asaasClient } from '@/lib/asaas/client';

export async function POST(req: Request) {
  const { amount, customerId, description } = await req.json();

  const payment = await asaasClient.payments.create({
    customer: customerId,
    billingType: 'PIX',
    value: amount,
    dueDate: new Date().toISOString().split('T')[0],
    description,
  });

  return Response.json({
    paymentId: payment.id,
    pixCode: payment.pixTransaction.payload,
    qrCode: payment.pixTransaction.qrCode.encodedImage
  });
}
```

### Webhook Handler

```typescript
// app/api/webhooks/asaas/route.ts
import { headers } from 'next/headers';
import crypto from 'crypto';

export async function POST(req: Request) {
  const body = await req.text();
  const signature = headers().get('asaas-signature');

  // Verify webhook signature
  const expectedSignature = crypto
    .createHmac('sha256', process.env.ASAAS_WEBHOOK_SECRET!)
    .update(body)
    .digest('hex');

  if (signature !== expectedSignature) {
    return new Response('Invalid signature', { status: 401 });
  }

  const event = JSON.parse(body);

  // Handle payment events
  switch (event.event) {
    case 'PAYMENT_RECEIVED':
      await handlePaymentReceived(event.payment);
      break;
    case 'PAYMENT_CONFIRMED':
      await handlePaymentConfirmed(event.payment);
      break;
  }

  return new Response('OK');
}
```

---

## Database Schema (Supabase)

```sql
-- Store payment records
create table payments (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id),
  asaas_payment_id text unique not null,
  amount decimal(10,2) not null,
  status text not null, -- pending, confirmed, received
  billing_type text not null, -- PIX, BOLETO, CREDIT_CARD
  created_at timestamptz default now(),
  updated_at timestamptz default now()
);

-- RLS policies
alter table payments enable row level security;

create policy "Users read own payments"
  on payments for select
  using (auth.uid() = user_id);
```

---

## Best Practices

### CPF/CNPJ Validation

```typescript
export function validateCPF(cpf: string): boolean {
  cpf = cpf.replace(/[^\d]/g, '');
  if (cpf.length !== 11) return false;

  // Validation logic...
  return true;
}

export function validateCNPJ(cnpj: string): boolean {
  cnpj = cnpj.replace(/[^\d]/g, '');
  if (cnpj.length !== 14) return false;

  // Validation logic...
  return true;
}
```

### Idempotency Keys

```typescript
const idempotencyKey = `payment-${userId}-${Date.now()}`;

const payment = await asaasClient.payments.create({
  // ... payment data
}, {
  headers: {
    'Idempotency-Key': idempotencyKey
  }
});
```

---

## Error Handling

```typescript
try {
  const payment = await asaasClient.payments.create(data);
} catch (error) {
  if (error.code === 'invalid_cpf') {
    return Response.json({ error: 'CPF inválido' }, { status: 400 });
  }
  if (error.code === 'insufficient_balance') {
    return Response.json({ error: 'Saldo insuficiente' }, { status: 402 });
  }

  console.error('Asaas API error:', error);
  return Response.json({ error: 'Erro no processamento' }, { status: 500 });
}
```

---

## References

- [Asaas API Documentation](https://docs.asaas.com/)
- [Asaas Node SDK](https://github.com/asaas/asaas-node)
- PIX Specification: Brazilian Central Bank

---

*MORPH-SPEC by Polymorphism Tech*
