API v1.0 Documentation

Integre a Tecnologia líder em Moçambique

Nossa API robusta e segura permite que você conecte o seu sistema aos principais métodos de pagamento e serviços digitais em minutos.

API v1.0

Documentação Técnica

Aceite pagamentos via M-Pesa, e-Mola, mKesh, Visa/Mastercard (MZN) e PayFast (ZAR) com uma única integração REST. Todos os métodos retornam JSON e usam autenticação Bearer.

Overview

#

A EasyHost API foi desenhada para ser rápida e segura. Expondo três endpoints principais que permitem desde a criação de pagamentos até a consulta de saldos em tempo real.

M-Pesa

STK Push direto para o telemóvel

e-Mola

Integração via USSD Push

Segurança

HMAC-SHA256 & TLS 1.3

Autenticação

#

Todas as chamadas server-side usam Bearer com a chave de API do comerciante (sk_live_... ou sk_sandbox_...). As chaves podem ser geradas no painel em Configurações → Chaves de API.

Cabeçalhos HTTP
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
Accept: application/json

Atenção: Nunca exponha a sua chave secreta em código front-end. Para o browser, utilize apenas o session_token emitido pelo endpoint /payment-session.

Base URL

#

Todas as requisições devem ser feitas para a seguinte URL base:

https://mescwrzkwjwtyjouueqq.supabase.co/functions/v1
Console de Testes

A chave fica apenas no seu browser e é usada pelos botões Try it out abaixo.

Ambiente Live

https://mescwrzkwjwtyjouueqq.supabase.co/functions/v1

Ambiente Sandbox

https://mescwrzkwjwtyjouueqq.supabase.co/functions/v1

Use sk_sandbox_ para testes

/payment-orchestrator

#

Endpoint principal para criar cobranças em qualquer método suportado. O orquestrador detecta automaticamente o melhor roteamento.

Requisitos C2B: Para validar qualquer transação Consumer-to-Business (C2B), os campos merchant_id, wallet_id e api_key são estritamente obrigatórios no corpo da requisição.

POST
/payment-orchestrator

Schema da Requisição

Request Body (JSON Schema)
{
  "type": "object",
  "required": ["merchant_id", "wallet_id", "api_key", "amount", "currency", "payment_method", "customer"],
  "properties": {
    "merchant_id":    { "type": "string", "description": "ID único do comerciante" },
    "wallet_id":      { "type": "string", "description": "ID da carteira de destino" },
    "api_key":        { "type": "string", "description": "Chave de API para validação C2B" },
    "amount":         { "type": "integer", "minimum": 100, "description": "Valor em centavos" },
    "currency":       { "type": "string", "enum": ["MZN", "ZAR", "USD"] },
    "payment_method": { "type": "string", "enum": ["mpesa", "emola", "mkesh", "card", "payfast"] },
    "description":    { "type": "string", "maxLength": 255 },
    "external_reference": { "type": "string", "maxLength": 64 },
    "callback_url":   { "type": "string", "format": "uri" },
    "customer": {
      "type": "object",
      "required": ["name"],
      "properties": {
        "name":  { "type": "string", "maxLength": 100 },
        "email": { "type": "string", "format": "email" },
        "phone": { "type": "string", "pattern": "^258[0-9]{9}$|^27[0-9]{9}$" }
      }
    },
    "card": {
      "type": "object",
      "description": "Obrigatório quando payment_method = card",
      "properties": {
        "number":      { "type": "string" },
        "exp_month":   { "type": "integer", "minimum": 1, "maximum": 12 },
        "exp_year":    { "type": "integer" },
        "cvv":         { "type": "string", "minLength": 3, "maxLength": 4 },
        "holder_name": { "type": "string" }
      }
    }
  }
}

Schema da Resposta

