Pular para o conteúdo principal

Receber produtos do Siprinho

Quando o vendedor clica no "+" de um produto no Siprinho — o produto consultado ou uma sugestão de substituto, cross-sell ou upsell —, esse item precisa chegar à cesta do ERP. O Siprinho sabe entregar o item de cinco formas, e cada loja usa uma delas por vez:

MétodoQuem entrega ao ERPO que o ERP implementa
Deeplinko Siprinho, na própria máquinaum protocolo sipru_{NOME_DO_ERP}:// registrado no Windows
API do ERPa nuvem Sipruum endpoint HTTPS que recebe um POST por item
Banco de dadoso Siprinho, na própria máquinauma tabela (ou procedure) que recebe o item
Arquivoo Siprinho, na própria máquinaa leitura de uma pasta onde cada item vira um arquivo
Área de transferênciao vendedor, com Ctrl+Vnada — o vendedor cola o código no ERP

O método é escolhido na tela de configuração do Siprinho, em Integração com o ERP. Em API (online) o item segue para a nuvem Sipru; em Integrações locais o Siprinho intercepta o clique e entrega o item na própria máquina, pelo método local escolhido.

Seja qual for o método, o ERP lança o item com o preço e as regras do próprio ERP. O preço que vem do Siprinho é informativo.

Visão geral do fluxo​

┌─────────┐  1. abre o canal (POST /channel)   ┌───────────┐
│ │ ──────────────────────────────────▶│ │
│ ERP │ ◀──────────────────────────────────│ Sipru API │
│ │ channelId + url │ │
└────┬────┘ └─────┬─────┘
│ 2. abre a url │
▼ │
┌──────────┐◀─────── carrega a sessão ───────────────┘
│ Siprinho │
└────┬─────┘
│ 3. o vendedor clica no "+"
▼
┌────────────────────────────────────────────────────────────┐
│ Integrações locais: deeplink, banco, arquivo ou clipboard │ ── na máquina
│ API (online): Sipru API ── POST no endpoint do ERP │ ── pela nuvem
└────────────────────────────────────────────────────────────┘

A sessão é aberta como descrito em Canal ao vivo. O Siprinho fica aberto o tempo todo, inclusive entre um atendimento e outro — o ERP não deve fechá-lo.

Os dados do item​

Nos métodos locais, quem decide o formato da entrega é o modelo configurado no Siprinho: um texto com campos entre chaves duplas, trocados pelos dados do item clicado. Os campos são os mesmos nos quatro métodos locais:

CampoO que traz
{{productCode}} ou {{sku}}Código do produto
{{ean}}Código de barras
{{quantity}}Quantidade
{{name}}Nome do produto
{{price}}Preço unitário (informativo)
{{source}}De onde veio o clique: o produto consultado ou uma sugestão
{{channelId}}, {{tempQuoteId}}O canal e a cotação temporária da sessão
{{companyId}}, {{locationId}}, {{pricingPolicy}}Empresa, loja e política de preço da sessão
{{vendedor.codigoExterno}}, {{vendedor.nome}}Vendedor da sessão
{{data}}, {{hora}}, {{timestamp}}Data, hora e data e hora completas do clique
{{eventId}}Identificador único do clique

Dois campos merecem atenção do lado do ERP:

  • {{tempQuoteId}} diz em qual cotação o item entra. Com mais de um atendimento aberto na mesma máquina, é ele que evita lançar o item na cesta errada.
  • {{eventId}} é único por clique. Use-o como chave para não lançar o mesmo clique duas vezes.

Campo que não existir no clique fica vazio. A lista completa, com os valores reais do último clique, está no botão Ver campos disponíveis — ver Campos que podem ser usados.

A cada clique, o Siprinho abre um endereço de protocolo que o ERP registrou no Windows, com os dados do item na query string. O modelo sugerido pelo Siprinho é:

sipru_{NOME_DO_ERP}://carrinho/adicionar?ean={{ean}}&quantity={{quantity}}&productCode={{productCode}}&name={{name}}

E um clique vira, por exemplo:

sipru_meuerp://carrinho/adicionar?ean=&quantity=2&productCode=007136&name=Parafuso%206mm
ParâmetroTipoObrigatórioDescrição
{NOME_DO_ERP}slug—Ex.: sipru_meuerp://. Mantenha igual ao valor enviado no header X-ERP-Name.
quantitynúmerosimQuantidade a incluir na cesta. Sempre enviado.
ean / productCodestring—Pelo menos um deles vem preenchido. Use o que o seu cadastro reconhecer.
namestring—Só um rótulo, útil na mensagem de erro se o produto não for encontrado no ERP.

