Construa um app frontend que conecta ao Venddor usando nossas APIs, SDKs e embeds.
Existem tres formas de integrar seu app com o Venddor:
| Tipo | Descrição | Melhor para |
|---|---|---|
| App Standalone | Seu app roda em seu proprio servidor e chama as APIs do Venddor | ERPs, CRMs, ferramentas de BI |
| App Embutido | Seu app carrega dentro do painel admin do Venddor via iframe | Dashboards, configuracoes avancadas |
| Widget Storefront | Componente JavaScript injetado na loja do tenant | Reviews, chat, recomendacoes |
Use o SDK JavaScript para simplificar chamadas API, autenticacao e tratamento de erros:
npm install @venddor/sdkimport { VenddorClient } from '@venddor/sdk';
// Initialize the client
const venddor = new VenddorClient({
clientId: 'YOUR_CLIENT_ID',
clientSecret: 'YOUR_CLIENT_SECRET', // server-side only!
apiUrl: 'https://api.io.venddor.com.br',
});
// Authenticate with a tenant
const auth = await venddor.authenticate(tenantId, accessToken);
// List products
const products = await venddor.products.list({
page: 1,
limit: 20,
status: 'active',
});
console.log(`Found ${products.total} products`);
// Create a product
const newProduct = await venddor.products.create({
name: 'Camiseta Premium',
price_cents: 8990,
description: 'High quality cotton t-shirt',
category_id: 'CAT_UUID',
});
// Get an order
const order = await venddor.orders.get('ORDER_ID');
console.log(`Order ${order.id}: R$ ${(order.total_cents / 100).toFixed(2)}`);
// Listen to webhooks (Express.js example)
app.post('/webhooks/venddor', venddor.webhooks.verify(), (req, res) => {
const event = req.body;
console.log(`Received ${event.event} for tenant ${event.tenant_id}`);
switch (event.event) {
case 'order.created':
handleNewOrder(event.data);
break;
case 'product.updated':
syncProduct(event.data);
break;
}
res.status(200).send('OK');
});pip install venddor-sdkfrom venddor import VenddorClient
# Initialize the client
client = VenddorClient(
client_id='YOUR_CLIENT_ID',
client_secret='YOUR_CLIENT_SECRET',
api_url='https://api.io.venddor.com.br'
)
# Authenticate
client.authenticate(tenant_id, access_token)
# List products
products = client.products.list(page=1, limit=20, status='active')
print(f"Found {products['total']} products")
# Create a product
new_product = client.products.create(
name='Camiseta Premium',
price_cents=8990,
description='High quality cotton t-shirt',
category_id='CAT_UUID'
)
# Process orders
orders = client.orders.list(status='pending')
for order in orders['items']:
print(f"Order {order['id']}: R$ {order['total_cents'] / 100:.2f}")
# Webhook verification (Flask example)
@app.route('/webhooks/venddor', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Venddor-Signature')
if not client.webhooks.verify(request.data, signature):
return 'Invalid signature', 401
event = request.json
if event['event'] == 'order.created':
process_new_order(event['data'])
return 'OK', 200Apps embutidos rodam dentro do painel admin do Venddor via iframe. Isso permite uma experiencia integrada sem sair do admin.
<!-- Your embedded app's HTML -->
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.venddor.com.br/embed-sdk.js"></script>
</head>
<body>
<div id="app"></div>
<script>
// The Venddor Embed SDK provides:
// - Authentication context (token, tenant)
// - Navigation helpers
// - Toast notifications
// - Theme synchronization (light/dark mode)
const venddor = VenddorEmbed.init();
venddor.on('ready', ({ token, tenantId, theme }) => {
console.log('Embedded in Venddor admin');
console.log('Token:', token);
console.log('Tenant:', tenantId);
console.log('Theme:', theme); // 'light' or 'dark'
// Your app logic here
fetchData(token, tenantId);
});
venddor.on('theme-change', (theme) => {
document.body.className = theme;
});
// Show a toast in the Venddor admin
venddor.toast('Product synced successfully!', 'success');
// Navigate to another admin page
venddor.navigate('/admin/products');
</script>
</body>
</html>Aqui esta um exemplo completo do fluxo OAuth redirect usando Node.js/Express:
const express = require('express');
const crypto = require('crypto');
const app = express();
const CLIENT_ID = process.env.VENDDOR_CLIENT_ID;
const CLIENT_SECRET = process.env.VENDDOR_CLIENT_SECRET;
const REDIRECT_URI = 'https://myapp.com/auth/callback';
const VENDDOR_API = 'https://api.io.venddor.com.br';
// Step 1: Redirect user to Venddor authorization
app.get('/auth/install', (req, res) => {
const state = crypto.randomBytes(16).toString('hex');
req.session.oauthState = state;
const authUrl = new URL(`${VENDDOR_API}/oauth/authorize`);
authUrl.searchParams.set('client_id', CLIENT_ID);
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('scope', 'read_products write_inventory');
authUrl.searchParams.set('state', state);
res.redirect(authUrl.toString());
});
// Step 2: Handle callback and exchange code for token
app.get('/auth/callback', async (req, res) => {
const { code, state, tenant_id } = req.query;
// Validate state to prevent CSRF
if (state !== req.session.oauthState) {
return res.status(403).send('Invalid state');
}
// Exchange code for access token
const tokenRes = await fetch(`${VENDDOR_API}/oauth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'authorization_code',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
code,
redirect_uri: REDIRECT_URI,
}),
});
const tokens = await tokenRes.json();
// Store tokens securely (database, encrypted session, etc.)
await saveTokens(tenant_id, tokens);
res.redirect('/dashboard');
});Widgets storefront sao componentes JavaScript que sao injetados na loja publica do tenant. Eles podem exibir conteudo adicional como reviews, chat ou recomendacoes.
// widget.js — loaded by the tenant's storefront
(function() {
const WIDGET_API = 'https://api.myapp.com/widget';
// Get the tenant context from the page
const tenantId = document.querySelector('meta[name="venddor-tenant"]')?.content;
const productId = document.querySelector('[data-product-id]')?.dataset.productId;
if (!tenantId || !productId) return;
// Fetch widget data
fetch(`${WIDGET_API}/reviews?tenant=${tenantId}&product=${productId}`)
.then(res => res.json())
.then(data => {
const container = document.createElement('div');
container.id = 'venddor-reviews-widget';
container.innerHTML = `
<h3>Customer Reviews (${data.total})</h3>
<div class="avg-rating">${'★'.repeat(Math.round(data.avg_rating))} ${data.avg_rating}/5</div>
${data.reviews.map(r => `
<div class="review">
<strong>${r.author}</strong> - ${'★'.repeat(r.rating)}
<p>${r.body}</p>
</div>
`).join('')}
`;
// Insert after the product description
const target = document.querySelector('.product-description');
if (target) target.after(container);
});
})();O Script Engine do Venddor permite escrever scripts JavaScript que rodam dentro da plataforma. Scripts podem reagir a eventos e modificar comportamentos sem precisar de um servidor separado.
// Example script: Auto-apply discount for bulk orders
// This runs inside the Venddor Script Engine
venddor.on('checkout.validate', async (ctx) => {
const cart = ctx.cart;
// If total quantity > 10, apply 5% discount
const totalQty = cart.items.reduce((sum, item) => sum + item.quantity, 0);
if (totalQty > 10) {
ctx.applyDiscount({
type: 'percentage',
value: 500, // 5.00% in basis points
label: 'Bulk order discount (5%)',
});
}
});
venddor.on('order.created', async (ctx) => {
const order = ctx.data;
// Send notification to Slack
await venddor.http.post('https://hooks.slack.com/services/...', {
text: `New order #${order.id}: R$ ${(order.total_cents / 100).toFixed(2)}`,
});
});| # | Pratica | Detalhes |
|---|---|---|
| 1 | Nunca exponha secrets | Mantenha Client Secret no servidor. Nunca no frontend, git ou logs. |
| 2 | Valide webhooks | Sempre verifique a assinatura HMAC antes de processar. |
| 3 | Trate rate limits | Implemente exponential backoff para HTTP 429. |
| 4 | Use HTTPS | Todos os endpoints de callback e webhook devem usar HTTPS. |
| 5 | Cache quando possivel | Cache respostas de API para reduzir chamadas e melhorar performance. |
| 6 | Solicite escopos minimos | Solicite apenas os escopos que seu app realmente precisa. |