Response 200 (JSON Schema)
{
  "type": "object",
  "properties": {
    "id":              { "type": "string", "example": "pay_987654321" },
    "status":          { "type": "string", "enum": ["pending", "processing", "completed", "failed", "expired"] },
    "amount":          { "type": "integer" },
    "currency":        { "type": "string" },
    "payment_method":  { "type": "string" },
    "provider":        { "type": "string", "description": "Ex: vodacom, movitel, mkesh, payfast, visa" },
    "provider_reference": { "type": "string", "nullable": true },
    "external_reference": { "type": "string", "nullable": true },
    "checkout_url":    { "type": "string", "format": "uri", "nullable": true },
    "expires_at":      { "type": "string", "format": "date-time" },
    "created_at":      { "type": "string", "format": "date-time" }
  }
}

Parâmetros

CampoTipoDescrição
merchant_idStringObrigatório. Seu identificador único de comerciante.
wallet_idStringObrigatório. Identificador da carteira onde o fundo será depositado.
api_keyStringObrigatório. Chave de autenticação para validação de transações C2B.
amountIntegerValor em centavos (ex: 1500 = 15.00 MZN)
currencyStringMZN, ZAR ou USD
payment_methodStringmpesa, emola, mkesh, card, payfast
customer.phoneStringObrigatório para mpesa, emola e mkesh (formato E.164 sem +)
callback_urlStringOpcional. URL chamada após mudança de estado.
external_referenceStringSeu identificador interno (idempotência).

Exemplos por payment_method

Cada método tem requisitos específicos. Veja request e response reais para cada um:

M-Pesa (Vodacom)
Request — mpesa
POST /payment-orchestrator
{
  "merchant_id": "mrc_887766",
  "wallet_id": "wlt_554433",
  "api_key": "sk_live_xxxxxx",
  "amount": 1500,
  "currency": "MZN",
  "payment_method": "mpesa",
  "description": "Fatura #12345",
  "external_reference": "INV-12345",
  "customer": {
    "name": "João Silva",
    "phone": "258840000000"
  }
}
Response 200 — mpesa (STK Push enviado)
{
  "id": "pay_01HXMP9A8K2N",
  "status": "pending",
  "amount": 1500,
  "currency": "MZN",
  "payment_method": "mpesa",
  "provider": "vodacom",
  "provider_reference": "MP240520.1010.A12345",
  "external_reference": "INV-12345",
  "checkout_url": null,
  "expires_at": "2024-05-20T10:05:00Z",
  "created_at": "2024-05-20T10:00:00Z"
}
e-Mola (Movitel)
Request — emola
POST /payment-orchestrator
{
  "merchant_id": "mrc_887766",
  "wallet_id": "wlt_554433",
  "api_key": "sk_live_xxxxxx",
  "amount": 2500,
  "currency": "MZN",
  "payment_method": "emola",
  "description": "Recarga conta",
  "customer": {
    "name": "Maria Santos",
    "phone": "258860000000"
  }
}
Response 200 — emola (USSD Push)
{
  "id": "pay_01HXMQ5C1L9P",
  "status": "pending",
  "amount": 2500,
  "currency": "MZN",
  "payment_method": "emola",
  "provider": "movitel",
  "provider_reference": "EM240520-7788",
  "checkout_url": null,
  "expires_at": "2024-05-20T10:05:00Z",
  "created_at": "2024-05-20T10:00:00Z"
}
mKesh (TMcel)
Request — mkesh
POST /payment-orchestrator
{
  "merchant_id": "mrc_887766",
  "wallet_id": "wlt_554433",
  "api_key": "sk_live_xxxxxx",
  "amount": 5000,
  "currency": "MZN",
  "payment_method": "mkesh",
  "description": "Assinatura mensal",
  "customer": {
    "name": "Carlos Mucavele",
    "phone": "258820000000"
  }
}
Response 200 — mkesh
{
  "id": "pay_01HXMR2F4M6Q",
  "status": "processing",
  "amount": 5000,
  "currency": "MZN",
  "payment_method": "mkesh",
  "provider": "mkesh",
  "provider_reference": "MK20240520-991122",
  "checkout_url": null,
  "expires_at": "2024-05-20T10:05:00Z",
  "created_at": "2024-05-20T10:00:00Z"
}
Cartão Visa / Mastercard (MZN)
Request — card (redirect 3DS recomendado)
POST /payment-orchestrator
{
  "merchant_id": "mrc_887766",
  "wallet_id": "wlt_554433",
  "api_key": "sk_live_xxxxxx",
  "amount": 12000,
  "currency": "MZN",
  "payment_method": "card",
  "description": "Compra Loja Online",
  "callback_url": "https://sualoja.co.mz/retorno",
  "customer": {
    "name": "Ana Macuácua",
    "email": "ana@exemplo.co.mz"
  }
}
Response 200 — card (devolve checkout_url para 3DS)
{
  "id": "pay_01HXMS8H7N2R",
  "status": "pending",
  "amount": 12000,
  "currency": "MZN",
  "payment_method": "card",
  "provider": "visa",
  "checkout_url": "https://checkout.easyhost.co.mz/c/pay_01HXMS8H7N2R",
  "expires_at": "2024-05-20T10:30:00Z",
  "created_at": "2024-05-20T10:00:00Z"
}
PayFast (ZAR — África do Sul)
Request — payfast
POST /payment-orchestrator
{
  "merchant_id": "mrc_887766",
  "wallet_id": "wlt_554433",
  "api_key": "sk_live_xxxxxx",
  "amount": 25000,
  "currency": "ZAR",
  "payment_method": "payfast",
  "description": "Order #98765",
  "callback_url": "https://yourshop.co.za/return",
  "customer": {
    "name": "Thabo Nkosi",
    "email": "thabo@example.co.za"
  }
}
Response 200 — payfast
{
  "id": "pay_01HXMT1J3P8S",
  "status": "pending",
  "amount": 25000,
  "currency": "ZAR",
  "payment_method": "payfast",
  "provider": "payfast",
  "checkout_url": "https://www.payfast.co.za/eng/process?uuid=...",
  "expires_at": "2024-05-20T10:30:00Z",
  "created_at": "2024-05-20T10:00:00Z"
}

