Pular para o conteúdo

Exemplos em PHP

Setup

Terminal window
composer require guzzlehttp/guzzle vlucas/phpdotenv
.env
MYSE_API_BASE=https://api.mysebr.com.br/nfemyse-v3/rest
MYSE_API_TOKEN=seu_token_aqui

Cliente HTTP centralizado

src/MyseApi.php
<?php
namespace App;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
class ApiError extends \Exception
{
public function __construct(
public int $httpStatus,
string $message,
public mixed $payload = null,
public ?string $requestId = null
) {
parent::__construct($message);
}
}
class MyseApi
{
private Client $client;
public function __construct()
{
$this->client = new Client([
'base_uri' => $_ENV['MYSE_API_BASE'],
'timeout' => 30,
'headers' => [
'Authorization' => 'Token ' . $_ENV['MYSE_API_TOKEN'],
'Content-Type' => 'application/json',
],
'http_errors' => false,
]);
}
public function call(string $method, string $path, ?array $body = null): array
{
$options = $body ? ['json' => $body] : [];
$res = $this->client->request($method, ltrim($path, '/'), $options);
$requestId = $res->getHeaderLine('X-Request-Id') ?: null;
$bodyStr = (string) $res->getBody();
$json = json_decode($bodyStr, true) ?? [];
// Erro HTTP duro
if ($res->getStatusCode() >= 400) {
throw new ApiError(
$res->getStatusCode(),
$json['error']['message'] ?? "HTTP {$res->getStatusCode()}",
$json,
$requestId
);
}
// Erro de domínio
if (($json['status'] ?? null) === '999') {
throw new ApiError(
200,
$json['data']['erro'] ?? $json['descricao'] ?? 'erro',
$json,
$json['meta']['request_id'] ?? $requestId
);
}
return $json;
}
}

Cenário: emitir NFCe com polling

<?php
function emitirNFCe(MyseApi $api, array $pedido): array
{
$body = [
'serie' => '65',
'cfop' => '5102',
'numero-origem' => 'PED-' . $pedido['id'],
'tp_pagamento' => $pedido['forma_pagamento'],
'cliente' => $pedido['cpf_cnpj_cliente'],
'itens' => array_map(fn($i) => [
'produto' => $i['sku'],
'quantidade' => (string) $i['qtd'],
'valor-unitario' => (string) $i['preco'],
], $pedido['itens']),
];
$emissao = $api->call('POST', '/nfce/emissao', $body);
if (!in_array($emissao['status'], ['001', '050'])) {
throw new \RuntimeException("Emissão falhou: {$emissao['descricao']}");
}
$recibo = $emissao['data']['recibo'];
error_log("[emissao] recibo=$recibo request_id={$emissao['meta']['request_id']}");
// Polling
$terminais = ['004', '010', '900', '999'];
$wait = 5.0;
$inicio = microtime(true);
while (microtime(true) - $inicio < 300) {
usleep((int) ($wait * 1_000_000));
$consulta = $api->call('GET', "/nfce/cupom/$recibo");
error_log("[polling] status={$consulta['status']}");
if (in_array($consulta['status'], $terminais)) {
if ($consulta['status'] === '004') {
return $consulta['data'];
}
throw new \RuntimeException("Status terminal não-sucesso: {$consulta['status']}");
}
$wait = min($wait * 1.5, 30);
}
throw new \RuntimeException("Timeout aguardando recibo $recibo");
}

Retry com backoff

function comRetry(callable $fn, int $max = 3): mixed
{
for ($i = 1; $i <= $max; $i++) {
try {
return $fn();
} catch (ApiError $e) {
if (($e->payload['status'] ?? null) === '999') throw $e;
if ($i === $max) throw $e;
sleep((int) pow(2, $i - 1));
}
}
}
// Uso
$nfce = comRetry(fn() => emitirNFCe($api, $pedido));

Helper de polling reutilizável

<?php
const TERMINAIS = ['004', '010', '900', '999'];
function aguardarRecibo(MyseApi $api, string $pathBase, string $recibo, int $timeoutSec = 300): array {
$wait = 5.0;
$inicio = microtime(true);
while (microtime(true) - $inicio < $timeoutSec) {
usleep((int) ($wait * 1_000_000));
$resp = $api->call('GET', "$pathBase/$recibo");
if (in_array($resp['status'], TERMINAIS)) return $resp;
$wait = min($wait * 1.5, 30);
}
throw new \RuntimeException("Timeout aguardando recibo $recibo");
}

