Sobre nós Guias Projetos Contactos
Админка
please wait

O ecossistema de apps da Shopify suporta milhões de lojas com funcionalidades personalizadas. Criar apps para Shopify abre oportunidades para resolver problemas dos comerciantes à escala ou criar integrações personalizadas para clientes específicos. Este guia aborda o desenvolvimento de apps para Shopify na perspetiva de um programador sénior.

Porquê Desenvolver para Shopify

O desenvolvimento para Shopify oferece oportunidades apelativas:

  1. Mercado Enorme: Milhões de lojas ativas em todo o mundo
  2. Distribuição na App Store: Marketplace integrado para descoberta
  3. Receita Recorrente: Suporte para faturação por subscrição
  4. APIs Bem Documentadas: Opções REST e GraphQL
  5. Stack Moderna: App Bridge para integração de UI sem fricção

Tipos de Apps

Apps Públicas

  • Listadas na Shopify App Store
  • Disponíveis para todos os comerciantes
  • Partilha de receitas com a Shopify
  • Processo de revisão rigoroso

Apps Personalizadas

  • Criadas para lojas específicas
  • Instalação direta
  • Sem listagem na App Store
  • Processo de aprovação mais simples

Apps Privadas

  • Opção legada (a ser descontinuada)
  • Em alternativa, use apps personalizadas

Primeiros Passos

Pré-requisitos

  1. Conta Shopify Partner (gratuita)
  2. Loja de desenvolvimento para testes
  3. ngrok ou semelhante para desenvolvimento local

Criar a App no Partner Dashboard

  1. Vá a Partners → Apps → Create app
  2. Configure os URLs da app:

- App URL: https://your-app.com/ - Allowed redirection URL: https://your-app.com/auth/callback 3. Tome nota das suas credenciais de API: - Client ID - Client Secret

Fluxo de Autenticação OAuth

Passo 1: Redirecionar para a Shopify

Quando um comerciante instala a sua app:

<?php
$client_id = 'your-client-id';
$scopes = 'read_products,write_products,read_orders,write_orders';
$redirect_uri = 'https://your-app.com/auth/callback';
$shop = $_GET['shop'];
$nonce = bin2hex(random_bytes(12));
// Guardar o nonce na sessão para verificação
$_SESSION['oauth_nonce'] = $nonce;
$oauth_url = "https://{$shop}/admin/oauth/authorize?" . http_build_query([
'client_id' => $client_id,
'scope' => $scopes,
'redirect_uri' => $redirect_uri,
'state' => $nonce,
'grant_options[]' => 'per-user'
]);
header("Location: {$oauth_url}");
exit();

Passo 2: Tratar o Callback

<?php
$client_id = 'your-client-id';
$client_secret = 'your-client-secret';
// Verificar a assinatura HMAC
$params = $_GET;
$hmac = $params['hmac'];
unset($params['hmac']);
ksort($params);
$computed_hmac = hash_hmac('sha256', http_build_query($params), $client_secret);
if (!hash_equals($hmac, $computed_hmac)) {
die('Invalid signature - possible attack');
}
// Verificar o nonce
if ($params['state'] !== $_SESSION['oauth_nonce']) {
die('Invalid state parameter');
}
// Trocar o code por um access token
$shop = $params['shop'];
$code = $params['code'];
$response = file_get_contents("https://{$shop}/admin/oauth/access_token", false, stream_context_create([
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/x-www-form-urlencoded',
'content' => http_build_query([
'client_id' => $client_id,
'client_secret' => $client_secret,
'code' => $code
])
]
]));
$data = json_decode($response, true);
$access_token = $data['access_token'];
// Guardar o access token de forma segura (base de dados)
saveAccessToken($shop, $access_token);
// Redirecionar para a app
header("Location: /app?shop={$shop}");

Integração com a REST API

Fazer Pedidos à API