Respostas de Erro

Response 4xx / 5xx (JSON Schema)
{
  "type": "object",
  "properties": {
    "error": {
      "type": "object",
      "properties": {
        "code":    { "type": "string", "example": "insufficient_funds" },
        "message": { "type": "string" },
        "param":   { "type": "string", "nullable": true },
        "request_id": { "type": "string" }
      }
    }
  }
}
Exemplo de Erro
{
  "error": {
    "code": "invalid_phone",
    "message": "O número informado não pertence à rede Vodacom.",
    "param": "customer.phone",
    "request_id": "req_01HXMU9K2Q7T"
  }
}
Try it out
SANDBOX
POST /payment-orchestrator

/payment-session

#

Gera um token de sessão seguro de curta duração para uso em checkout hospedado ou integrações front-end. Nunca exponha sk_live_ no browser.

POST
/payment-session

Schema da Requisição

Request Body (JSON Schema)
{
  "type": "object",
  "required": ["merchant_id", "wallet_id", "api_key", "amount", "currency"],
  "properties": {
    "merchant_id":      { "type": "string", "description": "ID único do comerciante" },
    "wallet_id":        { "type": "string", "description": "ID da carteira de destino" },
    "api_key":          { "type": "string", "description": "Chave de API para validação C2B" },
    "amount":           { "type": "integer", "minimum": 100 },
    "currency":         { "type": "string", "enum": ["MZN", "ZAR", "USD"] },
    "allowed_methods":  { "type": "array", "items": { "type": "string", "enum": ["mpesa","emola","mkesh","card","payfast"] } },
    "domain":           { "type": "string", "description": "Domínio que carregará o widget" },
    "success_url":      { "type": "string", "format": "uri" },
    "cancel_url":       { "type": "string", "format": "uri" },
    "expires_in":       { "type": "integer", "default": 900, "description": "Segundos até expirar (máx 3600)" },
    "metadata":         { "type": "object", "additionalProperties": { "type": "string" } }
  }
}

Schema da Resposta