Emitir NF-e (com transporte completo)

<?php
function emitirNFe(MyseApi $api, array $pedido): array {
$body = [
'serie' => '1',
'cfop' => '5102',
'numero-origem' => 'PED-' . $pedido['id'],
'tipo' => 'S',
'finalidade' => 'N',
'valor-despesas' => '0.00',
'cliente' => $pedido['cpf_cnpj_cliente'],
'transporte' => [
'modalidade' => '0',
'transportadora' => $pedido['transportadora'],
'placa-veiculo' => $pedido['placa'],
'valor-frete' => $pedido['frete'] ?? '0.00',
'valor-seguro' => '0.00',
'peso-bruto' => $pedido['peso_bruto'],
'peso-liquido' => $pedido['peso_liquido'],
'quantidade-volume' => '1',
'especie-volume' => 'Caixa',
'marca-volume' => 'Embalagem',
],
'itens' => array_map(fn($i) => [
'produto' => $i['sku'],
'quantidade' => (string) $i['qtd'],
'valor-unitario' => (string) $i['preco'],
], $pedido['itens']),
'faturas' => array_map(fn($f) => [
'valor' => (string) $f['valor'], 'data' => $f['data'],
], $pedido['faturas'] ?? []),
];
$emissao = $api->call('POST', '/nfe/emissao', $body);
if (!in_array($emissao['status'], ['001', '050'])) {
throw new \RuntimeException("Emissão falhou: {$emissao['descricao']}");
}
$resultado = aguardarRecibo($api, '/nfe/nota', $emissao['data']['recibo']);
if ($resultado['status'] !== '004') {
throw new \RuntimeException("Status terminal não-sucesso: {$resultado['status']}");
}
return $resultado['data']; // inclui sefaz.chave, url-danfe, url-xml
}

Cancelar NF-e

<?php
function cancelarNFe(MyseApi $api, string $chave, string $motivo): array {
if (strlen($motivo) < 15 || strlen($motivo) > 255) {
throw new \InvalidArgumentException('motivo deve ter entre 15 e 255 caracteres');
}
$cancel = $api->call('POST', '/nfe/cancelamento', compact('chave', 'motivo'));
if ($cancel['status'] !== '001') throw new \RuntimeException($cancel['descricao']);
return aguardarRecibo($api, '/nfe/cancelamento', $cancel['data']['recibo']);
}

Lançar carta de correção (CC-e)

<?php
function cartaCorrecaoNFe(MyseApi $api, string $chave, string $textoCorrecao): array {
if (strlen($textoCorrecao) < 15 || strlen($textoCorrecao) > 255) {
throw new \InvalidArgumentException('texto deve ter entre 15 e 255 caracteres');
}
// O campo "motivo" na CC-e é o TEXTO da correção (xCorrecao).
$cce = $api->call('POST', '/nfe/correcao', ['chave' => $chave, 'motivo' => $textoCorrecao]);
if ($cce['status'] !== '001') throw new \RuntimeException($cce['descricao']);
return aguardarRecibo($api, '/nfe/correcao', $cce['data']['recibo']);
}

Cancelar NFC-e

<?php
function cancelarNFCe(MyseApi $api, string $chave, string $motivo): array {
if (strlen($motivo) < 15 || strlen($motivo) > 255) {
throw new \InvalidArgumentException('motivo deve ter entre 15 e 255 caracteres');
}
$cancel = $api->call('POST', '/nfce/cancelamento', compact('chave', 'motivo'));
if ($cancel['status'] !== '001') throw new \RuntimeException($cancel['descricao']);
return aguardarRecibo($api, '/nfce/cancelamento', $cancel['data']['recibo']);
}

Emitir NFS-e (Nota Fiscal de Serviço)