<?php
function shopifyRequest($shop, $access_token, $endpoint, $method = 'GET', $data = null) {
$url = "https://{$shop}/admin/api/2024-01{$endpoint}";
$headers = [
"X-Shopify-Access-Token: {$access_token}",
"Content-Type: application/json"
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
if ($data && in_array($method, ['POST', 'PUT'])) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
throw new Exception("API Error: {$httpCode} - {$response}");
}
return json_decode($response, true);
}
// Exemplos
// Obter produtos
$products = shopifyRequest($shop, $token, '/products.json');
// Obter um único produto
$product = shopifyRequest($shop, $token, '/products/123456789.json');
// Criar produto
$newProduct = shopifyRequest($shop, $token, '/products.json', 'POST', [
'product' => [
'title' => 'New Product',
'body_html' => '<p>Description</p>',
'vendor' => 'My Store',
'product_type' => 'Widget',
'variants' => [
['price' => '19.99', 'sku' => 'WIDGET-001']
]
]
]);
// Atualizar produto
shopifyRequest($shop, $token, '/products/123456789.json', 'PUT', [
'product' => ['title' => 'Updated Title']
]);
// Eliminar produto
shopifyRequest($shop, $token, '/products/123456789.json', 'DELETE');

Integração com a GraphQL API

A GraphQL é recomendada para novas apps — mais eficiente e flexível:

<?php
function shopifyGraphQL($shop, $access_token, $query, $variables = []) {
$url = "https://{$shop}/admin/api/2024-01/graphql.json";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"X-Shopify-Access-Token: {$access_token}",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'query' => $query,
'variables' => $variables
]));
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
// Consultar produtos
$query = <<<'GRAPHQL'
query getProducts($first: Int!) {
products(first: $first, query: "status:active") {
edges {
node {
id
title
description
variants(first: 10) {
nodes {
id
price
inventoryQuantity
}
}
images(first: 1) {
edges {
node {
url
}
}
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
GRAPHQL;
$result = shopifyGraphQL($shop, $token, $query, ['first' => 50]);
// Mutation: Atualizar produto
$mutation = <<<'GRAPHQL'
mutation updateProduct($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
title
}
userErrors {
field
message
}
}
}
GRAPHQL;
$result = shopifyGraphQL($shop, $token, $mutation, [
'input' => [
'id' => 'gid://shopify/Product/123456789',
'title' => 'Updated via GraphQL'
]
]);

App Bridge UI

Incorpore a sua app de forma integrada no Shopify Admin:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="shopify-api-key" content="<?php echo $client_id; ?>" />
<script src="https://cdn.shopify.com/shopifycloud/app-bridge.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@shopify/polaris@latest/build/esm/styles.css" />
</head>
<body>
<!--Barra de título com ações-->
<ui-title-bar title="My App">
<button variant="primary" onclick="saveSettings()">Save</button>
<button onclick="showHelp()">Help</button>
</ui-title-bar>
<!--Menu de navegação-->
<ui-nav-menu>
<a href="/dashboard" rel="home">Dashboard</a>
<a href="/products">Products</a>
<a href="/settings">Settings</a>
</ui-nav-menu>
<div id="app">
<h1>Welcome to My App</h1>
<button id="pick-product">Select Product</button>
<div id="selected-product"></div>
</div>
<script>
// Resource Picker
document.getElementById('pick-product').addEventListener('click', async () => {
const selected = await shopify.resourcePicker({
type: 'product',
multiple: false
});
if (selected && selected.length > 0) {
document.getElementById('selected-product').textContent =
`Selected: ${selected[0].title}`;
}
});
// Toast notifications
function showSuccess(message) {
shopify.toast.show(message, { duration: 3000 });
}
function showError(message) {
shopify.toast.show(message, { isError: true });
}
// Modal
async function showHelp() {
await shopify.modal.alert({
title: 'Help',
message: 'This app helps you manage products more efficiently.'
});
}
// Confirm dialog
async function confirmDelete() {
const confirmed = await shopify.modal.confirm({
title: 'Delete Item',
message: 'Are you sure? This cannot be undone.'
});
if (confirmed) {
// Proceed with deletion
}
}
// Get session token for authenticated API calls
async function callBackend() {
const token = await shopify.idToken();
const response = await fetch('/api/data', {
headers: {
'Authorization': `Bearer ${token}`
}
});
return response.json();
}
</script>
</body>
</html>

Verificação de Session Token

Verifique pedidos provenientes da sua app incorporada:

<?php
function verifySessionToken($token, $client_secret) {
$parts = explode('.', $token);
if (count($parts) !== 3) {
return false;
}
list($header, $payload, $signature) = $parts;
// Verificar assinatura
$expected_sig = rtrim(strtr(
base64_encode(hash_hmac('sha256', "{$header}.{$payload}", $client_secret, true)),
'+/', '-_'
), '=');
if (!hash_equals($expected_sig, $signature)) {
return false;
}
// Descodificar e verificar o payload
$data = json_decode(base64_decode($payload), true);
// Verificar expiração
if ($data['exp'] < time()) {
return false;
}
return $data; // Contém shop, sub (utilizador), etc.
}
// Utilização no endpoint da API
$auth_header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (preg_match('/Bearer\s+(.+)/', $auth_header, $matches)) {
$token_data = verifySessionToken($matches[1], $client_secret);
if ($token_data) {
$shop = $token_data['dest']; // Domínio da loja
// Processar pedido autenticado
}
}

Webhooks

Subscreva eventos da loja:

<?php
// Registar webhook via API
$webhook_data = [
'webhook' => [
'topic' => 'orders/create',
'address' => 'https://your-app.com/webhooks/orders',
'format' => 'json'
]
];
shopifyRequest($shop, $token, '/webhooks.json', 'POST', $webhook_data);
// Endpoint do webhook
// webhooks/orders.php
$raw_body = file_get_contents('php://input');
$hmac_header = $_SERVER['HTTP_X_SHOPIFY_HMAC_SHA256'];
// Verificar webhook
$computed_hmac = base64_encode(hash_hmac('sha256', $raw_body, $client_secret, true));
if (!hash_equals($hmac_header, $computed_hmac)) {
http_response_code(401);
exit('Unauthorized');
}
// Processar webhook
$data = json_decode($raw_body, true);
$order_id = $data['id'];
$customer_email = $data['email'];
// Tratar a encomenda...
processNewOrder($data);
http_response_code(200);

Faturação da App

Cobre aos comerciantes pela sua app:

<?php
// Criar cobrança recorrente
$charge = shopifyGraphQL($shop, $token, <<<'GRAPHQL'
mutation createSubscription($name: String!, $price: Decimal!, $returnUrl: URL!) {
appSubscriptionCreate(
name: $name
returnUrl: $returnUrl
lineItems: [{
plan: {
appRecurringPricingDetails: {
price: { amount: $price, currencyCode: USD }
interval: EVERY_30_DAYS
}
}
}]
) {
appSubscription {
id
status
}
confirmationUrl
userErrors {
field
message
}
}
}
GRAPHQL, [
'name' => 'Pro Plan',
'price' => '9.99',
'returnUrl' => 'https://your-app.com/billing/confirm'
]);
// Redirecionar o comerciante para o URL de confirmação
if ($charge['data']['appSubscriptionCreate']['confirmationUrl']) {
header('Location: ' . $charge['data']['appSubscriptionCreate']['confirmationUrl']);
}

Testes

Loja de Desenvolvimento

  1. Crie uma loja de desenvolvimento no Partner Dashboard
  2. Instale a app diretamente (não é necessária revisão)
  3. Teste todas as funcionalidades

Dados de Teste

# Shopify CLI para gerar dados de teste
shopify populate products --count 20
shopify populate customers --count 10
shopify populate orders --count 5

Principais Conclusões

  1. Use GraphQL: Mais eficiente do que REST para queries complexas
  2. Verifique tudo: Assinaturas HMAC, session tokens, webhooks
  3. App Bridge para UI: Experiência do comerciante sem fricção
  4. Gerir rate limits: Respeite o throttling da API
  5. Proteja tokens: Nunca exponha access tokens no frontend
  6. Teste exaustivamente: Use lojas de desenvolvimento antes de produção

O desenvolvimento para Shopify oferece uma oportunidade rentável para programadores que dominem as suas APIs e uma mentalidade focada no comerciante.

 
 
 
Языки
Темы
Copyright © 1999 — 2026
ZK Interactive