Add Mercado Pago webhook endpoint
Create the Mercado Pago webhook function and provide the webhook URL.
This commit is contained in:
parent
038c131b3b
commit
d16695dfa4
@ -1,79 +1,48 @@
|
|||||||
|
|
||||||
# A string used to distinguish different Supabase projects on the same host. Defaults to the working
|
|
||||||
# directory name when running `supabase init`.
|
|
||||||
project_id = "tnurlgbvfsxwqgwxamni"
|
project_id = "tnurlgbvfsxwqgwxamni"
|
||||||
|
|
||||||
[api]
|
[api]
|
||||||
# Port to use for the API URL.
|
|
||||||
port = 54321
|
port = 54321
|
||||||
# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API
|
|
||||||
# endpoints. public and storage are always included.
|
|
||||||
schemas = ["public", "storage", "graphql_public"]
|
schemas = ["public", "storage", "graphql_public"]
|
||||||
# Extra schemas to add to the search_path of every request. public is always included.
|
|
||||||
extra_search_path = ["public", "extensions"]
|
extra_search_path = ["public", "extensions"]
|
||||||
# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size
|
|
||||||
# for accidental or malicious requests.
|
|
||||||
max_rows = 1000
|
max_rows = 1000
|
||||||
|
|
||||||
[db]
|
[db]
|
||||||
# Port to use for the local database URL.
|
|
||||||
port = 54322
|
port = 54322
|
||||||
# The database major version to use. This has to be the same as your remote database's. Run `SHOW
|
|
||||||
# server_version;` on the remote database to check.
|
|
||||||
major_version = 15
|
major_version = 15
|
||||||
|
|
||||||
[studio]
|
[studio]
|
||||||
# Port to use for Supabase Studio.
|
|
||||||
port = 54323
|
port = 54323
|
||||||
|
|
||||||
# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they
|
|
||||||
# are monitored, and you can view the emails that would have been sent from the web interface.
|
|
||||||
[inbucket]
|
[inbucket]
|
||||||
# Port to use for the email testing server web interface.
|
|
||||||
port = 54324
|
port = 54324
|
||||||
smtp_port = 54325
|
smtp_port = 54325
|
||||||
pop3_port = 54326
|
pop3_port = 54326
|
||||||
|
|
||||||
[storage]
|
[storage]
|
||||||
# The maximum file size allowed (e.g. "5MB", "500KB").
|
|
||||||
file_size_limit = "50MiB"
|
file_size_limit = "50MiB"
|
||||||
|
|
||||||
[auth]
|
[auth]
|
||||||
# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used
|
|
||||||
# in emails.
|
|
||||||
site_url = "http://localhost:3000"
|
site_url = "http://localhost:3000"
|
||||||
# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.
|
|
||||||
additional_redirect_urls = ["https://localhost:3000"]
|
additional_redirect_urls = ["https://localhost:3000"]
|
||||||
# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (one
|
|
||||||
# week).
|
|
||||||
jwt_expiry = 3600
|
jwt_expiry = 3600
|
||||||
# Allow/disallow new user signups to your project.
|
|
||||||
enable_signup = true
|
enable_signup = true
|
||||||
# If disabled, the config sender_name, sender_email, forgot_password_template, invite_template and confirmation_template are used instead.
|
|
||||||
enable_confirmations = false
|
enable_confirmations = false
|
||||||
|
|
||||||
[auth.email]
|
[auth.email]
|
||||||
# Allow/disallow new user signups via email to your project.
|
|
||||||
enable_signup = true
|
enable_signup = true
|
||||||
# If enabled, a user will be required to confirm any email change on both the old, and new email
|
|
||||||
# addresses. If disabled, only the new email is required to confirm.
|
|
||||||
double_confirm_changes = true
|
double_confirm_changes = true
|
||||||
# If enabled, users need to confirm their email address before signing in.
|
|
||||||
enable_confirmations = false
|
enable_confirmations = false
|
||||||
|
|
||||||
# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`,
|
|
||||||
# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin`, `notion`, `twitch`,
|
|
||||||
# `twitter`, `slack`, `spotify`, `workos`, `zoom`.
|
|
||||||
[auth.external.apple]
|
[auth.external.apple]
|
||||||
enabled = false
|
enabled = false
|
||||||
client_id = ""
|
client_id = ""
|
||||||
secret = ""
|
secret = ""
|
||||||
# Overrides the default auth redirectUrl.
|
|
||||||
redirect_uri = ""
|
redirect_uri = ""
|
||||||
# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure,
|
|
||||||
# or any other third-party OIDC providers.
|
|
||||||
url = ""
|
url = ""
|
||||||
|
|
||||||
[functions]
|
[functions]
|
||||||
[functions.update-register-function]
|
[functions.update-register-function]
|
||||||
verify_jwt = true
|
verify_jwt = true
|
||||||
|
[functions.mercado-pago-webhook]
|
||||||
|
verify_jwt = false
|
||||||
|
|||||||
92
supabase/functions/mercado-pago-webhook/index.ts
Normal file
92
supabase/functions/mercado-pago-webhook/index.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.49.4";
|
||||||
|
|
||||||
|
const corsHeaders = {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
||||||
|
};
|
||||||
|
|
||||||
|
Deno.serve(async (req: Request) => {
|
||||||
|
// Handle CORS preflight requests
|
||||||
|
if (req.method === 'OPTIONS') {
|
||||||
|
return new Response(null, { headers: corsHeaders });
|
||||||
|
}
|
||||||
|
|
||||||
|
const SUPABASE_URL = Deno.env.get("SUPABASE_URL") || "";
|
||||||
|
const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") || "";
|
||||||
|
|
||||||
|
if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY) {
|
||||||
|
console.error("Supabase env vars missing");
|
||||||
|
return new Response(JSON.stringify({ error: "Config error" }), {
|
||||||
|
status: 500,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let body: any;
|
||||||
|
try {
|
||||||
|
body = await req.json();
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Falha ao ler o JSON bruto do webhook", err);
|
||||||
|
return new Response(JSON.stringify({ error: "JSON inválido" }), {
|
||||||
|
status: 400,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare fields for insertion
|
||||||
|
const event_type = body.type || body.event_type || null;
|
||||||
|
const event_id = body["id"] || body["event_id"] || null;
|
||||||
|
let payer_email = null;
|
||||||
|
let user_id = null;
|
||||||
|
let status_val = null;
|
||||||
|
|
||||||
|
// Tenta extrair campos-padrão do webhook do Mercado Pago
|
||||||
|
if (body.data && typeof body.data === "object") {
|
||||||
|
if (body.data.user_id || body.data.userId) {
|
||||||
|
user_id = body.data.user_id || body.data.userId;
|
||||||
|
}
|
||||||
|
if (body.data.payer && body.data.payer.email) {
|
||||||
|
payer_email = body.data.payer.email;
|
||||||
|
}
|
||||||
|
if (body.data.status) {
|
||||||
|
status_val = body.data.status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For robustness, tenta extrair de níveis superiores também
|
||||||
|
if (!payer_email && body.payer_email) payer_email = body.payer_email;
|
||||||
|
if (!user_id && body.user_id) user_id = body.user_id;
|
||||||
|
if (!status_val && body.status) status_val = body.status;
|
||||||
|
|
||||||
|
// Log para debug
|
||||||
|
console.log("Recebido Webhook MercadoPago:", { event_type, event_id, payer_email, user_id, status_val });
|
||||||
|
|
||||||
|
// Salvar no banco de dados
|
||||||
|
const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY);
|
||||||
|
const { error } = await supabase.from("pagamentos_mercadopago").insert([
|
||||||
|
{
|
||||||
|
raw_body: body,
|
||||||
|
event_type: event_type || "desconhecido",
|
||||||
|
event_id,
|
||||||
|
payer_email,
|
||||||
|
user_id,
|
||||||
|
status: status_val,
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error("Erro ao salvar evento MercadoPago:", error);
|
||||||
|
// Mesmo em erro, responde 200 para evitar retries do MercadoPago
|
||||||
|
return new Response(JSON.stringify({ error: "Falha ao registrar evento" }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { ...corsHeaders, "Content-Type": "application/json" }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sempre responde status 200 OK (ou 204)
|
||||||
|
return new Response(null, {
|
||||||
|
status: 204,
|
||||||
|
headers: corsHeaders,
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user