using System.Net.Http.Json;
using System.Text.Json;
namespace Beconnect.PaymentService.Services;
///
/// Cliente para a API eMola (Mozambique).
///
/// Documentação eMola: https://developer.emola.co.mz
/// Implementação baseada no fluxo USSD push:
/// 1. POST /v1/pushTransaction → inicia cobrança no telemóvel do cliente
/// 2. GET /v1/transaction/{ref} → polling do estado da transação
///
/// Variáveis de ambiente necessárias:
/// EMOLA_API_URL, EMOLA_API_KEY, EMOLA_MERCHANT_NUMBER
///
public class EMolaService
{
private readonly HttpClient _http;
private readonly ILogger _logger;
private readonly string _merchantNumber;
public EMolaService(HttpClient http, ILogger logger, IConfiguration config)
{
_http = http;
_logger = logger;
_merchantNumber = config["EMola:MerchantNumber"] ?? throw new InvalidOperationException("EMola:MerchantNumber not configured");
}
///
/// Inicia uma cobrança USSD push. O cliente recebe notificação no telemóvel.
///
public async Task InitiatePushAsync(decimal amount, string customerPhone, string reference)
{
_logger.LogInformation("eMola push: {Amount} MT → {Phone} [ref={Ref}]", amount, customerPhone, reference);
var payload = new
{
amount = (int)Math.Round(amount), // eMola aceita inteiros (centavos implícitos)
customerMsisdn = customerPhone,
merchantMsisdn = _merchantNumber,
reference,
serviceProviderCode = _merchantNumber,
};
var response = await _http.PostAsJsonAsync("/v1/pushTransaction", payload);
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
_logger.LogError("eMola push failed [{Status}]: {Body}", response.StatusCode, body);
return new EMolaPushResult { Success = false, ErrorMessage = $"eMola error {response.StatusCode}: {body}" };
}
using var doc = JsonDocument.Parse(body);
var root = doc.RootElement;
return new EMolaPushResult
{
Success = true,
ExternalRef = root.TryGetProperty("transactionReference", out var r) ? r.GetString() : reference,
Status = root.TryGetProperty("responseCode", out var s) ? s.GetString() : "pending",
};
}
///
/// Verifica o estado de uma transação previamente iniciada.
///
public async Task CheckStatusAsync(string externalRef)
{
var response = await _http.GetAsync($"/v1/transaction/{Uri.EscapeDataString(externalRef)}");
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
return new EMolaStatusResult { Status = "unknown", Paid = false };
}
using var doc = JsonDocument.Parse(body);
var root = doc.RootElement;
var statusCode = root.TryGetProperty("responseCode", out var c) ? c.GetString() : null;
// eMola: "INS-0" = sucesso, qualquer outro = pendente/falha
return new EMolaStatusResult
{
Status = statusCode == "INS-0" ? "paid" : "pending",
Paid = statusCode == "INS-0",
Raw = body,
};
}
}
public record EMolaPushResult
{
public bool Success { get; init; }
public string? ExternalRef { get; init; }
public string? Status { get; init; }
public string? ErrorMessage { get; init; }
}
public record EMolaStatusResult
{
public string Status { get; init; } = "pending";
public bool Paid { get; init; }
public string? Raw { get; init; }
}