Response 200 (JSON Schema)
{
  "type": "object",
  "properties": {
    "session_token": { "type": "string", "description": "JWT de curta duração, usar no front-end" },
    "session_id":    { "type": "string" },
    "checkout_url":  { "type": "string", "format": "uri" },
    "expires_at":    { "type": "string", "format": "date-time" }
  }
}
Exemplo de Requisição
POST /payment-session
{
  "merchant_id": "mrc_887766",
  "wallet_id": "wlt_554433",
  "api_key": "sk_live_xxxxxx",
  "amount": 1500,
  "currency": "MZN",
  "allowed_methods": ["mpesa", "emola", "card"],
  "domain": "sualoja.co.mz",
  "success_url": "https://sualoja.co.mz/sucesso",
  "cancel_url": "https://sualoja.co.mz/cancelado",
  "expires_in": 900,
  "metadata": { "order_id": "ORD-998" }
}
Exemplo de Resposta
{
  "session_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "session_id": "ses_01HXMV3L5R9U",
  "checkout_url": "https://checkout.easyhost.co.mz/s/ses_01HXMV3L5R9U",
  "expires_at": "2024-05-20T10:15:00Z"
}
Try it out
SANDBOX
POST /payment-session

/wallet-balance

#

Consulta o saldo disponível em tempo real para cada carteira integrada (M-Pesa, e-Mola, mKesh, cartão e PayFast).

GET
/wallet-balance

Query Parameters (opcionais)

Query Schema
{
  "type": "object",
  "properties": {
    "currency": { "type": "string", "enum": ["MZN", "ZAR", "USD"] },
    "method":   { "type": "string", "enum": ["mpesa","emola","mkesh","card","payfast"] }
  }
}

Schema da Resposta

Response 200 (JSON Schema)
{
  "type": "object",
  "properties": {
    "wallets": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "payment_method": { "type": "string", "enum": ["mpesa","emola","mkesh","card","payfast"] },
          "provider":       { "type": "string" },
          "currency":       { "type": "string" },
          "available":      { "type": "integer", "description": "Disponível em centavos" },
          "pending":        { "type": "integer" },
          "updated_at":     { "type": "string", "format": "date-time" }
        }
      }
    }
  }
}
Exemplo de Resposta
{
  "wallets": [
    { "payment_method": "mpesa",   "provider": "vodacom",  "currency": "MZN", "available": 1250000, "pending":  35000, "updated_at": "2024-05-20T10:00:00Z" },
    { "payment_method": "emola",   "provider": "movitel",  "currency": "MZN", "available":  430000, "pending":      0, "updated_at": "2024-05-20T10:00:00Z" },
    { "payment_method": "mkesh",   "provider": "mkesh",    "currency": "MZN", "available":  180000, "pending":   5000, "updated_at": "2024-05-20T10:00:00Z" },
    { "payment_method": "card",    "provider": "visa",     "currency": "MZN", "available":  920000, "pending":  12000, "updated_at": "2024-05-20T10:00:00Z" },
    { "payment_method": "payfast", "provider": "payfast",  "currency": "ZAR", "available":  560000, "pending":  20000, "updated_at": "2024-05-20T10:00:00Z" }
  ]
}
Try it out
SANDBOX
GET /wallet-balance

Webhooks

#

Receba notificações em tempo real sempre que uma transação mudar de estado. Todos os webhooks são assinados com um segredo HMAC-SHA256.

Exemplo de Payload

{
  "id": "evt_123456789",
  "type": "payment.succeeded",
  "created_at": "2024-05-20T10:00:00Z",
  "data": {
    "id": "pay_987654321",
    "amount": 1500,
    "status": "completed",
    "payment_method": "mpesa",
    "external_reference": "258841234567"
  }
}

payment.succeeded

Enviado quando o pagamento é confirmado pelo provedor.

payment.failed

Enviado quando a transação expira ou é rejeitada.

Cabeçalhos enviados

Cada requisição POST inclui os seguintes cabeçalhos para que você possa validar a origem e prevenir replay attacks:

