Skip to main content

E-Commerce Checkout

Brazilian e-commerce flow — buyer pays via Pix, NF-e gets issued, label generated, customer notified on WhatsApp. Same shape that ships in codespar-core/examples/brazilian-ecommerce.

1 min read · updated
View MarkdownEdit on GitHub
TIME
~15 min
PROVIDERS
asaasnfe-iomelhor-envioz-api

A buyer pays via Pix, your agent issues an NF-e, generates a Melhor Envio shipping label, and confirms the order on WhatsApp — all driven by four meta-tool calls. This cookbook mirrors the canonical example at codespar-core/examples/brazilian-ecommerce/server.ts; the shape is what ships there.

The Complete Loop

COMPLETE LOOPWhat this agent does
4 meta-tools · one webhook
1
Charge
codespar_charge
Pix charge via Asaas (inbound)
2
Invoice
codespar_invoice
NF-e via NFe.io (rail: nfe)
3
Ship
codespar_ship
Melhor Envio domestic-label
4
Notify
codespar_notify
WhatsApp confirm via Z-API

Prerequisites

Install the SDK:

npm install @codespar/sdk

Set your API key:

.env
CODESPAR_API_KEY=csk_live_...

Connect four providers in /dashboard/connections before running the agent — Asaas (Pix charges), NFe.io (NF-e issuance), Melhor Envio (shipping labels), and Z-API (WhatsApp). The dashboard walks you through the auth fields each provider needs.

Asaas requires a customer to exist before you can issue a Pix charge against them. The /whatsapp flow in the canonical example creates the customer first via the raw asaas/create_customer tool — the typed charge() wrapper doesn't surface customer creation yet.

Steps in action

Each step maps to one meta-tool. The Pix charge is async — the buyer scans the QR code on their bank app and pays out-of-band. The agent waits for settlement before issuing the NF-e and the shipping label.

session.charge() is the typed wrapper for codespar_charge. It returns immediately with a tool_call_id, a Pix QR code, and a copy-paste BR-code. Settlement happens out-of-band when the buyer pays.

const charge = await session.charge({
  amount: 14900,
  currency: "BRL",
  method: "pix",
  customer: { id: "cus_..." },
  metadata: { order_id: "order_42" },
});
// charge.tool_call_id, charge.pix.qr_code, charge.pix.br_code

Full agent code

Drop-in handler for the post-Pix webhook. This is the same shape that ships in examples/brazilian-ecommerce/server.ts.

checkout.ts
import { CodeSpar } from "@codespar/sdk";

const codespar = new CodeSpar({ apiKey: process.env.CODESPAR_API_KEY! });

export async function handlePixPaid(orderId: string, customerId: string) {
  const session = await codespar.create("user_123", {
    servers: ["asaas", "nfe-io", "melhor-envio", "z-api"],
  });

  try {
    // 1. Pix charge (inbound). Returns immediately; settlement is async.
    const charge = await session.charge({
      amount: 14900,
      currency: "BRL",
      method: "pix",
      customer: { id: customerId },
      metadata: { order_id: orderId },
    });

    // 2. Wait for the buyer to pay before issuing the NF-e.
    await session.paymentStatusStream(charge.tool_call_id, {
      onUpdate: (s) => {
        if (s.state === "failed") throw new Error(`Pix failed: ${s.events.at(-1)}`);
      },
    });

    // 3. NF-e (products, state-regulated).
    const invoice = await session.execute("codespar_invoice", {
      rail: "nfe",
      customer: { /* … */ },
      items: [{ description: "Starter Kit", amount: 14900, ncm: "8471.30.12", cfop: "5102" }],
    });

    // 4. Melhor Envio domestic label.
    const label = await session.ship({
      action: "label",
      origin: { cep: "04538-132" },
      destination: { cep: "01310-100" },
      package: { weight_kg: 0.5, height_cm: 5, width_cm: 15, length_cm: 20 },
    });

    // 5. WhatsApp confirmation.
    await session.execute("codespar_notify", {
      channel: "whatsapp",
      to: "+5511999887766",
      message: `Pedido confirmado. NF-e ${invoice.data.access_key}. Rastreio ${label.data.tracking_code}.`,
    });
  } finally {
    await session.close();
  }
}

Don't issue the NF-e before settlement. A NF-e can only be cancelled within a short SEFAZ window (typically 24h, varies by state). If you issue based on the charge response and the buyer never pays, you've created a phantom invoice that needs cancellation paperwork. Always wait on paymentStatusStream (or paymentStatus) until succeeded.

Variations

Boleto fallback

Asaas supports both Pix and boleto on codespar_charge. Pass method: "boleto" and the response carries a barcode + PDF URL instead of a Pix QR code. Settlement is T+1 to T+3 business days; the same paymentStatusStream correlation works.

NFS-e instead of NF-e

If you sell services rather than products, drop the rail: "nfe" argument — codespar_invoice defaults to NFS-e (municipal). NFS-e doesn't need an NCM/CFOP classification.

Multi-tenant

Pass the real userId as the first argument to codespar.create(userId, config) to isolate credentials and sessions per end-customer. See Multi-Tenant Agent.

Card USD instead of Pix BRL

Set currency: "USD" and method: "card" on session.charge(...) — the router picks Stripe instead of Asaas / MP / iugu / Stone.

Next steps

Edit on GitHub

Last updated on