O endereço é configurável no Siprinho: se o ERP precisar de outros parâmetros (por exemplo, tempQuoteId={{tempQuoteId}}), basta incluí-los no modelo, com os campos acima.

  1. Não abrir uma segunda instância. Sem tratamento, cada clique abre o ERP de novo. A segunda ativação deve repassar o endereço para a instância já em execução (mutex ou lock + IPC local) e encerrar.
  2. Decodificar a query string (URL-decode) e localizar o produto por ean e/ou productCode.
  3. Tratar como fire-and-forget. O Siprinho não espera resposta. Se não houver cesta aberta ou o produto não for encontrado, o ERP avisa o usuário na própria tela — não existe retry nem fila.

Se o protocolo não estiver registrado, o Siprinho mostra um aviso vermelho no painel ("Não foi possível abrir o ERP"), para ninguém achar que o item foi lançado.

Exemplos de código por linguagem​

Como garantir instância única e tratar o endereço recebido, por linguagem.

C# (.NET)​

Exemplo de implementação de "captura" do evento de clique no Siprinho para adicionar o produto na cesta de pedidos no ERP.

Garantir instância única e repassar a URI (Mutex + Named Pipe)

using System.IO.Pipes;

const string NomeMutex = @"Local\MeuErp.SingleInstance";
const string NomePipe = "MeuErp.ActivationPipe";

using var mutex = new Mutex(initiallyOwned: true, NomeMutex, out var souAPrimeira);

if (!souAPrimeira)
{
// Repassa a URI recebida (args[0]) pra instância já aberta, e encerra.
using var cliente = new NamedPipeClientStream(".", NomePipe, PipeDirection.Out);
cliente.Connect(2000);
using var escritor = new StreamWriter(cliente) { AutoFlush = true };
escritor.Write(args[0]);
return;
}

// Só a primeira instância escuta o pipe em loop, em background:
_ = Task.Run(async () =>
{
while (true)
{
await using var servidor = new NamedPipeServerStream(NomePipe, PipeDirection.In, 1);
await servidor.WaitForConnectionAsync();
using var leitor = new StreamReader(servidor);
var uri = await leitor.ReadToEndAsync();
ProcessarUriCarrinho(uri);
}
});

Parsear o callback

static void ProcessarUriCarrinho(string uriTexto)
{
var uri = new Uri(uriTexto);
if (!string.Equals(uri.Host, "carrinho", StringComparison.OrdinalIgnoreCase) ||
!string.Equals(uri.AbsolutePath.Trim('/'), "adicionar", StringComparison.OrdinalIgnoreCase))
return;

var query = System.Web.HttpUtility.ParseQueryString(uri.Query);
var ean = query["ean"];
var productCode = query["productCode"];
var name = query["name"];
if (!int.TryParse(query["quantity"], out var quantity)) return;

AdicionarAoCarrinho(ean, productCode, quantity, name);
}

VB.NET​

Os mesmos dois passos do C#, num único bloco contínuo.

Imports System.Diagnostics
Imports System.IO.Pipes
Imports Microsoft.Win32

' Instância única (Mutex) + repasse via Named Pipe
Const NomeMutex As String = "Local\MeuErp.SingleInstance"
Const NomePipe As String = "MeuErp.ActivationPipe"
Dim souAPrimeira As Boolean
Dim mutex As New Mutex(True, NomeMutex, souAPrimeira)

If Not souAPrimeira Then
Using cliente As New NamedPipeClientStream(".", NomePipe, PipeDirection.Out)
cliente.Connect(2000)
Using escritor As New StreamWriter(cliente) With {.AutoFlush = True}
escritor.Write(uriRecebida)
End Using
End Using
Return
End If

' Parsear o callback
Sub ProcessarUriCarrinho(uriTexto As String)
Dim uri As New Uri(uriTexto)
If Not String.Equals(uri.Host, "carrinho", StringComparison.OrdinalIgnoreCase) Then Return
If Not String.Equals(uri.AbsolutePath.Trim("/"c), "adicionar", StringComparison.OrdinalIgnoreCase) Then Return

Dim query = System.Web.HttpUtility.ParseQueryString(uri.Query)
Dim quantity As Integer
If Not Integer.TryParse(query("quantity"), quantity) Then Return