<?php
function emitirNFSe(MyseApi $api, array $servico): array {
$body = [
'numero-origem' => 'OS-' . $servico['id'],
'observacao' => $servico['observacao'],
'nome-contato' => $servico['nome_contato'] ?? null,
'telefone-contato' => $servico['telefone_contato'] ?? null,
'data-fim' => $servico['data_fim'],
'cliente' => $servico['cpf_cnpj_cliente'],
'itens' => array_map(fn($i) => [
'produto' => $i['codigo_servico'],
'quantidade' => (string) $i['qtd'],
'valor-unitario' => (string) $i['preco'],
], $servico['itens']),
];
$emissao = $api->call('POST', '/nfse/emissao', $body);
if (!in_array($emissao['status'], ['001', '050'])) {
throw new \RuntimeException("Emissão NFSe falhou: {$emissao['descricao']}");
}
$resultado = aguardarRecibo($api, '/nfse/nota', $emissao['data']['recibo']);
if ($resultado['status'] !== '004') {
throw new \RuntimeException("Status terminal não-sucesso: {$resultado['status']}");
}
return $resultado['data'];
}

Cancelar NFS-e

<?php
function cancelarNFSe(MyseApi $api, string $chave, string $motivo): array {
if (strlen($motivo) < 15 || strlen($motivo) > 255) {
throw new \InvalidArgumentException('motivo deve ter entre 15 e 255 caracteres');
}
return $api->call('POST', '/nfse/cancelamento', compact('chave', 'motivo'));
}

Reconhecer nota de fornecedor (MD-e)

<?php
/**
* Códigos de reconhecimento:
* 210200 — Confirmação da operação (terminal, irreversível)
* 210210 — Ciência da operação (provisório, 15 dias)
* 210220 — Desconhecimento da operação
* 210240 — Operação não realizada
*/
function reconhecerNotaFornecedor(MyseApi $api, string $chave, string $codigo): array {
$validos = ['210200', '210210', '210220', '210240'];
if (!in_array($codigo, $validos, true)) {
throw new \InvalidArgumentException("Código inválido: $codigo");
}
$resp = $api->call('POST', '/nffornecedor/reconhecimento', [
'chave' => $chave,
'codigo-reconhecimento' => $codigo,
]);
if ($resp['status'] !== '001') throw new \RuntimeException($resp['descricao']);
return aguardarRecibo($api, '/nffornecedor', $resp['data']['recibo']);
}

Listar com paginação

function listarTodosClientes(MyseApi $api): array
{
$todos = [];
$page = 1;
$pageSize = 100;
while (true) {
$resp = $api->call('GET', "/clientes?page=$page&page_size=$pageSize");
if ($resp['status'] !== '005' || !is_array($resp['data'] ?? null)) break;
$todos = array_merge($todos, $resp['data']);
if (count($resp['data']) < $pageSize) break;
$page++;
usleep(200_000);
}
return $todos;
}

Tratamento de erros

try {
$nfce = emitirNFCe($api, $pedido);
echo "NFCe emitida: " . ($nfce['sefaz']['chave'] ?? '???') . "\n";
} catch (ApiError $e) {
error_log(json_encode([
'event' => 'api_error',
'http_status' => $e->httpStatus,
'request_id' => $e->requestId,
'message' => $e->getMessage(),
]));
throw $e;
}

Variação: cURL puro (sem Guzzle)

function curlCall(string $method, string $path, ?array $body = null): array {
$ch = curl_init($_ENV['MYSE_API_BASE'] . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'Authorization: Token ' . $_ENV['MYSE_API_TOKEN'],
'Content-Type: application/json',
],
CURLOPT_TIMEOUT => 30,
CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
CURLOPT_HEADER => true,
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);
$headers = substr($resp, 0, $headerSize);
$body = substr($resp, $headerSize);
return ['status' => $code, 'headers' => $headers, 'body' => json_decode($body, true)];
}

Health check (sem autenticação)

<?php
$res = file_get_contents($_ENV['MYSE_API_BASE'] . '/health');
$health = json_decode($res, true);
// ["status" => "ok", "database" => "ok", "timestamp" => "..."]

Alterar nota antes da emissão (PUT)

Disponível em NF-e (/nfe/{id}), NFC-e (/nfce/{id}) e NFS-e (/nfse/{id}). Só funciona enquanto a nota ainda não foi submetida para emissão.

<?php
function alterarNota(MyseApi $api, string $tipo, string $id, array $dados): array {
// $tipo: 'nfe' | 'nfce' | 'nfse'
return $api->call('PUT', "/$tipo/$id", $dados);
}
// Uso:
alterarNota($api, 'nfe', '12345', [
'serie' => '1', 'cfop' => '5102', 'cliente' => '12345678000199',
'itens' => [
['produto' => 'SKU-001', 'quantidade' => '3', 'valor-unitario' => '89.90'],
],
]);