HeaderDescrição
X-Webhook-SignatureAssinatura HMAC-SHA256 (hex) do corpo bruto.
X-Webhook-TimestampUnix timestamp do envio. Rejeite se > 5 min.
X-Webhook-IdID único do evento (use para idempotência).

Verificação da assinatura

Calcule um HMAC-SHA256 de timestamp + "." + raw_body usando seu webhook_secret e compare em tempo constante com o header recebido.

Node.js (Express)
import express from "express";
import crypto from "crypto";

const app = express();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
const MAX_AGE = 5 * 60; // 5 minutos

// IMPORTANTE: use o corpo BRUTO (raw) para calcular a assinatura
app.post("/webhooks/easyhost", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.header("X-Webhook-Signature");
  const timestamp = req.header("X-Webhook-Timestamp");
  const rawBody = req.body.toString("utf8");

  if (!signature || !timestamp) return res.status(400).send("missing headers");

  // 1. Proteção contra replay
  const age = Math.floor(Date.now() / 1000) - Number(timestamp);
  if (age > MAX_AGE) return res.status(401).send("timestamp too old");

  // 2. Calcular assinatura esperada
  const expected = crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  // 3. Comparação em tempo constante
  const valid = crypto.timingSafeEqual(
    Buffer.from(signature, "hex"),
    Buffer.from(expected, "hex")
  );
  if (!valid) return res.status(401).send("invalid signature");

  // 4. Processar evento
  const event = JSON.parse(rawBody);
  switch (event.type) {
    case "payment.succeeded":
      console.log("Pagamento confirmado:", event.data.id);
      break;
    case "payment.failed":
      console.log("Pagamento falhou:", event.data.id);
      break;
  }

  // Responda 2xx rápido — trabalho pesado deve ir para uma fila
  res.status(200).send("ok");
});

app.listen(3000);
Python (Flask)
import os, hmac, hashlib, time, json
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = os.environ["WEBHOOK_SECRET"].encode()
MAX_AGE = 5 * 60  # 5 minutos

@app.post("/webhooks/easyhost")
def handle_webhook():
    signature = request.headers.get("X-Webhook-Signature", "")
    timestamp = request.headers.get("X-Webhook-Timestamp", "")
    raw_body = request.get_data()  # bytes brutos

    if not signature or not timestamp:
        abort(400, "missing headers")

    # 1. Proteção contra replay
    if abs(int(time.time()) - int(timestamp)) > MAX_AGE:
        abort(401, "timestamp too old")

    # 2. Calcular assinatura esperada
    signed_payload = f"{timestamp}.".encode() + raw_body
    expected = hmac.new(WEBHOOK_SECRET, signed_payload, hashlib.sha256).hexdigest()

    # 3. Comparação em tempo constante
    if not hmac.compare_digest(signature, expected):
        abort(401, "invalid signature")

    # 4. Processar evento
    event = json.loads(raw_body)
    if event["type"] == "payment.succeeded":
        print("Pagamento confirmado:", event["data"]["id"])
    elif event["type"] == "payment.failed":
        print("Pagamento falhou:", event["data"]["id"])

    return "ok", 200

✓ Boas práticas

Use o corpo bruto, comparação em tempo constante e responda 2xx em menos de 5s.

↻ Idempotência

Use X-Webhook-Id como chave para evitar processar o mesmo evento duas vezes.

⟳ Retries

Reenviamos até 5x com backoff exponencial caso a resposta não seja 2xx.

Códigos de Erro

#

A API usa códigos de status HTTP padrão e retorna payloads de erro estruturados em JSON. Cada resposta de erro inclui um código legível por máquina, uma mensagem legível por humanos, o parâmetro relacionado (quando aplicável) e um ID único da requisição para rastreamento.