AdicionarAoCarrinho(query("ean"), query("productCode"), quantity, query("name"))
End Sub

VB6​

VB6 não tem Named Pipe nativo — usa-se um loopback TCP local (via controle Winsock) para repassar a URI entre instâncias, já que subclassar a janela para WM_COPYDATA é bem mais trabalhoso em VB6.

Instância única + repasse via Winsock loopback

' Instância principal — Form_Load
Private Sub Form_Load()
If App.PrevInstance Then
' nunca deveria chegar aqui: a mensageira já finaliza sozinha
End
End If
Winsock1.LocalPort = 51837
Winsock1.Listen
End Sub

Private Sub Winsock1_ConnectionRequest(ByVal requestID As Long)
Winsock1.Close
Winsock1.Accept requestID
End Sub

Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
Dim uriTexto As String
Winsock1.GetData uriTexto
ProcessarUriCarrinho uriTexto
Winsock1.Close
Winsock1.Listen
End Sub

' Instância mensageira — Sub Main (App.PrevInstance = True)
Sub EnviarParaInstanciaExistente(uriTexto As String)
Dim cliente As Winsock
Set cliente = New Winsock
cliente.Connect "127.0.0.1", 51837
Do While cliente.State <> sckConnected: DoEvents: Loop
cliente.SendData uriTexto
End Sub

Parsear o callback

Sub ProcessarUriCarrinho(uriTexto As String)
Dim partes() As String, parQuery() As String, i As Integer
Dim ean As String, productCode As String, nome As String, quantity As Long
Dim posInterrogacao As Long

posInterrogacao = InStr(uriTexto, "?")
If posInterrogacao = 0 Then Exit Sub

partes = Split(Mid$(uriTexto, posInterrogacao + 1), "&")
For i = 0 To UBound(partes)
parQuery = Split(partes(i), "=")
Select Case LCase$(parQuery(0))
Case "ean": ean = UnescapeURL(parQuery(1))
Case "productcode": productCode = UnescapeURL(parQuery(1))
Case "name": nome = UnescapeURL(parQuery(1))
Case "quantity": quantity = CLng(parQuery(1))
End Select
Next i

If quantity <= 0 Then Exit Sub
AdicionarAoCarrinho ean, productCode, quantity, nome
End Sub

Delphi​

Padrão clássico Delphi: CreateMutex + FindWindow/ SendMessage(WM_COPYDATA) para instância única e repasse — é o equivalente mais idiomático ao Named Pipe do .NET nesse ecossistema.

Instância única + repasse via WM_COPYDATA

const
NomeMutex = 'Local\MeuErp.SingleInstance';
NomeJanela = 'MeuErpJanelaPrincipal'; // Form1.Caption/Name único, usado pelo FindWindow

procedure TFormPrincipal.FormCreate(Sender: TObject);
var
Mutex: THandle;
JanelaExistente: HWND;
Dados: TCopyDataStruct;
UriRecebida: string;
begin
Mutex := CreateMutex(nil, True, NomeMutex);
if GetLastError = ERROR_ALREADY_EXISTS then
begin
JanelaExistente := FindWindow(nil, NomeJanela);
if (JanelaExistente <> 0) and (ParamCount >= 1) then
begin
UriRecebida := ParamStr(1);
Dados.dwData := 0;
Dados.cbData := (Length(UriRecebida) + 1) * SizeOf(Char);
Dados.lpData := PChar(UriRecebida);
SendMessage(JanelaExistente, WM_COPYDATA, 0, LPARAM(@Dados));
end;
Application.Terminate;
Exit;
end;

if ParamCount >= 1 then
ProcessarUriCarrinho(ParamStr(1)); // ativação direta (app ainda não estava aberto)
end;

procedure TFormPrincipal.WMCopyData(var Msg: TWMCopyData);
begin
ProcessarUriCarrinho(PChar(Msg.CopyDataStruct.lpData));
end;
// declarar em TFormPrincipal: procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;

Parsear o callback

uses System.NetEncoding, System.StrUtils;

procedure ProcessarUriCarrinho(const UriTexto: string);
var
Uri: TUri;
Query: TStringList;
Ean, ProductCode, Nome: string;
Quantity: Integer;
begin
Uri := TUri.Create(UriTexto);
try
if not SameText(Uri.Host, 'carrinho') then Exit;
if not SameText(Trim(Uri.Path, ['/']), 'adicionar') then Exit;