Consulta detalhada por recibo

<?php
$nfeCompleta = $api->call('GET', "/nfe/nota/$recibo/completa");
$nfceCompleta = $api->call('GET', "/nfce/cupom/$recibo/completa");

Listagens

<?php
$nfes = $api->call('GET', '/nfe?page=1&page_size=20');
$nfces = $api->call('GET', '/nfce?page=1&page_size=50');
$nfses = $api->call('GET', '/nfse?dataInicial=2026-05-01&page=1&page_size=20');
$fornecedor = $api->call('GET', '/nffornecedor?page=1&page_size=20');
// Forma legada (path-based)
$nfesLegado = $api->call('GET', '/nfe/paginacao/1/20');

Clientes (CRUD completo)

<?php
function cadastrarPF(MyseApi $api, array $dados): array {
return $api->call('POST', '/clientes', [
'tipo' => 'F',
'nome' => $dados['nome'],
'cpf' => $dados['cpf'],
'rg' => $dados['rg'] ?? null,
'consumidor-final' => '1',
'contato' => [
'email' => $dados['email'] ?? null,
'telefone' => $dados['telefone'] ?? null,
],
'endereco' => $dados['endereco'],
]);
}
function cadastrarPJ(MyseApi $api, array $dados): array {
return $api->call('POST', '/clientes', [
'tipo' => 'J',
'razao-social' => $dados['razao_social'],
'nome-fantasia' => $dados['nome_fantasia'] ?? null,
'cnpj' => $dados['cnpj'],
'inscricao-municipal' => $dados['inscricao_municipal'] ?? null,
'tipo-inscricao-estadual' => $dados['contribuinte'] ?? '9',
'inscricao-estadual' => $dados['inscricao_estadual'] ?? 'ISENTO',
'consumidor-final' => $dados['consumidor_final'] ?? '0',
'contato' => [
'email' => $dados['email'] ?? null,
'telefone' => $dados['telefone'] ?? null,
],
'endereco' => $dados['endereco'],
]);
}
// Consultar por CPF/CNPJ
$cliente = $api->call('GET', '/clientes/12345678000199');
// Buscar por nome
$encontrados = $api->call('GET', '/clientes/busca/exemplo');
$top = $api->call('GET', '/clientes/busca/top_clientes');
// Atualizar
$api->call('PUT', '/clientes/12345678000199', [
'tipo' => 'J',
'razao-social' => 'Empresa Exemplo (Novo Nome) Ltda',
]);
// Excluir
$api->call('DELETE', '/clientes/12345678000199');

Produtos (CRUD completo)

<?php
function cadastrarProduto(MyseApi $api, array $produto): array {
return $api->call('POST', '/produtos', [
'referencia' => $produto['sku'],
'nome' => $produto['nome'],
'valor' => (string) $produto['valor'],
'medida' => $produto['medida'] ?? 'UN',
'categoria' => $produto['categoria'] ?? null,
'ncm' => $produto['ncm'],
'origem' => $produto['origem'] ?? '0',
'cfop-preferencial' => $produto['cfop'] ?? '5102',
'tipo-produto' => 'P',
'configuracoes-fiscais' => $produto['config_fiscal'] ?? [],
]);
}
function cadastrarServico(MyseApi $api, array $servico): array {
return $api->call('POST', '/produtos', [
'referencia' => $servico['sku'],
'nome' => $servico['nome'],
'valor' => (string) $servico['valor'],
'medida' => 'H',
'tipo-produto' => 'S',
'codigo-servico' => $servico['codigo_servico'],
]);
}
// Consultar por referência
$produto = $api->call('GET', '/produtos/SKU-001');
// Buscar por nome (1º resultado)
$porNome = $api->call('GET', '/produtos/nome/Camiseta');
// Listar paginado com busca (use _todos para não filtrar)
$todos = $api->call('GET', '/produtos/paginacao/1/20/_todos');
$filtrados = $api->call('GET', '/produtos/paginacao/1/20/Camiseta');
// Atualizar
$api->call('PUT', '/produtos/SKU-001', [
'referencia' => 'SKU-001',
'nome' => 'Camiseta Polo M (Novo)',
'valor' => '99.90',
]);
// Excluir
$api->call('DELETE', '/produtos/SKU-001');