StatusDescriçãoCenário típico
400Bad RequestPayload malformado, campos obrigatórios ausentes ou tipos incorretos.
401UnauthorizedAPI key ausente, inválida ou expirada.
403ForbiddenAPI key válida, mas sem permissão para o recurso solicitado.
404Not FoundRecurso inexistente (ex: pagamento, sessão ou cliente não encontrado).
422Unprocessable EntitySintaxe válida, mas regra de negócio violada (ex: saldo insuficiente).
429Too Many RequestsLimite de requisições excedido (rate limit). Aguarde e tente novamente.
500Internal Server ErrorErro inesperado no servidor. Entre em contato com o suporte.
502Bad GatewayProvedor de pagamento externo indisponível ou resposta inválida.
503Service UnavailableManutenção programada ou sobrecarga temporária da API.

Estrutura padrão do erro

Payload de erro (schema)
{
  "error": {
    "code": "invalid_request",
    "message": "Descrição legível do erro",
    "param": "amount",
    "request_id": "req_7f8a9b2c3d4e"
  }
}

400 — Bad Request

{
  "error": {
    "code": "missing_parameter",
    "message": "O campo 'amount' é obrigatório.",
    "param": "amount",
    "request_id": "req_9a8b7c6d5e4f"
  }
}
{
  "error": {
    "code": "invalid_parameter",
    "message": "'currency' deve ser uma das opções suportadas: MZN, ZAR, USD.",
    "param": "currency",
    "request_id": "req_1a2b3c4d5e6f"
  }
}

401 — Unauthorized

{
  "error": {
    "code": "unauthorized",
    "message": "API key ausente. Inclua o header Authorization: Bearer <token>.",
    "param": "authorization",
    "request_id": "req_2b3c4d5e6f7g"
  }
}
{
  "error": {
    "code": "invalid_api_key",
    "message": "A API key fornecida não é válida ou foi revogada.",
    "param": "authorization",
    "request_id": "req_3c4d5e6f7g8h"
  }
}

403 — Forbidden

{
  "error": {
    "code": "insufficient_permissions",
    "message": "Sua chave não tem permissão para acessar este recurso.",
    "param": null,
    "request_id": "req_4d5e6f7g8h9i"
  }
}
{
  "error": {
    "code": "ip_not_allowed",
    "message": "Requisição bloqueada. O IP 102.130.61.x não está na whitelist.",
    "param": null,
    "request_id": "req_5e6f7g8h9i0j"
  }
}

404 — Not Found

{
  "error": {
    "code": "resource_not_found",
    "message": "Pagamento 'pay_123456789' não encontrado.",
    "param": "id",
    "request_id": "req_6f7g8h9i0j1k"
  }
}
{
  "error": {
    "code": "endpoint_not_found",
    "message": "O endpoint '/v1/unknown' não existe. Consulte a documentação.",
    "param": null,
    "request_id": "req_7g8h9i0j1k2l"
  }
}

422 — Unprocessable Entity

{
  "error": {
    "code": "amount_below_minimum",
    "message": "O valor mínimo para M-Pesa é 10 MZN.",
    "param": "amount",
    "request_id": "req_8h9i0j1k2l3m"
  }
}
{
  "error": {
    "code": "provider_rejected",
    "message": "A operadora M-Pesa recusou a transação (saldo insuficiente do cliente).",
    "param": null,
    "request_id": "req_9i0j1k2l3m4n"
  }
}

429 — Too Many Requests

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Limite de 100 requisições/min excedido. Tente novamente em 45s.",
    "param": null,
    "request_id": "req_0j1k2l3m4n5o",
    "retry_after": 45
  }
}

500 — Internal Server Error

{
  "error": {
    "code": "internal_error",
    "message": "Ocorreu um erro interno. Nossa equipe foi notificada automaticamente.",
    "param": null,
    "request_id": "req_1k2l3m4n5o6p"
  }
}

502 / 503 — Gateway / Unavailable

{
  "error": {
    "code": "provider_timeout",
    "message": "Provedor M-Pesa não respondeu em tempo hábil. Tente novamente.",
    "param": null,
    "request_id": "req_2l3m4n5o6p7q"
  }
}
{
  "error": {
    "code": "service_unavailable",
    "message": "API em manutenção programada. Previsão de retorno: 14h00 UTC.",
    "param": null,
    "request_id": "req_3m4n5o6p7q8r"
  }
}