Query := TStringList.Create;
try
Query.Delimiter := '&';
Query.DelimitedText := Uri.Query;
Ean := TNetEncoding.URL.Decode(Query.Values['ean']);
ProductCode := TNetEncoding.URL.Decode(Query.Values['productCode']);
Nome := TNetEncoding.URL.Decode(Query.Values['name']);
if not TryStrToInt(Query.Values['quantity'], Quantity) then Exit;

AdicionarAoCarrinho(Ean, ProductCode, Quantity, Nome);
finally
Query.Free;
end;
finally
Uri.Free;
end;
end;

Java​

Instância única com FileLock; e repasse local via ServerSocket em loopback.

Instância única (FileLock) + repasse via socket loopback

import java.io.*;
import java.net.*;
import java.nio.channels.*;

static final int PORTA = 51837;

static boolean souAPrimeiraInstancia() throws IOException {
File lockFile = new File(System.getProperty("java.io.tmpdir"), "meuerp.lock");
FileChannel canal = new RandomAccessFile(lockFile, "rw").getChannel();
FileLock lock = canal.tryLock();
return lock != null; // se null, já existe outra instância rodando
}

// Instância principal: escuta em background
static void iniciarEscuta() {
new Thread(() -> {
try (ServerSocket servidor = new ServerSocket(PORTA, 0, InetAddress.getLoopbackAddress())) {
while (true) {
try (Socket cliente = servidor.accept();
BufferedReader leitor = new BufferedReader(new InputStreamReader(cliente.getInputStream()))) {
processarUriCarrinho(leitor.readLine());
}
}
} catch (IOException e) { /* log */ }
}, "sipru-activation-listener").start();
}

// Instância mensageira: repassa a URI e encerra
static void enviarParaInstanciaExistente(String uriTexto) throws IOException {
try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), PORTA);
PrintWriter escritor = new PrintWriter(socket.getOutputStream(), true)) {
escritor.println(uriTexto);
}
}

Parsear o callback

import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

static void processarUriCarrinho(String uriTexto) {
URI uri = URI.create(uriTexto);
if (!"carrinho".equalsIgnoreCase(uri.getHost())) return;
if (!"adicionar".equalsIgnoreCase(uri.getPath().replace("/", ""))) return;

Map<String, String> query = new HashMap<>();
for (String par : uri.getQuery().split("&")) {
String[] kv = par.split("=", 2);
query.put(kv[0].toLowerCase(), kv.length > 1
? URLDecoder.decode(kv[1], StandardCharsets.UTF_8) : "");
}

int quantity;
try {
quantity = Integer.parseInt(query.get("quantity"));
} catch (Exception e) {
return;
}

adicionarAoCarrinho(query.get("ean"), query.get("productcode"), quantity, query.get("name"));
}

Python​

Instância única e repasse via socket loopback (mesma abordagem do Java, portável e sem dependências externas).

Instância única + repasse via socket loopback

import socket
import threading

PORTA = 51837

def sou_a_primeira_instancia() -> bool:
global _socket_trava
_socket_trava = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
_socket_trava.bind(("127.0.0.1", PORTA))
_socket_trava.listen(5)
return True
except OSError:
return False # porta já ocupada por outra instância

def iniciar_escuta() -> None:
def loop():
while True:
cliente, _ = _socket_trava.accept()
with cliente:
uri_texto = cliente.recv(4096).decode("utf-8")
processar_uri_carrinho(uri_texto)

threading.Thread(target=loop, daemon=True, name="sipru-activation-listener").start()

def enviar_para_instancia_existente(uri_texto: str) -> None:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as cliente:
cliente.connect(("127.0.0.1", PORTA))
cliente.sendall(uri_texto.encode("utf-8"))

Parsear o callback

from urllib.parse import urlparse, parse_qs

def processar_uri_carrinho(uri_texto: str) -> None:
uri = urlparse(uri_texto)
if uri.hostname != "carrinho" or uri.path.strip("/") != "adicionar":
return

query = {k.lower(): v[0] for k, v in parse_qs(uri.query).items()}
try:
quantity = int(query["quantity"])
except (KeyError, ValueError):
return

adicionar_ao_carrinho(query.get("ean"), query.get("productcode"), quantity, query.get("name"))

API do ERP​

Com o Siprinho em API (online), o clique segue para a nuvem Sipru, e o Sipru faz um POST no endpoint do ERP com o item. É o único endpoint que o parceiro implementa, e precisa estar acessível pela internet.

Para onde o Sipru envia​