💡 Dica de debugging

Sempre guarde o request_id retornado em erros 5xx. Ao abrir um ticket de suporte, inclua esse ID — ele permite que nossa equipe rastreie a requisição completa nos logs em segundos.

SDKs

#

Exemplos prontos de integração em Node.js e Python. Todos usam automaticamente a Base URL do Supabase e o header Authorization com a API Key configurada acima.

Node.js (fetch nativo)

easyhost-node.js
const BASE_URL = "https://mescwrzkwjwtyjouueqq.supabase.co/functions/v1";
const API_KEY = process.env.EASYHOST_API_KEY || "sk_live_your_key";

async function createPayment(body) {
  const res = await fetch(`${BASE_URL}/payment-orchestrator`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

async function createSession(body) {
  const res = await fetch(`${BASE_URL}/payment-session`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

async function getWalletBalance(currency = "MZN") {
  const res = await fetch(`${BASE_URL}/wallet-balance?currency=${currency}`, {
    method: "GET",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Accept": "application/json",
    },
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

// Uso
(async () => {
  const payment = await createPayment({
    merchant_id: "mrc_887766",
    wallet_id: "wlt_554433",
    api_key: "sk_live_xxxxxx",
    amount: 1500,
    currency: "MZN",
    payment_method: "mpesa",
    customer: { name: "João Silva", phone: "258840000000" },
    external_reference: "ORD-001",
  });
  console.log(payment.id, payment.status);

  const balance = await getWalletBalance("MZN");
  console.log(balance.wallets);
})();

Python (requests)

easyhost-python.py
import os
import requests

BASE_URL = "https://mescwrzkwjwtyjouueqq.supabase.co/functions/v1"
API_KEY = os.getenv("EASYHOST_API_KEY", "sk_live_your_key")
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
}

def create_payment(body: dict) -> dict:
    url = f"{BASE_URL}/payment-orchestrator"
    r = requests.post(url, json=body, headers=HEADERS, timeout=30)
    r.raise_for_status()
    return r.json()

def create_session(body: dict) -> dict:
    url = f"{BASE_URL}/payment-session"
    r = requests.post(url, json=body, headers=HEADERS, timeout=30)
    r.raise_for_status()
    return r.json()

def get_wallet_balance(currency: str = "MZN") -> dict:
    url = f"{BASE_URL}/wallet-balance"
    params = {"currency": currency}
    r = requests.get(url, headers=HEADERS, params=params, timeout=30)
    r.raise_for_status()
    return r.json()

# Uso
if __name__ == "__main__":
    payment = create_payment({
        "merchant_id": "mrc_887766",
        "wallet_id": "wlt_554433",
        "api_key": "sk_live_xxxxxx",
        "amount": 1500,
        "currency": "MZN",
        "payment_method": "mpesa",
        "customer": {"name": "João Silva", "phone": "258840000000"},
        "external_reference": "ORD-001",
    })
    print(payment["id"], payment["status"])

    balance = get_wallet_balance("MZN")
    print(balance["wallets"])

⚡ Instalação rápida

Node: nativo (fetch). Python: pip install requests.

✓ Variáveis de ambiente

Use EASYHOST_API_KEY para não hardcodear a chave. O fallback usa o valor do Console de Testes.

Clientes

#

Gerencie o cadastro de clientes para automatizar cobranças recorrentes e manter um histórico detalhado.

Criar Cliente
curl -X POST https://mescwrzkwjwtyjouueqq.supabase.co/functions/v1/customers \
  -H "Authorization: Bearer sk_live_your_key" \
  -d '{
    "merchant_id": "mrc_887766",
    "wallet_id": "wlt_554433",
    "api_key": "sk_live_xxxxxx",
    "full_name": "Maria Santos",
    "email": "maria@exemplo.com",
    "phone": "258820000000"
  }'

Precisa de ajuda com a integração?

Nossa equipe de engenharia está pronta para ajudar você a colocar sua solução no ar em menos de 30 minutos.

Fale Connosco