O destino é resolvido nesta ordem de precedência:

  1. o cartCallbackUrl do canal, quando enviado na abertura — override por cotação;
  2. a URL de item adicionado cadastrada para a loja pelo time Sipru;
  3. o endpoint de pedidos da mesma configuração — usado como fallback por quem não separa os dois.

Sem nenhum dos três, o clique não vira POST, e o vendedor vê na tela que o item não foi enviado ao ERP.

Em produção a URL precisa ser HTTPS. HTTP é aceito apenas em homologação, para você conseguir apontar para um host interno durante a integração.

Autenticação​

Configurável por canal ou por loja, com credencial estática:

modeO que o Sipru envia
noneNenhum header de autenticação
bearerAuthorization: Bearer <token>
api_keyX-API-Key: <token>

OAuth client credentials não é suportado.

O payload​

{
"idempotencyKey": "erp_cart_4f1c8b2e-...",
"event": "cart_item_added",
"channelId": "8f2b...",
"tempQuoteId": "temp-12345",
"quoteId": null,
"cliente": { "cnpj": "04252011000110", "razaoSocial": "…", "nomeFantasia": "…" },
"vendedor": { "cpf": "12345678909", "externalLogin": "joao.silva", "email": "vendedor@loja.com.br" },
"tenant": { "companyId": "empresa-x", "locationId": "07526557000100" },
"item": {
"productCode": "72411",
"ean": "7891234567890",
"name": "Argamassa AC-III 20kg",
"quantity": 4,
"unitPrice": 34.9,
"coinsReward": 12,
"source": "crossSell"
},
"timestamp": "2026-08-31T18:44:02.117Z"
}

O Idempotency-Key também vai no header, com o mesmo valor.

item.source diz de onde veio o clique: product (o produto que o ERP consultou), substitutes, crossSell ou upsell. Guarde-o: é o que permite medir quanto da venda veio de uma sugestão da Sipru.

O que o Sipru espera de volta​

Responder 2xx. Qualquer 2xx serve.

O Sipru tenta 2 vezes, com timeout de 8 segundos, e só repete em status transitório — 408, 429 e 5xx. Um 4xx não transitório recusa o item de vez, sem nova tentativa. Se o item não entrar, o vendedor vê isso na tela.

Deduplique

Como há retry, o mesmo item pode chegar duas vezes. Use o idempotencyKey (header ou corpo) como chave de deduplicação do seu lado.

O contrato formal está em webhooks.cartItemAdded na referência da API.

Banco de dados​

A cada clique, o Siprinho executa um comando SQL no banco do ERP, direto da máquina do vendedor. O comando é escrito por quem configura o Siprinho, com os campos do item. Bancos suportados: SQL Server, MySQL / MariaDB, PostgreSQL, Oracle, Firebird, SQLite, Microsoft Access e ODBC (qualquer outro banco com driver ODBC instalado).

O caminho mais simples é uma tabela de entrada que o ERP lê:

CREATE TABLE SIPRU_CARRINHO (
EVENT_ID VARCHAR(64) NOT NULL PRIMARY KEY,
TEMP_QUOTE_ID VARCHAR(30),
CODIGO VARCHAR(120),
EAN VARCHAR(60),
QTD VARCHAR(10),
CRIADO_EM VARCHAR(30),
PROCESSADO CHAR(1) DEFAULT 'N'
);

E o comando configurado no Siprinho:

INSERT INTO SIPRU_CARRINHO (EVENT_ID, TEMP_QUOTE_ID, CODIGO, EAN, QTD, CRIADO_EM)
VALUES ({{eventId}}, {{tempQuoteId}}, {{productCode}}, {{ean}}, {{quantity}}, {{timestamp}})

O comando também pode chamar uma procedure do ERP, se o banco aceitar a chamada como um comando comum.

Como o Siprinho monta o comando:

  • Cada campo vai como parâmetro, separado do texto do comando. Não precisa de aspas, e um nome com apóstrofo (ex.: D'Ávila) não quebra a query.
  • Campos só ocupam o lugar de valores. Nome de tabela ou de coluna vai escrito direto no comando.
  • O botão Testar query executa o comando com os dados do último clique e desfaz tudo no final: a tabela precisa aceitar a gravação, mas nada fica gravado no teste.

Do lado do ERP:

  1. Crie um usuário de banco só para o Siprinho, com permissão apenas de gravar na tabela de entrada. A senha fica criptografada no arquivo de configuração do Siprinho, em cada máquina.
  2. Libere a rede: cada máquina com o Siprinho conecta direto no servidor do banco.
  3. Leia os itens novos — por polling, trigger ou o mecanismo de fila do próprio ERP — e lance cada um na cesta da cotação TEMP_QUOTE_ID.
  4. Marque o item como processado (ou apague a linha) depois de lançado. Com EVENT_ID como chave, o mesmo clique nunca entra duas vezes.

Se o banco estiver fora do ar ou o comando falhar, o Siprinho mostra o erro em vermelho no painel. Não há retry: o clique se perde, e o vendedor clica de novo.

Arquivo​

A cada clique, o Siprinho grava um arquivo numa pasta que o ERP acompanha.

  • Pasta: a padrão é %APPDATA%\SipruWidget\Integracao, do usuário do Windows logado. Se o ERP roda com outro usuário, ou em outra máquina, configure no Siprinho uma pasta que os dois enxerguem.
  • Nome e conteúdo: o mesmo modelo vira o nome e o conteúdo do arquivo. Com {{productCode}}, um clique no produto ABC-123 gera ABC-123.txt com ABC-123 dentro. Caracteres que o Windows não aceita em nome de arquivo (como / e :) viram _ no nome, mas ficam intactos no conteúdo.
  • Gravação atômica: o arquivo nasce com a extensão temporária (padrão .tmp) e só é renomeado para a extensão final (padrão .txt) depois de totalmente gravado.
  • Codificação: UTF-8 por padrão, ou ANSI (Windows-1252) para sistemas mais antigos.
O mesmo nome substitui o arquivo

Se o modelo gerar o mesmo nome duas vezes, o segundo arquivo substitui o primeiro — dois cliques no mesmo produto antes de o ERP ler a pasta viram um só. Inclua {{eventId}} no modelo para cada clique gerar um arquivo próprio:

{{eventId}};{{tempQuoteId}};{{productCode}};{{quantity}}

Do lado do ERP:

  1. Acompanhe a pasta — por evento do sistema de arquivos ou por polling.
  2. Leia só a extensão final (.txt). Arquivo .tmp ainda está sendo gravado.
  3. Leia com a mesma codificação configurada no Siprinho.
  4. Apague ou mova o arquivo depois de lançar o item, para não lê-lo de novo.
var pasta = Environment.ExpandEnvironmentVariables(@"%APPDATA%\SipruWidget\Integracao");
var observador = new FileSystemWatcher(pasta, "*.txt") { EnableRaisingEvents = true };

// O Siprinho grava .tmp e renomeia para .txt quando termina.
observador.Renamed += (_, e) =>
{
var conteudo = File.ReadAllText(e.FullPath, Encoding.UTF8);
var campos = conteudo.Split(';'); // eventId;tempQuoteId;productCode;quantity
AdicionarAoCarrinho(campos[1], campos[2], int.Parse(campos[3]));
File.Delete(e.FullPath);
};

Se a pasta não existir ou não tiver permissão de escrita, o Siprinho mostra o erro em vermelho no painel.

Área de transferência​

A cada clique, o Siprinho copia um texto para a área de transferência do Windows, e o vendedor cola no ERP com Ctrl+V. O padrão copia só o código do produto ({{productCode}}); dá para juntar mais campos e texto livre, por exemplo {{productCode}};{{quantity}}.

Do lado do ERP não há nada a implementar: o vendedor cola o código no campo de produto, como se tivesse digitado. Se o modelo copiar mais de um campo, o campo do ERP precisa aceitar esse formato.

É o método mais simples de ligar, e o único em que o item depende de uma ação do vendedor para entrar na cesta.

Checklist de implementação​

  1. Combinar com a loja qual método o Siprinho vai usar e configurar em Integração com o ERP.
  2. Deeplink: registrar sipru_{NOME_DO_ERP}://, garantir instância única e tratar o endereço como fire-and-forget.
  3. API do ERP: publicar o endpoint HTTPS, combinar a autenticação, responder 2xx e deduplicar por idempotencyKey.
  4. Banco de dados: criar a tabela de entrada e um usuário só com permissão de gravar nela, liberar a rede e deduplicar por {{eventId}}.
  5. Arquivo: combinar a pasta, ler só a extensão final, apagar depois de lançar e incluir {{eventId}} no nome.
  6. Em todos: lançar o item na cotação certa (tempQuoteId), com o preço do cadastro do ERP, nunca o que veio do Siprinho.